[vlc-commits] demux: playlist: xspf: revector

Francois Cartegnie git at videolan.org
Wed Sep 6 13:10:30 CEST 2017


vlc | branch: master | Francois Cartegnie <fcvlcdev at free.fr> | 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 <yoann at videolan.org>
@@ -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;




More information about the vlc-commits mailing list