vlc | branch: master | Francois Cartegnie <[email protected]> | Tue Sep 5 22:36:17 2017 +0200| [e21706e9e3bdc13cfe33bd292ba7cddcc3954ee6] | committer: Francois Cartegnie
demux: playlist: xspf: revector "ppl can't do xml" Refactored code, better rogue nodes handling, properly handles empty nodes and skipping, reclaims badly referenced nodes. > http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=e21706e9e3bdc13cfe33bd292ba7cddcc3954ee6 --- modules/demux/playlist/itml.c | 5 +- modules/demux/playlist/itml.h | 3 +- modules/demux/playlist/xspf.c | 761 ++++++++++++++++++------------------------ 3 files changed, 327 insertions(+), 442 deletions(-) diff --git a/modules/demux/playlist/itml.c b/modules/demux/playlist/itml.c index bd1b1bebe4..36341d36e6 100644 --- a/modules/demux/playlist/itml.c +++ b/modules/demux/playlist/itml.c @@ -227,7 +227,7 @@ static bool parse_dict( stream_t *p_demux, input_item_node_t *p_input_node, /* call the simple handler */ else if( p_handler->pf_handler.smpl ) { - p_handler->pf_handler.smpl( p_track, psz_key, psz_value ); + p_handler->pf_handler.smpl( p_track, psz_key, psz_value, p_demux->p_sys ); } FREENULL(psz_value); p_handler = NULL; @@ -364,8 +364,9 @@ static void free_track( track_elem_t *p_track ) } static bool save_data( track_elem_t *p_track, const char *psz_name, - char *psz_value) + char *psz_value, void *opaque ) { + VLC_UNUSED(opaque); /* exit if setting is impossible */ if( !psz_name || !psz_value || !p_track ) return false; diff --git a/modules/demux/playlist/itml.h b/modules/demux/playlist/itml.h index e93987bff2..e09c8e4288 100644 --- a/modules/demux/playlist/itml.h +++ b/modules/demux/playlist/itml.h @@ -32,7 +32,8 @@ #define SIMPLE_INTERFACE (track_elem_t *p_track,\ const char *psz_name,\ - char *psz_value) + char *psz_value,\ + void *opaque) #define COMPLEX_INTERFACE (stream_t *p_demux,\ input_item_node_t *p_input_node,\ track_elem_t *p_track,\ diff --git a/modules/demux/playlist/xspf.c b/modules/demux/playlist/xspf.c index 2058c5b401..29c7e3697c 100644 --- a/modules/demux/playlist/xspf.c +++ b/modules/demux/playlist/xspf.c @@ -1,8 +1,7 @@ /******************************************************************************* * xspf.c : XSPF playlist import functions ******************************************************************************* - * Copyright (C) 2006-2011 VLC authors and VideoLAN - * $Id$ + * Copyright (C) 2006-2017 VLC authors and VideoLAN * * Authors: Daniel Stränger <vlc at schmaller dot de> * Yoann Peronneau <[email protected]> @@ -39,10 +38,13 @@ #include <vlc_url.h> #include "playlist.h" +#include <limits.h> + #define SIMPLE_INTERFACE (input_item_t *p_input,\ const char *psz_name,\ - char *psz_value) -#define COMPLEX_INTERFACE (stream_t *p_demux,\ + char *psz_value,\ + void *opaque) +#define COMPLEX_INTERFACE (stream_t *p_stream,\ input_item_node_t *p_input_node,\ xml_reader_t *p_xml_reader,\ const char *psz_element,\ @@ -69,13 +71,14 @@ typedef struct } pf_handler; bool cmplx; } xml_elem_hnd_t; -struct demux_sys_t + +typedef struct { input_item_t **pp_tracklist; int i_tracklist_entries; int i_track_id; char * psz_base; -}; +} xspf_sys_t; static int ReadDir(stream_t *, input_item_node_t *); @@ -84,30 +87,30 @@ static int ReadDir(stream_t *, input_item_node_t *); */ int Import_xspf(vlc_object_t *p_this) { - stream_t *p_demux = (stream_t *)p_this; + stream_t *p_stream = (stream_t *)p_this; - CHECK_FILE(p_demux); + CHECK_FILE(p_stream); - if( !stream_HasExtension( p_demux, ".xspf" ) - && !stream_IsMimeType( p_demux->p_source, "application/xspf+xml" ) ) + if( !stream_HasExtension( p_stream, ".xspf" ) + && !stream_IsMimeType( p_stream->p_source, "application/xspf+xml" ) ) return VLC_EGENERIC; - demux_sys_t *sys = calloc(1, sizeof (*sys)); + xspf_sys_t *sys = calloc(1, sizeof (*sys)); if (unlikely(sys == NULL)) return VLC_ENOMEM; - msg_Dbg(p_demux, "using XSPF playlist reader"); - p_demux->p_sys = sys; - p_demux->pf_readdir = ReadDir; - p_demux->pf_control = access_vaDirectoryControlHelper; + msg_Dbg(p_stream, "using XSPF playlist reader"); + p_stream->p_sys = sys; + p_stream->pf_readdir = ReadDir; + p_stream->pf_control = access_vaDirectoryControlHelper; return VLC_SUCCESS; } void Close_xspf(vlc_object_t *p_this) { - stream_t *p_demux = (stream_t *)p_this; - demux_sys_t *p_sys = p_demux->p_sys; + stream_t *p_stream = (stream_t *)p_this; + xspf_sys_t *p_sys = p_stream->p_sys; for (int i = 0; i < p_sys->i_tracklist_entries; i++) if (p_sys->pp_tracklist[i]) input_item_Release(p_sys->pp_tracklist[i]); @@ -119,9 +122,9 @@ void Close_xspf(vlc_object_t *p_this) /** * \brief demuxer function for XSPF parsing */ -static int ReadDir(stream_t *p_demux, input_item_node_t *p_subitems) +static int ReadDir(stream_t *p_stream, input_item_node_t *p_subitems) { - demux_sys_t *sys = p_demux->p_sys; + xspf_sys_t *sys = p_stream->p_sys; int i_ret = -1; xml_reader_t *p_xml_reader = NULL; const char *name = NULL; @@ -129,31 +132,31 @@ static int ReadDir(stream_t *p_demux, input_item_node_t *p_subitems) sys->pp_tracklist = NULL; sys->i_tracklist_entries = 0; sys->i_track_id = -1; - sys->psz_base = strdup(p_demux->psz_url); + sys->psz_base = strdup(p_stream->psz_url); /* create new xml parser from stream */ - p_xml_reader = xml_ReaderCreate(p_demux, p_demux->p_source); + p_xml_reader = xml_ReaderCreate(p_stream, p_stream->p_source); if (!p_xml_reader) goto end; /* locating the root node */ if (xml_ReaderNextNode(p_xml_reader, &name) != XML_READER_STARTELEM) { - msg_Err(p_demux, "can't read xml stream"); + msg_Err(p_stream, "can't read xml stream"); goto end; } /* checking root node name */ if (strcmp(name, "playlist")) { - msg_Err(p_demux, "invalid root node name <%s>", name); + msg_Err(p_stream, "invalid root node name <%s>", name); goto end; } if(xml_ReaderIsEmptyElement(p_xml_reader)) goto end; - i_ret = parse_playlist_node(p_demux, p_subitems, + i_ret = parse_playlist_node(p_stream, p_subitems, p_xml_reader, "playlist", false ) ? 0 : -1; for (int i = 0 ; i < sys->i_tracklist_entries ; i++) @@ -178,102 +181,83 @@ static const xml_elem_hnd_t *get_handler(const xml_elem_hnd_t *tab, size_t n, co return &tab[i]; return NULL; } -#define get_handler(tab, name) get_handler(tab, ARRAY_SIZE(tab), name) + +static const char *get_node_attribute(xml_reader_t *p_xml_reader, const char *psz_name) +{ + const char *name, *value; + while ((name = xml_ReaderNextAttr(p_xml_reader, &value)) != NULL) + { + if (!strcmp(name, psz_name)) + return value; + } + return NULL; +} /** - * \brief parse the root node of a XSPF playlist - * \param p_demux demuxer instance + * \brief generic node parsing of a XSPF playlist + * \param p_stream stream instance + * \param input_item_node_t current input node * \param p_input_item current input item * \param p_xml_reader xml reader instance - * \param psz_element name of element to parse + * \param psz_root_node current node name to parse + * \param pl_elements xml_elem_hnd_t handlers array + * \param i_pl_elements xml_elem_hnd_t array count */ -static bool parse_playlist_node COMPLEX_INTERFACE +static bool parse_node(stream_t *p_stream, + input_item_node_t *p_input_node, input_item_t *p_input_item, + xml_reader_t *p_xml_reader, const char *psz_root_node, + const xml_elem_hnd_t *pl_elements, size_t i_pl_elements) { - demux_sys_t *sys = p_demux->p_sys; - input_item_t *p_input_item = p_input_node->p_item; + bool b_ret = false; + char *psz_value = NULL; - bool b_version_found = false; + const char *name; int i_node; - bool b_ret = false; const xml_elem_hnd_t *p_handler = NULL; - if(b_empty_node) - return true; - - 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 */ - const char *name, *value; - while ((name = xml_ReaderNextAttr(p_xml_reader, &value)) != NULL) - { - if (!strcmp(name, "version")) - { - b_version_found = true; - if (strcmp(value, "0") && strcmp(value, "1")) - msg_Warn(p_demux, "unsupported XSPF version %s", value); - } - else if (!strcmp(name, "xmlns") || !strcmp(name, "xmlns:vlc")) - ; - else if (!strcmp(name, "xml:base")) - { - free(sys->psz_base); - sys->psz_base = strdup(value); - } - else - msg_Warn(p_demux, "invalid <playlist> attribute: \"%s\"", name); - } - /* attribute version is mandatory !!! */ - if (!b_version_found) - msg_Warn(p_demux, "<playlist> requires \"version\" attribute"); - - psz_value = NULL; while ((i_node = xml_ReaderNextNode(p_xml_reader, &name)) > XML_READER_NONE) { const bool b_empty = xml_ReaderIsEmptyElement(p_xml_reader); + switch (i_node) { case XML_READER_STARTELEM: FREENULL(psz_value); if (!*name) { - msg_Err(p_demux, "invalid XML stream"); + msg_Err(p_stream, "invalid XML stream"); goto end; } - p_handler = get_handler(pl_elements, name); + + p_handler = get_handler(pl_elements, i_pl_elements, name); if (!p_handler) { - msg_Err(p_demux, "unexpected element <%s>", name); - goto end; + msg_Warn(p_stream, "skipping unexpected element <%s>", name); + if(!skip_element(NULL, NULL, p_xml_reader, name, b_empty)) + return false; } - - /* complex content is parsed in a separate function */ - if (p_handler->cmplx) + else { - if (!p_handler->pf_handler.cmplx(p_demux, p_input_node, - p_xml_reader, p_handler->name, - b_empty)) - return false; - p_handler = NULL; + /* complex content is parsed in a separate function */ + if (p_handler->cmplx) + { + if (!p_handler->pf_handler.cmplx(p_stream, p_input_node, + p_xml_reader, p_handler->name, + b_empty)) + return false; + /* Complex reader does read the named end element */ + p_handler = NULL; + } } break; case XML_READER_TEXT: - FREENULL(psz_value); - if(p_handler) + free(psz_value); + if(!p_handler) + { + psz_value = NULL; + } + else { psz_value = strdup(name); if (unlikely(!psz_value)) @@ -282,80 +266,129 @@ static bool parse_playlist_node COMPLEX_INTERFACE break; case XML_READER_ENDELEM: - /* leave if the current parent node <playlist> is terminated */ - if (!strcmp(name, psz_element)) + /* leave if the current parent node is terminated */ + if (!strcmp(name, psz_root_node)) { 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)) + + if(p_handler) { - msg_Err(p_demux, "there's no open element left for <%s>", name); - goto end; - } + /* there MUST have been a start tag for that element name */ + if (strcmp(p_handler->name, name)) + { + msg_Err(p_stream, "there's no open element left for <%s>", name); + goto end; + } - if (p_handler->pf_handler.smpl) - p_handler->pf_handler.smpl(p_input_item, p_handler->name, psz_value); - FREENULL(psz_value); - p_handler = NULL; + if (p_handler->pf_handler.smpl) + p_handler->pf_handler.smpl(p_input_item, p_handler->name, + psz_value, p_stream->p_sys); + + free(psz_value); + psz_value = NULL; + p_handler = NULL; + } break; } } end: free(psz_value); + return b_ret; } /** + * \brief parse the root node of a XSPF playlist + * \param p_stream stream instance + * \param p_input_item current input item + * \param p_xml_reader xml reader instance + * \param psz_element name of element to parse + */ +static bool parse_playlist_node COMPLEX_INTERFACE +{ + xspf_sys_t *sys = p_stream->p_sys; + + if(b_empty_node) + return false; + + /* read all playlist attributes */ + const char *psz_version = get_node_attribute(p_xml_reader, "version"); + if(!psz_version || (strcmp(psz_version, "0") && strcmp(psz_version, "1"))) + { + /* attribute version is mandatory !!! */ + if(!psz_version) + msg_Warn(p_stream, "<playlist> requires \"version\" attribute"); + else + msg_Warn(p_stream, "unsupported XSPF version %s", psz_version); + return false; + } + + const char *psz_base = get_node_attribute(p_xml_reader, "xml:base"); + if(psz_base) + { + free(sys->psz_base); + sys->psz_base = strdup(psz_base); + } + + 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 }, + }; + + return parse_node(p_stream, p_input_node, p_input_node->p_item, + p_xml_reader, psz_element, + pl_elements, ARRAY_SIZE(pl_elements)); +} + +/** * \brief parses the tracklist node which only may contain <track>s */ static bool parse_tracklist_node COMPLEX_INTERFACE { VLC_UNUSED(psz_element); - const char *name; - unsigned i_ntracks = 0; - int i_node; if(b_empty_node) return true; - /* now parse the <track>s */ - while ((i_node = xml_ReaderNextNode(p_xml_reader, &name)) > XML_READER_NONE) - { - const bool b_empty = xml_ReaderIsEmptyElement(p_xml_reader); - if (i_node == XML_READER_STARTELEM) - { - if (strcmp(name, "track")) - { - msg_Err(p_demux, "unexpected child of <trackList>: <%s>", - name); - return false; - } + /* parse the child elements */ + static const xml_elem_hnd_t pl_elements[] = + { {"track", {.cmplx = parse_track_node}, true }, + }; - /* parse the track data in a separate function */ - if (parse_track_node(p_demux, p_input_node, p_xml_reader, "track", b_empty)) - i_ntracks++; - } - else if (i_node == XML_READER_ENDELEM) - break; - } + return parse_node(p_stream, p_input_node, p_input_node->p_item, + p_xml_reader, psz_element, + pl_elements, ARRAY_SIZE(pl_elements)); +} - /* the <trackList> has to be terminated */ - if (i_node != XML_READER_ENDELEM) - { - msg_Err(p_demux, "there's a missing </trackList>"); - return false; - } - if (strcmp(name, "trackList")) +/** + * \brief handles the <location> elements + */ +static bool parse_location SIMPLE_INTERFACE +{ + VLC_UNUSED(psz_name); + xspf_sys_t *p_sys = (xspf_sys_t *) opaque; + char* psz_uri = ProcessMRL( psz_value, p_sys->psz_base ); + if(psz_uri) { - msg_Err(p_demux, "expected: </trackList>, found: </%s>", name); - return false; + input_item_SetURI(p_input, psz_uri); + free(psz_uri); } - - msg_Dbg(p_demux, "parsed %u tracks successfully", i_ntracks); - return true; + return psz_uri != NULL; } /** @@ -364,18 +397,28 @@ static bool parse_tracklist_node COMPLEX_INTERFACE */ static bool parse_track_node COMPLEX_INTERFACE { - input_item_t *p_input_item = p_input_node->p_item; - const char *name; - char *psz_value = NULL; - const xml_elem_hnd_t *p_handler = NULL; - demux_sys_t *p_sys = p_demux->p_sys; - int i_node; + xspf_sys_t *p_sys = p_stream->p_sys; if(b_empty_node) return true; + input_item_t *p_new_input = input_item_New(NULL, NULL); + if (!p_new_input) + return false; + + /* increfs p_new_input */ + input_item_node_t *p_new_node = input_item_node_Create(p_new_input); + if(!p_new_node) + { + input_item_Release(p_new_input); + return false; + } + + /* reset i_track_id */ + p_sys->i_track_id = -1; + static const xml_elem_hnd_t track_elements[] = - { {"location", {NULL}, false }, + { {"location", {.smpl = parse_location}, false }, {"identifier", {NULL}, false }, {"title", {.smpl = set_item_info}, false }, {"creator", {.smpl = set_item_info}, false }, @@ -390,154 +433,68 @@ static bool parse_track_node COMPLEX_INTERFACE {"extension", {.cmplx = parse_extension_node}, true }, }; - 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_sys->i_track_id = -1; - - while ((i_node = xml_ReaderNextNode(p_xml_reader, &name)) > XML_READER_NONE) + bool b_ret = parse_node(p_stream, p_new_node, p_new_input, + p_xml_reader, psz_element, + track_elements, ARRAY_SIZE(track_elements)); + if(b_ret) { - const bool b_empty = xml_ReaderIsEmptyElement(p_xml_reader); - switch (i_node) - { - case XML_READER_STARTELEM: - FREENULL(psz_value); - - if (!*name) - { - msg_Err(p_demux, "invalid XML stream"); - goto end; - } - 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) - { - if (!p_handler->pf_handler.cmplx(p_demux, p_new_node, - p_xml_reader, p_handler->name, - b_empty)) { - input_item_node_Delete(p_new_node); - input_item_Release(p_new_input); - return false; - } + input_item_CopyOptions(p_new_input, p_input_node->p_item); - p_handler = NULL; - } - break; + /* 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); - case XML_READER_TEXT: - FREENULL(psz_value); - if(p_handler) + if (p_sys->i_track_id < 0 || + p_sys->i_track_id == INT_MAX || + (size_t)p_sys->i_track_id >= (SIZE_MAX / sizeof(p_new_input))) + { + input_item_node_AppendNode(p_input_node, p_new_node); + p_new_node = NULL; + } + else + { + /* Extend array as needed */ + 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) { - psz_value = strdup(name); - if (unlikely(!psz_value)) - goto end; + p_sys->pp_tracklist = pp; + while (p_sys->i_track_id >= p_sys->i_tracklist_entries) + pp[p_sys->i_tracklist_entries++] = NULL; } - break; - - case XML_READER_ENDELEM: - /* leave if the current parent node <track> is terminated */ - if (!strcmp(name, psz_element)) - { - free(psz_value); - - /* 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_sys->i_track_id < 0 - || (size_t)p_sys->i_track_id >= (SIZE_MAX / sizeof(p_new_input))) - { - input_item_node_AppendNode(p_input_node, p_new_node); - input_item_Release(p_new_input); - return true; - } + } - 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) - { - input_item_Release(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); - input_item_Release(p_new_input); - input_item_node_Delete(p_new_node); - return false; - } + if (p_sys->i_track_id < p_sys->i_tracklist_entries) + { + input_item_t **pp_insert = &p_sys->pp_tracklist[p_sys->i_track_id]; - 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)) + if (*pp_insert != NULL) { - msg_Err(p_demux, "there's no open element left for <%s>", name); - goto end; - } - - /* special case: location */ - if (!strcmp(p_handler->name, "location")) - { - if (psz_value == NULL) - input_item_SetURI(p_new_input, "vlc://nop"); - else - { - char* psz_uri = ProcessMRL( psz_value, p_sys->psz_base ); - - if( !psz_uri ) - { - msg_Warn( p_demux, "unable to process MRL: %s", psz_value ); - goto end; - } - - input_item_SetURI(p_new_input, psz_uri); - free(psz_uri); - } - - input_item_CopyOptions(p_new_input, p_input_item); + msg_Warn(p_stream, "track ID %d collision", p_sys->i_track_id); + input_item_node_AppendItem(p_input_node, 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); + *pp_insert = p_new_input; + p_new_input = NULL; } - FREENULL(psz_value); - p_handler = NULL; - break; + } + else b_ret = false; } } - msg_Err(p_demux, "unexpected end of xml data"); -end: + if(p_new_node) + input_item_node_Delete(p_new_node); /* decrefs p_new_input */ + if(p_new_input) + input_item_Release(p_new_input); - input_item_Release(p_new_input); - input_item_node_Delete(p_new_node); - free(psz_value); - return false; + return b_ret; } /** @@ -545,6 +502,7 @@ end: */ static bool set_item_info SIMPLE_INTERFACE { + VLC_UNUSED(opaque); /* exit if setting is impossible */ if (!psz_name || !psz_value || !p_input) return false; @@ -576,6 +534,7 @@ static bool set_item_info SIMPLE_INTERFACE */ static bool set_option SIMPLE_INTERFACE { + VLC_UNUSED(opaque); /* exit if setting is impossible */ if (!psz_name || !psz_value || !p_input) return false; @@ -588,217 +547,141 @@ static bool set_option SIMPLE_INTERFACE } /** - * \brief parse the extension node of a XSPF playlist + * \brief handles the <vlc:id> elements */ -static bool parse_extension_node COMPLEX_INTERFACE +static bool parse_vlcid SIMPLE_INTERFACE +{ + VLC_UNUSED(p_input); VLC_UNUSED(psz_name); + xspf_sys_t *sys = (xspf_sys_t *) opaque; + if(psz_value) + sys->i_track_id = atoi(psz_value); + return true; +} + +/** + * \brief parse the vlc:node of a XSPF playlist + */ +static bool parse_vlcnode_node COMPLEX_INTERFACE { - demux_sys_t *sys = p_demux->p_sys; 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; - const xml_elem_hnd_t *p_handler = NULL; - input_item_t *p_new_input = NULL; if(b_empty_node) return true; - 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 */ - const char *name, *value; - while ((name = xml_ReaderNextAttr(p_xml_reader, &value)) != NULL) + const char *psz_attr = get_node_attribute(p_xml_reader, "title"); + if(psz_attr) { - if (!strcmp(name, "title")) - { - free(psz_title); - psz_title = strdup(value); - if (likely(psz_title != NULL)) - vlc_xml_decode(psz_title); - } - else if (!strcmp(name, "application")) - { - free(psz_application); - psz_application = strdup(value); - } - else - msg_Warn(p_demux, "invalid <%s> attribute:\"%s\"", psz_element, - name); + psz_title = strdup(psz_attr); + if (likely(psz_title != NULL)) + vlc_xml_decode(psz_title); } - /* attribute title is mandatory except for <extension> */ - if (!strcmp(psz_element, "vlc:node")) + /* attribute title is mandatory */ + if (!psz_title) { - if (!psz_title) - { - msg_Warn(p_demux, "<vlc:node> requires \"title\" attribute"); - goto error; - } - p_new_input = input_item_NewDirectory("vlc://nop", psz_title, - ITEM_NET_UNKNOWN); - if (p_new_input) - { - p_input_node = - input_item_node_AppendItem(p_input_node, p_new_input); - p_input_item = p_new_input; - } + msg_Warn(p_stream, "<vlc:node> requires \"title\" attribute"); + return false; } - else if (!strcmp(psz_element, "extension")) + input_item_t *p_new_input = + input_item_NewDirectory("vlc://nop", psz_title, ITEM_NET_UNKNOWN); + free(psz_title); + if (p_new_input) { - if (!psz_application) - { - msg_Warn(p_demux, "<extension> requires \"application\" attribute"); - goto error; - } - /* 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); - skip_element( NULL, NULL, p_xml_reader, NULL, b_empty_node ); - goto success; - } + p_input_node = + input_item_node_AppendItem(p_input_node, p_new_input); + p_input_item = p_new_input; } /* parse the child elements */ - while ((i_node = xml_ReaderNextNode(p_xml_reader, &name)) > XML_READER_NONE) - { - const bool b_empty = xml_ReaderIsEmptyElement(p_xml_reader); - switch (i_node) - { - case XML_READER_STARTELEM: - FREENULL(psz_value); + static const xml_elem_hnd_t pl_elements[] = + { {"vlc:node", {.cmplx = parse_vlcnode_node}, true }, + {"vlc:item", {.cmplx = parse_extitem_node}, true }, + {"vlc:id", {.smpl = parse_vlcid}, false }, + {"vlc:option", {.smpl = set_option}, false }, + }; - if (!*name) - { - msg_Err(p_demux, "invalid xml stream"); - goto error; - } - p_handler = get_handler(pl_elements, name); - if (!p_handler) - { - msg_Err(p_demux, "unexpected element <%s>", name); - goto error; - } - /* complex content is parsed in a separate function */ - if (p_handler->cmplx) - { - if (p_handler->pf_handler.cmplx(p_demux, p_input_node, - p_xml_reader, p_handler->name, - b_empty)) - { - p_handler = NULL; - } - else - goto error; - } - break; + bool b_ret = parse_node(p_stream, p_input_node, p_input_item, + p_xml_reader, psz_element, + pl_elements, ARRAY_SIZE(pl_elements)); - case XML_READER_TEXT: - FREENULL(psz_value); - if(p_handler) - { - psz_value = strdup(name); - if (unlikely(!psz_value)) - goto error; - } - break; + if (p_new_input) + input_item_Release(p_new_input); - case XML_READER_ENDELEM: - /* leave if the current parent node is terminated */ - if (!strcmp(name, psz_element)) - goto success; + return b_ret; +} - /* 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 error; - } +/** + * \brief parse the extension node of a XSPF playlist + */ +static bool parse_extension_node COMPLEX_INTERFACE +{ + if(b_empty_node) + return false; - /* special tag <vlc:id> */ - if (!strcmp(p_handler->name, "vlc:id") && psz_value ) - { - sys->i_track_id = atoi(psz_value); - } - else if (p_handler->pf_handler.smpl) - { - p_handler->pf_handler.smpl(p_input_item, p_handler->name, - psz_value); - } - FREENULL(psz_value); - p_handler = NULL; - break; - } + const char *psz_application = get_node_attribute(p_xml_reader, "application"); + if (!psz_application) + { + msg_Warn(p_stream, "<extension> requires \"application\" attribute"); + return false; } -success: ; - bool b_success = true; -out: - if (p_new_input) - input_item_Release(p_new_input); - free(psz_application); - free(psz_title); - free(psz_value); - return b_success; -error: - b_success = false; - goto out; + /* Skip the extension if the application is not vlc + This will skip all children of the current node */ + if (strcmp(psz_application, "http://www.videolan.org/vlc/playlist/0")) + { + msg_Dbg(p_stream, "Skipping \"%s\" extension tag", psz_application); + return skip_element( NULL, NULL, p_xml_reader, psz_element, b_empty_node ); + } + + /* parse the child elements */ + static const xml_elem_hnd_t pl_elements[] = + { {"vlc:node", {.cmplx = parse_vlcnode_node}, true }, + {"vlc:id", {.smpl = parse_vlcid}, false }, + {"vlc:option", {.smpl = set_option}, false }, + }; + + return parse_node(p_stream, p_input_node, p_input_node->p_item, + p_xml_reader, psz_element, + pl_elements, ARRAY_SIZE(pl_elements)); } /** * \brief parse the extension item node of a XSPF playlist */ + static bool parse_extitem_node COMPLEX_INTERFACE { VLC_UNUSED(psz_element); - demux_sys_t *sys = p_demux->p_sys; + xspf_sys_t *sys = p_stream->p_sys; input_item_t *p_new_input = NULL; int i_tid = -1; if(!b_empty_node) return false; - /* read all extension item attributes */ - const char *name, *value; - while ((name = xml_ReaderNextAttr(p_xml_reader, &value)) != NULL) - { - /* attribute: href */ - if (!strcmp(name, "tid")) - i_tid = atoi(value); - /* unknown attribute */ - else - msg_Warn(p_demux, "invalid <vlc:item> attribute: \"%s\"", name); - } + const char *psz_tid = get_node_attribute(p_xml_reader, "tid"); + if(psz_tid) + i_tid = atoi(psz_tid); /* attribute href is mandatory */ - if (i_tid < 0) + if (!psz_tid || i_tid < 0) { - msg_Warn(p_demux, "<vlc:item> requires \"tid\" attribute"); + msg_Warn(p_stream, "<vlc:item> requires valid \"tid\" attribute"); return false; } - if (i_tid >= sys->i_tracklist_entries) + if (i_tid >= sys->i_tracklist_entries || + !(p_new_input = sys->pp_tracklist[ i_tid ]) ) { - msg_Warn(p_demux, "invalid \"tid\" attribute"); - return false; + msg_Warn(p_stream, "non existing \"tid\" %d referenced", i_tid); + return true; } - p_new_input = sys->pp_tracklist[ i_tid ]; - if (p_new_input) - { - input_item_node_AppendItem(p_input_node, p_new_input); - input_item_Release(p_new_input); - sys->pp_tracklist[i_tid] = NULL; - } + input_item_node_AppendItem(p_input_node, p_new_input); + input_item_Release(p_new_input); + sys->pp_tracklist[i_tid] = NULL; return true; } @@ -808,7 +691,7 @@ static bool parse_extitem_node COMPLEX_INTERFACE */ static bool skip_element COMPLEX_INTERFACE { - VLC_UNUSED(p_demux); VLC_UNUSED(p_input_node); + VLC_UNUSED(p_stream); VLC_UNUSED(p_input_node); if(b_empty_node) return true; _______________________________________________ vlc-commits mailing list [email protected] https://mailman.videolan.org/listinfo/vlc-commits
