Commit d2f2be39 authored by Rémi Denis-Courmont's avatar Rémi Denis-Courmont

Remove xtag

It is behaving a little bit different from the libxml parser which has
caused weird bugs in the past. Anyway, everybody has libxml (I am
planning to write an Expat parser too but not there yet).
parent 7ca10966
......@@ -359,7 +359,6 @@ $Id$
* xml: LibXML xml parser
* xosd: X On Screen Display interface
* xscreensaver: Xscreensaver inhibition
* xtag: XTAG xml parser
* yuv: yuv video output
* yuvp: YUVP to YUVA/RGBA chroma converter
* yuy2_i420: yuy2 to 4:2:0 conversions functions
......
SOURCES_xtag = xtag.c
SOURCES_xml = libxml.c
libvlc_LTLIBRARIES += libxtag_plugin.la
/*****************************************************************************
* xtag.c : a trivial parser for XML-like tags
*****************************************************************************
* Copyright (C) 2003-2004 Commonwealth Scientific and Industrial Research
* Organisation (CSIRO) Australia
* Copyright (C) 2000-2004 the VideoLAN team
*
* $Id$
*
* Authors: Conrad Parker <Conrad.Parker@csiro.au>
* Andre Pang <Andre.Pang@csiro.au>
* Gildas Bazin <gbazin@videolan.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <vlc_common.h>
#include <vlc_plugin.h>
#include <vlc_xml.h>
#include <vlc_block.h>
#include <vlc_stream.h>
#include <vlc_memory.h>
#include <ctype.h>
#include <stdarg.h>
#undef XTAG_DEBUG
typedef struct _XList
{
struct _XList *prev;
struct _XList *next;
void *data;
} XList;
/*
* struct XTag is kind of a union ... it normally represents a whole
* tag (and its children), but it could alternatively represent some
* PCDATA. Basically, if tag->pcdata is non-NULL, interpret only it and
* ignore the name, attributes and inner_tags.
*/
typedef struct _XTag
{
char *name;
char *pcdata;
struct _XTag *parent;
XList *attributes;
XList *children;
XList *current_child;
} XTag;
typedef struct _XAttribute
{
char *name;
char *value;
} XAttribute;
typedef struct _XTagParser
{
int valid; /* boolean */
XTag *current_tag;
char *start;
char *end;
} XTagParser;
/*****************************************************************************
* Module descriptor
*****************************************************************************/
static int ReaderOpen( vlc_object_t * );
static void ReaderClose( vlc_object_t * );
vlc_module_begin ()
set_description( N_("Simple XML Parser") )
set_capability( "xml reader", 5 )
set_callbacks( ReaderOpen, ReaderClose )
vlc_module_end ()
struct xml_reader_sys_t
{
XTag *p_root; /* Root tag */
XTag *p_curtag; /* Current tag */
XList *p_curattr; /* Current attribute */
bool b_endtag;
};
static int ReaderRead( xml_reader_t * );
static int ReaderNodeType( xml_reader_t * );
static char *ReaderName( xml_reader_t * );
static char *ReaderValue( xml_reader_t * );
static int ReaderNextAttr( xml_reader_t * );
static int ReaderUseDTD ( xml_reader_t * );
static XTag *xtag_new_parse( const char *, int );
static char *xtag_get_name( XTag * );
#if 0
static char *xtag_get_pcdata( XTag * );
static char *xtag_get_attribute( XTag *, char * );
#endif
static XTag *xtag_first_child( XTag *, char * );
static XTag *xtag_next_child( XTag *, char * );
static void xtag_free( XTag * );
#if 0
static int xtag_snprint( char *, int, XTag * );
#endif
/*****************************************************************************
* Reader functions
*****************************************************************************/
static int ReaderOpen( vlc_object_t *p_this )
{
xml_reader_t *p_reader = (xml_reader_t *)p_this;
stream_t *s = p_reader->p_stream;
char *p_buffer;
int i_size, i_pos = 0, i_buffer = 2048;
XTag *p_root;
/* Open and read file */
p_buffer = malloc( i_buffer );
if( p_buffer == NULL )
return VLC_ENOMEM;
while( ( i_size = stream_Read( s, &p_buffer[i_pos], 2048 ) ) == 2048 )
{
i_pos += i_size;
i_buffer += i_size;
p_buffer = realloc_or_free( p_buffer, i_buffer );
if( !p_buffer )
return VLC_ENOMEM;
}
if( i_pos + i_size == 0 )
{
msg_Dbg( p_this, "empty XML" );
free( p_buffer );
return VLC_ENOMEM;
}
p_buffer[ i_pos + i_size ] = '\0'; /* 0 terminated string */
p_root = xtag_new_parse( p_buffer, i_buffer );
free( p_buffer );
if( !p_root )
{
msg_Warn( p_this, "couldn't parse XML" );
return VLC_ENOMEM;
}
p_reader->p_sys = malloc( sizeof(xml_reader_sys_t) );
if( !p_reader->p_sys )
{
xtag_free( p_root );
return VLC_ENOMEM;
}
p_reader->p_sys->p_root = p_root;
p_reader->p_sys->p_curtag = NULL;
p_reader->p_sys->p_curattr = NULL;
p_reader->p_sys->b_endtag = false;
p_reader->pf_read = ReaderRead;
p_reader->pf_node_type = ReaderNodeType;
p_reader->pf_name = ReaderName;
p_reader->pf_value = ReaderValue;
p_reader->pf_next_attr = ReaderNextAttr;
p_reader->pf_use_dtd = ReaderUseDTD;
return VLC_SUCCESS;
}
static void ReaderClose( vlc_object_t *p_this )
{
xml_reader_t *p_reader = (xml_reader_t *)p_this;
xtag_free( p_reader->p_sys->p_root );
free( p_reader->p_sys );
}
static int ReaderUseDTD ( xml_reader_t *p_reader )
{
VLC_UNUSED(p_reader);
return VLC_EGENERIC;
}
static int ReaderRead( xml_reader_t *p_reader )
{
XTag *p_child;
if( !p_reader->p_sys->p_curtag )
{
p_reader->p_sys->p_curtag = p_reader->p_sys->p_root;
return 1;
}
while( true )
{
if( (p_child = xtag_next_child( p_reader->p_sys->p_curtag, 0 )) )
{
p_reader->p_sys->p_curtag = p_child;
p_reader->p_sys->p_curattr = NULL;
p_reader->p_sys->b_endtag = false;
return 1;
}
if( p_reader->p_sys->p_curtag->name && /* no end tag for pcdata */
!p_reader->p_sys->b_endtag )
{
p_reader->p_sys->b_endtag = true;
return 1;
}
p_reader->p_sys->b_endtag = false;
if( !p_reader->p_sys->p_curtag->parent ) return 0;
p_reader->p_sys->p_curtag = p_reader->p_sys->p_curtag->parent;
}
return 0;
}
static int ReaderNodeType( xml_reader_t *p_reader )
{
if( p_reader->p_sys->p_curtag->name && p_reader->p_sys->b_endtag )
return XML_READER_ENDELEM;
if( p_reader->p_sys->p_curtag->name )
return XML_READER_STARTELEM;
if( p_reader->p_sys->p_curtag->pcdata )
return XML_READER_TEXT;
return XML_READER_NONE;
}
static char *ReaderName( xml_reader_t *p_reader )
{
const char *psz_name;
if( !p_reader->p_sys->p_curattr )
{
psz_name = xtag_get_name( p_reader->p_sys->p_curtag );
#ifdef XTAG_DEBUG
fprintf( stderr, "TAG: %s\n", psz_name );
#endif
}
else
psz_name = ((XAttribute *)p_reader->p_sys->p_curattr->data)->name;
return psz_name ? strdup( psz_name ) : NULL;
}
static char *ReaderValue( xml_reader_t *p_reader )
{
const char *psz_name;
if( p_reader->p_sys->p_curtag->pcdata )
{
#ifdef XTAG_DEBUG
fprintf( stderr, "%s\n", p_reader->p_sys->p_curtag->pcdata );
#endif
return strdup( p_reader->p_sys->p_curtag->pcdata );
}
if( !p_reader->p_sys->p_curattr ) return NULL;
#ifdef XTAG_DEBUG
fprintf( stderr, "%s=%s\n", ((XAttribute *)p_reader->p_sys->p_curattr->data)->name,
((XAttribute *)p_reader->p_sys->p_curattr->data)->value );
#endif
psz_name = ((XAttribute *)p_reader->p_sys->p_curattr->data)->value;
return psz_name ? strdup( psz_name ) : NULL;
}
static int ReaderNextAttr( xml_reader_t *p_reader )
{
if( !p_reader->p_sys->p_curattr )
p_reader->p_sys->p_curattr = p_reader->p_sys->p_curtag->attributes;
else if( p_reader->p_sys->p_curattr )
p_reader->p_sys->p_curattr = p_reader->p_sys->p_curattr->next;
return p_reader->p_sys->p_curattr ? VLC_SUCCESS : VLC_EGENERIC;
}
/*****************************************************************************
* XTAG parser functions
*****************************************************************************/
static XList *xlist_append( XList *list, void *data )
{
XList *l, *last;
l = (XList *)xmalloc( sizeof(XList) );
l->prev = l->next = NULL;
l->data = data;
if( !list )
return l;
/* Find the last element */
last = list;
while( last->next )
last = last->next;
last->next = l;
l->prev = last;
return list;
}
static void xlist_free( XList *list )
{
XList *l, *ln;
for( l = list; l; l = ln )
{
ln = l->next;
free( l );
}
}
/* Character classes */
#define X_NONE 0
#define X_WHITESPACE 1<<0
#define X_OPENTAG 1<<1
#define X_CLOSETAG 1<<2
#define X_DQUOTE 1<<3
#define X_SQUOTE 1<<4
#define X_EQUAL 1<<5
#define X_SLASH 1<<6
#define X_QMARK 1<<7
#define X_DASH 1<<8
#define X_EMARK 1<<9
static int xtag_cin( char c, int char_class )
{
if( char_class & X_WHITESPACE ) if( isspace(c) ) return true;
if( char_class & X_OPENTAG ) if( c == '<' ) return true;
if( char_class & X_CLOSETAG ) if( c == '>' ) return true;
if( char_class & X_DQUOTE ) if( c == '"' ) return true;
if( char_class & X_SQUOTE ) if( c == '\'' ) return true;
if( char_class & X_EQUAL ) if( c == '=' ) return true;
if( char_class & X_SLASH ) if( c == '/' ) return true;
if( char_class & X_QMARK ) if( c == '?' ) return true;
if( char_class & X_DASH ) if( c == '-' ) return true;
if( char_class & X_EMARK ) if( c == '!' ) return true;
return false;
}
static int xtag_index( XTagParser *parser, int char_class )
{
char *s = parser->start;
int i;
for( i = 0; s[i] && s != parser->end; i++ )
{
if( xtag_cin( s[i], char_class ) ) return i;
}
return -1;
}
static void xtag_skip_over( XTagParser *parser, int char_class )
{
char *s = parser->start;
int i;
if( !parser->valid ) return;
for( i = 0; s[i] && s != parser->end; i++ )
{
if( !xtag_cin( s[i], char_class ) )
{
parser->start = &s[i];
return;
}
}
return;
}
static void xtag_skip_whitespace( XTagParser * parser )
{
xtag_skip_over( parser, X_WHITESPACE );
}
static char *xtag_slurp_to( XTagParser *parser, int good_end, int bad_end )
{
char *ret, *s = parser->start;
int xi;
if( !parser->valid ) return NULL;
xi = xtag_index( parser, good_end | bad_end );
if( xi > 0 && xtag_cin (s[xi], good_end) )
{
ret = xmalloc( xi+1 );
strncpy( ret, s, xi );
ret[xi] = '\0';
parser->start = &s[xi];
return ret;
}
return NULL;
}
static int xtag_assert_and_pass( XTagParser *parser, int char_class )
{
char *s = parser->start;
if( !parser->valid ) return false;
if( !xtag_cin( s[0], char_class ) )
{
parser->valid = false;
return false;
}
parser->start = &s[1];
return true;
}
static char *xtag_slurp_quoted( XTagParser *parser )
{
char * ret, *s;
int quote = X_DQUOTE; /* quote char to match on */
int xi;
if( !parser->valid ) return NULL;
xtag_skip_whitespace( parser );
s = parser->start;
if( xtag_cin( s[0], X_SQUOTE ) ) quote = X_SQUOTE;
if( !xtag_assert_and_pass( parser, quote ) ) return NULL;
s = parser->start;
for( xi = 0; s[xi]; xi++ )
{
if( xtag_cin( s[xi], quote ) )
{
if( !(xi > 1 && s[xi-1] == '\\') ) break;
}
}
ret = xmalloc( xi+1 );
strncpy( ret, s, xi );
ret[xi] = '\0';
parser->start = &s[xi];
if( !xtag_assert_and_pass( parser, quote ) )
{
free( ret );
return NULL;
}
return ret;
}
static XAttribute *xtag_parse_attribute( XTagParser *parser )
{
XAttribute *attr;
char *name, *value;
char *s;
if( !parser->valid )
return NULL;
xtag_skip_whitespace( parser );
name = xtag_slurp_to( parser, X_WHITESPACE|X_EQUAL, X_SLASH|X_CLOSETAG );
if( !name )
return NULL;
xtag_skip_whitespace( parser );
s = parser->start;
if( !xtag_assert_and_pass( parser, X_EQUAL ) )
{
#ifdef XTAG_DEBUG
fprintf( stderr, "xtag: attr failed EQUAL on <%s>\n", name );
#endif
goto err_free_name;
}
xtag_skip_whitespace( parser );
value = xtag_slurp_quoted( parser );
if( value == NULL )
{
#ifdef XTAG_DEBUG
fprintf (stderr, "Got NULL quoted attribute value\n");
#endif
goto err_free_name;
}
attr = xmalloc( sizeof (*attr) );
attr->name = name;
attr->value = value;
return attr;
err_free_name:
free (name);
parser->valid = false;
return NULL;
}
static XTag *xtag_parse_tag( XTagParser *parser )
{
XTag *tag, *inner;
XAttribute *attr;
char *name;
char *pcdata;
char *s;
int xi;
if( !parser->valid ) return NULL;
s = parser->start;
/* if this starts a comment tag, skip until end */
if( (parser->end - parser->start) > 7 &&
xtag_cin( s[0], X_OPENTAG ) && xtag_cin( s[1], X_EMARK ) &&
xtag_cin( s[2], X_DASH ) && xtag_cin( s[3], X_DASH ) )
{
parser->start = s = &s[4];
while( (xi = xtag_index( parser, X_DASH )) >= 0 )
{
parser->start = s = &s[xi+1];
if( xtag_cin( s[0], X_DASH ) && xtag_cin( s[1], X_CLOSETAG ) )
{
parser->start = &s[2];
xtag_skip_whitespace( parser );
return xtag_parse_tag( parser );
}
}
return NULL;
}
/* ignore processing instructions '<?' ... '?>' */
if( (parser->end - parser->start) > 4 &&
xtag_cin( s[0], X_OPENTAG ) && xtag_cin( s[1], X_QMARK ) )
{
parser->start = s = &s[2];
while ((xi = xtag_index( parser, X_QMARK )) >= 0) {
if (xtag_cin( s[xi+1], X_CLOSETAG )) {
parser->start = &s[xi+2];
xtag_skip_whitespace( parser );
return xtag_parse_tag( parser );
}
}
return NULL;
}
/* ignore doctype '<!DOCTYPE' ... '>' */
if ( (parser->end - parser->start) > 8 &&
!strncmp( s, "<!DOCTYPE", 9 ) ) {
xi = xtag_index( parser, X_CLOSETAG );
if ( xi > 0 ) {
parser->start = &s[xi+1];
xtag_skip_whitespace( parser );
return xtag_parse_tag( parser );
}
else {
return NULL;
}
}
if( (pcdata = xtag_slurp_to( parser, X_OPENTAG, X_NONE )) != NULL )
{
tag = xmalloc( sizeof(*tag) );
tag->name = NULL;
tag->pcdata = pcdata;
tag->parent = parser->current_tag;
tag->attributes = NULL;
tag->children = NULL;
tag->current_child = NULL;
return tag;
}
/* if this starts a close tag, return NULL and let the parent take it */
if( xtag_cin( s[0], X_OPENTAG ) && xtag_cin( s[1], X_SLASH ) )
return NULL;
/* parse CDATA content */
if ( (parser->end - parser->start) > 8 &&
!strncmp( s, "<![CDATA[", 9 ) ) {
parser->start = s = &s[9];
while (parser->end - s > 2) {
if (strncmp( s, "]]>", 3 ) == 0) {
if ( !(tag = malloc( sizeof(*tag))) ) return NULL;
if ( !(pcdata = malloc( s - parser->start + 1)) )
{
free( tag );
return NULL;
}
strncpy( pcdata, parser->start, s - parser->start );
pcdata[s - parser->start]='\0';
parser->start = &s[3];
tag->name = NULL;
tag->pcdata = pcdata;
tag->parent = parser->current_tag;
tag->attributes = NULL;
tag->children = NULL;
tag->current_child = NULL;
return tag;
}
else {
s++;
}
}
return NULL;
}
if( !xtag_assert_and_pass( parser, X_OPENTAG ) ) return NULL;
name = xtag_slurp_to( parser, X_WHITESPACE|X_SLASH|X_CLOSETAG, X_NONE );
if( name == NULL ) return NULL;
#ifdef XTAG_DEBUG
fprintf (stderr, "<%s ...\n", name);
#endif
tag = xmalloc( sizeof(*tag) );
tag->name = name;
tag->pcdata = NULL;
tag->parent = parser->current_tag;
tag->attributes = NULL;
tag->children = NULL;
tag->current_child = NULL;
s = parser->start;
if( xtag_cin( s[0], X_WHITESPACE ) )
{
while( (attr = xtag_parse_attribute( parser )) != NULL )
{
tag->attributes = xlist_append( tag->attributes, attr );
}
}
xtag_skip_whitespace( parser );
s = parser->start;
if( xtag_cin( s[0], X_CLOSETAG ) )
{
parser->current_tag = tag;
xtag_assert_and_pass( parser, X_CLOSETAG );
while( (inner = xtag_parse_tag( parser ) ) != NULL )
{
tag->children = xlist_append( tag->children, inner );
}
parser->current_tag = tag->parent;
xtag_skip_whitespace( parser );
xtag_assert_and_pass( parser, X_OPENTAG );
xtag_assert_and_pass( parser, X_SLASH );
name = xtag_slurp_to( parser, X_WHITESPACE | X_CLOSETAG, X_NONE );
if( name )
{
if( strcmp( name, tag->name ) )
{
#ifdef XTAG_DEBUG
fprintf (stderr, "got %s expected %s\n", name, tag->name);
#endif
parser->valid = false;
}
free( name );
}
xtag_skip_whitespace( parser );
xtag_assert_and_pass( parser, X_CLOSETAG );
xtag_skip_whitespace( parser );
}
else
{
xtag_assert_and_pass( parser, X_SLASH );
xtag_assert_and_pass( parser, X_CLOSETAG );
xtag_skip_whitespace( parser );
}
return tag;
}
static void xtag_free( XTag *xtag )
{
XList *l;
XAttribute *attr;
XTag *child;
if( !xtag )
return;