]> git.sesse.net Git - vlc/blob - modules/misc/xml/xtag.c
Replace argument = realloc( argument, size ); with realloc_or_free() in modules/...
[vlc] / modules / misc / xml / xtag.c
1 /*****************************************************************************
2  * xtag.c : a trivial parser for XML-like tags
3  *****************************************************************************
4  * Copyright (C) 2003-2004 Commonwealth Scientific and Industrial Research
5  *                         Organisation (CSIRO) Australia
6  * Copyright (C) 2000-2004 the VideoLAN team
7  *
8  * $Id$
9  *
10  * Authors: Conrad Parker <Conrad.Parker@csiro.au>
11  *          Andre Pang <Andre.Pang@csiro.au>
12  *          Gildas Bazin <gbazin@videolan.org>
13  *
14  * This program is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation; either version 2 of the License, or
17  * (at your option) any later version.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  *
24  * You should have received a copy of the GNU General Public License
25  * along with this program; if not, write to the Free Software
26  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
27  *****************************************************************************/
28
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32
33 #include <vlc_common.h>
34 #include <vlc_plugin.h>
35
36 #include <vlc_xml.h>
37 #include <vlc_block.h>
38 #include <vlc_stream.h>
39 #include <vlc_memory.h>
40
41 #include <ctype.h>
42 #include <stdarg.h>
43
44 #include <assert.h>
45
46 #undef XTAG_DEBUG
47
48 typedef struct _XList
49 {
50     struct _XList *prev;
51     struct _XList *next;
52     void *data;
53 } XList;
54
55 /*
56  * struct XTag is kind of a union ... it normally represents a whole
57  * tag (and its children), but it could alternatively represent some
58  * PCDATA. Basically, if tag->pcdata is non-NULL, interpret only it and
59  * ignore the name, attributes and inner_tags.
60  */
61 typedef struct _XTag
62 {
63     char *name;
64     char *pcdata;
65     struct _XTag *parent;
66     XList *attributes;
67     XList *children;
68     XList *current_child;
69 } XTag;
70
71 typedef struct _XAttribute
72 {
73     char *name;
74     char *value;
75 } XAttribute;
76
77 typedef struct _XTagParser
78 {
79     int valid; /* boolean */
80     XTag *current_tag;
81     char *start;
82     char *end;
83 } XTagParser;
84
85 /*****************************************************************************
86  * Module descriptor
87  *****************************************************************************/
88 static int  Open ( vlc_object_t * );
89 static void Close( vlc_object_t * );
90
91 vlc_module_begin ()
92     set_description( N_("Simple XML Parser") )
93     set_capability( "xml", 5 )
94     set_callbacks( Open, Close )
95 vlc_module_end ()
96
97 struct xml_reader_sys_t
98 {
99     XTag *p_root; /* Root tag */
100     XTag *p_curtag; /* Current tag */
101     XList *p_curattr; /* Current attribute */
102     bool b_endtag;
103 };
104
105 static xml_reader_t *ReaderCreate( xml_t *, stream_t * );
106 static void ReaderDelete( xml_reader_t * );
107 static int ReaderRead( xml_reader_t * );
108 static int ReaderNodeType( xml_reader_t * );
109 static char *ReaderName( xml_reader_t * );
110 static char *ReaderValue( xml_reader_t * );
111 static int ReaderNextAttr( xml_reader_t * );
112
113 static int ReaderUseDTD ( xml_reader_t *, bool );
114
115 static void CatalogLoad( xml_t *, const char * );
116 static void CatalogAdd( xml_t *, const char *, const char *, const char * );
117
118 static XTag *xtag_new_parse( const char *, int );
119 static char *xtag_get_name( XTag * );
120 #if 0
121 static char *xtag_get_pcdata( XTag * );
122 static char *xtag_get_attribute( XTag *, char * );
123 #endif
124 static XTag *xtag_first_child( XTag *, char * );
125 static XTag *xtag_next_child( XTag *, char * );
126 static void  xtag_free( XTag * );
127 #if 0
128 static int xtag_snprint( char *, int, XTag * );
129 #endif
130
131 /*****************************************************************************
132  * Module initialization
133  *****************************************************************************/
134 static int Open( vlc_object_t *p_this )
135 {
136     xml_t *p_xml = (xml_t *)p_this;
137
138     p_xml->pf_reader_create = ReaderCreate;
139     p_xml->pf_reader_delete = ReaderDelete;
140
141     p_xml->pf_catalog_load = CatalogLoad;
142     p_xml->pf_catalog_add  = CatalogAdd;
143
144     return VLC_SUCCESS;
145 }
146
147 /*****************************************************************************
148  * Module deinitialization
149  *****************************************************************************/
150 static void Close( vlc_object_t *p_this )
151 {
152     VLC_UNUSED(p_this);
153     return;
154 }
155
156 /*****************************************************************************
157  * Catalogue functions
158  *****************************************************************************/
159 static void CatalogLoad( xml_t *p_xml, const char *psz_filename )
160 {
161     VLC_UNUSED(psz_filename);
162     msg_Dbg( p_xml, "catalog support not implemented" );
163 }
164
165 static void CatalogAdd( xml_t *p_xml, const char *psz_arg1,
166                           const char *psz_arg2, const char *psz_filename )
167 {
168     VLC_UNUSED(p_xml); VLC_UNUSED(psz_arg1); VLC_UNUSED(psz_arg2);
169     VLC_UNUSED(psz_filename);
170 }
171
172 /*****************************************************************************
173  * Reader functions
174  *****************************************************************************/
175 static xml_reader_t *ReaderCreate( xml_t *p_xml, stream_t *s )
176 {
177     xml_reader_t *p_reader;
178     char *p_buffer;
179     int i_size, i_pos = 0, i_buffer = 2048;
180     XTag *p_root;
181
182     /* Open and read file */
183     p_buffer = malloc( i_buffer );
184     if( p_buffer == NULL )
185         return NULL;
186
187     while( ( i_size = stream_Read( s, &p_buffer[i_pos], 2048 ) ) == 2048 )
188     {
189         i_pos += i_size;
190         i_buffer += i_size;
191         p_buffer = realloc_or_free( p_buffer, i_buffer );
192         if( !p_buffer )
193             return NULL;
194     }
195     if( i_pos + i_size == 0 )
196     {
197         msg_Dbg( p_xml, "empty XML" );
198         free( p_buffer );
199         return NULL;
200     }
201     p_buffer[ i_pos + i_size ] = '\0'; /* 0 terminated string */
202
203     p_root = xtag_new_parse( p_buffer, i_buffer );
204     if( !p_root )
205     {
206         msg_Warn( p_xml, "couldn't parse XML" );
207         free( p_buffer );
208         return NULL;
209     }
210
211     free( p_buffer );
212     p_reader = malloc( sizeof(xml_reader_t) );
213     if( !p_reader )
214         return NULL;
215     p_reader->p_sys = malloc( sizeof(xml_reader_sys_t) );
216     if( !p_reader->p_sys )
217     {
218         free( p_reader );
219         return NULL;
220     }
221     p_reader->p_sys->p_root = p_root;
222     p_reader->p_sys->p_curtag = NULL;
223     p_reader->p_sys->p_curattr = NULL;
224     p_reader->p_sys->b_endtag = false;
225     p_reader->p_xml = p_xml;
226
227     p_reader->pf_read = ReaderRead;
228     p_reader->pf_node_type = ReaderNodeType;
229     p_reader->pf_name = ReaderName;
230     p_reader->pf_value = ReaderValue;
231     p_reader->pf_next_attr = ReaderNextAttr;
232     p_reader->pf_use_dtd = ReaderUseDTD;
233
234     return p_reader;
235 }
236
237 static void ReaderDelete( xml_reader_t *p_reader )
238 {
239     xtag_free( p_reader->p_sys->p_root );
240     free( p_reader->p_sys );
241     free( p_reader );
242 }
243
244 static int ReaderUseDTD ( xml_reader_t *p_reader, bool b_use )
245 {
246     VLC_UNUSED(p_reader); VLC_UNUSED(b_use);
247     return VLC_EGENERIC;
248 }
249
250 static int ReaderRead( xml_reader_t *p_reader )
251 {
252     XTag *p_child;
253
254     if( !p_reader->p_sys->p_curtag )
255     {
256         p_reader->p_sys->p_curtag = p_reader->p_sys->p_root;
257         return 1;
258     }
259
260     while( true )
261     {
262         if( (p_child = xtag_next_child( p_reader->p_sys->p_curtag, 0 )) )
263         {
264             p_reader->p_sys->p_curtag = p_child;
265             p_reader->p_sys->p_curattr = NULL;
266             p_reader->p_sys->b_endtag = false;
267             return 1;
268         }
269
270         if( p_reader->p_sys->p_curtag->name && /* no end tag for pcdata */
271             !p_reader->p_sys->b_endtag )
272         {
273             p_reader->p_sys->b_endtag = true;
274             return 1;
275         }
276
277         p_reader->p_sys->b_endtag = false;
278         if( !p_reader->p_sys->p_curtag->parent ) return 0;
279         p_reader->p_sys->p_curtag = p_reader->p_sys->p_curtag->parent;
280     }
281
282     return 0;
283 }
284
285 static int ReaderNodeType( xml_reader_t *p_reader )
286 {
287     if( p_reader->p_sys->p_curtag->name && p_reader->p_sys->b_endtag )
288         return XML_READER_ENDELEM;
289     if( p_reader->p_sys->p_curtag->name )
290         return XML_READER_STARTELEM;
291     if( p_reader->p_sys->p_curtag->pcdata )
292         return XML_READER_TEXT;
293     return XML_READER_NONE;
294 }
295
296 static char *ReaderName( xml_reader_t *p_reader )
297 {
298     const char *psz_name;
299
300     if( !p_reader->p_sys->p_curattr )
301     {
302         psz_name = xtag_get_name( p_reader->p_sys->p_curtag );
303 #ifdef XTAG_DEBUG
304         fprintf( stderr, "TAG: %s\n", psz_name );
305 #endif
306     }
307     else
308         psz_name = ((XAttribute *)p_reader->p_sys->p_curattr->data)->name;
309
310     return psz_name ? strdup( psz_name ) : NULL;
311 }
312
313 static char *ReaderValue( xml_reader_t *p_reader )
314 {
315     const char *psz_name;
316     if( p_reader->p_sys->p_curtag->pcdata )
317     {
318 #ifdef XTAG_DEBUG
319         fprintf( stderr, "%s\n", p_reader->p_sys->p_curtag->pcdata );
320 #endif
321         return strdup( p_reader->p_sys->p_curtag->pcdata );
322     }
323
324     if( !p_reader->p_sys->p_curattr ) return NULL;
325
326 #ifdef XTAG_DEBUG
327     fprintf( stderr, "%s=%s\n", ((XAttribute *)p_reader->p_sys->p_curattr->data)->name,
328             ((XAttribute *)p_reader->p_sys->p_curattr->data)->value );
329 #endif
330
331     psz_name = ((XAttribute *)p_reader->p_sys->p_curattr->data)->value;
332
333     return psz_name ? strdup( psz_name ) : NULL;
334 }
335
336 static int ReaderNextAttr( xml_reader_t *p_reader )
337 {
338     if( !p_reader->p_sys->p_curattr )
339         p_reader->p_sys->p_curattr = p_reader->p_sys->p_curtag->attributes;
340     else if( p_reader->p_sys->p_curattr )
341         p_reader->p_sys->p_curattr = p_reader->p_sys->p_curattr->next;
342
343     return p_reader->p_sys->p_curattr ? VLC_SUCCESS : VLC_EGENERIC;
344 }
345
346 /*****************************************************************************
347  * XTAG parser functions
348  *****************************************************************************/
349
350 static XList *xlist_append( XList *list, void *data )
351 {
352     XList *l, *last;
353
354     l = (XList *)malloc( sizeof(XList) );
355     assert( l );
356     l->prev = l->next = NULL;
357     l->data = data;
358
359     if( !list )
360         return l;
361
362     /* Find the last element */
363     last = list;
364     while( last->next )
365         last = last->next;
366
367     last->next = l;
368     l->prev = last;
369     return list;
370 }
371
372 static void xlist_free( XList *list )
373 {
374     XList *l, *ln;
375
376     for( l = list; l; l = ln )
377     {
378         ln = l->next;
379         free( l );
380     }
381 }
382
383 /* Character classes */
384 #define X_NONE           0
385 #define X_WHITESPACE  1<<0
386 #define X_OPENTAG     1<<1
387 #define X_CLOSETAG    1<<2
388 #define X_DQUOTE      1<<3
389 #define X_SQUOTE      1<<4
390 #define X_EQUAL       1<<5
391 #define X_SLASH       1<<6
392 #define X_QMARK       1<<7
393 #define X_DASH        1<<8
394 #define X_EMARK       1<<9
395
396 static int xtag_cin( char c, int char_class )
397 {
398     if( char_class & X_WHITESPACE ) if( isspace(c) ) return true;
399     if( char_class & X_OPENTAG )    if( c == '<' ) return true;
400     if( char_class & X_CLOSETAG )   if( c == '>' ) return true;
401     if( char_class & X_DQUOTE )     if( c == '"' ) return true;
402     if( char_class & X_SQUOTE )     if( c == '\'' ) return true;
403     if( char_class & X_EQUAL )      if( c == '=' ) return true;
404     if( char_class & X_SLASH )      if( c == '/' ) return true;
405     if( char_class & X_QMARK )      if( c == '?' ) return true;
406     if( char_class & X_DASH  )      if( c == '-' ) return true;
407     if( char_class & X_EMARK )      if( c == '!' ) return true;
408
409     return false;
410 }
411
412 static int xtag_index( XTagParser *parser, int char_class )
413 {
414     char *s = parser->start;
415     int i;
416
417     for( i = 0; s[i] && s != parser->end; i++ )
418     {
419         if( xtag_cin( s[i], char_class ) ) return i;
420     }
421
422     return -1;
423 }
424
425 static void xtag_skip_over( XTagParser *parser, int char_class )
426 {
427     char *s = parser->start;
428     int i;
429
430     if( !parser->valid ) return;
431
432     for( i = 0; s[i] && s != parser->end; i++ )
433     {
434         if( !xtag_cin( s[i], char_class ) )
435         {
436             parser->start = &s[i];
437             return;
438         }
439     }
440
441     return;
442 }
443
444 static void xtag_skip_whitespace( XTagParser * parser )
445 {
446     xtag_skip_over( parser, X_WHITESPACE );
447 }
448
449 static char *xtag_slurp_to( XTagParser *parser, int good_end, int bad_end )
450 {
451     char *ret, *s = parser->start;
452     int xi;
453
454     if( !parser->valid ) return NULL;
455
456     xi = xtag_index( parser, good_end | bad_end );
457
458     if( xi > 0 && xtag_cin (s[xi], good_end) )
459     {
460         ret = malloc( xi+1 );
461         assert( ret );
462         strncpy( ret, s, xi );
463         ret[xi] = '\0';
464         parser->start = &s[xi];
465         return ret;
466     }
467
468     return NULL;
469 }
470
471 static int xtag_assert_and_pass( XTagParser *parser, int char_class )
472 {
473     char *s = parser->start;
474
475     if( !parser->valid ) return false;
476
477     if( !xtag_cin( s[0], char_class ) )
478     {
479         parser->valid = false;
480         return false;
481     }
482
483     parser->start = &s[1];
484
485     return true;
486 }
487
488 static char *xtag_slurp_quoted( XTagParser *parser )
489 {
490     char * ret, *s;
491     int quote = X_DQUOTE; /* quote char to match on */
492     int xi;
493
494     if( !parser->valid ) return NULL;
495
496     xtag_skip_whitespace( parser );
497
498     s = parser->start;
499
500     if( xtag_cin( s[0], X_SQUOTE ) ) quote = X_SQUOTE;
501
502     if( !xtag_assert_and_pass( parser, quote ) ) return NULL;
503
504     s = parser->start;
505
506     for( xi = 0; s[xi]; xi++ )
507     {
508         if( xtag_cin( s[xi], quote ) )
509         {
510             if( !(xi > 1 && s[xi-1] == '\\') ) break;
511         }
512     }
513
514     ret = malloc( xi+1 );
515     assert( ret );
516     strncpy( ret, s, xi );
517     ret[xi] = '\0';
518     parser->start = &s[xi];
519
520     if( !xtag_assert_and_pass( parser, quote ) )
521     {
522         free( ret );
523         return NULL;
524     }
525
526     return ret;
527 }
528
529 static XAttribute *xtag_parse_attribute( XTagParser *parser )
530 {
531     XAttribute *attr;
532     char *name, *value;
533     char *s;
534
535     if( !parser->valid )
536         return NULL;
537
538     xtag_skip_whitespace( parser );
539  
540     name = xtag_slurp_to( parser, X_WHITESPACE|X_EQUAL, X_SLASH|X_CLOSETAG );
541     if( !name )
542         return NULL;
543
544     xtag_skip_whitespace( parser );
545     s = parser->start;
546
547     if( !xtag_assert_and_pass( parser, X_EQUAL ) )
548     {
549 #ifdef XTAG_DEBUG
550         fprintf( stderr, "xtag: attr failed EQUAL on <%s>\n", name );
551 #endif
552         goto err_free_name;
553     }
554
555     xtag_skip_whitespace( parser );
556
557     value = xtag_slurp_quoted( parser );
558
559     if( value == NULL )
560     {
561 #ifdef XTAG_DEBUG
562         fprintf (stderr, "Got NULL quoted attribute value\n");
563 #endif
564         goto err_free_name;
565     }
566
567     attr = malloc( sizeof (*attr) );
568     assert( attr );
569     attr->name = name;
570     attr->value = value;
571     return attr;
572
573  err_free_name:
574     free (name);
575     parser->valid = false;
576     return NULL;
577 }
578
579 static XTag *xtag_parse_tag( XTagParser *parser )
580 {
581     XTag *tag, *inner;
582     XAttribute *attr;
583     char *name;
584     char *pcdata;
585     char *s;
586      int xi;
587
588     if( !parser->valid ) return NULL;
589
590     s = parser->start;
591
592     /* if this starts a comment tag, skip until end */
593     if( (parser->end - parser->start) > 7 &&
594           xtag_cin( s[0], X_OPENTAG ) && xtag_cin( s[1], X_EMARK ) &&
595         xtag_cin( s[2], X_DASH ) && xtag_cin( s[3], X_DASH ) )
596     {
597         parser->start = s = &s[4];
598         while( (xi = xtag_index( parser, X_DASH )) >= 0 )
599         {
600             parser->start = s = &s[xi+1];
601             if( xtag_cin( s[0], X_DASH ) && xtag_cin( s[1], X_CLOSETAG ) )
602             {
603                 parser->start = &s[2];
604                 xtag_skip_whitespace( parser );
605                 return xtag_parse_tag( parser );
606             }
607         }
608         return NULL;
609     }
610
611     /* ignore processing instructions '<?' ... '?>' */
612     if( (parser->end - parser->start) > 4 &&
613           xtag_cin( s[0], X_OPENTAG ) && xtag_cin( s[1], X_QMARK ) )
614     {
615         parser->start = s = &s[2];
616         while ((xi = xtag_index( parser, X_QMARK )) >= 0) {
617             if (xtag_cin( s[xi+1], X_CLOSETAG )) {
618                 parser->start = &s[xi+2];
619                 xtag_skip_whitespace( parser );
620                 return xtag_parse_tag( parser );
621             }
622         }
623         return NULL;
624     }
625
626     /* ignore doctype  '<!DOCTYPE' ... '>' */
627     if ( (parser->end - parser->start) > 8 &&
628             !strncmp( s, "<!DOCTYPE", 9 ) ) {
629         xi = xtag_index( parser, X_CLOSETAG );
630         if ( xi > 0 ) {
631             parser->start = &s[xi+1];
632             xtag_skip_whitespace( parser );
633             return xtag_parse_tag( parser );
634         }
635         else {
636             return NULL;
637         }
638     }
639
640     if( (pcdata = xtag_slurp_to( parser, X_OPENTAG, X_NONE )) != NULL )
641     {
642         tag = malloc( sizeof(*tag) );
643         assert( tag );
644         tag->name = NULL;
645         tag->pcdata = pcdata;
646         tag->parent = parser->current_tag;
647         tag->attributes = NULL;
648         tag->children = NULL;
649         tag->current_child = NULL;
650
651         return tag;
652     }
653
654     /* if this starts a close tag, return NULL and let the parent take it */
655     if( xtag_cin( s[0], X_OPENTAG ) && xtag_cin( s[1], X_SLASH ) )
656         return NULL;
657
658     /* parse CDATA content */
659     if ( (parser->end - parser->start) > 8 &&
660             !strncmp( s, "<![CDATA[", 9 ) ) {
661         parser->start = s = &s[9];
662         while (parser->end - s > 2) {
663             if (strncmp( s, "]]>", 3 ) == 0) {
664                 if ( !(tag = malloc( sizeof(*tag))) ) return NULL;
665                 if ( !(pcdata = malloc( s - parser->start + 1)) )
666                 {
667                     free( tag );
668                     return NULL;
669                 }
670                 strncpy( pcdata, parser->start, s - parser->start );
671                 pcdata[s - parser->start]='\0';
672                 parser->start = &s[3];
673                 tag->name = NULL;
674                 tag->pcdata = pcdata;
675                 tag->parent = parser->current_tag;
676                 tag->attributes = NULL;
677                 tag->children = NULL;
678                 tag->current_child = NULL;
679                 return tag;
680             }
681             else {
682                 s++;
683             }
684         }
685         return NULL;
686     }
687
688     if( !xtag_assert_and_pass( parser, X_OPENTAG ) ) return NULL;
689
690     name = xtag_slurp_to( parser, X_WHITESPACE|X_SLASH|X_CLOSETAG, X_NONE );
691     if( name == NULL ) return NULL;
692
693 #ifdef XTAG_DEBUG
694     fprintf (stderr, "<%s ...\n", name);
695 #endif
696
697     tag = malloc( sizeof(*tag) );
698     assert( tag );
699     tag->name = name;
700     tag->pcdata = NULL;
701     tag->parent = parser->current_tag;
702     tag->attributes = NULL;
703     tag->children = NULL;
704     tag->current_child = NULL;
705
706     s = parser->start;
707
708     if( xtag_cin( s[0], X_WHITESPACE ) )
709     {
710         while( (attr = xtag_parse_attribute( parser )) != NULL )
711         {
712             tag->attributes = xlist_append( tag->attributes, attr );
713         }
714     }
715
716     xtag_skip_whitespace( parser );
717
718     s = parser->start;
719
720     if( xtag_cin( s[0], X_CLOSETAG ) )
721     {
722         parser->current_tag = tag;
723
724         xtag_assert_and_pass( parser, X_CLOSETAG );
725
726         while( (inner = xtag_parse_tag( parser ) ) != NULL )
727         {
728             tag->children = xlist_append( tag->children, inner );
729         }
730
731         parser->current_tag = tag->parent;
732         xtag_skip_whitespace( parser );
733
734         xtag_assert_and_pass( parser, X_OPENTAG );
735         xtag_assert_and_pass( parser, X_SLASH );
736         name = xtag_slurp_to( parser, X_WHITESPACE | X_CLOSETAG, X_NONE );
737         if( name )
738         {
739             if( strcmp( name, tag->name ) )
740             {
741 #ifdef XTAG_DEBUG
742                 fprintf (stderr, "got %s expected %s\n", name, tag->name);
743 #endif
744                 parser->valid = false;
745             }
746             free( name );
747         }
748
749         xtag_skip_whitespace( parser );
750         xtag_assert_and_pass( parser, X_CLOSETAG );
751         xtag_skip_whitespace( parser );
752     }
753     else
754     {
755         xtag_assert_and_pass( parser, X_SLASH );
756         xtag_assert_and_pass( parser, X_CLOSETAG );
757         xtag_skip_whitespace( parser );
758     }
759
760     return tag;
761 }
762
763 static void xtag_free( XTag *xtag )
764 {
765     XList *l;
766     XAttribute *attr;
767     XTag *child;
768
769     if( !xtag )
770         return;
771
772     free( xtag->name );
773     free( xtag->pcdata );
774
775     for( l = xtag->attributes; l; l = l->next )
776     {
777         if( (attr = (XAttribute *)l->data) != NULL )
778         {
779             free( attr->name );
780             free( attr->value );
781             free( attr );
782         }
783     }
784     xlist_free( xtag->attributes );
785
786     for( l = xtag->children; l; l = l->next )
787     {
788         child = (XTag *)l->data;
789         xtag_free( child );
790     }
791     xlist_free( xtag->children );
792
793     free( xtag );
794 }
795
796 static XTag *xtag_new_parse( const char *s, int n )
797 {
798     XTagParser parser;
799     XTag *tag, *ttag, *wrapper;
800
801     parser.valid = true;
802     parser.current_tag = NULL;
803     parser.start = (char *)s;
804
805     if( n == -1 ) parser.end = NULL;
806     else if( n == 0 )
807     {
808 #ifdef XTAG_DEBUG
809         fprintf (stderr, "empty buffer\n");
810 #endif
811         return NULL;
812     }
813     else parser.end = (char *)&s[n];
814
815     /* can't have whitespace pcdata outside rootnode */
816     xtag_skip_whitespace( &parser );
817
818     tag = xtag_parse_tag( &parser );
819
820     if( !parser.valid )
821     {
822 #ifdef XTAG_DEBUG
823         fprintf (stderr, "invalid file\n");
824 #endif
825         xtag_free( tag );
826         return NULL;
827     }
828
829     if( (ttag = xtag_parse_tag( &parser )) != NULL )
830     {
831         if( !parser.valid )
832         {
833             xtag_free( ttag );
834             return tag;
835         }
836
837         wrapper = malloc( sizeof(XTag) );
838         assert( wrapper );
839         wrapper->name = NULL;
840         wrapper->pcdata = NULL;
841         wrapper->parent = NULL;
842         wrapper->attributes = NULL;
843         wrapper->children = NULL;
844         wrapper->current_child = NULL;
845
846         wrapper->children = xlist_append( wrapper->children, tag );
847         wrapper->children = xlist_append( wrapper->children, ttag );
848
849         while( (ttag = xtag_parse_tag( &parser )) != NULL )
850         {
851             if( !parser.valid )
852             {
853                 xtag_free( ttag );
854                 return wrapper;
855             }
856
857             wrapper->children = xlist_append( wrapper->children, ttag );
858         }
859         return wrapper;
860     }
861
862     return tag;
863 }
864
865 static char *xtag_get_name( XTag *xtag )
866 {
867     return xtag ? xtag->name : NULL;
868 }
869
870 #if 0
871 static char *xtag_get_pcdata( XTag *xtag )
872 {
873     XList *l;
874     XTag *child;
875
876     if( xtag == NULL ) return NULL;
877
878     for( l = xtag->children; l; l = l->next )
879     {
880         child = (XTag *)l->data;
881         if( child->pcdata != NULL )
882         {
883             return child->pcdata;
884         }
885     }
886
887     return NULL;
888 }
889
890 static char *xtag_get_attribute( XTag *xtag, char *attribute )
891 {
892     XList *l;
893     XAttribute *attr;
894
895     if( xtag == NULL ) return NULL;
896
897     for( l = xtag->attributes; l; l = l->next )
898     {
899         if( (attr = (XAttribute *)l->data) != NULL )
900         {
901             if( !strcmp( attr->name, attribute ) ) return attr->value;
902         }
903     }
904
905     return NULL;
906 }
907 #endif
908
909 static XTag *xtag_first_child( XTag *xtag, char *name )
910 {
911     XList *l;
912     XTag *child;
913
914     if( xtag == NULL ) return NULL;
915     if( (l = xtag->children) == NULL ) return NULL;
916
917     if( name == NULL )
918     {
919         xtag->current_child = l;
920         return (XTag *)l->data;
921     }
922
923     for( ; l; l = l->next )
924     {
925         child = (XTag *)l->data;
926
927         if( !strcmp( child->name, name ) )
928         {
929             xtag->current_child = l;
930             return child;
931         }
932     }
933
934     xtag->current_child = NULL;
935
936     return NULL;
937 }
938
939 static XTag *xtag_next_child( XTag *xtag, char *name )
940 {
941     XList *l;
942     XTag *child;
943
944     if( xtag == NULL ) return NULL;
945
946     if( (l = xtag->current_child) == NULL )
947         return xtag_first_child( xtag, name );
948
949     if( (l = l->next) == NULL ) return NULL;
950
951     if( name == NULL )
952     {
953         xtag->current_child = l;
954         return (XTag *)l->data;
955     }
956
957     for( ; l; l = l->next )
958     {
959         child = (XTag *)l->data;
960
961         if( !strcmp( child->name, name ) )
962         {
963             xtag->current_child = l;
964             return child;
965         }
966     }
967
968     xtag->current_child = NULL;
969
970     return NULL;
971 }
972
973 #if 0
974 /*
975  * This snprints function takes a variable list of char *, the last of
976  * which must be NULL, and prints each in turn to buf.
977  * Returns C99-style total length that would have been written, even if
978  * this is larger than n.
979  */
980 static int xtag_snprints( char *buf, int n, ... )
981 {
982     va_list ap;
983     char *s;
984     int len, to_copy, total = 0;
985
986     va_start( ap, n );
987  
988     for( s = va_arg( ap, char * ); s; s = va_arg( ap, char *) )
989     {
990         len = strlen (s);
991
992         if( (to_copy = __MIN(n, len) ) > 0 )
993         {
994             memcpy( buf, s, to_copy );
995             buf += to_copy;
996             n -= to_copy;
997         }
998
999         total += len;
1000     }
1001
1002     va_end( ap );
1003
1004     return total;
1005 }
1006
1007 static int xtag_snprint( char *buf, int n, XTag *xtag )
1008 {
1009     int nn, written = 0;
1010     XList *l;
1011     XAttribute *attr;
1012     XTag *child;
1013
1014 #define FORWARD(N) \
1015     buf += __MIN(n, N); \
1016     n = __MAX(n-N, 0);  \
1017     written += N;
1018
1019     if( xtag == NULL )
1020     {
1021         if( n > 0 ) buf[0] = '\0';
1022         return 0;
1023     }
1024
1025     if( xtag->pcdata )
1026     {
1027         nn = xtag_snprints( buf, n, xtag->pcdata, NULL );
1028         FORWARD( nn );
1029
1030         return written;
1031     }
1032
1033     if( xtag->name )
1034     {
1035         nn = xtag_snprints( buf, n, "<", xtag->name, NULL );
1036         FORWARD( nn );
1037
1038         for( l = xtag->attributes; l; l = l->next )
1039         {
1040             attr = (XAttribute *)l->data;
1041  
1042             nn = xtag_snprints( buf, n, " ", attr->name, "=\"", attr->value,
1043                                 "\"", NULL);
1044             FORWARD( nn );
1045         }
1046
1047         if( xtag->children == NULL )
1048         {
1049             nn = xtag_snprints ( buf, n, "/>", NULL );
1050             FORWARD( nn );
1051
1052             return written;
1053         }
1054
1055         nn = xtag_snprints( buf, n, ">", NULL );
1056         FORWARD( nn );
1057     }
1058
1059     for( l = xtag->children; l; l = l->next )
1060     {
1061         child = (XTag *)l->data;
1062
1063         nn = xtag_snprint( buf, n, child );
1064         FORWARD( nn );
1065     }
1066
1067     if( xtag->name )
1068     {
1069         nn = xtag_snprints( buf, n, "</", xtag->name, ">", NULL );
1070         FORWARD( nn );
1071     }
1072
1073     return written;
1074 }
1075 #endif
1076