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