1 /*****************************************************************************
2  *
3  * FILE:	sxml.c
4  * DESCRIPTION:	Skimpy XML Parser/Grafter Library
5  * DATE:	Wed, Sep  8 2004
6  * UPDATED:	Fri, Mar 26 2010
7  * AUTHOR:	Kouichi ABE (WALL) / ��������
8  * E-MAIL:	kouichi@MysticWALL.COM
9  * URL:		http://www.MysticWALL.COM/
10  * COPYRIGHT:	(c) 2004-2010 �������졿Kouichi ABE (WALL), All rights reserved.
11  * LICENSE:
12  *
13  *  Copyright (c) 2004-2010 Kouichi ABE (WALL) <kouichi@MysticWALL.COM>,
14  *  All rights reserved.
15  *
16  *  Redistribution and use in source and binary forms, with or without
17  *  modification, are permitted provided that the following conditions
18  *  are met:
19  *
20  *   1. Redistributions of source code must retain the above copyright
21  *      notice, this list of conditions and the following disclaimer.
22  *   2. Redistributions in binary form must reproduce the above copyright
23  *      notice, this list of conditions and the following disclaimer in the
24  *      documentation and/or other materials provided with the distribution.
25  *
26  *   THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
27  *   ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28  *   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29  *   ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
30  *   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31  *   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32  *   OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33  *   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  *   LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35  *   OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36  *   SUCH DAMAGE.
37  *
38  * $Id: sxml.c,v 1.9 2010/03/26 07:56:17 kouichi Exp $
39  *
40  *****************************************************************************/
41 
42 #if	HAVE_CONFIG_H
43 #include "config.h"
44 #endif	/* HAVE_CONFIG_H */
45 
46 #include <stdio.h>
47 #if	HAVE_STDLIB_H
48 #include <stdlib.h>
49 #endif	/* HAVE_STDLIB_H */
50 #if	HAVE_STRING_H
51 #include <string.h>
52 #endif	/* HAVE_STRING_H */
53 #if	HAVE_SYS_TYPES_H
54 #include <sys/types.h>
55 #endif	/* HAVE_SYS_TYPES_H */
56 #include <sys/stat.h>
57 #include <sys/mman.h>
58 #include "sxml.h"
59 
60 /******************************************************************************
61  *
62  *	Macros and structures definition
63  *
64  *****************************************************************************/
65 #ifndef	_BOOL_T
66 #define	_BOOL_T
67 typedef enum {
68   false	= 0,
69   true	= 1
70 } bool;
71 #endif	/* _BOOL_T */
72 
73 #define	EOL	'\0'
74 #define	HTAB	'\011'
75 #define	LF	'\012'
76 #define	VTAB	'\013'
77 #define	NP	'\014'
78 #define	CR	'\015'
79 #define	SPC	'\040'
80 
81 /******************************************************************************
82  *
83  *	Local functions declaration
84  *
85  *****************************************************************************/
86 static char *		strxsep(char **, const char *);
87 static int		whitespace(const char *);
88 static char *		mkstr(const char *, const char *);
89 static sxml_node_t *	create_new_node(sxml_node_t *);
90 static void		delete_node(sxml_node_t *);
91 static sxml_attr_t *	set_attribute(sxml_attr_t *, const char *,
92 				      const char *);
93 static int		set_element(sxml_element_t *, char *);
94 static void		delete_element(sxml_element_t *);
95 static int		parse(sxml_node_t *, const char *, size_t);
96 static int		mkval(sxml_node_t * node, const char * vp,
97 			      const char * p);
98 
99 static void		print_node(sxml_node_t *, int, FILE *);
100 
101 /******************************************************************************
102  *
103  *	Functions definition
104  *
105  *****************************************************************************/
106 static char *
strxsep(stringp,delim)107 strxsep(stringp, delim)
108 	register char **	stringp;
109 	register const char *	delim;
110 {
111   register char *	s;
112   register const char *	spanp;
113   register int		c;
114   register int		sc;
115   char *		tok;
116 
117   if ((s = *stringp) == NULL) {
118     return NULL;
119   }
120   for (tok = s; ; ) {
121     c	  = *s++;
122     spanp = delim;
123     if (c == '"') {
124       register char *	p;
125 
126       for (p = s; *s != '"'; s++) {
127 	if (*s == '\0') {
128 	  s = p;	/* reset */
129 	  break;
130 	}
131       }
132       if (*s == '"') {
133 	c = *s++;
134       }
135     }
136     do {
137       if ((sc = *spanp++) == c) {
138 	if (c == '\0') {
139 	  s = NULL;
140 	}
141 	else {
142 	  s[-1] = '\0';
143 	}
144 	*stringp = s;
145 	return tok;
146       }
147     } while (sc != '\0');
148   }
149   /* NOTREACHED */
150 }
151 
152 static int
whitespace(s)153 whitespace(s)
154 	const char *	s;
155 {
156   int	c = 0;
157 
158   while (*s == SPC || *s == HTAB || *s == VTAB || *s == CR || *s == LF ||
159 	 *s == NP) {
160     c++;
161     s++;
162   }
163 
164   return c;
165 }
166 
167 static char *
mkstr(sp,ep)168 mkstr(sp, ep)
169 	const char *	sp;
170 	const char *	ep;
171 {
172   if (sp != NULL && ep != NULL) {
173     size_t	len;
174 
175     len = ep - sp;
176     if (len > 0) {
177       char *	new;
178 
179       /* strip white space of string end */
180       while (*(sp + len - 1) == CR   || *(sp + len - 1) == LF ||
181 	     *(sp + len - 1) == SPC  ||
182 	     *(sp + len - 1) == HTAB || *(sp + len - 1) == VTAB) {
183 	len--;
184       }
185       new = (char *)calloc(len + 1, sizeof(char));
186       if (new != NULL) {
187 	memcpy(new, sp, len);
188       }
189       return new;
190     }
191   }
192 
193   return NULL;
194 }
195 
196 static sxml_node_t *
create_new_node(parent)197 create_new_node(parent)
198 	sxml_node_t *	parent;
199 {
200   sxml_node_t *	new;
201 
202   new = (sxml_node_t *)calloc(1, sizeof(sxml_node_t));
203   if (new != NULL) {
204     new->parent	    = parent;
205     new->child	    = NULL;	/* first child */
206     new->last_child = NULL;	/* last child */
207     new->next	    = NULL;	/* sibling */
208     new->prev	    = NULL;	/* sibling */
209     new->type	    = SXML_ELEMENT;
210   }
211 
212   return new;
213 }
214 
215 static void
delete_node(node)216 delete_node(node)
217 	sxml_node_t *	node;
218 {
219   register sxml_node_t *	np;
220   register sxml_node_t *	np_next;
221 
222   for (np = node; np != NULL; np = np_next) {
223     np_next = np->next;
224     switch (np->type) {
225       case SXML_PROLOG:
226 	delete_element(&np->value.element);
227 	break;
228       case SXML_ELEMENT:
229 	delete_element(&np->value.element);
230 	break;
231       case SXML_CONTENT:
232 	free((char *)np->value.content);	np->value.content = NULL;
233       default:
234 	break;
235     }
236     if (np->child != NULL)  { delete_node(np->child); }
237     free(np); np = NULL;
238   }
239 }
240 
241 static sxml_attr_t *
set_attribute(ap,name,value)242 set_attribute(ap, name, value)
243 	sxml_attr_t *	ap;
244 	const char *	name;
245 	const char *	value;
246 {
247   sxml_attr_t *	new;
248 
249   new = (sxml_attr_t *)malloc(sizeof(sxml_attr_t));
250   if (new != NULL) {
251     new->name	= strdup(name);
252     new->value	= strdup(value);
253     new->next	= ap;
254   }
255 
256   return new;
257 }
258 
259 static int
set_element(e,s)260 set_element(e, s)
261 	sxml_element_t *	e;	/* element */
262 	char *			s;	/* element string */
263 {
264   static const char	sep[] = " \t\r\n";
265   char *		name;
266   register char *	attr;
267 
268   name	   = strxsep(&s, sep);
269   e->name  = strdup(name);
270   e->attrs = NULL;
271 
272   for (attr = strxsep(&s, sep); attr != NULL; attr = strxsep(&s, sep)) {
273     register char *	p;
274     register char *	q;
275 
276     if (*attr == EOL) { continue; }
277     p = strchr(attr, '=');
278     if (p) {
279       *p = EOL;
280       p += 2;
281       q	 = strchr(p, '"');
282       if (q != NULL) {
283 	*q = EOL;
284 	e->attrs = set_attribute(e->attrs, attr, p);
285       }
286     }
287   }
288 
289   return 0;
290 }
291 
292 static void
delete_element(e)293 delete_element(e)
294 	sxml_element_t *	e;
295 {
296   free((char *)e->name);	e->name = NULL;
297   if (e->attrs != NULL) {
298     register sxml_attr_t *	ap;
299     register sxml_attr_t *	ap_next;
300 
301     for (ap = e->attrs; ap != NULL; ap = ap_next) {
302       ap_next = ap->next;
303       free((char *)ap->name);	ap->name  = NULL;
304       free((char *)ap->value);	ap->value = NULL;
305       free((sxml_attr_t *)ap);	ap	  = NULL;
306     }
307   }
308 }
309 
310 static int
parse(vertex,text,size)311 parse(vertex, text, size)
312 	sxml_node_t *	vertex;
313 	const char *	text;
314 	size_t		size;
315 {
316   typedef enum {
317     ST_INIT,
318     ST_TAG_BEGIN,
319     ST_TAG_END,
320     ST_PROLOG_BEGIN,
321     ST_PROLOG_END,
322     ST_COMMENT_BEGIN,
323     ST_COMMENT_S1,
324     ST_COMMENT,
325     ST_COMMENT_S2,
326     ST_COMMENT_END,
327     ST_CTEXT,
328     ST_CDATA,
329     ST_ELEMENT,
330     ST_ATTRIBUTE,
331     ST_VALUE
332   } state_t;
333   const char *	p	= text;
334   const char *	vp	= NULL;	/* start position of value */
335   state_t	state	= ST_INIT;
336   sxml_node_t *	node	= vertex;	/* current node */
337   const char *	endp	= text + size;
338   static bool	prolog	= false;
339 
340   while (p < endp) {
341     p += whitespace(p);
342     switch (state) {
343       case ST_INIT:
344 	if	(*p == '<') { state = ST_TAG_BEGIN; }
345 	else if (p == endp) { return  0; }
346 	else		    { return -1; }
347 	break;
348       case ST_TAG_BEGIN:
349 	if	(*p == '?') { state = ST_PROLOG_BEGIN; }
350 	else if (*p == '!') { state = ST_COMMENT_BEGIN; }
351 	else if (*p == '/') { state = ST_TAG_END; }
352 	else		    { state = ST_ELEMENT; p--; }
353 	break;
354       case ST_TAG_END:
355 	if (*p == '>')	{ state = ST_INIT;
356 			  if (node->parent != NULL) { node = node->parent; }
357 			}
358 	break;
359       case ST_PROLOG_BEGIN:
360 	if (*p < 'a' || *p > 'z') { return -2; }
361 	else			  { state = ST_ELEMENT; p--; continue; }
362 	break;
363       case ST_PROLOG_END:
364 	if (*p == '>')	{ state = ST_INIT; prolog = false; }
365 	else		{ return -3; }
366 	break;
367       case ST_COMMENT_BEGIN:
368 	if (*p == '-')			 { state = ST_COMMENT_S1; }
369 	else if (*p >= 'A' || *p <= 'Z') { state = ST_CTEXT; }
370 	else				 { return -4; }
371 	break;
372       case ST_COMMENT_S1:
373 	if (*p == '-')	{ state = ST_COMMENT; }
374 	else		{ return -5; }
375 	break;
376       case ST_COMMENT:
377 	if (*p == '-') { state = ST_COMMENT_S2; }
378 	break;
379       case ST_COMMENT_S2:
380 	if (*p == '-')	{ state = ST_COMMENT_END; }
381 	else		{ state = ST_COMMENT; }
382 	break;
383       case ST_COMMENT_END:
384 	if (*p == '>')	{ state = ST_INIT; }
385 	else		{ state = ST_COMMENT; }
386 	break;
387       case ST_CTEXT:
388 	if (*p == '>')	{ state = ST_INIT; }
389 	break;
390       case ST_ELEMENT: {
391 	  state_t	nest1 = ST_ELEMENT;
392 	  const char *	sp    = p;	/* start position of element */
393 	  char *	s;		/* element with attributes */
394 
395 	  while (p < endp) {
396 	    switch (nest1) {
397 	      case ST_ELEMENT:
398 		if	(*p == '"')  { nest1 = ST_ATTRIBUTE; }
399 		else if (*p == '>')  { state = ST_VALUE; vp = NULL; goto end; }
400 		else if (*p == '/')  { state = ST_TAG_END; goto end; }
401 		else if (*p == '?') {
402 		  if (prolog)	{  state = ST_PROLOG_END; goto end; }
403 		  else		{ prolog = true; sp = ++p; /* remove '?' */ }
404 		}
405 		break;
406 	      case ST_ATTRIBUTE:
407 		if (*p == '"') { nest1 = ST_ELEMENT; }
408 		break;
409 	      default:
410 		break;
411 	    }
412 	    p++;
413 	  }
414 	end:
415 	  s = mkstr(sp, p);
416 	  if (s != NULL) {
417 	    sxml_node_t *	n;	/* new node */
418 
419 	    n = create_new_node(node);	if (n == NULL) { return -6; }
420 	    set_element(&n->value.element, s);
421 	    if (node->child != NULL) {
422 	      node->last_child->next = n;
423 	      n->prev		     = node->last_child;
424 	      node->last_child	     = n;
425 	    }
426 	    else {
427 	      node->last_child = node->child = n;
428 	    }
429 	    if (prolog) {	/* prolog has no children */
430 	      n->type = SXML_PROLOG;
431 	    }
432 	    else {
433 	      node = n;	/* set new node to current node */
434 	    }
435 	    free(s);	s = NULL;
436 	  }
437 	}
438 	break;
439       case ST_VALUE:
440 	if (vp == NULL) { vp = p; }
441 	if (strncmp(p, "<![CDATA[", 9) == 0) {
442 	  const char *	src = p + 9;
443 	  char *	dst = (char *)p;
444 	  size_t	len = endp - src;
445 
446 	  dst	= memmove(dst, src, len);
447 	  p	= dst - 1;	/* XXX: */
448 	  endp -= 9;
449 	  state	= ST_CDATA;
450 	}
451 	else if (*p == '<') {
452 	  if (mkval(node, vp, p) == 0)	{ state = ST_TAG_BEGIN; }
453 	  else				{ return -7; }
454 	}
455 	break;
456       case ST_CDATA:
457 	if (strncmp(p, "]]>", 3) == 0) {
458 	  const char *	src = p + 3;
459 	  char *	dst = (char *)p;
460 	  size_t	len = endp - src;
461 
462 	  dst	= memmove(dst, src, len);
463 	  p	= dst - 1;	/* XXX: */
464 	  endp -= 3;
465 	  state	= ST_VALUE;
466 	}
467 	break;
468       default:
469 	return -9;
470     }
471     p++;
472   }
473 
474   return 0;
475 }
476 
477 static int
mkval(node,vp,p)478 mkval(node, vp, p)
479 	sxml_node_t *	node;
480 	const char *	vp;
481 	const char *	p;
482 {
483   char *	s;
484 
485   s = mkstr(vp, p);
486   if (s != NULL) {
487     sxml_node_t *	n;	/* new node */
488 
489     n = create_new_node(node);	if (n == NULL) { return -1; }
490     n->type	     = SXML_CONTENT;
491     n->value.content = s;
492     node->child	     = node->last_child = n;
493   }
494 
495   return 0;
496 }
497 
498 /*****************************************************************************/
499 
500 sxml_node_t *
sxml_parse_file(fd)501 sxml_parse_file(fd)
502 	int	fd;	/* XML file descriptor */
503 {
504   sxml_node_t *	root = NULL;	/* root node */
505 
506   if (fd != -1) {
507     struct stat	sbuf;
508 
509     if (fstat(fd, &sbuf) == 0) {
510       void *	md;
511 
512       md = mmap(0, (size_t)sbuf.st_size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd,
513 		(off_t)0);
514       if (md != MAP_FAILED) {
515 	root   = create_new_node(NULL); root->type = SXML_VERTEX;
516 	if (root != NULL) {
517 	  int	status;
518 
519 	  status = parse(root, (const char *)md, (size_t)sbuf.st_size);
520 	  if (status != 0) {
521 #if	DEBUG
522 	    fprintf(stderr, "DEBUG[sxml_parse_file] status=%d\n", status);
523 #endif	/* DEBUG */
524 	    delete_node(root);
525 	    root = NULL;
526 	  }
527 	}
528 	munmap(md, (size_t)sbuf.st_size);
529       }
530     }
531   }
532 
533   return root;
534 }
535 
536 void
sxml_delete_node(node)537 sxml_delete_node(node)
538 	sxml_node_t *	node;
539 {
540   delete_node(node);
541 }
542 
543 sxml_node_t *
sxml_find_prolog(node,name)544 sxml_find_prolog(node, name)
545 	sxml_node_t *	node;	/* start node to find element */
546 	const char *	name;	/* element name */
547 {
548   register sxml_node_t *	np;
549 
550   for (np = node; np != NULL; np = np->next) {
551     if (np->type == SXML_PROLOG) {
552       if (name == NULL) { return np; }	/* first matched */
553       if (strcmp(name, np->value.element.name) == 0) {
554 	return np;
555       }
556     }
557     /* descend child */
558     if (np->child != NULL) {
559       sxml_node_t *	child;
560 
561       child = sxml_find_prolog(np->child, name);
562       if (child != NULL) {
563 	return child;
564       }
565     }
566   }
567 
568   return NULL;
569 }
570 
571 sxml_node_t *
sxml_find_element(node,name,attr,value)572 sxml_find_element(node, name, attr, value)
573 	sxml_node_t *	node;	/* start node to find element */
574 	const char *	name;	/* element name */
575 	const char *	attr;	/* attribute name */
576 	const char *	value;	/* attribute value */
577 {
578   struct found_s {
579     sxml_node_t *	name;
580     sxml_node_t *	attr;
581     sxml_node_t *	value;
582   };
583   register sxml_node_t *	np;
584 
585   for (np = node; np != NULL; np = np->next) {
586     struct found_s	found = { NULL, NULL, NULL };
587 
588     if (np->type == SXML_ELEMENT) {
589       register sxml_attr_t *	ap;
590 
591       if (name != NULL && strcmp(name, np->value.element.name) == 0) {
592 	found.name = np;
593       }
594       for (ap = np->value.element.attrs; ap != NULL; ap = ap->next) {
595 	if (attr != NULL && ap->name && strcmp(attr, ap->name) == 0) {
596 	  found.attr = np;
597 	}
598 	if (value != NULL && ap->value && strcmp(value, ap->value) == 0) {
599 	  found.value = np;
600 	}
601 
602 	if (attr != NULL && value != NULL) {
603 	  if (found.attr != NULL && found.value != NULL) { break; }
604 	}
605 	else if (attr  != NULL) { if (found.attr  != NULL) { break; } }
606 	else if (value != NULL) { if (found.value != NULL) { break; } }
607       }
608 
609       if (name != NULL && attr != NULL && value != NULL) {
610 	if (found.name != NULL && found.attr != NULL && found.value != NULL) {
611 	  return np;
612 	}
613       }
614       else if (name != NULL && attr != NULL) {
615 	if (found.name != NULL && found.attr != NULL) { return np; }
616       }
617       else if (name != NULL && value != NULL) {
618 	if (found.name != NULL && found.value != NULL) { return np; }
619       }
620       else if (attr != NULL && value != NULL) {
621 	if (found.attr != NULL && found.value != NULL) { return np; }
622       }
623       else if (name != NULL) {
624 	if (found.name != NULL) { return np; }
625       }
626       else if (attr != NULL) {
627 	if (found.attr != NULL) { return np; }
628       }
629       else if (value != NULL) {
630 	if (found.value != NULL) { return np; }
631       }
632     }
633 
634     /* descend child */
635     if (np->child != NULL) {
636       sxml_node_t *	child;
637 
638       child = sxml_find_element(np->child, name, attr, value);
639       if (child != NULL) {
640 	return child;
641       }
642     }
643   }
644 
645   return NULL;
646 }
647 
648 const char *
sxml_get_attribute(node,name)649 sxml_get_attribute(node, name)
650 	sxml_node_t *	node;	/* prolog/element node */
651 	const char *	name;	/* name of attribute */
652 {
653   if (node->type == SXML_ELEMENT || node->type == SXML_PROLOG) {
654     if (node->value.element.attrs != NULL) {
655       register sxml_attr_t *	ap;
656 
657       for (ap = node->value.element.attrs; ap != NULL; ap = ap->next) {
658 	if (ap->name != NULL && strcmp(name, ap->name) == 0) {
659 	  return ap->value;
660 	}
661       }
662     }
663   }
664 
665   return NULL;
666 }
667 
668 const char *
sxml_get_content(node)669 sxml_get_content(node)
670 	sxml_node_t *	node;
671 {
672   return (node != NULL && node->type == SXML_CONTENT) ?
673 	  node->value.content : NULL;
674 }
675 
676 /*****************************************************************************
677  *
678  *****************************************************************************/
679 void
sxml_print_tree(node,fout)680 sxml_print_tree(node, fout)
681 	sxml_node_t *	node;
682 	register FILE *	fout;
683 {
684   if (fout != NULL) {
685     print_node(node, node->type == SXML_VERTEX ? -1 : 0, fout);
686     fflush(fout);
687   }
688 }
689 
690 static void
print_node(node,depth,fout)691 print_node(node, depth, fout)
692 	sxml_node_t *	node;
693 	int		depth;
694 	register FILE *	fout;
695 {
696   register sxml_node_t *	np;
697 
698   for (np = node; np; np = np->next) {
699     switch (np->type) {
700       case SXML_PROLOG: {
701 	  register sxml_attr_t *	ap;
702 
703 	  fprintf(fout, "<?%s", np->value.element.name);
704 	  for (ap = np->value.element.attrs; ap != NULL; ap = ap->next) {
705 	    fprintf(fout, " %s=\"%s\"", ap->name, ap->value);
706 	  }
707 	  fputs("?>\n", fout);
708 	}
709 	break;
710       case SXML_ELEMENT: {
711 	  register int			i;
712 	  register sxml_attr_t *	ap;
713 
714           for (i = 0; i < depth; i++) {
715 	    fputs("  ", fout);
716 	  }
717 	  fprintf(fout, "<%s", np->value.element.name);
718 	  for (ap = np->value.element.attrs; ap != NULL; ap = ap->next) {
719 	    fprintf(fout, " %s=\"%s\"", ap->name, ap->value);
720 	  }
721 	  fputs(">", fout);
722 	  if (np->child && np->child->type == SXML_ELEMENT) {
723 	    fputs("\n", fout);
724 	  }
725 	}
726 	break;
727       case SXML_CONTENT:
728 	fprintf(fout, "%s", np->value.content);
729       default:
730 	break;
731     }
732     if (np->child)  { print_node(np->child, depth + 1, fout); }
733     if (np->type == SXML_ELEMENT) {
734       if (np->child && np->child->type == SXML_ELEMENT) {
735 	register int	i;
736 
737 	for (i = 0; i < depth; i++) {
738 	  fputs("  ", fout);
739 	}
740       }
741       fprintf(fout, "</%s>\n", np->value.element.name);
742     }
743   }
744 }
745 
746 void
sxml_print_node(node,fout)747 sxml_print_node(node, fout)
748 	sxml_node_t *	node;
749 	register FILE *	fout;
750 {
751   if (node != NULL) {
752     switch (node->type) {
753       case SXML_ELEMENT: {
754   	  register sxml_attr_t *	ap;
755 
756   	  fprintf(fout, "<%s", node->value.element.name);
757   	  for (ap = node->value.element.attrs; ap != NULL; ap = ap->next) {
758   	    fprintf(fout, " %s=\"%s\"", ap->name, ap->value);
759   	  }
760   	  fprintf(fout, ">\n");
761    	}
762     	break;
763       case SXML_CONTENT:
764       	fprintf(fout, "%s\n", node->value.content);
765       default:
766   	  break;
767     }
768     fflush(fout);
769   }
770 }
771 
772 /*****************************************************************************
773  *
774  *****************************************************************************/
775 
776 sxml_node_t *
sxml_new_vertex(void)777 sxml_new_vertex(void)
778 {
779   sxml_node_t *	new;
780 
781   new = create_new_node(NULL);
782   if (new != NULL) {
783     new->type = SXML_VERTEX;
784   }
785 
786   return new;
787 }
788 
789 sxml_node_t *
sxml_new_prolog(parent,name)790 sxml_new_prolog(parent, name)
791 	sxml_node_t *	parent;
792 	const char *	name;	/* name of prolog */
793 {
794   sxml_node_t *	new;	/* node of new element */
795 
796   new = create_new_node(parent);
797   if (new != NULL) {
798     new->value.element.name	= strdup(name);
799     new->value.element.attrs	= NULL;
800     new->type			= SXML_PROLOG;
801 
802     if (parent->child != NULL) {	/* parent has children */
803       new->next			= parent->child;
804       parent->child->prev	= new;
805       parent->child		= new;
806     }
807     else {	/* I'm fist child of the parent */
808       parent->last_child = parent->child = new;
809     }
810   }
811 
812   return new;
813 }
814 
815 sxml_node_t *
sxml_new_element(parent,name)816 sxml_new_element(parent, name)
817 	sxml_node_t *	parent;
818 	const char *	name;	/* name of element */
819 {
820   sxml_node_t *	new;	/* node of new element */
821 
822   new = create_new_node(parent);
823   if (new != NULL) {
824     new->value.element.name	= strdup(name);
825     new->value.element.attrs	= NULL;
826 
827     if (parent->child != NULL) {	/* parent has children */
828       parent->last_child->next	= new;
829       new->prev			= parent->last_child;
830       parent->last_child	= new;
831     }
832     else {	/* I'm fist child of the parent */
833       parent->last_child = parent->child = new;
834     }
835   }
836 
837   return new;
838 }
839 
840 sxml_node_t *
sxml_set_content(element,content)841 sxml_set_content(element, content)
842 	sxml_node_t *	element;
843 	const char *	content;	/* content of element */
844 {
845   sxml_node_t *	new;	/* content */
846 
847   new = create_new_node(element);
848   if (new != NULL) {
849     new->type		= SXML_CONTENT;
850     new->value.content	= strdup(content);
851     element->child	= element->last_child = new;
852   }
853 
854   return new;
855 }
856 
857 int
sxml_set_attribute(node,name,value)858 sxml_set_attribute(node, name, value)
859 	sxml_node_t *	node;
860 	const char *	name;
861 	const char *	value;
862 {
863   if (node->type == SXML_ELEMENT || node->type == SXML_PROLOG) {
864     sxml_attr_t *	ap;
865 
866     ap = set_attribute(node->value.element.attrs, name, value);
867     if (ap != NULL) {
868       node->value.element.attrs = ap;
869       return 0;
870     }
871   }
872 
873   return -1;
874 }
875 
876 int
sxml_set_fattribute(sxml_node_t * node,const char * name,const char * fmt,...)877 sxml_set_fattribute(
878 	sxml_node_t *	node,
879 	const char *	name,
880 	const char *	fmt,
881 	...)
882 {
883   sxml_attr_t *	ap = NULL;
884 
885   if (node->type == SXML_ELEMENT || node->type == SXML_PROLOG) {
886     char *	val;
887 
888 #define	MAGIC_SIZE	(4*1024)	/* XXX: groundless value */
889     val = calloc(MAGIC_SIZE, sizeof(char));
890     if (val != NULL) {
891       va_list	vp;
892 
893       va_start(vp, fmt);
894       vsnprintf(val, MAGIC_SIZE, fmt, vp);
895       va_end(vp);
896 
897       ap = set_attribute(node->value.element.attrs, name, val);
898       if (ap != NULL) {
899        	node->value.element.attrs = ap;
900       }
901       free(val);
902     }
903   }
904 
905   return (ap != NULL ? 0 : -1);
906 }
907 
908 sxml_node_t *
sxml_set_node(parent,name,content)909 sxml_set_node(parent, name, content)
910 	sxml_node_t *	parent;
911 	const char *	name;		/* name of element */
912 	const char *	content;	/* content of element */
913 {
914   sxml_node_t *	new;
915 
916   new = sxml_new_element(parent, name);
917   if (new != NULL) {
918     sxml_set_content(new, content);
919   }
920 
921   return new;
922 }
923 
924 sxml_node_t *
sxml_set_fnode(sxml_node_t * parent,const char * name,const char * fmt,...)925 sxml_set_fnode(
926 	sxml_node_t *	parent,
927 	const char *	name,	/* name of element */
928 	const char *	fmt,	/* format for content of element */
929 	...)
930 {
931   sxml_node_t *	new = NULL;
932   char *	val;
933 
934   val = calloc(MAGIC_SIZE, sizeof(char));
935   if (val != NULL) {
936     va_list	vp;
937 
938     va_start(vp, fmt);
939     vsnprintf(val, MAGIC_SIZE, fmt, vp);
940     va_end(vp);
941 
942     new = sxml_new_element(parent, name);
943     if (new != NULL) {
944       sxml_set_content(new, val);
945     }
946     free(val);
947   }
948 
949   return new;
950 }
951