X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fdemux%2Fplaylist%2Fxspf.c;h=86067596a87a0fa82b4085bf719a8a096dad839b;hb=bc53b5d44c9283b95000e711d861f1b983a24b01;hp=ed1ed924a83025f54ac0eda0d1ac0f21eebd143b;hpb=b4feb9f9a326f3fc58a52b315ba34ad9d083263b;p=vlc diff --git a/modules/demux/playlist/xspf.c b/modules/demux/playlist/xspf.c index ed1ed924a8..86067596a8 100644 --- a/modules/demux/playlist/xspf.c +++ b/modules/demux/playlist/xspf.c @@ -1,25 +1,25 @@ /******************************************************************************* * xspf.c : XSPF playlist import functions ******************************************************************************* - * Copyright (C) 2006 the VideoLAN team + * Copyright (C) 2006-2011 VLC authors and VideoLAN * $Id$ * * Authors: Daniel Stränger * Yoann Peronneau * - * 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 + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 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. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser 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. + * You should have received a copy of the GNU Lesser 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. ******************************************************************************/ /** * \file modules/demux/playlist/xspf.c @@ -36,9 +36,39 @@ #include #include #include -#include "xspf.h" #include "playlist.h" +#define FREE_VALUE() do { free(psz_value);psz_value=NULL; } while(0) + +#define SIMPLE_INTERFACE (input_item_t *p_input,\ + const char *psz_name,\ + char *psz_value) +#define COMPLEX_INTERFACE (demux_t *p_demux,\ + input_item_node_t *p_input_node,\ + xml_reader_t *p_xml_reader,\ + const char *psz_element) + +/* prototypes */ +static bool parse_playlist_node COMPLEX_INTERFACE; +static bool parse_tracklist_node COMPLEX_INTERFACE; +static bool parse_track_node COMPLEX_INTERFACE; +static bool parse_extension_node COMPLEX_INTERFACE; +static bool parse_extitem_node COMPLEX_INTERFACE; +static bool set_item_info SIMPLE_INTERFACE; +static bool set_option SIMPLE_INTERFACE; +static bool skip_element COMPLEX_INTERFACE; + +/* datatypes */ +typedef struct +{ + const char *name; + union + { + bool (*smpl) SIMPLE_INTERFACE; + bool (*cmplx) COMPLEX_INTERFACE; + } pf_handler; + bool cmplx; +} xml_elem_hnd_t; struct demux_sys_t { input_item_t **pp_tracklist; @@ -47,112 +77,95 @@ struct demux_sys_t char * psz_base; }; -static int Control( demux_t *, int, va_list ); -static int Demux( demux_t * ); +static int Demux(demux_t *); /** * \brief XSPF submodule initialization function */ -int Import_xspf( vlc_object_t *p_this ) +int Import_xspf(vlc_object_t *p_this) { - DEMUX_BY_EXTENSION_OR_FORCED_MSG( ".xspf", "xspf-open", - "using XSPF playlist reader" ); + DEMUX_BY_EXTENSION_OR_FORCED_MSG(".xspf", "xspf-open", + "using XSPF playlist reader"); return VLC_SUCCESS; } -void Close_xspf( vlc_object_t *p_this ) +void Close_xspf(vlc_object_t *p_this) { demux_t *p_demux = (demux_t *)p_this; - int i; - for(i = 0; i < p_demux->p_sys->i_tracklist_entries; i++) - { - if(p_demux->p_sys->pp_tracklist[i]) - vlc_gc_decref( p_demux->p_sys->pp_tracklist[i] ); - } - free( p_demux->p_sys->pp_tracklist ); - free( p_demux->p_sys->psz_base ); - free( p_demux->p_sys ); + demux_sys_t *p_sys = p_demux->p_sys; + for (int i = 0; i < p_sys->i_tracklist_entries; i++) + if (p_sys->pp_tracklist[i]) + vlc_gc_decref(p_sys->pp_tracklist[i]); + free(p_sys->pp_tracklist); + free(p_sys->psz_base); + free(p_sys); } /** * \brief demuxer function for XSPF parsing */ -int Demux( demux_t *p_demux ) +static int Demux(demux_t *p_demux) { - int i_ret = 1; - xml_t *p_xml = NULL; + int i_ret = -1; xml_reader_t *p_xml_reader = NULL; - char *psz_name = NULL; - INIT_PLAYLIST_STUFF; + const char *name = NULL; + input_item_t *p_current_input = GetCurrentItem(p_demux); p_demux->p_sys->pp_tracklist = NULL; p_demux->p_sys->i_tracklist_entries = 0; p_demux->p_sys->i_track_id = -1; p_demux->p_sys->psz_base = NULL; /* create new xml parser from stream */ - p_xml = xml_Create( p_demux ); - if( !p_xml ) - i_ret = -1; - else - { - p_xml_reader = xml_ReaderCreate( p_xml, p_demux->s ); - if( !p_xml_reader ) - i_ret = -1; - } + p_xml_reader = xml_ReaderCreate(p_demux, p_demux->s); + if (!p_xml_reader) + goto end; /* locating the root node */ - if( i_ret == 1 ) + if (xml_ReaderNextNode(p_xml_reader, &name) != XML_READER_STARTELEM) { - do - { - if( xml_ReaderRead( p_xml_reader ) != 1 ) - { - msg_Err( p_demux, "can't read xml stream" ); - i_ret = -1; - } - } while( i_ret == VLC_SUCCESS && - xml_ReaderNodeType( p_xml_reader ) != XML_READER_STARTELEM ); + msg_Err(p_demux, "can't read xml stream"); + goto end; } + /* checking root node name */ - if( i_ret == 1 ) + if (strcmp(name, "playlist")) { - psz_name = xml_ReaderName( p_xml_reader ); - if( !psz_name || strcmp( psz_name, "playlist" ) ) - { - msg_Err( p_demux, "invalid root node name: %s", psz_name ); - i_ret = -1; - } - FREE_NAME(); + msg_Err(p_demux, "invalid root node name <%s>", name); + goto end; } - if( i_ret == 1 ) - i_ret = parse_playlist_node( p_demux, p_current_input, - p_xml_reader, "playlist" ) ? 0 : -1; + input_item_node_t *p_subitems = + input_item_node_Create(p_current_input); + + i_ret = parse_playlist_node(p_demux, p_subitems, + p_xml_reader, "playlist") ? 0 : -1; - int i; - for( i = 0 ; i < p_demux->p_sys->i_tracklist_entries ; i++ ) + for (int i = 0 ; i < p_demux->p_sys->i_tracklist_entries ; i++) { input_item_t *p_new_input = p_demux->p_sys->pp_tracklist[i]; - if( p_new_input ) + if (p_new_input) { - input_item_AddSubItem( p_current_input, p_new_input ); + input_item_node_AppendItem(p_subitems, p_new_input); } } - HANDLE_PLAY_AND_RELEASE; - if( p_xml_reader ) - xml_ReaderDelete( p_xml, p_xml_reader ); - if( p_xml ) - xml_Delete( p_xml ); + input_item_node_PostAndDelete(p_subitems); + +end: + vlc_gc_decref(p_current_input); + if (p_xml_reader) + xml_ReaderDelete(p_xml_reader); return i_ret; /* Needed for correct operation of go back */ } -/** \brief dummy function for demux callback interface */ -static int Control( demux_t *p_demux, int i_query, va_list args ) +static const xml_elem_hnd_t *get_handler(const xml_elem_hnd_t *tab, size_t n, const char *name) { - VLC_UNUSED(p_demux); VLC_UNUSED(i_query); VLC_UNUSED(args); - return VLC_EGENERIC; + for (size_t i = 0; i < n / sizeof(xml_elem_hnd_t); i++) + if (!strcmp(name, tab[i].name)) + return &tab[i]; + return NULL; } +#define get_handler(tab, name) get_handler(tab, sizeof tab, name) /** * \brief parse the root node of a XSPF playlist @@ -163,167 +176,118 @@ static int Control( demux_t *p_demux, int i_query, va_list args ) */ static bool parse_playlist_node COMPLEX_INTERFACE { - char *psz_name = NULL; + input_item_t *p_input_item = p_input_node->p_item; char *psz_value = NULL; bool b_version_found = false; int i_node; - xml_elem_hnd_t *p_handler = NULL; - - xml_elem_hnd_t pl_elements[] = - { {"title", SIMPLE_CONTENT, {.smpl = set_item_info} }, - {"creator", SIMPLE_CONTENT, {.smpl = set_item_info} }, - {"annotation", SIMPLE_CONTENT, {.smpl = set_item_info} }, - {"info", SIMPLE_CONTENT, {NULL} }, - {"location", SIMPLE_CONTENT, {NULL} }, - {"identifier", SIMPLE_CONTENT, {NULL} }, - {"image", SIMPLE_CONTENT, {.smpl = set_item_info} }, - {"date", SIMPLE_CONTENT, {NULL} }, - {"license", SIMPLE_CONTENT, {NULL} }, - {"attribution", COMPLEX_CONTENT, {.cmplx = skip_element} }, - {"link", SIMPLE_CONTENT, {NULL} }, - {"meta", SIMPLE_CONTENT, {NULL} }, - {"extension", COMPLEX_CONTENT, {.cmplx = parse_extension_node} }, - {"trackList", COMPLEX_CONTENT, {.cmplx = parse_tracklist_node} }, - {NULL, UNKNOWN_CONTENT, {NULL} } + bool b_ret = false; + const xml_elem_hnd_t *p_handler = NULL; + + static const xml_elem_hnd_t pl_elements[] = + { {"title", {.smpl = set_item_info}, false }, + {"creator", {.smpl = set_item_info}, false }, + {"annotation", {.smpl = set_item_info}, false }, + {"info", {NULL}, false }, + {"location", {NULL}, false }, + {"identifier", {NULL}, false }, + {"image", {.smpl = set_item_info}, false }, + {"date", {NULL}, false }, + {"license", {NULL}, false }, + {"attribution", {.cmplx = skip_element}, true }, + {"link", {NULL}, false }, + {"meta", {NULL}, false }, + {"extension", {.cmplx = parse_extension_node}, true }, + {"trackList", {.cmplx = parse_tracklist_node}, true }, }; - - /* read all playlist attributes */ - while( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS ) +/* read all playlist attributes */ + const char *name, *value; + while ((name = xml_ReaderNextAttr(p_xml_reader, &value)) != NULL) { - psz_name = xml_ReaderName( p_xml_reader ); - psz_value = xml_ReaderValue( p_xml_reader ); - if( !psz_name || !psz_value ) - { - msg_Err( p_demux, "invalid xml stream @ " ); - FREE_ATT(); - return false; - } /* attribute: version */ - if( !strcmp( psz_name, "version" ) ) + if (!strcmp(name, "version")) { b_version_found = true; - if( strcmp( psz_value, "0" ) && strcmp( psz_value, "1" ) ) - msg_Warn( p_demux, "unsupported XSPF version" ); + if (strcmp(value, "0") && strcmp(value, "1")) + msg_Warn(p_demux, "unsupported XSPF version %s", value); } /* attribute: xmlns */ - else if( !strcmp( psz_name, "xmlns" ) || !strcmp( psz_name, "xmlns:vlc" ) ) + else if (!strcmp(name, "xmlns") || !strcmp(name, "xmlns:vlc")) ; - else if( !strcmp( psz_name, "xml:base" ) ) + else if (!strcmp(name, "xml:base") && psz_value) { - p_demux->p_sys->psz_base = decode_URI_duplicate( psz_value ); + free(p_demux->p_sys->psz_base); + p_demux->p_sys->psz_base = strdup(psz_value); } /* unknown attribute */ else - msg_Warn( p_demux, "invalid attribute:\"%s\"", psz_name); - - FREE_ATT(); + msg_Warn(p_demux, "invalid attribute: \"%s\"", name); } /* attribute version is mandatory !!! */ - if( !b_version_found ) - msg_Warn( p_demux, " requires \"version\" attribute" ); + if (!b_version_found) + msg_Warn(p_demux, " requires \"version\" attribute"); /* parse the child elements - we only take care of */ - while( xml_ReaderRead( p_xml_reader ) == 1 ) + psz_value = NULL; + while ((i_node = xml_ReaderNextNode(p_xml_reader, &name)) > 0) + switch (i_node) { - i_node = xml_ReaderNodeType( p_xml_reader ); - switch( i_node ) + /* element start tag */ + case XML_READER_STARTELEM: + if (!*name) { - case XML_READER_NONE: - break; - case XML_READER_STARTELEM: - /* element start tag */ - psz_name = xml_ReaderName( p_xml_reader ); - if( !psz_name || !*psz_name ) - { - msg_Err( p_demux, "invalid xml stream" ); - FREE_ATT(); - return false; - } - /* choose handler */ - for( p_handler = pl_elements; - p_handler->name && strcmp( psz_name, p_handler->name ); - p_handler++ ); - if( !p_handler->name ) - { - msg_Err( p_demux, "unexpected element <%s>", psz_name ); - FREE_ATT(); - return false; - } - FREE_NAME(); - /* complex content is parsed in a separate function */ - if( p_handler->type == COMPLEX_CONTENT ) - { - if( p_handler->pf_handler.cmplx( p_demux, - p_input_item, - p_xml_reader, - p_handler->name ) ) - { - p_handler = NULL; - FREE_ATT(); - } - else - { - FREE_ATT(); - return false; - } - } - break; - - case XML_READER_TEXT: - /* simple element content */ - FREE_ATT(); - psz_value = xml_ReaderValue( p_xml_reader ); - if( !psz_value ) - { - msg_Err( p_demux, "invalid xml stream" ); - FREE_ATT(); - return false; - } - break; - - case XML_READER_ENDELEM: - /* element end tag */ - psz_name = xml_ReaderName( p_xml_reader ); - if( !psz_name ) - { - msg_Err( p_demux, "invalid xml stream" ); - FREE_ATT(); - return false; - } - /* leave if the current parent node is terminated */ - if( !strcmp( psz_name, psz_element ) ) - { - FREE_ATT(); - return true; - } - /* there MUST have been a start tag for that element name */ - if( !p_handler || !p_handler->name - || strcmp( p_handler->name, psz_name )) - { - msg_Err( p_demux, "there's no open element left for <%s>", - psz_name ); - FREE_ATT(); - return false; - } - - if( p_handler->pf_handler.smpl ) - { - p_handler->pf_handler.smpl( p_input_item, p_handler->name, - psz_value ); - } - FREE_ATT(); - p_handler = NULL; - break; - - default: - /* unknown/unexpected xml node */ - msg_Err( p_demux, "unexpected xml node %i", i_node ); - FREE_ATT(); + msg_Err(p_demux, "invalid XML stream"); + goto end; + } + /* choose handler */ + p_handler = get_handler(pl_elements, name); + if (!p_handler) + { + msg_Err(p_demux, "unexpected element <%s>", name); + goto end; + } + /* complex content is parsed in a separate function */ + if (p_handler->cmplx) + { + FREE_VALUE(); + if (!p_handler->pf_handler.cmplx(p_demux, p_input_node, + p_xml_reader, p_handler->name)) return false; + p_handler = NULL; + } + break; + + /* simple element content */ + case XML_READER_TEXT: + psz_value = strdup(name); + if (unlikely(!name)) + goto end; + break; + + /* element end tag */ + case XML_READER_ENDELEM: + /* leave if the current parent node is terminated */ + if (!strcmp(name, psz_element)) + { + b_ret = true; + goto end; + } + /* there MUST have been a start tag for that element name */ + if (!p_handler || !p_handler->name || strcmp(p_handler->name, name)) + { + msg_Err(p_demux, "there's no open element left for <%s>", name); + goto end; } - FREE_NAME(); + + if (p_handler->pf_handler.smpl) + p_handler->pf_handler.smpl(p_input_item, p_handler->name, psz_value); + FREE_VALUE(); + p_handler = NULL; + break; } - return false; + +end: + free(psz_value); + return b_ret; } /** @@ -332,59 +296,43 @@ static bool parse_playlist_node COMPLEX_INTERFACE static bool parse_tracklist_node COMPLEX_INTERFACE { VLC_UNUSED(psz_element); - char *psz_name = NULL; + const char *name; + unsigned i_ntracks = 0; int i_node; - int i_ntracks = 0; /* now parse the s */ - while( xml_ReaderRead( p_xml_reader ) == 1 ) + while ((i_node = xml_ReaderNextNode(p_xml_reader, &name)) > 0) { - i_node = xml_ReaderNodeType( p_xml_reader ); - if( i_node == XML_READER_STARTELEM ) + if (i_node == XML_READER_STARTELEM) { - psz_name = xml_ReaderName( p_xml_reader ); - if( !psz_name ) - { - msg_Err( p_demux, "unexpected end of xml data" ); - FREE_NAME(); - return false; - } - if( strcmp( psz_name, "track") ) + if (strcmp(name, "track")) { - msg_Err( p_demux, "unexpected child of : <%s>", - psz_name ); - FREE_NAME(); + msg_Err(p_demux, "unexpected child of : <%s>", + name); return false; } - FREE_NAME(); /* parse the track data in a separate function */ - if( parse_track_node( p_demux, p_input_item, - p_xml_reader,"track" ) == true ) + if (parse_track_node(p_demux, p_input_node, p_xml_reader, "track")) i_ntracks++; } - else if( i_node == XML_READER_ENDELEM ) + else if (i_node == XML_READER_ENDELEM) break; } /* the has to be terminated */ - if( xml_ReaderNodeType( p_xml_reader ) != XML_READER_ENDELEM ) + if (i_node != XML_READER_ENDELEM) { - msg_Err( p_demux, "there's a missing " ); - FREE_NAME(); + msg_Err(p_demux, "there's a missing "); return false; } - psz_name = xml_ReaderName( p_xml_reader ); - if( !psz_name || strcmp( psz_name, "trackList" ) ) + if (strcmp(name, "trackList")) { - msg_Err( p_demux, "expected: , found: ", psz_name ); - FREE_NAME(); + msg_Err(p_demux, "expected: , found: ", name); return false; } - FREE_NAME(); - - msg_Dbg( p_demux, "parsed %i tracks successfully", i_ntracks ); + msg_Dbg(p_demux, "parsed %u tracks successfully", i_ntracks); return true; } @@ -394,219 +342,178 @@ static bool parse_tracklist_node COMPLEX_INTERFACE */ static bool parse_track_node COMPLEX_INTERFACE { - int i_node; - char *psz_name = NULL; + input_item_t *p_input_item = p_input_node->p_item; + const char *name; char *psz_value = NULL; - xml_elem_hnd_t *p_handler = NULL; - - xml_elem_hnd_t track_elements[] = - { {"location", SIMPLE_CONTENT, {NULL} }, - {"identifier", SIMPLE_CONTENT, {NULL} }, - {"title", SIMPLE_CONTENT, {.smpl = set_item_info} }, - {"creator", SIMPLE_CONTENT, {.smpl = set_item_info} }, - {"annotation", SIMPLE_CONTENT, {.smpl = set_item_info} }, - {"info", SIMPLE_CONTENT, {NULL} }, - {"image", SIMPLE_CONTENT, {.smpl = set_item_info} }, - {"album", SIMPLE_CONTENT, {.smpl = set_item_info} }, - {"trackNum", SIMPLE_CONTENT, {.smpl = set_item_info} }, - {"duration", SIMPLE_CONTENT, {.smpl = set_item_info} }, - {"link", SIMPLE_CONTENT, {NULL} }, - {"meta", SIMPLE_CONTENT, {NULL} }, - {"extension", COMPLEX_CONTENT, {.cmplx = parse_extension_node} }, - {NULL, UNKNOWN_CONTENT, {NULL} } - }; + const xml_elem_hnd_t *p_handler = NULL; + demux_sys_t *p_sys = p_demux->p_sys; + int i_node; - input_item_t *p_new_input = input_item_New( p_demux, NULL, NULL ); + static const xml_elem_hnd_t track_elements[] = + { {"location", {NULL}, false }, + {"identifier", {NULL}, false }, + {"title", {.smpl = set_item_info}, false }, + {"creator", {.smpl = set_item_info}, false }, + {"annotation", {.smpl = set_item_info}, false }, + {"info", {.smpl = set_item_info}, false }, + {"image", {.smpl = set_item_info}, false }, + {"album", {.smpl = set_item_info}, false }, + {"trackNum", {.smpl = set_item_info}, false }, + {"duration", {.smpl = set_item_info}, false }, + {"link", {NULL}, false }, + {"meta", {NULL}, false }, + {"extension", {.cmplx = parse_extension_node}, true }, + }; - if( !p_new_input ) - { - /* malloc has failed for input_item_New, so bailout early */ + input_item_t *p_new_input = input_item_New(NULL, NULL); + if (!p_new_input) return false; - } + input_item_node_t *p_new_node = input_item_node_Create(p_new_input); /* reset i_track_id */ - p_demux->p_sys->i_track_id = -1; + p_sys->i_track_id = -1; - while( xml_ReaderRead( p_xml_reader ) == 1 ) + while ((i_node = xml_ReaderNextNode(p_xml_reader, &name)) > 0) + switch (i_node) { - i_node = xml_ReaderNodeType( p_xml_reader ); - switch( i_node ) + /* element start tag */ + case XML_READER_STARTELEM: + if (!*name) { - case XML_READER_NONE: - break; - - case XML_READER_STARTELEM: - /* element start tag */ - psz_name = xml_ReaderName( p_xml_reader ); - if( !psz_name || !*psz_name ) - { - msg_Err( p_demux, "invalid xml stream" ); - FREE_ATT(); - return false; - } - /* choose handler */ - for( p_handler = track_elements; - p_handler->name && strcmp( psz_name, p_handler->name ); - p_handler++ ); - if( !p_handler->name ) - { - msg_Err( p_demux, "unexpected element <%s>", psz_name ); - FREE_ATT(); - return false; - } - FREE_NAME(); - /* complex content is parsed in a separate function */ - if( p_handler->type == COMPLEX_CONTENT ) - { - if( !p_new_input ) - { - msg_Err( p_demux, - "at <%s> level no new item has been allocated", - p_handler->name ); - FREE_ATT(); - return false; - } - if( p_handler->pf_handler.cmplx( p_demux, - p_new_input, - p_xml_reader, - p_handler->name ) ) - { - p_handler = NULL; - FREE_ATT(); - } - else - { - FREE_ATT(); - return false; - } - } - break; + msg_Err(p_demux, "invalid XML stream"); + goto end; + } + /* choose handler */ + p_handler = get_handler(track_elements, name); + if (!p_handler) + { + msg_Err(p_demux, "unexpected element <%s>", name); + goto end; + } + /* complex content is parsed in a separate function */ + if (p_handler->cmplx) + { + FREE_VALUE(); - case XML_READER_TEXT: - /* simple element content */ - FREE_ATT(); - psz_value = xml_ReaderValue( p_xml_reader ); - if( !psz_value ) - { - msg_Err( p_demux, "invalid xml stream" ); - FREE_ATT(); - return false; - } - break; + if (!p_handler->pf_handler.cmplx(p_demux, p_new_node, + p_xml_reader, p_handler->name)) { + input_item_node_Delete(p_new_node); + return false; + } - case XML_READER_ENDELEM: - /* element end tag */ - psz_name = xml_ReaderName( p_xml_reader ); - if( !psz_name ) - { - msg_Err( p_demux, "invalid xml stream" ); - FREE_ATT(); - return false; - } - /* leave if the current parent node is terminated */ - if( !strcmp( psz_name, psz_element ) ) - { - FREE_ATT(); + p_handler = NULL; + } + break; + + /* simple element content */ + case XML_READER_TEXT: + free(psz_value); + psz_value = strdup(name); + if (unlikely(!psz_value)) + goto end; + break; + + /* element end tag */ + case XML_READER_ENDELEM: + /* leave if the current parent node is terminated */ + if (!strcmp(name, psz_element)) + { + free(psz_value); - if( p_demux->p_sys->i_track_id < 0 ) - { - input_item_AddSubItem( p_input_item, p_new_input ); - vlc_gc_decref( p_new_input ); - return true; - } + /* Make sure we have a URI */ + char *psz_uri = input_item_GetURI(p_new_input); + if (!psz_uri) + input_item_SetURI(p_new_input, "vlc://nop"); + else + free(psz_uri); - if( p_demux->p_sys->i_track_id >= - p_demux->p_sys->i_tracklist_entries ) - { - input_item_t **pp; - pp = realloc( p_demux->p_sys->pp_tracklist, - (p_demux->p_sys->i_track_id + 1) * sizeof(*pp) ); - if( !pp ) - return false; - p_demux->p_sys->pp_tracklist = pp; - while( p_demux->p_sys->i_track_id >= - p_demux->p_sys->i_tracklist_entries ) - pp[p_demux->p_sys->i_tracklist_entries++] = NULL; - } + if (p_sys->i_track_id < 0 + || (unsigned)p_sys->i_track_id >= (SIZE_MAX / sizeof(p_new_input))) + { + input_item_node_AppendNode(p_input_node, p_new_node); + vlc_gc_decref(p_new_input); + return true; + } - p_demux->p_sys->pp_tracklist[ - p_demux->p_sys->i_track_id ] = p_new_input; - return true; - } - /* there MUST have been a start tag for that element name */ - if( !p_handler || !p_handler->name - || strcmp( p_handler->name, psz_name )) + if (p_sys->i_track_id >= p_sys->i_tracklist_entries) + { + input_item_t **pp; + pp = realloc(p_sys->pp_tracklist, + (p_sys->i_track_id + 1) * sizeof(*pp)); + if (!pp) { - msg_Err( p_demux, "there's no open element left for <%s>", - psz_name ); - FREE_ATT(); + vlc_gc_decref(p_new_input); + input_item_node_Delete(p_new_node); return false; } + p_sys->pp_tracklist = pp; + while (p_sys->i_track_id >= p_sys->i_tracklist_entries) + pp[p_sys->i_tracklist_entries++] = NULL; + } + else if (p_sys->pp_tracklist[p_sys->i_track_id] != NULL) + { + msg_Err(p_demux, "track ID %d collision", p_sys->i_track_id); + vlc_gc_decref(p_new_input); + input_item_node_Delete(p_new_node); + return false; + } - /* special case: location */ - if( !strcmp( p_handler->name, "location" ) ) - { - char *psz_uri = NULL; - psz_uri = decode_URI_duplicate( psz_value ); - - if( !psz_uri ) - { - FREE_ATT(); - return false; - } + p_sys->pp_tracklist[ p_sys->i_track_id ] = p_new_input; + input_item_node_Delete(p_new_node); + return true; + } + /* there MUST have been a start tag for that element name */ + if (!p_handler || !p_handler->name || strcmp(p_handler->name, name)) + { + msg_Err(p_demux, "there's no open element left for <%s>", name); + goto end; + } - if( p_demux->p_sys->psz_base && !strstr( psz_uri, "://" ) ) - { - char* psz_tmp; - if( asprintf( &psz_tmp, "%s%s", p_demux->p_sys->psz_base, - psz_uri ) == -1 ) - { - free( psz_uri ); - FREE_ATT(); - return NULL; - } - free( psz_uri ); - psz_uri = psz_tmp; - } - input_item_SetURI( p_new_input, psz_uri ); - free( psz_uri ); - input_item_CopyOptions( p_input_item, p_new_input ); - psz_uri = NULL; - FREE_ATT(); - p_handler = NULL; - } - else + /* special case: location */ + if (!strcmp(p_handler->name, "location")) + { + if (psz_value == NULL) + input_item_SetURI(p_new_input, "vlc://nop"); + else + /* FIXME (#4005): This is broken. Scheme-relative (//...) locations + * and anchors (#...) are not resolved correctly. Also, + * host-relative (/...) and directory-relative locations + * ("relative path" in vernacular) should be resolved. + * Last, psz_base should default to the XSPF resource + * location if missing (not the current working directory). + * -- Courmisch */ + if (p_sys->psz_base && !strstr(psz_value, "://")) + { + char* psz_tmp; + if (asprintf(&psz_tmp, "%s%s", p_sys->psz_base, psz_value) + == -1) { - /* there MUST be an item */ - if( !p_new_input ) - { - msg_Err( p_demux, "item not yet created at <%s>", - psz_name ); - FREE_ATT(); - return false; - } - if( p_handler->pf_handler.smpl ) - { - p_handler->pf_handler.smpl( p_new_input, - p_handler->name, - psz_value ); - FREE_ATT(); - } + goto end; } - FREE_ATT(); - p_handler = NULL; - break; - - default: - /* unknown/unexpected xml node */ - msg_Err( p_demux, "unexpected xml node %i", i_node ); - FREE_ATT(); - return false; + input_item_SetURI(p_new_input, psz_tmp); + free(psz_tmp); + } + else + input_item_SetURI(p_new_input, psz_value); + input_item_CopyOptions(p_input_item, p_new_input); + } + else + { + /* there MUST be an item */ + if (p_handler->pf_handler.smpl) + p_handler->pf_handler.smpl(p_new_input, p_handler->name, + psz_value); } - FREE_NAME(); + FREE_VALUE(); + p_handler = NULL; + break; } - msg_Err( p_demux, "unexpected end of xml data" ); - FREE_ATT(); + msg_Err(p_demux, "unexpected end of xml data"); + +end: + + input_item_node_Delete(p_new_node); + free(psz_value); return false; } @@ -616,46 +523,32 @@ static bool parse_track_node COMPLEX_INTERFACE static bool set_item_info SIMPLE_INTERFACE { /* exit if setting is impossible */ - if( !psz_name || !psz_value || !p_input ) + if (!psz_name || !psz_value || !p_input) return false; - /* re-convert xml special characters inside psz_value */ - resolve_xml_special_chars( psz_value ); + resolve_xml_special_chars(psz_value); /* handle each info element in a separate "if" clause */ - if( !strcmp( psz_name, "title" ) ) - { - input_item_SetTitle( p_input, psz_value ); - } - else if( !strcmp( psz_name, "creator" ) ) - { - input_item_SetArtist( p_input, psz_value ); - } - else if( !strcmp( psz_name, "album" ) ) + if (!strcmp(psz_name, "title")) + input_item_SetTitle(p_input, psz_value); + else if (!strcmp(psz_name, "creator")) + input_item_SetArtist(p_input, psz_value); + else if (!strcmp(psz_name, "album")) + input_item_SetAlbum(p_input, psz_value); + else if (!strcmp(psz_name, "trackNum")) + input_item_SetTrackNum(p_input, psz_value); + else if (!strcmp(psz_name, "duration")) { - input_item_SetAlbum( p_input, psz_value ); - - } - else if( !strcmp( psz_name, "trackNum" ) ) - { - input_item_SetTrackNum( p_input, psz_value ); - } - else if( !strcmp( psz_name, "duration" ) ) - { - long i_num = atol( psz_value ); - input_item_SetDuration( p_input, (mtime_t) i_num*1000 ); - } - else if( !strcmp( psz_name, "annotation" ) ) - { - input_item_SetDescription( p_input, psz_value ); - } - else if( !strcmp( psz_name, "image" ) ) - { - char *psz_uri = decode_URI_duplicate( psz_value ); - input_item_SetArtURL( p_input, psz_uri ); - free( psz_uri ); + long i_num = atol(psz_value); + input_item_SetDuration(p_input, (mtime_t) i_num*1000); } + else if (!strcmp(psz_name, "annotation")) + input_item_SetDescription(p_input, psz_value); + else if (!strcmp(psz_name, "info")) + input_item_SetURL(p_input, psz_value); + else if (!strcmp(psz_name, "image")) + input_item_SetArtURL(p_input, psz_value); return true; } @@ -665,13 +558,13 @@ static bool set_item_info SIMPLE_INTERFACE static bool set_option SIMPLE_INTERFACE { /* exit if setting is impossible */ - if( !psz_name || !psz_value || !p_input ) + if (!psz_name || !psz_value || !p_input) return false; /* re-convert xml special characters inside psz_value */ - resolve_xml_special_chars( psz_value ); + resolve_xml_special_chars(psz_value); - input_item_AddOption( p_input, psz_value, 0 ); + input_item_AddOption(p_input, psz_value, 0); return true; } @@ -681,133 +574,131 @@ static bool set_option SIMPLE_INTERFACE */ static bool parse_extension_node COMPLEX_INTERFACE { - char *psz_name = NULL; + input_item_t *p_input_item = p_input_node->p_item; char *psz_value = NULL; char *psz_title = NULL; char *psz_application = NULL; int i_node; bool b_release_input_item = false; - xml_elem_hnd_t *p_handler = NULL; + const xml_elem_hnd_t *p_handler = NULL; input_item_t *p_new_input = NULL; - xml_elem_hnd_t pl_elements[] = - { {"vlc:node", COMPLEX_CONTENT, {.cmplx = parse_extension_node} }, - {"vlc:item", COMPLEX_CONTENT, {.cmplx = parse_extitem_node} }, - {"vlc:id", SIMPLE_CONTENT, {NULL} }, - {"vlc:option", SIMPLE_CONTENT, {.smpl = set_option} }, - {NULL, UNKNOWN_CONTENT, {NULL} } + static const xml_elem_hnd_t pl_elements[] = + { {"vlc:node", {.cmplx = parse_extension_node}, true }, + {"vlc:item", {.cmplx = parse_extitem_node}, true }, + {"vlc:id", {NULL}, false }, + {"vlc:option", {.smpl = set_option}, false }, }; /* read all extension node attributes */ - while( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS ) + const char *name, *value; + while ((name = xml_ReaderNextAttr(p_xml_reader, &value)) != NULL) { - psz_name = xml_ReaderName( p_xml_reader ); - psz_value = xml_ReaderValue( p_xml_reader ); - if( !psz_name || !psz_value ) - { - msg_Err( p_demux, "invalid xml stream @ " ); - FREE_ATT(); - return false; - } /* attribute: title */ - if( !strcmp( psz_name, "title" ) ) + if (!strcmp(name, "title")) { - resolve_xml_special_chars( psz_value ); - psz_title = strdup( psz_value ); + free(psz_title); + psz_title = strdup(value); + if (likely(psz_title != NULL)) + resolve_xml_special_chars(psz_title); } /* extension attribute: application */ - else if( !strcmp( psz_name, "application" ) ) + else if (!strcmp(name, "application")) { - psz_application = strdup( psz_value ); + free(psz_application); + psz_application = strdup(value); } /* unknown attribute */ else - msg_Warn( p_demux, "invalid <%s> attribute:\"%s\"", psz_element, - psz_name ); - - FREE_ATT(); + msg_Warn(p_demux, "invalid <%s> attribute:\"%s\"", psz_element, + name); } /* attribute title is mandatory except for */ - if( !strcmp( psz_element, "vlc:node" ) ) + if (!strcmp(psz_element, "vlc:node")) { - if( !psz_title ) + if (!psz_title) { - msg_Warn( p_demux, " requires \"title\" attribute" ); + msg_Warn(p_demux, " requires \"title\" attribute"); return false; } - p_new_input = input_item_NewWithType( VLC_OBJECT( p_demux ), - "vlc://nop", psz_title, 0, NULL, 0, -1, - ITEM_TYPE_DIRECTORY ); - if( p_new_input ) + p_new_input = input_item_NewWithType("vlc://nop", psz_title, + 0, NULL, 0, -1, + ITEM_TYPE_DIRECTORY); + if (p_new_input) { - input_item_AddSubItem( p_input_item, p_new_input ); + p_input_node = + input_item_node_AppendItem(p_input_node, p_new_input); p_input_item = p_new_input; b_release_input_item = true; } - free( psz_title ); + free(psz_title); } - else if( !strcmp( psz_element, "extension" ) ) + else if (!strcmp(psz_element, "extension")) { - if( !psz_application ) + if (!psz_application) { - msg_Warn( p_demux, " requires \"application\" attribute" ); + msg_Warn(p_demux, " requires \"application\" attribute"); return false; } - else if( strcmp( psz_application, "http://www.videolan.org/vlc/playlist/0" ) ) + /* Skip the extension if the application is not vlc + This will skip all children of the current node */ + else if (strcmp(psz_application, "http://www.videolan.org/vlc/playlist/0")) { - msg_Dbg( p_demux, "Skipping \"%s\" extension tag", psz_application ); - free( psz_application ); - return false; + msg_Dbg(p_demux, "Skipping \"%s\" extension tag", psz_application); + free(psz_application); + /* Skip all children */ + for (unsigned lvl = 1; lvl;) + switch (xml_ReaderNextNode(p_xml_reader, NULL)) + { + case XML_READER_STARTELEM: lvl++; break; + case XML_READER_ENDELEM: lvl--; break; + case 0: case -1: return -1; + } + return true; } } - free( psz_application ); + free(psz_application); + /* parse the child elements */ - while( xml_ReaderRead( p_xml_reader ) == 1 ) + while ((i_node = xml_ReaderNextNode(p_xml_reader, &name)) > 0) { - i_node = xml_ReaderNodeType( p_xml_reader ); - switch( i_node ) + switch (i_node) { - case XML_READER_NONE: - break; + /* element start tag */ case XML_READER_STARTELEM: - /* element start tag */ - psz_name = xml_ReaderName( p_xml_reader ); - if( !psz_name || !*psz_name ) + if (!*name) { - msg_Err( p_demux, "invalid xml stream" ); - FREE_ATT(); - if( b_release_input_item ) vlc_gc_decref( p_new_input ); + msg_Err(p_demux, "invalid xml stream"); + FREE_VALUE(); + if (b_release_input_item) vlc_gc_decref(p_new_input); return false; } /* choose handler */ - for( p_handler = pl_elements; - p_handler->name && strcmp( psz_name, p_handler->name ); - p_handler++ ); - if( !p_handler->name ) + p_handler = get_handler(pl_elements, name); + if (!p_handler) { - msg_Err( p_demux, "unexpected element <%s>", psz_name ); - FREE_ATT(); - if( b_release_input_item ) vlc_gc_decref( p_new_input ); + msg_Err(p_demux, "unexpected element <%s>", name); + FREE_VALUE(); + if (b_release_input_item) vlc_gc_decref(p_new_input); return false; } - FREE_NAME(); /* complex content is parsed in a separate function */ - if( p_handler->type == COMPLEX_CONTENT ) + if (p_handler->cmplx) { - if( p_handler->pf_handler.cmplx( p_demux, - p_input_item, + if (p_handler->pf_handler.cmplx(p_demux, + p_input_node, p_xml_reader, - p_handler->name ) ) + p_handler->name)) { p_handler = NULL; - FREE_ATT(); + FREE_VALUE(); } else { - FREE_ATT(); - if( b_release_input_item ) vlc_gc_decref( p_new_input ); + FREE_VALUE(); + if (b_release_input_item) vlc_gc_decref(p_new_input); return false; } } @@ -815,69 +706,52 @@ static bool parse_extension_node COMPLEX_INTERFACE case XML_READER_TEXT: /* simple element content */ - FREE_ATT(); - psz_value = xml_ReaderValue( p_xml_reader ); - if( !psz_value ) + FREE_VALUE(); + psz_value = strdup(name); + if (unlikely(!psz_value)) { - msg_Err( p_demux, "invalid xml stream" ); - FREE_ATT(); - if( b_release_input_item ) vlc_gc_decref( p_new_input ); + FREE_VALUE(); + if (b_release_input_item) vlc_gc_decref(p_new_input); return false; } break; + /* element end tag */ case XML_READER_ENDELEM: - /* element end tag */ - psz_name = xml_ReaderName( p_xml_reader ); - if( !psz_name ) - { - msg_Err( p_demux, "invalid xml stream" ); - FREE_ATT(); - if( b_release_input_item ) vlc_gc_decref( p_new_input ); - return false; - } /* leave if the current parent node is terminated */ - if( !strcmp( psz_name, psz_element ) ) + if (!strcmp(name, psz_element)) { - FREE_ATT(); - if( b_release_input_item ) vlc_gc_decref( p_new_input ); + FREE_VALUE(); + if (b_release_input_item) vlc_gc_decref(p_new_input); return true; } /* there MUST have been a start tag for that element name */ - if( !p_handler || !p_handler->name - || strcmp( p_handler->name, psz_name )) + if (!p_handler || !p_handler->name + || strcmp(p_handler->name, name)) { - msg_Err( p_demux, "there's no open element left for <%s>", - psz_name ); - FREE_ATT(); - if( b_release_input_item ) vlc_gc_decref( p_new_input ); + msg_Err(p_demux, "there's no open element left for <%s>", + name); + FREE_VALUE(); + if (b_release_input_item) vlc_gc_decref(p_new_input); return false; } /* special tag */ - if( !strcmp( p_handler->name, "vlc:id" ) ) + if (!strcmp(p_handler->name, "vlc:id")) { - p_demux->p_sys->i_track_id = atoi( psz_value ); + p_demux->p_sys->i_track_id = atoi(psz_value); } - else if( p_handler->pf_handler.smpl ) + else if (p_handler->pf_handler.smpl) { - p_handler->pf_handler.smpl( p_input_item, p_handler->name, - psz_value ); + p_handler->pf_handler.smpl(p_input_item, p_handler->name, + psz_value); } - FREE_ATT(); + FREE_VALUE(); p_handler = NULL; break; - - default: - /* unknown/unexpected xml node */ - msg_Err( p_demux, "unexpected xml node %i", i_node ); - FREE_ATT(); - if( b_release_input_item ) vlc_gc_decref( p_new_input ); - return false; } - FREE_NAME(); } - if( b_release_input_item ) vlc_gc_decref( p_new_input ); + if (b_release_input_item) vlc_gc_decref(p_new_input); return false; } @@ -888,58 +762,41 @@ static bool parse_extitem_node COMPLEX_INTERFACE { VLC_UNUSED(psz_element); input_item_t *p_new_input = NULL; - char *psz_name = NULL; - char *psz_value = NULL; int i_tid = -1; /* read all extension item attributes */ - while( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS ) + const char *name, *value; + while ((name = xml_ReaderNextAttr(p_xml_reader, &value)) != NULL) { - psz_name = xml_ReaderName( p_xml_reader ); - psz_value = xml_ReaderValue( p_xml_reader ); - if( !psz_name || !psz_value ) - { - msg_Err( p_demux, "invalid xml stream @ " ); - FREE_ATT(); - return false; - } /* attribute: href */ - if( !strcmp( psz_name, "tid" ) ) - { - i_tid = atoi( psz_value ); - } + if (!strcmp(name, "tid")) + i_tid = atoi(value); /* unknown attribute */ else - msg_Warn( p_demux, "invalid attribute:\"%s\"", psz_name); - - FREE_ATT(); + msg_Warn(p_demux, "invalid attribute: \"%s\"", name); } /* attribute href is mandatory */ - if( i_tid < 0 ) + if (i_tid < 0) { - msg_Warn( p_demux, " requires \"tid\" attribute" ); + msg_Warn(p_demux, " requires \"tid\" attribute"); return false; } - if( i_tid >= p_demux->p_sys->i_tracklist_entries ) + if (i_tid >= p_demux->p_sys->i_tracklist_entries) { - msg_Warn( p_demux, "invalid \"tid\" attribute" ); + msg_Warn(p_demux, "invalid \"tid\" attribute"); return false; } p_new_input = p_demux->p_sys->pp_tracklist[ i_tid ]; - if( p_new_input ) + if (p_new_input) { - input_item_AddSubItem( p_input_item, p_new_input ); - vlc_gc_decref( p_new_input ); + input_item_node_AppendItem(p_input_node, p_new_input); + vlc_gc_decref(p_new_input); p_demux->p_sys->pp_tracklist[i_tid] = NULL; } - /* kludge for #1293 - XTAG sends ENDELEM for self closing tag */ - /* (libxml sends NONE) */ - xml_ReaderRead( p_xml_reader ); - return true; } @@ -948,24 +805,16 @@ static bool parse_extitem_node COMPLEX_INTERFACE */ static bool skip_element COMPLEX_INTERFACE { - VLC_UNUSED(p_demux); VLC_UNUSED(p_input_item); - char *psz_endname; + VLC_UNUSED(p_demux); VLC_UNUSED(p_input_node); + VLC_UNUSED(psz_element); - while( xml_ReaderRead( p_xml_reader ) == 1 ) - { - if( xml_ReaderNodeType( p_xml_reader ) == XML_READER_ENDELEM ) + for (unsigned lvl = 1; lvl;) + switch (xml_ReaderNextNode(p_xml_reader, NULL)) { - psz_endname = xml_ReaderName( p_xml_reader ); - if( !psz_endname ) - return false; - if( !strcmp( psz_element, psz_endname ) ) - { - free( psz_endname ); - return true; - } - else - free( psz_endname ); + case XML_READER_STARTELEM: lvl++; break; + case XML_READER_ENDELEM: lvl--; break; + case 0: case -1: return false; } - } - return false; + + return true; }