1 static const char version[] = "$Id$";
2
3 /*
4 * Copyright 2002, Meiosys SA (www.meiosys.com). All rights reserved.
5 *
6 * See the COPYING file for the terms of usage and distribution.
7 */
8
9 /* domnode - simple XML DOM-like interface
10 * Copyright (c) 2002 Michael B. Allen <mballen@erols.com>
11 *
12 * The MIT License
13 *
14 * Permission is hereby granted, free of charge, to any person obtaining a
15 * copy of this software and associated documentation files (the "Software"),
16 * to deal in the Software without restriction, including without limitation
17 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
18 * and/or sell copies of the Software, and to permit persons to whom the
19 * Software is furnished to do so, subject to the following conditions:
20 *
21 * The above copyright notice and this permission notice shall be included
22 * in all copies or substantial portions of the Software.
23 *
24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
27 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
28 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
29 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
30 * OTHER DEALINGS IN THE SOFTWARE.
31 */
32
33 #ifdef HAVE_CONFIG_H
34 #include "config.h"
35 #endif
36
37 #include <sd/domnode.h>
38 #include <sd/stack.h>
39 #include <sd/malloc.h>
40 #include <sd/error.h>
41
42 #include <stdlib.h>
43 #include <string.h>
44 #include <ctype.h>
45
46 #ifdef HAVE_LANGINFO_H
47 # include <langinfo.h>
48 #endif
49
50 #include <assert.h>
51 #include <expat.h>
52
53 struct udata {
54 char cdata[2048];
55 size_t wptr;
56 sd_stack_t* elements;
57 sd_domnode_t* root;
58 };
59
60 /******************************************************************************/
udata_new(void)61 static struct udata* udata_new(void)
62 {
63 struct udata* this;
64
65 this = sd_calloc(1, sizeof(*this));
66
67 this->cdata[0] = 0;
68 this->wptr = 0;
69 this->elements = sd_stack_new(0);
70 this->root = NULL;
71
72 return this;
73 }
74
75 /******************************************************************************/
udata_delete(struct udata * this)76 static void udata_delete(struct udata* this)
77 {
78 if (!this)
79 return;
80
81 sd_stack_delete(this->elements, NULL);
82 free(this);
83 }
84
85 /******************************************************************************/
udata_pop_cdata(struct udata * this)86 static void udata_pop_cdata(struct udata* this)
87 {
88 this->wptr = 0;
89 memset(this->cdata, 0, sizeof(this->cdata));
90 }
91
92 /******************************************************************************/
udata_push_cdata(struct udata * this,const XML_Char * s,int len)93 static int udata_push_cdata(struct udata* this, const XML_Char* s, int len)
94 {
95 XML_Char *str;
96
97 if (!this || !s || !len)
98 return -1;
99
100 str = (XML_Char *)s + len;
101 while (s < str && isspace(*s)) {
102 s++;
103 len--;
104 }
105
106 if (s == str) return -1;
107
108 str = (XML_Char *)s + len - 1;
109 while (str > s && isspace(*str)) {
110 str--;
111 len--;
112 }
113
114 if ((this->wptr + len) >= sizeof(this->cdata) - 1){
115 sd_error("cdata buffer exceeded (maximum %d bytes)\n",
116 sizeof(this->cdata));
117 return -1;
118 }
119
120 strncpy(&this->cdata[this->wptr], s, len);
121 this->wptr += len;
122 this->cdata[this->wptr] = 0;
123
124 return 0;
125 }
126
127 /******************************************************************************/
foreach_delete(sd_domnode_t * anode,void * unused)128 static int foreach_delete(sd_domnode_t* anode, void* unused)
129 {
130 sd_domnode_delete(anode);
131 return 0;
132 }
133
134 /******************************************************************************/
135 extern sd_domnode_t*
__sd_domnode_new(const char * name,const char * value,int is_elem)136 __sd_domnode_new(const char* name, const char* value, int is_elem)
137 {
138 sd_domnode_t* this;
139
140 this = sd_calloc(1, sizeof(*this));
141
142 this->name = name ? sd_strdup(name) : NULL;
143 this->value = value ? sd_strdup(value): NULL;
144 this->children = is_elem ? sd_list_new(10) : NULL;
145 this->attrs = is_elem ? sd_list_new(10) : NULL;
146
147 return this;
148 }
149
150 /******************************************************************************/
sd_domnode_new(const char * name,const char * value)151 extern sd_domnode_t* sd_domnode_new(const char* name, const char* value)
152 {
153 return __sd_domnode_new(name, value, 1);
154 }
155
156 /******************************************************************************/
sd_domnode_delete(sd_domnode_t * this)157 extern void sd_domnode_delete(sd_domnode_t* this)
158 {
159 if (!this)
160 return;
161
162 free((void*) this->name);
163 free((void*) this->value);
164
165 sd_list_foreach(this->children, (sd_list_func_t) foreach_delete, NULL);
166 sd_list_delete(this->children);
167
168 sd_list_foreach(this->attrs, (sd_list_func_t) foreach_delete, NULL);
169 sd_list_delete(this->attrs);
170
171 free(this);
172 }
173
174 /******************************************************************************/
175 static void
start_handler(struct udata * udata,const XML_Char * name,const XML_Char ** atts)176 start_handler(struct udata* udata, const XML_Char* name, const XML_Char** atts)
177 {
178 sd_domnode_t* parent;
179 sd_domnode_t* child;
180 int i;
181
182 if (!udata || !name || !atts)
183 return;
184
185 child = __sd_domnode_new(name, NULL, 1);
186
187 for (i = 0; atts[i]; i += 2)
188 sd_list_add(child->attrs, __sd_domnode_new(atts[i], atts[i + 1], 0));
189
190 udata_pop_cdata(udata);
191
192 /* root node has no parent. save it. */
193 if ( (parent = sd_stack_peek(udata->elements)) == NULL)
194 udata->root = child;
195 else
196 sd_list_add(parent->children, child);
197
198 sd_stack_push(udata->elements, child);
199 }
200
201 /******************************************************************************/
end_handler(struct udata * udata,const XML_Char * name)202 static void end_handler(struct udata* udata, const XML_Char* name)
203 {
204 udata_pop_cdata(udata);
205 sd_stack_pop(udata->elements);
206 }
207
208 /******************************************************************************/
cdata_handler(struct udata * udata,const XML_Char * s,int len)209 static void cdata_handler(struct udata* udata, const XML_Char* s, int len)
210 {
211 sd_domnode_t* parent = sd_stack_peek(udata->elements);
212
213 assert(parent != NULL);
214
215 if (udata_push_cdata(udata, s, len) == -1)
216 return;
217
218 free((void*) parent->value);
219 parent->value = strdup(udata->cdata);
220 }
221
222 /******************************************************************************/
comment_handler(struct udata * udata,const XML_Char * s)223 static void comment_handler(struct udata* udata, const XML_Char* s)
224 {
225 sd_domnode_t* parent = sd_stack_peek(udata->elements);
226
227 assert(parent != NULL);
228
229 sd_list_add(parent->children, __sd_domnode_new("#comment", s, 0));
230 }
231
232 /******************************************************************************/
sd_domnode_fread(sd_domnode_t * this,FILE * stream)233 extern int sd_domnode_fread(sd_domnode_t* this, FILE* stream)
234 {
235 XML_Parser p;
236 struct udata* udata;
237 int ret = 0;
238
239 if (!this || !stream)
240 return -1;
241
242 if ((p = XML_ParserCreate(NULL)) == NULL)
243 return -1;
244
245 udata = udata_new();
246
247 XML_SetStartElementHandler (p, (XML_StartElementHandler) start_handler);
248 XML_SetEndElementHandler (p, (XML_EndElementHandler) end_handler);
249 XML_SetCharacterDataHandler (p, (XML_CharacterDataHandler) cdata_handler);
250 XML_SetCommentHandler (p, (XML_CommentHandler) comment_handler);
251 XML_SetUserData (p, udata);
252
253 for ( ;; ) {
254 size_t n;
255 void* buf;
256 int done;
257
258 if ((buf = XML_GetBuffer(p, BUFSIZ)) == NULL) {
259 ret = -1;
260 break;
261 }
262
263 if ((n = fread(buf, 1, BUFSIZ, stream)) == 0 && ferror(stream)) {
264 ret = -1;
265 break;
266 }
267
268 if (!XML_ParseBuffer(p, n, (done = feof(stream)))) {
269 sd_error("XML error: %s [%d:%d - %ld]\n",
270 XML_ErrorString(XML_GetErrorCode(p)),
271 XML_GetCurrentLineNumber(p),
272 XML_GetCurrentColumnNumber(p),
273 XML_GetCurrentByteIndex(p));
274 ret = -1;
275 break;
276 }
277
278 if (done)
279 break;
280 }
281
282 if (udata->root)
283 {
284 free((void*) this->name);
285 free((void*) this->value);
286 sd_list_foreach(this->children, (sd_list_func_t) foreach_delete, NULL);
287 sd_list_delete(this->children);
288 sd_list_foreach(this->attrs, (sd_list_func_t) foreach_delete, NULL);
289 sd_list_delete(this->attrs);
290
291 this->name = udata->root->name;
292 this->value = udata->root->value;
293 this->children = udata->root->children;
294 this->attrs = udata->root->attrs;
295
296 free(udata->root);
297 udata->root = NULL;
298 }
299
300 udata_delete(udata);
301 XML_ParserFree(p);
302 return ret;
303 }
304
305 /******************************************************************************/
306 extern int
sd_domnode_read(sd_domnode_t * this,const char * abuffer,size_t asize)307 sd_domnode_read(sd_domnode_t* this, const char* abuffer, size_t asize)
308 {
309 XML_Parser p;
310 struct udata* udata;
311 int ret = 0;
312
313 if (!this)
314 return -1;
315
316 if ((p = XML_ParserCreate(NULL)) == NULL)
317 return -1;
318
319 udata = udata_new();
320
321 XML_SetStartElementHandler (p, (XML_StartElementHandler) start_handler);
322 XML_SetEndElementHandler (p, (XML_EndElementHandler) end_handler);
323 XML_SetCharacterDataHandler (p, (XML_CharacterDataHandler) cdata_handler);
324 XML_SetCommentHandler (p, (XML_CommentHandler) comment_handler);
325 XML_SetUserData (p, udata);
326
327 if (!XML_Parse(p, abuffer, asize, 1)) {
328 sd_error("XML error: %s [%d:%d - %ld]\n",
329 XML_ErrorString(XML_GetErrorCode(p)),
330 XML_GetCurrentLineNumber(p),
331 XML_GetCurrentColumnNumber(p),
332 XML_GetCurrentByteIndex(p));
333 ret = -1;
334 }
335
336 if (udata->root)
337 {
338 free((void*) this->name);
339 free((void*) this->value);
340 sd_list_foreach(this->children, (sd_list_func_t) foreach_delete, NULL);
341 sd_list_delete(this->children);
342 sd_list_foreach(this->attrs, (sd_list_func_t) foreach_delete, NULL);
343 sd_list_delete(this->attrs);
344
345 this->name = udata->root->name;
346 this->value = udata->root->value;
347 this->children = udata->root->children;
348 this->attrs = udata->root->attrs;
349
350 free(udata->root);
351 udata->root = NULL;
352 }
353
354 udata_delete(udata);
355 XML_ParserFree(p);
356 return ret;
357 }
358
359 /******************************************************************************/
_sd_domnode_fwrite(const sd_domnode_t * this,FILE * stream,int indent)360 static int _sd_domnode_fwrite(const sd_domnode_t* this, FILE* stream, int indent)
361 {
362 sd_list_iter_t* iter;
363 int i;
364
365 if (!this || !this->name || !stream)
366 return -1;
367
368 for (i = 0; i < indent; i++)
369 fprintf(stream, " ");
370
371 if (this->name && strcmp(this->name, "#comment") == 0) {
372 fprintf(stream, "<!-- %s -->\n", this->value);
373 return 0;
374 }
375
376 fprintf(stream, "<%s", this->name);
377
378 for (iter = sd_list_begin(this->attrs); iter != sd_list_end(this->attrs);
379 iter = sd_list_iter_next(iter)) {
380 sd_domnode_t* node = iter->data;
381
382 fprintf(stream, " %s=\"%s\"", node->name, node->value);
383 }
384
385 if (this->value || sd_list_get_nelem(this->children)) {
386 fprintf(stream, ">\n");
387
388 if (this->value) {
389 for (i = 0; i < indent + 1; i++)
390 fprintf(stream, " ");
391 fprintf(stream, "%s\n", this->value);
392 }
393
394 for (iter = sd_list_begin(this->children); iter != sd_list_end(this->children);
395 iter = sd_list_iter_next(iter)) {
396 sd_domnode_t* node = iter->data;
397
398 if (_sd_domnode_fwrite(node, stream, indent + 1) == -1)
399 return -1;
400 }
401
402 for (i = 0; i < indent; i++)
403 fprintf(stream, " ");
404 fprintf(stream, "</%s>\n", this->name);
405 } else {
406 fprintf(stream, "/>\n");
407 }
408
409 return 0;
410 }
411
412 /******************************************************************************/
sd_domnode_fwrite(const sd_domnode_t * this,FILE * stream)413 extern int sd_domnode_fwrite(const sd_domnode_t* this, FILE* stream)
414 {
415 #ifdef HAVE_NL_LANGINFO
416 fprintf(stream, "<?xml version=\"1.0\" encoding=\"%s\"?>\n\n",
417 nl_langinfo(CODESET));
418 #else
419 fprintf(stream, "<?xml version=\"1.0\"?>\n\n");
420 #endif
421
422 return _sd_domnode_fwrite(this, stream, 0);
423 }
424
425 /******************************************************************************/
sd_domnode_load(sd_domnode_t * this,const char * afilename)426 extern int sd_domnode_load(sd_domnode_t* this, const char* afilename)
427 {
428 FILE* fp;
429 int ret = 0;
430
431 if ( (fp = fopen(afilename, "r")) == NULL)
432 return -1;
433
434 ret = sd_domnode_fread(this, fp);
435
436 fclose(fp);
437 return ret;
438 }
439
440 /******************************************************************************/
sd_domnode_store(const sd_domnode_t * this,const char * afilename)441 extern int sd_domnode_store(const sd_domnode_t* this, const char* afilename)
442 {
443 FILE* fp;
444 int ret = 0;
445
446 if ( (fp = fopen(afilename, "w")) == NULL)
447 return -1;
448
449 ret = sd_domnode_fwrite(this, fp);
450
451 fclose(fp);
452 return ret;
453 }
454
455 /******************************************************************************/
456 extern sd_domnode_t*
sd_domnode_search(const sd_domnode_t * this,const char * name)457 sd_domnode_search(const sd_domnode_t* this, const char* name)
458 {
459 sd_list_iter_t* i;
460
461 for (i = sd_list_begin(this->children); i != sd_list_end(this->children);
462 i = sd_list_iter_next(i)) {
463 sd_domnode_t* node = i->data;
464
465 if (strcmp(node->name, name) == 0)
466 return node;
467 }
468
469 for (i = sd_list_begin(this->attrs); i != sd_list_end(this->attrs);
470 i = sd_list_iter_next(i)) {
471 sd_domnode_t* node = i->data;
472
473 if (strcmp(node->name, name) == 0)
474 return node;
475 }
476
477 for (i = sd_list_begin(this->children); i != sd_list_end(this->children);
478 i = sd_list_iter_next(i)) {
479 sd_domnode_t* node = i->data;
480
481 if ((node = sd_domnode_search(node, name)) != NULL)
482 return node;
483 }
484
485 return NULL;
486 }
487
488 /******************************************************************************/
489 extern sd_domnode_t*
sd_domnode_attrs_put(sd_domnode_t * anode,sd_domnode_t * attr)490 sd_domnode_attrs_put(sd_domnode_t* anode, sd_domnode_t* attr)
491 {
492 sd_list_iter_t* i;
493
494 if (!anode || !anode->attrs || !attr || !attr->value)
495 return NULL;
496
497 if ( (i = sd_list_lookadd(anode->attrs, attr)) == sd_list_end(anode->attrs))
498 return NULL;
499
500 return i->data;
501 }
502
503 /******************************************************************************/
504 extern sd_domnode_t*
sd_domnode_attrs_get(const sd_domnode_t * anode,const char * name)505 sd_domnode_attrs_get(const sd_domnode_t* anode, const char* name)
506 {
507 sd_list_iter_t* i;
508
509 if (!anode || !anode->attrs || !name || !*name)
510 return NULL;
511
512 for (i = sd_list_begin(anode->attrs); i != sd_list_end(anode->attrs);
513 i = sd_list_iter_next(i)) {
514 sd_domnode_t* node = i->data;
515
516 if (strcmp(node->name, name) == 0)
517 return node;
518 }
519
520 return NULL;
521 }
522
523 /******************************************************************************/
524 extern sd_domnode_t*
sd_domnode_attrs_remove(sd_domnode_t * anode,const char * name)525 sd_domnode_attrs_remove(sd_domnode_t* anode, const char* name)
526 {
527 sd_list_iter_t* i;
528
529 if (!anode || !anode->attrs || !name || !*name)
530 return NULL;
531
532 for (i = sd_list_begin(anode->attrs); i != sd_list_end(anode->attrs);
533 i = sd_list_iter_next(i)) {
534 sd_domnode_t* node = i->data;
535
536 if (strcmp(node->name, name) == 0) {
537 sd_list_iter_del(i);
538 return node;
539 }
540 }
541
542 return NULL;
543 }
544