xtag.c
上传用户:kjfoods
上传日期:2020-07-06
资源大小:29949k
文件大小:26k
源码类别:

midi

开发平台:

Unix_Linux

  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: 681be12be1eb3b24b22a475aaf6732469c68ca3d $
  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. #ifdef HAVE_CONFIG_H
  29. # include "config.h"
  30. #endif
  31. #include <vlc_common.h>
  32. #include <vlc_plugin.h>
  33. #include "vlc_xml.h"
  34. #include "vlc_block.h"
  35. #include "vlc_stream.h"
  36. #include <ctype.h>
  37. #include <stdarg.h>
  38. #include <assert.h>
  39. #undef XTAG_DEBUG
  40. typedef struct _XList
  41. {
  42.     struct _XList *prev;
  43.     struct _XList *next;
  44.     void *data;
  45. } XList;
  46. /*
  47.  * struct XTag is kind of a union ... it normally represents a whole
  48.  * tag (and its children), but it could alternatively represent some
  49.  * PCDATA. Basically, if tag->pcdata is non-NULL, interpret only it and
  50.  * ignore the name, attributes and inner_tags.
  51.  */
  52. typedef struct _XTag
  53. {
  54.     char *name;
  55.     char *pcdata;
  56.     struct _XTag *parent;
  57.     XList *attributes;
  58.     XList *children;
  59.     XList *current_child;
  60. } XTag;
  61. typedef struct _XAttribute
  62. {
  63.     char *name;
  64.     char *value;
  65. } XAttribute;
  66. typedef struct _XTagParser
  67. {
  68.     int valid; /* boolean */
  69.     XTag *current_tag;
  70.     char *start;
  71.     char *end;
  72. } XTagParser;
  73. /*****************************************************************************
  74.  * Module descriptor
  75.  *****************************************************************************/
  76. static int  Open ( vlc_object_t * );
  77. static void Close( vlc_object_t * );
  78. vlc_module_begin ()
  79.     set_description( N_("Simple XML Parser") )
  80.     set_capability( "xml", 5 )
  81.     set_callbacks( Open, Close )
  82. vlc_module_end ()
  83. struct xml_reader_sys_t
  84. {
  85.     XTag *p_root; /* Root tag */
  86.     XTag *p_curtag; /* Current tag */
  87.     XList *p_curattr; /* Current attribute */
  88.     bool b_endtag;
  89. };
  90. static xml_reader_t *ReaderCreate( xml_t *, stream_t * );
  91. static void ReaderDelete( xml_reader_t * );
  92. static int ReaderRead( xml_reader_t * );
  93. static int ReaderNodeType( xml_reader_t * );
  94. static char *ReaderName( xml_reader_t * );
  95. static char *ReaderValue( xml_reader_t * );
  96. static int ReaderNextAttr( xml_reader_t * );
  97. static int ReaderUseDTD ( xml_reader_t *, bool );
  98. static void CatalogLoad( xml_t *, const char * );
  99. static void CatalogAdd( xml_t *, const char *, const char *, const char * );
  100. static XTag *xtag_new_parse( const char *, int );
  101. static char *xtag_get_name( XTag * );
  102. #if 0
  103. static char *xtag_get_pcdata( XTag * );
  104. static char *xtag_get_attribute( XTag *, char * );
  105. #endif
  106. static XTag *xtag_first_child( XTag *, char * );
  107. static XTag *xtag_next_child( XTag *, char * );
  108. static void  xtag_free( XTag * );
  109. #if 0
  110. static int xtag_snprint( char *, int, XTag * );
  111. #endif
  112. /*****************************************************************************
  113.  * Module initialization
  114.  *****************************************************************************/
  115. static int Open( vlc_object_t *p_this )
  116. {
  117.     xml_t *p_xml = (xml_t *)p_this;
  118.     p_xml->pf_reader_create = ReaderCreate;
  119.     p_xml->pf_reader_delete = ReaderDelete;
  120.     p_xml->pf_catalog_load = CatalogLoad;
  121.     p_xml->pf_catalog_add  = CatalogAdd;
  122.     return VLC_SUCCESS;
  123. }
  124. /*****************************************************************************
  125.  * Module deinitialization
  126.  *****************************************************************************/
  127. static void Close( vlc_object_t *p_this )
  128. {
  129.     VLC_UNUSED(p_this);
  130.     return;
  131. }
  132. /*****************************************************************************
  133.  * Catalogue functions
  134.  *****************************************************************************/
  135. static void CatalogLoad( xml_t *p_xml, const char *psz_filename )
  136. {
  137.     VLC_UNUSED(psz_filename);
  138.     msg_Dbg( p_xml, "catalog support not implemented" );
  139. }
  140. static void CatalogAdd( xml_t *p_xml, const char *psz_arg1,
  141.                           const char *psz_arg2, const char *psz_filename )
  142. {
  143.     VLC_UNUSED(p_xml); VLC_UNUSED(psz_arg1); VLC_UNUSED(psz_arg2);
  144.     VLC_UNUSED(psz_filename);
  145. }
  146. /*****************************************************************************
  147.  * Reader functions
  148.  *****************************************************************************/
  149. static xml_reader_t *ReaderCreate( xml_t *p_xml, stream_t *s )
  150. {
  151.     xml_reader_t *p_reader;
  152.     char *p_buffer, *p_new;
  153.     int i_size, i_pos = 0, i_buffer = 2048;
  154.     XTag *p_root;
  155.     /* Open and read file */
  156.     p_buffer = malloc( i_buffer );
  157.     if( p_buffer == NULL )
  158.         return NULL;
  159.     while( ( i_size = stream_Read( s, &p_buffer[i_pos], 2048 ) ) == 2048 )
  160.     {
  161.         i_pos += i_size;
  162.         i_buffer += i_size;
  163.         p_new = realloc( p_buffer, i_buffer );
  164.         if( !p_new )
  165.         {
  166.             free( p_buffer );
  167.             return NULL;
  168.         }
  169.         p_buffer = p_new;
  170.     }
  171.     if( i_pos + i_size == 0 )
  172.     {
  173.         msg_Dbg( p_xml, "empty XML" );
  174.         free( p_buffer );
  175.         return NULL;
  176.     }
  177.     p_buffer[ i_pos + i_size ] = ''; /* 0 terminated string */
  178.     p_root = xtag_new_parse( p_buffer, i_buffer );
  179.     if( !p_root )
  180.     {
  181.         msg_Warn( p_xml, "couldn't parse XML" );
  182.         free( p_buffer );
  183.         return NULL;
  184.     }
  185.     free( p_buffer );
  186.     p_reader = malloc( sizeof(xml_reader_t) );
  187.     if( !p_reader )
  188.         return NULL;
  189.     p_reader->p_sys = malloc( sizeof(xml_reader_sys_t) );
  190.     if( !p_reader->p_sys )
  191.     {
  192.         free( p_reader );
  193.         return NULL;
  194.     }
  195.     p_reader->p_sys->p_root = p_root;
  196.     p_reader->p_sys->p_curtag = NULL;
  197.     p_reader->p_sys->p_curattr = NULL;
  198.     p_reader->p_sys->b_endtag = false;
  199.     p_reader->p_xml = p_xml;
  200.     p_reader->pf_read = ReaderRead;
  201.     p_reader->pf_node_type = ReaderNodeType;
  202.     p_reader->pf_name = ReaderName;
  203.     p_reader->pf_value = ReaderValue;
  204.     p_reader->pf_next_attr = ReaderNextAttr;
  205.     p_reader->pf_use_dtd = ReaderUseDTD;
  206.     return p_reader;
  207. }
  208. static void ReaderDelete( xml_reader_t *p_reader )
  209. {
  210.     xtag_free( p_reader->p_sys->p_root );
  211.     free( p_reader->p_sys );
  212.     free( p_reader );
  213. }
  214. static int ReaderUseDTD ( xml_reader_t *p_reader, bool b_use )
  215. {
  216.     VLC_UNUSED(p_reader); VLC_UNUSED(b_use);
  217.     return VLC_EGENERIC;
  218. }
  219. static int ReaderRead( xml_reader_t *p_reader )
  220. {
  221.     XTag *p_child;
  222.     if( !p_reader->p_sys->p_curtag )
  223.     {
  224.         p_reader->p_sys->p_curtag = p_reader->p_sys->p_root;
  225.         return 1;
  226.     }
  227.     while( true )
  228.     {
  229.         if( (p_child = xtag_next_child( p_reader->p_sys->p_curtag, 0 )) )
  230.         {
  231.             p_reader->p_sys->p_curtag = p_child;
  232.             p_reader->p_sys->p_curattr = NULL;
  233.             p_reader->p_sys->b_endtag = false;
  234.             return 1;
  235.         }
  236.         if( p_reader->p_sys->p_curtag->name && /* no end tag for pcdata */
  237.             !p_reader->p_sys->b_endtag )
  238.         {
  239.             p_reader->p_sys->b_endtag = true;
  240.             return 1;
  241.         }
  242.         p_reader->p_sys->b_endtag = false;
  243.         if( !p_reader->p_sys->p_curtag->parent ) return 0;
  244.         p_reader->p_sys->p_curtag = p_reader->p_sys->p_curtag->parent;
  245.     }
  246.     return 0;
  247. }
  248. static int ReaderNodeType( xml_reader_t *p_reader )
  249. {
  250.     if( p_reader->p_sys->p_curtag->name && p_reader->p_sys->b_endtag )
  251.         return XML_READER_ENDELEM;
  252.     if( p_reader->p_sys->p_curtag->name )
  253.         return XML_READER_STARTELEM;
  254.     if( p_reader->p_sys->p_curtag->pcdata )
  255.         return XML_READER_TEXT;
  256.     return XML_READER_NONE;
  257. }
  258. static char *ReaderName( xml_reader_t *p_reader )
  259. {
  260.     const char *psz_name;
  261.     if( !p_reader->p_sys->p_curattr )
  262.     {
  263.         psz_name = xtag_get_name( p_reader->p_sys->p_curtag );
  264. #ifdef XTAG_DEBUG
  265.         fprintf( stderr, "TAG: %sn", psz_name );
  266. #endif
  267.     }
  268.     else
  269.         psz_name = ((XAttribute *)p_reader->p_sys->p_curattr->data)->name;
  270.     return psz_name ? strdup( psz_name ) : NULL;
  271. }
  272. static char *ReaderValue( xml_reader_t *p_reader )
  273. {
  274.     const char *psz_name;
  275.     if( p_reader->p_sys->p_curtag->pcdata )
  276.     {
  277. #ifdef XTAG_DEBUG
  278.         fprintf( stderr, "%sn", p_reader->p_sys->p_curtag->pcdata );
  279. #endif
  280.         return strdup( p_reader->p_sys->p_curtag->pcdata );
  281.     }
  282.     if( !p_reader->p_sys->p_curattr ) return NULL;
  283. #ifdef XTAG_DEBUG
  284.     fprintf( stderr, "%s=%sn", ((XAttribute *)p_reader->p_sys->p_curattr->data)->name,
  285.             ((XAttribute *)p_reader->p_sys->p_curattr->data)->value );
  286. #endif
  287.     psz_name = ((XAttribute *)p_reader->p_sys->p_curattr->data)->value;
  288.     return psz_name ? strdup( psz_name ) : NULL;
  289. }
  290. static int ReaderNextAttr( xml_reader_t *p_reader )
  291. {
  292.     if( !p_reader->p_sys->p_curattr )
  293.         p_reader->p_sys->p_curattr = p_reader->p_sys->p_curtag->attributes;
  294.     else if( p_reader->p_sys->p_curattr )
  295.         p_reader->p_sys->p_curattr = p_reader->p_sys->p_curattr->next;
  296.     return p_reader->p_sys->p_curattr ? VLC_SUCCESS : VLC_EGENERIC;
  297. }
  298. /*****************************************************************************
  299.  * XTAG parser functions
  300.  *****************************************************************************/
  301. static XList *xlist_append( XList *list, void *data )
  302. {
  303.     XList *l, *last;
  304.     l = (XList *)malloc( sizeof(XList) );
  305.     l->prev = l->next = NULL;
  306.     l->data = data;
  307.     if( !list )
  308.         return l;
  309.     for( last = list; last; last = last->next )
  310.     {
  311.         if( !last->next )
  312.             break;
  313.     }
  314.     if( last ) last->next = l;
  315.     l->prev = last;
  316.     return list;
  317. }
  318. static void xlist_free( XList *list )
  319. {
  320.     XList *l, *ln;
  321.     for( l = list; l; l = ln )
  322.     {
  323.         ln = l->next;
  324.         free( l );
  325.     }
  326. }
  327. /* Character classes */
  328. #define X_NONE           0
  329. #define X_WHITESPACE  1<<0
  330. #define X_OPENTAG     1<<1
  331. #define X_CLOSETAG    1<<2
  332. #define X_DQUOTE      1<<3
  333. #define X_SQUOTE      1<<4
  334. #define X_EQUAL       1<<5
  335. #define X_SLASH       1<<6
  336. #define X_QMARK       1<<7
  337. #define X_DASH        1<<8
  338. #define X_EMARK       1<<9
  339. static int xtag_cin( char c, int char_class )
  340. {
  341.     if( char_class & X_WHITESPACE ) if( isspace(c) ) return true;
  342.     if( char_class & X_OPENTAG )    if( c == '<' ) return true;
  343.     if( char_class & X_CLOSETAG )   if( c == '>' ) return true;
  344.     if( char_class & X_DQUOTE )     if( c == '"' ) return true;
  345.     if( char_class & X_SQUOTE )     if( c == ''' ) return true;
  346.     if( char_class & X_EQUAL )      if( c == '=' ) return true;
  347.     if( char_class & X_SLASH )      if( c == '/' ) return true;
  348.     if( char_class & X_QMARK )      if( c == '?' ) return true;
  349.     if( char_class & X_DASH  )      if( c == '-' ) return true;
  350.     if( char_class & X_EMARK )      if( c == '!' ) return true;
  351.     return false;
  352. }
  353. static int xtag_index( XTagParser *parser, int char_class )
  354. {
  355.     char *s = parser->start;
  356.     int i;
  357.     for( i = 0; s[i] && s != parser->end; i++ )
  358.     {
  359.         if( xtag_cin( s[i], char_class ) ) return i;
  360.     }
  361.     return -1;
  362. }
  363. static void xtag_skip_over( XTagParser *parser, int char_class )
  364. {
  365.     char *s = parser->start;
  366.     int i;
  367.     if( !parser->valid ) return;
  368.     for( i = 0; s[i] && s != parser->end; i++ )
  369.     {
  370.         if( !xtag_cin( s[i], char_class ) )
  371.         {
  372.             parser->start = &s[i];
  373.             return;
  374.         }
  375.     }
  376.     return;
  377. }
  378. static void xtag_skip_whitespace( XTagParser * parser )
  379. {
  380.     xtag_skip_over( parser, X_WHITESPACE );
  381. }
  382. static char *xtag_slurp_to( XTagParser *parser, int good_end, int bad_end )
  383. {
  384.     char *ret, *s = parser->start;
  385.     int xi;
  386.     if( !parser->valid ) return NULL;
  387.     xi = xtag_index( parser, good_end | bad_end );
  388.     if( xi > 0 && xtag_cin (s[xi], good_end) )
  389.     {
  390.         ret = malloc( xi+1 );
  391.         strncpy( ret, s, xi );
  392.         ret[xi] = '';
  393.         parser->start = &s[xi];
  394.         return ret;
  395.     }
  396.     return NULL;
  397. }
  398. static int xtag_assert_and_pass( XTagParser *parser, int char_class )
  399. {
  400.     char *s = parser->start;
  401.     if( !parser->valid ) return false;
  402.     if( !xtag_cin( s[0], char_class ) )
  403.     {
  404.         parser->valid = false;
  405.         return false;
  406.     }
  407.     parser->start = &s[1];
  408.     return true;
  409. }
  410. static char *xtag_slurp_quoted( XTagParser *parser )
  411. {
  412.     char * ret, *s;
  413.     int quote = X_DQUOTE; /* quote char to match on */
  414.     int xi;
  415.     if( !parser->valid ) return NULL;
  416.     xtag_skip_whitespace( parser );
  417.     s = parser->start;
  418.     if( xtag_cin( s[0], X_SQUOTE ) ) quote = X_SQUOTE;
  419.     if( !xtag_assert_and_pass( parser, quote ) ) return NULL;
  420.     s = parser->start;
  421.     for( xi = 0; s[xi]; xi++ )
  422.     {
  423.         if( xtag_cin( s[xi], quote ) )
  424.         {
  425.             if( !(xi > 1 && s[xi-1] == '\') ) break;
  426.         }
  427.     }
  428.     ret = malloc( xi+1 );
  429.     strncpy( ret, s, xi );
  430.     ret[xi] = '';
  431.     parser->start = &s[xi];
  432.     if( !xtag_assert_and_pass( parser, quote ) )
  433.     {
  434.         free( ret );
  435.         return NULL;
  436.     }
  437.     return ret;
  438. }
  439. static XAttribute *xtag_parse_attribute( XTagParser *parser )
  440. {
  441.     XAttribute *attr;
  442.     char *name, *value;
  443.     char *s;
  444.     if( !parser->valid )
  445.         return NULL;
  446.     xtag_skip_whitespace( parser );
  447.  
  448.     name = xtag_slurp_to( parser, X_WHITESPACE|X_EQUAL, X_SLASH|X_CLOSETAG );
  449.     if( !name )
  450.         return NULL;
  451.     xtag_skip_whitespace( parser );
  452.     s = parser->start;
  453.     if( !xtag_assert_and_pass( parser, X_EQUAL ) )
  454.     {
  455. #ifdef XTAG_DEBUG
  456.         fprintf( stderr, "xtag: attr failed EQUAL on <%s>n", name );
  457. #endif
  458.         goto err_free_name;
  459.     }
  460.     xtag_skip_whitespace( parser );
  461.     value = xtag_slurp_quoted( parser );
  462.     if( value == NULL )
  463.     {
  464. #ifdef XTAG_DEBUG
  465.         fprintf (stderr, "Got NULL quoted attribute valuen");
  466. #endif
  467.         goto err_free_name;
  468.     }
  469.     attr = malloc( sizeof (*attr) );
  470.     attr->name = name;
  471.     attr->value = value;
  472.     return attr;
  473.  err_free_name:
  474.     free (name);
  475.     parser->valid = false;
  476.     return NULL;
  477. }
  478. static XTag *xtag_parse_tag( XTagParser *parser )
  479. {
  480.     XTag *tag, *inner;
  481.     XAttribute *attr;
  482.     char *name;
  483.     char *pcdata;
  484.     char *s;
  485.      int xi;
  486.     if( !parser->valid ) return NULL;
  487.     s = parser->start;
  488.     /* if this starts a comment tag, skip until end */
  489.     if( (parser->end - parser->start) > 7 &&
  490.           xtag_cin( s[0], X_OPENTAG ) && xtag_cin( s[1], X_EMARK ) &&
  491.         xtag_cin( s[2], X_DASH ) && xtag_cin( s[3], X_DASH ) )
  492.     {
  493.         parser->start = s = &s[4];
  494.         while( (xi = xtag_index( parser, X_DASH )) >= 0 )
  495.         {
  496.             parser->start = s = &s[xi+1];
  497.             if( xtag_cin( s[0], X_DASH ) && xtag_cin( s[1], X_CLOSETAG ) )
  498.             {
  499.                 parser->start = &s[2];
  500.                 xtag_skip_whitespace( parser );
  501.                 return xtag_parse_tag( parser );
  502.             }
  503.         }
  504.         return NULL;
  505.     }
  506.     /* ignore processing instructions '<?' ... '?>' */
  507.     if( (parser->end - parser->start) > 4 &&
  508.           xtag_cin( s[0], X_OPENTAG ) && xtag_cin( s[1], X_QMARK ) )
  509.     {
  510.         parser->start = s = &s[2];
  511.         while ((xi = xtag_index( parser, X_QMARK )) >= 0) {
  512.             if (xtag_cin( s[xi+1], X_CLOSETAG )) {
  513.                 parser->start = &s[xi+2];
  514.                 xtag_skip_whitespace( parser );
  515.                 return xtag_parse_tag( parser );
  516.             }
  517.         }
  518.         return NULL;
  519.     }
  520.     /* ignore doctype  '<!DOCTYPE' ... '>' */
  521.     if ( (parser->end - parser->start) > 8 &&
  522.             !strncmp( s, "<!DOCTYPE", 9 ) ) {
  523.         xi = xtag_index( parser, X_CLOSETAG );
  524.         if ( xi > 0 ) {
  525.             parser->start = s = &s[xi+1];
  526.             xtag_skip_whitespace( parser );
  527.             return xtag_parse_tag( parser );
  528.         }
  529.         else {
  530.             return NULL;
  531.         }
  532.     }
  533.     if( (pcdata = xtag_slurp_to( parser, X_OPENTAG, X_NONE )) != NULL )
  534.     {
  535.         tag = malloc( sizeof(*tag) );
  536.         tag->name = NULL;
  537.         tag->pcdata = pcdata;
  538.         tag->parent = parser->current_tag;
  539.         tag->attributes = NULL;
  540.         tag->children = NULL;
  541.         tag->current_child = NULL;
  542.         return tag;
  543.     }
  544.     /* if this starts a close tag, return NULL and let the parent take it */
  545.     if( xtag_cin( s[0], X_OPENTAG ) && xtag_cin( s[1], X_SLASH ) )
  546.         return NULL;
  547.     /* parse CDATA content */
  548.     if ( (parser->end - parser->start) > 8 &&
  549.             !strncmp( s, "<![CDATA[", 9 ) ) {
  550.         parser->start = s = &s[9];
  551.         while (parser->end - s > 2) {
  552.             if (strncmp( s, "]]>", 3 ) == 0) {
  553.                 if ( !(tag = malloc( sizeof(*tag))) ) return NULL;
  554.                 if ( !(pcdata = malloc( s - parser->start + 1)) )
  555.                 {
  556.                     free( tag );
  557.                     return NULL;
  558.                 }
  559.                 strncpy( pcdata, parser->start, s - parser->start );
  560.                 pcdata[s - parser->start]='';
  561.                 parser->start = s = &s[3];
  562.                 tag->name = NULL;
  563.                 tag->pcdata = pcdata;
  564.                 tag->parent = parser->current_tag;
  565.                 tag->attributes = NULL;
  566.                 tag->children = NULL;
  567.                 tag->current_child = NULL;
  568.                 return tag;
  569.             }
  570.             else {
  571.                 s++;
  572.             }
  573.         }
  574.         return NULL;
  575.     }
  576.     if( !xtag_assert_and_pass( parser, X_OPENTAG ) ) return NULL;
  577.     name = xtag_slurp_to( parser, X_WHITESPACE|X_SLASH|X_CLOSETAG, X_NONE );
  578.     if( name == NULL ) return NULL;
  579. #ifdef XTAG_DEBUG
  580.     fprintf (stderr, "<%s ...n", name);
  581. #endif
  582.     tag = malloc( sizeof(*tag) );
  583.     tag->name = name;
  584.     tag->pcdata = NULL;
  585.     tag->parent = parser->current_tag;
  586.     tag->attributes = NULL;
  587.     tag->children = NULL;
  588.     tag->current_child = NULL;
  589.     s = parser->start;
  590.     if( xtag_cin( s[0], X_WHITESPACE ) )
  591.     {
  592.         while( (attr = xtag_parse_attribute( parser )) != NULL )
  593.         {
  594.             tag->attributes = xlist_append( tag->attributes, attr );
  595.         }
  596.     }
  597.     xtag_skip_whitespace( parser );
  598.     s = parser->start;
  599.     if( xtag_cin( s[0], X_CLOSETAG ) )
  600.     {
  601.         parser->current_tag = tag;
  602.         xtag_assert_and_pass( parser, X_CLOSETAG );
  603.         while( (inner = xtag_parse_tag( parser ) ) != NULL )
  604.         {
  605.             tag->children = xlist_append( tag->children, inner );
  606.         }
  607.         parser->current_tag = tag->parent;
  608.         xtag_skip_whitespace( parser );
  609.         xtag_assert_and_pass( parser, X_OPENTAG );
  610.         xtag_assert_and_pass( parser, X_SLASH );
  611.         name = xtag_slurp_to( parser, X_WHITESPACE | X_CLOSETAG, X_NONE );
  612.         if( name )
  613.         {
  614.             if( strcmp( name, tag->name ) )
  615.             {
  616. #ifdef XTAG_DEBUG
  617.                 fprintf (stderr, "got %s expected %sn", name, tag->name);
  618. #endif
  619.                 parser->valid = false;
  620.             }
  621.             free( name );
  622.         }
  623.         xtag_skip_whitespace( parser );
  624.         xtag_assert_and_pass( parser, X_CLOSETAG );
  625.         xtag_skip_whitespace( parser );
  626.     }
  627.     else
  628.     {
  629.         xtag_assert_and_pass( parser, X_SLASH );
  630.         xtag_assert_and_pass( parser, X_CLOSETAG );
  631.         xtag_skip_whitespace( parser );
  632.     }
  633.     return tag;
  634. }
  635. static void xtag_free( XTag *xtag )
  636. {
  637.     XList *l;
  638.     XAttribute *attr;
  639.     XTag *child;
  640.     if( !xtag )
  641.         return;
  642.     free( xtag->name );
  643.     free( xtag->pcdata );
  644.     for( l = xtag->attributes; l; l = l->next )
  645.     {
  646.         if( (attr = (XAttribute *)l->data) != NULL )
  647.         {
  648.             free( attr->name );
  649.             free( attr->value );
  650.             free( attr );
  651.         }
  652.     }
  653.     xlist_free( xtag->attributes );
  654.     for( l = xtag->children; l; l = l->next )
  655.     {
  656.         child = (XTag *)l->data;
  657.         xtag_free( child );
  658.     }
  659.     xlist_free( xtag->children );
  660.     free( xtag );
  661. }
  662. static XTag *xtag_new_parse( const char *s, int n )
  663. {
  664.     XTagParser parser;
  665.     XTag *tag, *ttag, *wrapper;
  666.     parser.valid = true;
  667.     parser.current_tag = NULL;
  668.     parser.start = (char *)s;
  669.     if( n == -1 ) parser.end = NULL;
  670.     else if( n == 0 )
  671.     {
  672. #ifdef XTAG_DEBUG
  673.         fprintf (stderr, "empty buffern");
  674. #endif
  675.         return NULL;
  676.     }
  677.     else parser.end = (char *)&s[n];
  678.     /* can't have whitespace pcdata outside rootnode */
  679.     xtag_skip_whitespace( &parser );
  680.     tag = xtag_parse_tag( &parser );
  681.     if( !parser.valid )
  682.     {
  683. #ifdef XTAG_DEBUG
  684.         fprintf (stderr, "invalid filen");
  685. #endif
  686.         xtag_free( tag );
  687.         return NULL;
  688.     }
  689.     if( (ttag = xtag_parse_tag( &parser )) != NULL )
  690.     {
  691.         if( !parser.valid )
  692.         {
  693.             xtag_free( ttag );
  694.             return tag;
  695.         }
  696.         wrapper = malloc( sizeof(XTag) );
  697.         wrapper->name = NULL;
  698.         wrapper->pcdata = NULL;
  699.         wrapper->parent = NULL;
  700.         wrapper->attributes = NULL;
  701.         wrapper->children = NULL;
  702.         wrapper->current_child = NULL;
  703.         wrapper->children = xlist_append( wrapper->children, tag );
  704.         wrapper->children = xlist_append( wrapper->children, ttag );
  705.         while( (ttag = xtag_parse_tag( &parser )) != NULL )
  706.         {
  707.             if( !parser.valid )
  708.             {
  709.                 xtag_free( ttag );
  710.                 return wrapper;
  711.             }
  712.             wrapper->children = xlist_append( wrapper->children, ttag );
  713.         }
  714.         return wrapper;
  715.     }
  716.     return tag;
  717. }
  718. static char *xtag_get_name( XTag *xtag )
  719. {
  720.     return xtag ? xtag->name : NULL;
  721. }
  722. #if 0
  723. static char *xtag_get_pcdata( XTag *xtag )
  724. {
  725.     XList *l;
  726.     XTag *child;
  727.     if( xtag == NULL ) return NULL;
  728.     for( l = xtag->children; l; l = l->next )
  729.     {
  730.         child = (XTag *)l->data;
  731.         if( child->pcdata != NULL )
  732.         {
  733.             return child->pcdata;
  734.         }
  735.     }
  736.     return NULL;
  737. }
  738. static char *xtag_get_attribute( XTag *xtag, char *attribute )
  739. {
  740.     XList *l;
  741.     XAttribute *attr;
  742.     if( xtag == NULL ) return NULL;
  743.     for( l = xtag->attributes; l; l = l->next )
  744.     {
  745.         if( (attr = (XAttribute *)l->data) != NULL )
  746.         {
  747.             if( !strcmp( attr->name, attribute ) ) return attr->value;
  748.         }
  749.     }
  750.     return NULL;
  751. }
  752. #endif
  753. static XTag *xtag_first_child( XTag *xtag, char *name )
  754. {
  755.     XList *l;
  756.     XTag *child;
  757.     if( xtag == NULL ) return NULL;
  758.     if( (l = xtag->children) == NULL ) return NULL;
  759.     if( name == NULL )
  760.     {
  761.         xtag->current_child = l;
  762.         return (XTag *)l->data;
  763.     }
  764.     for( ; l; l = l->next )
  765.     {
  766.         child = (XTag *)l->data;
  767.         if( !strcmp( child->name, name ) )
  768.         {
  769.             xtag->current_child = l;
  770.             return child;
  771.         }
  772.     }
  773.     xtag->current_child = NULL;
  774.     return NULL;
  775. }
  776. static XTag *xtag_next_child( XTag *xtag, char *name )
  777. {
  778.     XList *l;
  779.     XTag *child;
  780.     if( xtag == NULL ) return NULL;
  781.     if( (l = xtag->current_child) == NULL )
  782.         return xtag_first_child( xtag, name );
  783.     if( (l = l->next) == NULL ) return NULL;
  784.     if( name == NULL )
  785.     {
  786.         xtag->current_child = l;
  787.         return (XTag *)l->data;
  788.     }
  789.     for( ; l; l = l->next )
  790.     {
  791.         child = (XTag *)l->data;
  792.         if( !strcmp( child->name, name ) )
  793.         {
  794.             xtag->current_child = l;
  795.             return child;
  796.         }
  797.     }
  798.     xtag->current_child = NULL;
  799.     return NULL;
  800. }
  801. #if 0
  802. /*
  803.  * This snprints function takes a variable list of char *, the last of
  804.  * which must be NULL, and prints each in turn to buf.
  805.  * Returns C99-style total length that would have been written, even if
  806.  * this is larger than n.
  807.  */
  808. static int xtag_snprints( char *buf, int n, ... )
  809. {
  810.     va_list ap;
  811.     char *s;
  812.     int len, to_copy, total = 0;
  813.     va_start( ap, n );
  814.  
  815.     for( s = va_arg( ap, char * ); s; s = va_arg( ap, char *) )
  816.     {
  817.         len = strlen (s);
  818.         if( (to_copy = __MIN(n, len) ) > 0 )
  819.         {
  820.             memcpy( buf, s, to_copy );
  821.             buf += to_copy;
  822.             n -= to_copy;
  823.         }
  824.         total += len;
  825.     }
  826.     va_end( ap );
  827.     return total;
  828. }
  829. static int xtag_snprint( char *buf, int n, XTag *xtag )
  830. {
  831.     int nn, written = 0;
  832.     XList *l;
  833.     XAttribute *attr;
  834.     XTag *child;
  835. #define FORWARD(N) 
  836.     buf += __MIN(n, N); 
  837.     n = __MAX(n-N, 0);  
  838.     written += N;
  839.     if( xtag == NULL )
  840.     {
  841.         if( n > 0 ) buf[0] = '';
  842.         return 0;
  843.     }
  844.     if( xtag->pcdata )
  845.     {
  846.         nn = xtag_snprints( buf, n, xtag->pcdata, NULL );
  847.         FORWARD( nn );
  848.         return written;
  849.     }
  850.     if( xtag->name )
  851.     {
  852.         nn = xtag_snprints( buf, n, "<", xtag->name, NULL );
  853.         FORWARD( nn );
  854.         for( l = xtag->attributes; l; l = l->next )
  855.         {
  856.             attr = (XAttribute *)l->data;
  857.  
  858.             nn = xtag_snprints( buf, n, " ", attr->name, "="", attr->value,
  859.                                 """, NULL);
  860.             FORWARD( nn );
  861.         }
  862.         if( xtag->children == NULL )
  863.         {
  864.             nn = xtag_snprints ( buf, n, "/>", NULL );
  865.             FORWARD( nn );
  866.             return written;
  867.         }
  868.         nn = xtag_snprints( buf, n, ">", NULL );
  869.         FORWARD( nn );
  870.     }
  871.     for( l = xtag->children; l; l = l->next )
  872.     {
  873.         child = (XTag *)l->data;
  874.         nn = xtag_snprint( buf, n, child );
  875.         FORWARD( nn );
  876.     }
  877.     if( xtag->name )
  878.     {
  879.         nn = xtag_snprints( buf, n, "</", xtag->name, ">", NULL );
  880.         FORWARD( nn );
  881.     }
  882.     return written;
  883. }
  884. #endif