[vlc-devel] [PATCHv3 01/13] access: re-refactor pf_readdir

Thomas Guillem thomas at gllm.fr
Tue May 17 18:24:45 CEST 2016


This commit changes pf_readdir callback to its original behavior. Accesses and
streams now add items to a node.

Archive stream_filters will now be able to add nodes to a node (when an archive
has directory). This was not possible before.

This commit also adds an access_fsdir helper to help fs accesses (file, smb,
nfs, ftp, sftp) adding items to a node. These accesses need the same treatment
that is now done by this helper:
 - hide hidden files or not (depending on "show-hiddenfiles" option)
 - skip some file extensions (depending on "ignore-filetypes" option)
 - sort items by type and alphabetically (depending on "directory-sort"
   option).
 - For a next commit: attach slaves to items

The directory demux won't do these operations anymore for every access/stream.

This commit doesn't change the interruptible state of the pf_readdir function,
accesses/streams are still interruptible in the middle of a pf_readdir call.

This partially reverts commit 88ffe1587824c27f35705ee28e607f80ca335b46.
---
 include/vlc_access.h                |  57 +++++++++++++--
 include/vlc_stream.h                |   6 +-
 modules/access/archive/stream.c     |  25 ++++---
 modules/access/directory.c          |  39 ++++++----
 modules/access/dsm/access.c         | 119 +++++++++++++++---------------
 modules/access/fs.h                 |   3 +-
 modules/access/ftp.c                |  57 ++++++++-------
 modules/access/nfs.c                |  71 ++++++++++--------
 modules/access/sftp.c               |  38 +++++-----
 modules/access/smb.c                |  31 ++++----
 modules/demux/playlist/directory.c  | 137 ++---------------------------------
 modules/services_discovery/upnp.cpp | 112 ++++++++++++-----------------
 modules/services_discovery/upnp.hpp |  14 ++--
 modules/stream_filter/inflate.c     |   6 +-
 modules/stream_filter/prefetch.c    |   6 +-
 src/input/access.c                  | 139 ++++++++++++++++++++++++++++++++++--
 src/input/stream.c                  |   9 ++-
 src/input/stream_filter.c           |   4 +-
 src/libvlccore.sym                  |   3 +
 19 files changed, 468 insertions(+), 408 deletions(-)

diff --git a/include/vlc_access.h b/include/vlc_access.h
index 4578734..9ac8a81 100644
--- a/include/vlc_access.h
+++ b/include/vlc_access.h
@@ -88,11 +88,7 @@ struct access_t
      * XXX A access should set one and only one of them */
     ssize_t     (*pf_read)   ( access_t *, uint8_t *, size_t );  /* Return -1 if no data yet, 0 if no more data, else real data read */
     block_t    *(*pf_block)  ( access_t * );                     /* Return a block of data in his 'natural' size, NULL if not yet data or eof */
-
-    /* pf_readdir: Read the next input_item_t from the directory stream. It
-     * returns the next input item on success or NULL in case of error or end
-     * of stream. The item must be released with input_item_Release. */
-    input_item_t *(*pf_readdir)( access_t * );
+    int         (*pf_readdir)( access_t *, input_item_node_t * );/* Fills the provided item_node, see doc/browsing.txt for details */
 
     /* Called for each seek.
      * XXX can be null */
@@ -239,6 +235,11 @@ static inline void access_InitFields( access_t *p_a )
 }
 
 /**
+ * \defgroup access_helper Access Helpers
+ * @{
+ */
+
+/**
  * Default pf_control callback for directory accesses.
  */
 VLC_API int access_vaDirectoryControlHelper( access_t *p_access, int i_query, va_list args );
@@ -268,7 +269,51 @@ VLC_API int access_vaDirectoryControlHelper( access_t *p_access, int i_query, va
     } while(0);
 
 /**
- * @}
+ * Access pf_readdir helper struct
+ * \see access_fsdir_init()
+ * \see access_fsdir_additem()
+ * \see access_fsdir_finish()
+ */
+struct access_fsdir
+{
+    input_item_node_t *p_node;
+    bool b_show_hiddenfiles;
+    char *psz_ignored_exts;
+    char *psz_sort;
+};
+
+/**
+ * Init a access_fsdir struct
+ *
+ * \param p_fsdir need to be cleaned with access_fsdir_finish()
+ * \param p_node node that will be used to add items
+ */
+VLC_API void access_fsdir_init(struct access_fsdir *p_fsdir,
+                               access_t *p_access, input_item_node_t *p_node);
+
+/**
+ * Finish adding items to the node
+ *
+ * \param b_success if true, items of the node will be sorted according
+ * "directory-sort" option.
+ */
+VLC_API void access_fsdir_finish(struct access_fsdir *p_fsdir, bool b_success);
+
+/**
+ * Add a new input_item_t entry to the node of the access_fsdir struct.
+ *
+ * \param p_fsdir previously inited access_fsdir struct
+ * \param psz_uri uri of the new item
+ * \param psz_filename file name of the new item
+ * \param i_type see \ref input_item_type_e
+ * \param i_net see \ref input_item_net_type
+ */
+VLC_API int access_fsdir_additem(struct access_fsdir *p_fsdir,
+                                 const char *psz_uri, const char *psz_filename,
+                                 int i_type, int i_net);
+
+/**
+ * @} @}
  */
 
 #endif
diff --git a/include/vlc_stream.h b/include/vlc_stream.h
index d783487..0b64f61 100644
--- a/include/vlc_stream.h
+++ b/include/vlc_stream.h
@@ -57,7 +57,7 @@ struct stream_t
 
     /* */
     ssize_t     (*pf_read)(stream_t *, void *, size_t);
-    input_item_t *(*pf_readdir)( stream_t * );
+    int         (*pf_readdir)( stream_t *, input_item_node_t * );
     int         (*pf_seek)(stream_t *, uint64_t);
     int         (*pf_control)( stream_t *, int i_query, va_list );
 
@@ -157,7 +157,7 @@ VLC_API void stream_Delete( stream_t *s );
 VLC_API int stream_Control( stream_t *s, int i_query, ... );
 VLC_API block_t * stream_Block( stream_t *s, size_t );
 VLC_API char * stream_ReadLine( stream_t * );
-VLC_API input_item_t *stream_ReadDir( stream_t * );
+VLC_API int stream_ReadDir( stream_t *, input_item_node_t * );
 
 /**
  * Low level custom stream creation.
@@ -248,7 +248,7 @@ VLC_API stream_t* stream_FilterNew( stream_t *p_source, const char *psz_stream_f
  * Default ReadDir implementation for stream Filter. This implementation just
  * forward the pf_readdir call to the p_source stream.
  */
-VLC_API input_item_t *stream_FilterDefaultReadDir( stream_t *s );
+VLC_API int stream_FilterDefaultReadDir( stream_t *s, input_item_node_t *p_node );
 
 /**
  * Sets stream_FilterDefaultReadDir as the pf_readdir callback for this stream filter
diff --git a/modules/access/archive/stream.c b/modules/access/archive/stream.c
index 3d70725..2fb7986 100644
--- a/modules/access/archive/stream.c
+++ b/modules/access/archive/stream.c
@@ -128,34 +128,39 @@ static ssize_t SeekCallback(struct archive *p_archive, void *p_object, ssize_t i
     return stream_Tell(p_stream->p_source);
 }
 
-static input_item_t *Browse(stream_t *p_stream)
+static int Browse(stream_t *p_stream, input_item_node_t *p_node)
 {
     stream_sys_t *p_sys = p_stream->p_sys;
     struct archive_entry *p_entry;
-    input_item_t *p_item = NULL;
 
-    if (archive_read_next_header(p_sys->p_archive, &p_entry) == ARCHIVE_OK)
+    while(archive_read_next_header(p_sys->p_archive, &p_entry) == ARCHIVE_OK)
     {
         char *psz_uri = NULL;
         char *psz_access_uri = NULL;
         int i_ret = asprintf(&psz_access_uri, "%s%c%s", p_stream->psz_url,
                              ARCHIVE_SEP_CHAR, archive_entry_pathname(p_entry));
         if (i_ret == -1)
-            return NULL;
+            goto error;
         i_ret = asprintf(&psz_uri, "archive://%s", psz_access_uri);
         free(psz_access_uri);
-        if( i_ret == -1 )
-            return NULL;
+        if(i_ret == -1)
+            goto error;
 
-        p_item = input_item_New(psz_uri, archive_entry_pathname(p_entry));
+        input_item_t *p_item = input_item_New(psz_uri, archive_entry_pathname(p_entry));
         free( psz_uri );
-        if(p_item == NULL)
-            return NULL;
+        if (p_item == NULL)
+            goto error;
 
+        input_item_CopyOptions(p_node->p_item, p_item);
+        input_item_node_AppendItem(p_node, p_item);
         msg_Dbg(p_stream, "declaring playlist entry %s", archive_entry_pathname(p_entry));
+        input_item_Release(p_item);
     }
 
-    return p_item;
+    return VLC_SUCCESS;
+
+error:
+    return VLC_ENOMEM;
 }
 
 int StreamOpen(vlc_object_t *p_object)
diff --git a/modules/access/directory.c b/modules/access/directory.c
index ff908eb..683de55 100644
--- a/modules/access/directory.c
+++ b/modules/access/directory.c
@@ -46,7 +46,6 @@ struct access_sys_t
 {
     char *base_uri;
     DIR *dir;
-    bool special_files;
 };
 
 /*****************************************************************************
@@ -70,7 +69,6 @@ int DirInit (access_t *access, DIR *dir)
         goto error;
 
     sys->dir = dir;
-    sys->special_files = var_InheritBool(access, "list-special-files");
 
     access->p_sys = sys;
     access->pf_readdir = DirRead;
@@ -113,12 +111,18 @@ void DirClose(vlc_object_t *obj)
     free(sys);
 }
 
-input_item_t *DirRead(access_t *access)
+int DirRead (access_t *access, input_item_node_t *node)
 {
     access_sys_t *sys = access->p_sys;
     const char *entry;
+    int ret = VLC_SUCCESS;
 
-    while ((entry = vlc_readdir(sys->dir)) != NULL)
+    bool special_files = var_InheritBool(access, "list-special-files");
+
+    struct access_fsdir fsdir;
+    access_fsdir_init(&fsdir, access, node);
+
+    while (ret == VLC_SUCCESS && (entry = vlc_readdir(sys->dir)) != NULL)
     {
         struct stat st;
         int type;
@@ -136,17 +140,17 @@ input_item_t *DirRead(access_t *access)
         switch (st.st_mode & S_IFMT)
         {
             case S_IFBLK:
-                if (!sys->special_files)
+                if (!special_files)
                     continue;
                 type = ITEM_TYPE_DISC;
                 break;
             case S_IFCHR:
-                if (!sys->special_files)
+                if (!special_files)
                     continue;
                 type = ITEM_TYPE_CARD;
                 break;
             case S_IFIFO:
-                if (!sys->special_files)
+                if (!special_files)
                     continue;
                 type = ITEM_TYPE_STREAM;
                 break;
@@ -165,20 +169,25 @@ input_item_t *DirRead(access_t *access)
         /* Create an input item for the current entry */
         char *encoded = vlc_uri_encode(entry);
         if (unlikely(encoded == NULL))
-            continue;
+        {
+            ret = VLC_ENOMEM;
+            break;
+        }
 
         char *uri;
         if (unlikely(asprintf(&uri, "%s/%s", sys->base_uri, encoded) == -1))
             uri = NULL;
         free(encoded);
         if (unlikely(uri == NULL))
-            continue;
-
-        input_item_t *item = input_item_NewExt(uri, entry, -1, type,
-                                               ITEM_NET_UNKNOWN);
+        {
+            ret = VLC_ENOMEM;
+            break;
+        }
+        ret = access_fsdir_additem(&fsdir, uri, entry, type, ITEM_NET_UNKNOWN);
         free(uri);
-        if (likely(item != NULL))
-            return item;
     }
-    return NULL;
+
+    access_fsdir_finish(&fsdir, ret == VLC_SUCCESS);
+
+    return ret;
 }
diff --git a/modules/access/dsm/access.c b/modules/access/dsm/access.c
index bac034b..94155ca 100644
--- a/modules/access/dsm/access.c
+++ b/modules/access/dsm/access.c
@@ -98,7 +98,8 @@ static int BrowserInit( access_t *p_access );
 static int get_address( access_t *p_access );
 static int login( access_t *p_access );
 static bool get_path( access_t *p_access );
-static input_item_t* new_item( access_t *p_access, const char *psz_name, int i_type );
+static int add_item( access_t *p_access,  struct access_fsdir *p_fsdir,
+                     const char *psz_name, int i_type );
 
 struct access_sys_t
 {
@@ -115,11 +116,6 @@ struct access_sys_t
 
     smb_fd              i_fd;               /**< SMB fd for the file we're reading */
     smb_tid             i_tid;              /**< SMB Tree ID we're connected to */
-
-    size_t              i_browse_count;
-    size_t              i_browse_idx;
-    smb_share_list      shares;
-    smb_stat_list       files;
 };
 
 /*****************************************************************************
@@ -211,11 +207,8 @@ static void Close( vlc_object_t *p_this )
     if( p_sys->p_session )
         smb_session_destroy( p_sys->p_session );
     vlc_UrlClean( &p_sys->url );
-    if( p_sys->shares )
-        smb_share_list_destroy( p_sys->shares );
-    if( p_sys->files )
-        smb_stat_list_destroy( p_sys->files );
     free( p_sys->psz_fullpath );
+
     free( p_sys );
 }
 
@@ -525,16 +518,15 @@ static int Control( access_t *p_access, int i_query, va_list args )
     return VLC_SUCCESS;
 }
 
-static input_item_t *new_item( access_t *p_access, const char *psz_name,
-                               int i_type )
+static int add_item( access_t *p_access, struct access_fsdir *p_fsdir,
+                     const char *psz_name, int i_type )
 {
-    input_item_t *p_item;
     char         *psz_uri;
     int           i_ret;
 
     char *psz_encoded_name = vlc_uri_encode( psz_name );
     if( psz_encoded_name == NULL )
-        return NULL;
+        return VLC_ENOMEM;
     const char *psz_sep = p_access->psz_location[0] != '\0'
         && p_access->psz_location[strlen(p_access->psz_location) -1] != '/'
         ? "/" : "";
@@ -542,90 +534,91 @@ static input_item_t *new_item( access_t *p_access, const char *psz_name,
                       psz_sep, psz_encoded_name );
     free( psz_encoded_name );
     if( i_ret == -1 )
-        return NULL;
-
-    p_item = input_item_NewExt( psz_uri, psz_name, -1, i_type, ITEM_NET );
-    free( psz_uri );
-    if( p_item == NULL )
-        return NULL;
+        return VLC_ENOMEM;
 
-    return p_item;
+    return access_fsdir_additem( p_fsdir, psz_uri, psz_name, i_type, ITEM_NET );
 }
 
-static input_item_t* BrowseShare( access_t *p_access )
+static int BrowseShare( access_t *p_access, input_item_node_t *p_node )
 {
     access_sys_t *p_sys = p_access->p_sys;
+    smb_share_list  shares;
     const char     *psz_name;
-    input_item_t   *p_item = NULL;
+    size_t          share_count;
+    int             i_ret = VLC_SUCCESS;
 
-    if( !p_sys->i_browse_count )
-    {
-        size_t i_count;
-        if( smb_share_get_list( p_sys->p_session, &p_sys->shares, &i_count )
-            != DSM_SUCCESS )
-            return NULL;
-        else
-            p_sys->i_browse_count = i_count;
-    }
-    for( ; !p_item && p_sys->i_browse_idx < p_sys->i_browse_count
-         ; p_sys->i_browse_idx++ )
+    if( smb_share_get_list( p_sys->p_session, &shares, &share_count )
+        != DSM_SUCCESS )
+        return VLC_EGENERIC;
+
+    struct access_fsdir fsdir;
+    access_fsdir_init( &fsdir, p_access, p_node );
+
+    for( size_t i = 0; i < share_count && i_ret == VLC_SUCCESS; i++ )
     {
-        psz_name = smb_share_list_at( p_sys->shares, p_sys->i_browse_idx );
+        psz_name = smb_share_list_at( shares, i );
 
         if( psz_name[strlen( psz_name ) - 1] == '$')
             continue;
 
-        p_item = new_item( p_access, psz_name, ITEM_TYPE_DIRECTORY );
-        if( !p_item )
-            return NULL;
+        i_ret = add_item( p_access, &fsdir, psz_name, ITEM_TYPE_DIRECTORY );
     }
-    return p_item;
+
+    access_fsdir_finish( &fsdir, i_ret == VLC_SUCCESS );
+
+    smb_share_list_destroy( shares );
+    return i_ret;
 }
 
-static input_item_t* BrowseDirectory( access_t *p_access )
+static int BrowseDirectory( access_t *p_access, input_item_node_t *p_node )
 {
     access_sys_t *p_sys = p_access->p_sys;
+    smb_stat_list   files;
     smb_stat        st;
-    input_item_t   *p_item = NULL;
     char           *psz_query;
     const char     *psz_name;
-    int             i_ret;
+    size_t          files_count;
+    int             i_ret = VLC_SUCCESS;
 
-    if( !p_sys->i_browse_count )
+    if( p_sys->psz_path != NULL )
     {
-        if( p_sys->psz_path != NULL )
-        {
-            i_ret = asprintf( &psz_query, "%s\\*", p_sys->psz_path );
-            if( i_ret == -1 )
-                return NULL;
-            p_sys->files = smb_find( p_sys->p_session, p_sys->i_tid, psz_query );
-            free( psz_query );
-        }
-        else
-            p_sys->files = smb_find( p_sys->p_session, p_sys->i_tid, "\\*" );
-        if( p_sys->files == NULL )
-            return NULL;
-        p_sys->i_browse_count = smb_stat_list_count( p_sys->files );
+        if( asprintf( &psz_query, "%s\\*", p_sys->psz_path ) == -1 )
+            return VLC_ENOMEM;
+        files = smb_find( p_sys->p_session, p_sys->i_tid, psz_query );
+        free( psz_query );
     }
+    else
+        files = smb_find( p_sys->p_session, p_sys->i_tid, "\\*" );
+
+    if( files == NULL )
+        return VLC_EGENERIC;
+
+    struct access_fsdir fsdir;
+    access_fsdir_init( &fsdir, p_access, p_node );
 
-    if( p_sys->i_browse_idx < p_sys->i_browse_count )
+    files_count = smb_stat_list_count( files );
+    for( size_t i = 0; i < files_count && i_ret == VLC_SUCCESS; i++ )
     {
         int i_type;
 
-        st = smb_stat_list_at( p_sys->files, p_sys->i_browse_idx++ );
+        st = smb_stat_list_at( files, i );
 
         if( st == NULL )
-            return NULL;
+        {
+            continue;
+        }
 
         psz_name = smb_stat_name( st );
 
         i_type = smb_stat_get( st, SMB_STAT_ISDIR ) ?
                  ITEM_TYPE_DIRECTORY : ITEM_TYPE_FILE;
-        p_item = new_item( p_access, psz_name, i_type );
-        if( !p_item )
-            return NULL;
+        i_ret = add_item( p_access, &fsdir, psz_name, i_type );
     }
-    return p_item;
+
+    access_fsdir_finish( &fsdir, i_ret == VLC_SUCCESS );
+
+    smb_stat_list_destroy( files );
+    return i_ret;
 }
 
 static int DirControl( access_t *p_access, int i_query, va_list args )
diff --git a/modules/access/fs.h b/modules/access/fs.h
index eb67411..3bcf048 100644
--- a/modules/access/fs.h
+++ b/modules/access/fs.h
@@ -25,5 +25,6 @@ void FileClose (vlc_object_t *);
 
 int DirOpen (vlc_object_t *);
 int DirInit (access_t *p_access, DIR *handle);
-input_item_t* DirRead (access_t *);
+int DirRead (access_t *, input_item_node_t *);
+int DirControl (access_t *, int, va_list);
 void DirClose (vlc_object_t *);
diff --git a/modules/access/ftp.c b/modules/access/ftp.c
index 3a878b1..7017ea1 100644
--- a/modules/access/ftp.c
+++ b/modules/access/ftp.c
@@ -112,7 +112,7 @@ vlc_module_end ()
 static ssize_t Read( access_t *, uint8_t *, size_t );
 static int Seek( access_t *, uint64_t );
 static int Control( access_t *, int, va_list );
-static input_item_t* DirRead( access_t * );
+static int DirRead( access_t *, input_item_node_t * );
 static int DirControl( access_t *, int, va_list );
 #ifdef ENABLE_SOUT
 static int OutSeek( sout_access_out_t *, off_t );
@@ -877,37 +877,46 @@ static ssize_t Read( access_t *p_access, uint8_t *p_buffer, size_t i_len )
 /*****************************************************************************
  * DirRead:
  *****************************************************************************/
-static input_item_t* DirRead( access_t *p_access )
+static int DirRead (access_t *p_access, input_item_node_t *p_current_node)
 {
     access_sys_t *p_sys = p_access->p_sys;
-    input_item_t *p_item = NULL;
+    int i_ret = VLC_SUCCESS;
 
     assert( p_sys->data.fd != -1 );
     assert( !p_sys->out );
 
-    char *psz_line;
-    if( p_sys->data.p_tls != NULL )
-        psz_line = vlc_tls_GetLine( p_sys->data.p_tls );
-    else
-        psz_line = net_Gets( p_access, p_sys->data.fd );
-    if( psz_line == NULL )
-        return NULL;
-
-    char *psz_uri;
-    if( asprintf( &psz_uri, "%s://%s:%d%s%s/%s",
-                  ( p_sys->tlsmode == NONE ) ? "ftp" :
-                  ( ( p_sys->tlsmode == IMPLICIT ) ? "ftps" : "ftpes" ),
-                  p_sys->url.psz_host, p_sys->url.i_port,
-                  p_sys->url.psz_path ? "/" : "",
-                  p_sys->url.psz_path ? p_sys->url.psz_path : "",
-                  psz_line ) != -1 )
+    struct access_fsdir fsdir;
+    access_fsdir_init( &fsdir, p_access, p_current_node );
+
+    while (i_ret == VLC_SUCCESS)
     {
-        p_item = input_item_NewExt( psz_uri, psz_line, -1, ITEM_TYPE_UNKNOWN,
-                                    ITEM_NET );
-        free( psz_uri );
+        char *psz_line;
+        if( p_sys->data.p_tls != NULL )
+            psz_line = vlc_tls_GetLine( p_sys->data.p_tls );
+        else
+            psz_line = net_Gets( p_access, p_sys->data.fd );
+
+        if( psz_line == NULL )
+            break;
+
+        char *psz_uri;
+        if( asprintf( &psz_uri, "%s://%s:%d%s%s/%s",
+                      ( p_sys->tlsmode == NONE ) ? "ftp" :
+                      ( ( p_sys->tlsmode == IMPLICIT ) ? "ftps" : "ftpes" ),
+                      p_sys->url.psz_host, p_sys->url.i_port,
+                      p_sys->url.psz_path ? "/" : "",
+                      p_sys->url.psz_path ? p_sys->url.psz_path : "",
+                      psz_line ) != -1 )
+        {
+            i_ret = access_fsdir_additem( &fsdir, psz_uri, psz_line,
+                                          ITEM_TYPE_UNKNOWN, ITEM_NET );
+            free( psz_uri );
+        }
+        free( psz_line );
     }
-    free( psz_line );
-    return p_item;
+
+    access_fsdir_finish( &fsdir, i_ret == VLC_SUCCESS );
+    return i_ret;
 }
 
 static int DirControl( access_t *p_access, int i_query, va_list args )
diff --git a/modules/access/nfs.c b/modules/access/nfs.c
index 310bc95..9e5b6a2 100644
--- a/modules/access/nfs.c
+++ b/modules/access/nfs.c
@@ -83,7 +83,6 @@ struct access_sys_t
         {
             char **         ppsz_names;
             int             i_count;
-            unsigned int    i_current;
         } exports;
         struct
         {
@@ -315,26 +314,33 @@ NfsGetUrl(vlc_url_t *p_url, const char *psz_file)
         return psz_url;
 }
 
-static input_item_t *
-DirRead(access_t *p_access)
+static int
+DirRead(access_t *p_access, input_item_node_t *p_node)
 {
     access_sys_t *p_sys = p_access->p_sys;
     struct nfsdirent *p_nfsdirent;
+    int i_ret = VLC_SUCCESS;
     assert(p_sys->p_nfsdir);
 
-    p_nfsdirent = nfs_readdir(p_sys->p_nfs, p_sys->p_nfsdir);
-    if (p_nfsdirent == NULL)
-        return NULL;
-    else
+    struct access_fsdir fsdir;
+    access_fsdir_init(&fsdir, p_access, p_node);
+
+    while (i_ret == VLC_SUCCESS
+        && (p_nfsdirent = nfs_readdir(p_sys->p_nfs, p_sys->p_nfsdir)) != NULL)
     {
-        input_item_t *p_item;
         char *psz_name_encoded = vlc_uri_encode(p_nfsdirent->name);
         if (psz_name_encoded == NULL)
-            return NULL;
+        {
+            i_ret = VLC_ENOMEM;
+            break;
+        }
         char *psz_url = NfsGetUrl(&p_sys->encoded_url, psz_name_encoded);
         free(psz_name_encoded);
         if (psz_url == NULL)
-            return NULL;
+        {
+            i_ret = VLC_ENOMEM;
+            break;
+        }
 
         int i_type;
         switch (p_nfsdirent->type)
@@ -348,34 +354,44 @@ DirRead(access_t *p_access)
         default:
             i_type = ITEM_TYPE_UNKNOWN;
         }
-        p_item = input_item_NewExt(psz_url, p_nfsdirent->name, -1, i_type,
-                                   ITEM_NET);
+        i_ret = access_fsdir_additem(&fsdir, psz_url, p_nfsdirent->name,
+                                     i_type, ITEM_NET);
         free(psz_url);
-        return p_item;
     }
+
+    access_fsdir_finish(&fsdir, i_ret == VLC_SUCCESS);
+
+    return i_ret;
 }
 
-static input_item_t *
-MountRead(access_t *p_access)
+static int
+MountRead(access_t *p_access, input_item_node_t *p_node)
 {
     access_sys_t *p_sys = p_access->p_sys;
     assert(p_sys->p_mount != NULL && p_sys->res.exports.i_count >= 0);
+    int i_ret = VLC_SUCCESS;
 
-    unsigned int i_count = p_sys->res.exports.i_count;
-    unsigned int i_current = p_sys->res.exports.i_current;
-    if (i_current >= i_count)
-        return NULL;
+    struct access_fsdir fsdir;
+    access_fsdir_init(&fsdir, p_access, p_node);
 
-    char *psz_name = p_sys->res.exports.ppsz_names[i_current];
-    p_sys->res.exports.i_current++;
+    for (int i = 0; i < p_sys->res.exports.i_count && i_ret == VLC_SUCCESS; ++i)
+    {
+        char *psz_name = p_sys->res.exports.ppsz_names[i];
 
-    char *psz_url = NfsGetUrl(&p_sys->encoded_url, psz_name);
-    if (psz_url == NULL)
-        return NULL;
+        char *psz_url = NfsGetUrl(&p_sys->encoded_url, psz_name);
+        if (psz_url == NULL)
+        {
+            i_ret = VLC_ENOMEM;
+            break;
+        }
+        i_ret = access_fsdir_additem(&fsdir, psz_url, psz_name,
+                                     ITEM_TYPE_DIRECTORY, ITEM_NET);
+        free(psz_url);
+    }
+
+    access_fsdir_finish(&fsdir, i_ret == VLC_SUCCESS);
 
-    input_item_t *p_item = input_item_NewDirectory(psz_url, psz_name, ITEM_NET);
-    free(psz_url);
-    return p_item;
+    return i_ret;
 }
 
 static int
@@ -706,7 +722,6 @@ Open(vlc_object_t *p_obj)
 
         p_sys->res.exports.ppsz_names = NULL;
         p_sys->res.exports.i_count = -1;
-        p_sys->res.exports.i_current = 0;
 
         if (mount_getexports_async(p_sys->p_mount, p_sys->p_nfs_url->server,
                                    mount_export_cb, p_access) < 0)
diff --git a/modules/access/sftp.c b/modules/access/sftp.c
index f5488df..550d69d 100644
--- a/modules/access/sftp.c
+++ b/modules/access/sftp.c
@@ -80,7 +80,7 @@ static ssize_t  Read( access_t *, uint8_t *, size_t );
 static int      Seek( access_t *, uint64_t );
 static int      Control( access_t *, int, va_list );
 
-static input_item_t* DirRead( access_t *p_access );
+static int DirRead( access_t *, input_item_node_t * );
 static int DirControl( access_t *, int, va_list );
 
 struct access_sys_t
@@ -464,11 +464,11 @@ static int Control( access_t* p_access, int i_query, va_list args )
  * Directory access
  *****************************************************************************/
 
-static input_item_t* DirRead( access_t *p_access )
+static int DirRead (access_t *p_access, input_item_node_t *p_current_node)
 {
     access_sys_t *p_sys = p_access->p_sys;
     LIBSSH2_SFTP_ATTRIBUTES attrs;
-    input_item_t *p_item = NULL;
+    int i_ret = VLC_SUCCESS;
     int err;
     /* Allocate 1024 bytes for file name. Longer names are skipped.
      * libssh2 does not support seeking in directory streams.
@@ -479,9 +479,13 @@ static input_item_t* DirRead( access_t *p_access )
     char *psz_file = malloc( i_size );
 
     if( !psz_file )
-        return NULL;
+        return VLC_ENOMEM;
 
-    while( !p_item && 0 != ( err = libssh2_sftp_readdir( p_sys->file, psz_file, i_size, &attrs ) ) )
+    struct access_fsdir fsdir;
+    access_fsdir_init( &fsdir, p_access, p_current_node );
+
+    while( i_ret == VLC_SUCCESS
+        && 0 != ( err = libssh2_sftp_readdir( p_sys->file, psz_file, i_size, &attrs ) ) )
     {
         if( err < 0 )
         {
@@ -499,38 +503,34 @@ static input_item_t* DirRead( access_t *p_access )
             break;
         }
 
-        if( psz_file[0] == '.' )
-        {
-            continue;
-        }
-
         /* Create an input item for the current entry */
 
         char *psz_full_uri, *psz_uri;
 
         psz_uri = vlc_uri_encode( psz_file );
         if( psz_uri == NULL )
-            continue;
+        {
+            i_ret = VLC_ENOMEM;
+            break;
+        }
 
         if( asprintf( &psz_full_uri, "%s/%s", p_sys->psz_base_url, psz_uri ) == -1 )
         {
             free( psz_uri );
-            continue;
+            i_ret = VLC_ENOMEM;
+            break;
         }
         free( psz_uri );
 
         int i_type = LIBSSH2_SFTP_S_ISDIR( attrs.permissions ) ? ITEM_TYPE_DIRECTORY : ITEM_TYPE_FILE;
-        p_item = input_item_NewExt( psz_full_uri, psz_file, -1, i_type, ITEM_NET );
+        i_ret = access_fsdir_additem( &fsdir, psz_full_uri, psz_file, i_type,
+                                      ITEM_NET );
         free( psz_full_uri );
-
-        if( p_item == NULL )
-        {
-            break;
-        }
     }
 
+    access_fsdir_finish( &fsdir, i_ret == VLC_SUCCESS );
     free( psz_file );
-    return p_item;
+    return i_ret;
 }
 
 static int DirControl( access_t *p_access, int i_query, va_list args )
diff --git a/modules/access/smb.c b/modules/access/smb.c
index c007f71..5f6bd2e 100644
--- a/modules/access/smb.c
+++ b/modules/access/smb.c
@@ -84,7 +84,7 @@ static ssize_t Read( access_t *, uint8_t *, size_t );
 static int Seek( access_t *, uint64_t );
 static int Control( access_t *, int, va_list );
 #ifndef _WIN32
-static input_item_t* DirRead( access_t * );
+static int DirRead( access_t *, input_item_node_t * );
 static int DirControl( access_t *, int, va_list );
 #endif
 
@@ -344,13 +344,16 @@ static ssize_t Read( access_t *p_access, uint8_t *p_buffer, size_t i_len )
 /*****************************************************************************
  * DirRead:
  *****************************************************************************/
-static input_item_t* DirRead (access_t *p_access )
+static int DirRead (access_t *p_access, input_item_node_t *p_node )
 {
     access_sys_t *p_sys = p_access->p_sys;
     struct smbc_dirent *p_entry;
-    input_item_t *p_item = NULL;
+    int i_ret = VLC_SUCCESS;
 
-    while( !p_item && ( p_entry = smbc_readdir( p_sys->i_smb ) ) )
+    struct access_fsdir fsdir;
+    access_fsdir_init( &fsdir, p_access, p_node );
+
+    while( i_ret == VLC_SUCCESS && ( p_entry = smbc_readdir( p_sys->i_smb ) ) )
     {
         char *psz_uri;
         const char *psz_server = p_sys->url.psz_host;
@@ -383,22 +386,26 @@ static input_item_t* DirRead (access_t *p_access )
         char *psz_encoded_name = NULL;
         if( psz_name != NULL
          && ( psz_encoded_name = vlc_uri_encode( psz_name ) ) == NULL )
-            return NULL;
+        {
+            i_ret = VLC_ENOMEM;
+            break;
+        }
         if( smb_get_uri( p_access, &psz_uri, NULL, NULL, NULL,
                          psz_server, psz_path, psz_encoded_name ) < 0 )
         {
             free(psz_encoded_name);
-            return NULL;
+            i_ret = VLC_ENOMEM;
+            break;
         }
         free(psz_encoded_name);
-
-        p_item = input_item_NewExt( psz_uri, p_entry->name, -1, i_type,
-                                    ITEM_NET );
+        i_ret = access_fsdir_additem( &fsdir, psz_uri, p_entry->name,
+                                      i_type, ITEM_NET );
         free( psz_uri );
-        if( !p_item )
-            return NULL;
     }
-    return p_item;
+
+    access_fsdir_finish( &fsdir, i_ret == VLC_SUCCESS );
+
+    return i_ret;
 }
 
 static int DirControl( access_t *p_access, int i_query, va_list args )
diff --git a/modules/demux/playlist/directory.c b/modules/demux/playlist/directory.c
index e1ff41d..314568f 100644
--- a/modules/demux/playlist/directory.c
+++ b/modules/demux/playlist/directory.c
@@ -67,146 +67,17 @@ void Close_Dir ( vlc_object_t *p_this )
     free( p_demux->p_sys );
 }
 
-/**
- * Does the provided URI/path/stuff has one of the extension provided ?
- *
- * \param psz_exts A comma separated list of extension without dot, or only
- * one ext (ex: "avi,mkv,webm")
- * \param psz_uri The uri/path to check (ex: "file:///home/foo/bar.avi"). If
- * providing an URI, it must not contain a query string.
- *
- * \return true if the uri/path has one of the provided extension
- * false otherwise.
- */
-static bool has_ext( const char *psz_exts, const char *psz_uri )
-{
-    if( psz_exts == NULL )
-        return false;
-
-    const char *ext = strrchr( psz_uri, '.' );
-    if( ext == NULL )
-        return false;
-
-    size_t extlen = strlen( ++ext );
-
-    for( const char *type = psz_exts, *end; type[0]; type = end + 1 )
-    {
-        end = strchr( type, ',' );
-        if( end == NULL )
-            end = type + strlen( type );
-
-        if( type + extlen == end && !strncasecmp( ext, type, extlen ) )
-            return true;
-
-        if( *end == '\0' )
-            break;
-    }
-
-    return false;
-}
-
-static int compar_type( input_item_t *p1, input_item_t *p2 )
-{
-    if( p1->i_type != p2->i_type )
-    {
-        if( p1->i_type == ITEM_TYPE_DIRECTORY )
-            return -1;
-        if( p2->i_type == ITEM_TYPE_DIRECTORY )
-            return 1;
-    }
-    return 0;
-}
-
-static int compar_collate( input_item_t *p1, input_item_t *p2 )
-{
-    int i_ret = compar_type( p1, p2 );
-
-    if( i_ret != 0 )
-        return i_ret;
-
-#ifdef HAVE_STRCOLL
-    /* The program's LOCAL defines if case is ignored */
-    return strcoll( p1->psz_name, p2->psz_name );
-#else
-    return strcasecmp( p1->psz_name, p2->psz_name );
-#endif
-}
-
-static int compar_version( input_item_t *p1, input_item_t *p2 )
-{
-    int i_ret = compar_type( p1, p2 );
-
-    if( i_ret != 0 )
-        return i_ret;
-
-    return strverscmp( p1->psz_name, p2->psz_name );
-}
-
 static int Demux( demux_t *p_demux )
 {
-    int i_ret = VLC_SUCCESS;
-    input_item_t *p_input;
-    input_item_node_t *p_node;
-    input_item_t *p_item;
-    char *psz_ignored_exts;
-    bool b_show_hiddenfiles;
-
-    p_input = GetCurrentItem( p_demux );
-    p_node = input_item_node_Create( p_input );
-    if( p_node == NULL )
-    {
-        input_item_Release( p_input );
-        return VLC_ENOMEM;
-    }
-    p_node->b_can_loop = p_demux->p_sys->b_dir_can_loop;
+    input_item_t *p_input = GetCurrentItem(p_demux);
+    input_item_node_t *p_node = input_item_node_Create( p_input );
     input_item_Release(p_input);
 
-    b_show_hiddenfiles = var_InheritBool( p_demux, "show-hiddenfiles" );
-    psz_ignored_exts = var_InheritString( p_demux, "ignore-filetypes" );
-
-    while( !i_ret && ( p_item = stream_ReadDir( p_demux->s ) ) )
-    {
-        int i_name_len = p_item->psz_name ? strlen( p_item->psz_name ) : 0;
-
-        /* skip null, "." and ".." and hidden files if option is activated */
-        if( !i_name_len || strcmp( p_item->psz_name, "." ) == 0
-         || strcmp( p_item->psz_name, ".." ) == 0
-         || ( !b_show_hiddenfiles && p_item->psz_name[0] == '.' ) )
-            goto skip_item;
-        /* skip ignored files */
-        if( has_ext( psz_ignored_exts, p_item->psz_name ) )
-            goto skip_item;
-
-        input_item_CopyOptions( p_item, p_node->p_item );
-        if( !input_item_node_AppendItem( p_node, p_item ) )
-            i_ret = VLC_ENOMEM;
-skip_item:
-        input_item_Release( p_item );
-    }
-    free( psz_ignored_exts );
-
-    if( i_ret )
+    if( stream_ReadDir( p_demux->s, p_node ) )
     {
         msg_Warn( p_demux, "unable to read directory" );
         input_item_node_Delete( p_node );
-        return i_ret;
-    }
-
-    if( !p_demux->p_sys->b_dir_sorted )
-    {
-        input_item_compar_cb compar_cb = NULL;
-        char *psz_sort = var_InheritString( p_demux, "directory-sort" );
-
-        if( psz_sort )
-        {
-            if( !strcasecmp( psz_sort, "version" ) )
-                compar_cb = compar_version;
-            else if( strcasecmp( psz_sort, "none" ) )
-                compar_cb = compar_collate;
-        }
-        free( psz_sort );
-        if( compar_cb )
-            input_item_node_Sort( p_node, compar_cb );
+        return VLC_EGENERIC;
     }
 
     input_item_node_PostAndDelete( p_node );
diff --git a/modules/services_discovery/upnp.cpp b/modules/services_discovery/upnp.cpp
index bc54024..c5c30a8 100644
--- a/modules/services_discovery/upnp.cpp
+++ b/modules/services_discovery/upnp.cpp
@@ -69,7 +69,6 @@ struct services_discovery_sys_t
 struct access_sys_t
 {
     UpnpInstanceWrapper* p_upnp;
-    Access::MediaServer* p_server;
 };
 
 UpnpInstanceWrapper* UpnpInstanceWrapper::s_instance;
@@ -755,15 +754,9 @@ int Upnp_i11e_cb::run( Upnp_EventType eventType, void *p_event, void *p_cookie )
     return 0;
 }
 
-MediaServer::MediaServer( access_t *p_access )
-    : psz_root_( NULL )
-    , psz_objectId_( NULL )
-    , access_( p_access )
-    , xmlDocument_( NULL )
-    , containerNodeList_( NULL )
-    , containerNodeIndex_( 0 )
-    , itemNodeList_( NULL )
-    , itemNodeIndex_( 0 )
+MediaServer::MediaServer( access_t *p_access, input_item_node_t *node )
+    : access_( p_access )
+    , node_( node )
 {
     vlc_url_t url;
     vlc_UrlParse( &url, p_access->psz_location );
@@ -778,29 +771,24 @@ MediaServer::MediaServer( access_t *p_access )
 
 MediaServer::~MediaServer()
 {
-    ixmlNodeList_free( containerNodeList_ );
-    ixmlNodeList_free( itemNodeList_ );
-    ixmlDocument_free( xmlDocument_ );
     free( psz_objectId_ );
     free( psz_root_ );
 }
 
-input_item_t* MediaServer::newItem( const char *objectID, const char *title )
+void MediaServer::addItem(const char *objectID, const char *title )
 {
     char* psz_url;
 
     if( asprintf( &psz_url, "upnp://%s?ObjectID=%s", psz_root_, objectID ) < 0 )
-        return NULL;
+        return;
 
     input_item_t* p_item = input_item_NewDirectory( psz_url, title, ITEM_NET );
     free( psz_url);
-    return p_item;
-}
-
-input_item_t* MediaServer::newItem(const char* title, const char*,
-                                   mtime_t duration, const char* psz_url)
-{
-    return input_item_NewFile( psz_url, title, duration, ITEM_NET );
+    if ( !p_item )
+        return;
+    input_item_CopyOptions( p_item, node_->p_item );
+    input_item_node_AppendItem( node_, p_item );
+    input_item_Release( p_item );
 }
 
 int MediaServer::sendActionCb( Upnp_EventType eventType,
@@ -922,7 +910,7 @@ browseActionCleanup:
 /*
  * Fetches and parses the UPNP response
  */
-void MediaServer::fetchContents()
+bool MediaServer::fetchContents()
 {
     IXML_Document* p_response = _browseAction( psz_objectId_,
                                       "BrowseDirectChildren",
@@ -933,45 +921,31 @@ void MediaServer::fetchContents()
     if ( !p_response )
     {
         msg_Err( access_, "No response from browse() action" );
-        return;
+        return false;
     }
 
-    xmlDocument_ = parseBrowseResult( p_response );
+    IXML_Document* p_result = parseBrowseResult( p_response );
 
     ixmlDocument_free( p_response );
 
-    if ( !xmlDocument_ )
+    if ( !p_result )
     {
         msg_Err( access_, "browse() response parsing failed" );
-        return;
+        return false;
     }
 
 #ifndef NDEBUG
-    msg_Dbg( access_, "Got DIDL document: %s", ixmlPrintDocument( xmlDocument_ ) );
+    msg_Dbg( access_, "Got DIDL document: %s", ixmlPrintDocument( p_result ) );
 #endif
 
-    containerNodeList_ = ixmlDocument_getElementsByTagName( xmlDocument_, "container" );
-    itemNodeList_ = ixmlDocument_getElementsByTagName( xmlDocument_, "item" );
-}
-
-input_item_t* MediaServer::getNextItem()
-{
-    input_item_t *p_item = NULL;
+    IXML_NodeList* containerNodeList =
+                ixmlDocument_getElementsByTagName( p_result, "container" );
 
-    if( !xmlDocument_ )
+    if ( containerNodeList )
     {
-        fetchContents();
-        if( !xmlDocument_ )
-            return NULL;
-    }
-
-    if ( containerNodeList_ )
-    {
-        for ( ; !p_item && containerNodeIndex_ < ixmlNodeList_length( containerNodeList_ )
-              ; containerNodeIndex_++ )
+        for ( unsigned int i = 0; i < ixmlNodeList_length( containerNodeList ); i++ )
         {
-            IXML_Element* containerElement = (IXML_Element*)ixmlNodeList_item( containerNodeList_,
-                                                                                containerNodeIndex_ );
+            IXML_Element* containerElement = (IXML_Element*)ixmlNodeList_item( containerNodeList, i );
 
             const char* objectID = ixmlElement_getAttribute( containerElement,
                                                              "id" );
@@ -982,18 +956,22 @@ input_item_t* MediaServer::getNextItem()
                                                           "dc:title" );
             if ( !title )
                 continue;
-            p_item = newItem(objectID, title);
+            addItem(objectID, title);
         }
+        ixmlNodeList_free( containerNodeList );
     }
 
-    if( itemNodeList_ )
+    IXML_NodeList* itemNodeList = ixmlDocument_getElementsByTagName( p_result,
+                                                                     "item" );
+    if ( itemNodeList )
     {
-        for ( ; !p_item && itemNodeIndex_ < ixmlNodeList_length( itemNodeList_ ) ; itemNodeIndex_++ )
+        for ( unsigned int i = 0; i < ixmlNodeList_length( itemNodeList ); i++ )
         {
-            IXML_Element* itemElement = ( IXML_Element* )ixmlNodeList_item( itemNodeList_,
-                                                                            itemNodeIndex_ );
+            IXML_Element* itemElement =
+                        ( IXML_Element* )ixmlNodeList_item( itemNodeList, i );
 
-            const char* objectID = ixmlElement_getAttribute( itemElement, "id" );
+            const char* objectID =
+                        ixmlElement_getAttribute( itemElement, "id" );
             if ( !objectID )
                 continue;
 
@@ -1029,8 +1007,9 @@ input_item_t* MediaServer::getNextItem()
                                                           i_seconds );
                 }
 
-                p_item = newItem( title, objectID, i_duration,
-                                  psz_resource_url );
+                input_item_t* p_item =
+                    input_item_NewExt( psz_resource_url, title, i_duration,
+                                       ITEM_TYPE_FILE, ITEM_NET );
                 if ( p_item != NULL )
                 {
                     const char* psz_artist = xml_getChildElementValue( itemElement, "upnp:artist" );
@@ -1055,17 +1034,26 @@ input_item_t* MediaServer::getNextItem()
                     if ( psz_albumArt != NULL )
                         input_item_SetArtworkURL( p_item, psz_albumArt );
                 }
+                input_item_CopyOptions( p_item, node_->p_item );
+                input_item_node_AppendItem( node_, p_item );
+                input_item_Release( p_item );
             }
             ixmlNodeList_free( p_resource_list );
         }
+        ixmlNodeList_free( itemNodeList );
     }
 
-    return p_item;
+    ixmlDocument_free( p_result );
+    return true;
 }
 
-static input_item_t* ReadDirectory( access_t *p_access )
+static int ReadDirectory( access_t *p_access, input_item_node_t* p_node )
 {
-    return p_access->p_sys->p_server->getNextItem();
+    MediaServer server( p_access, p_node );
+
+    if ( !server.fetchContents() )
+        return VLC_EGENERIC;
+    return VLC_SUCCESS;
 }
 
 static int ControlDirectory( access_t *p_access, int i_query, va_list args )
@@ -1091,16 +1079,9 @@ static int Open( vlc_object_t *p_this )
         return VLC_ENOMEM;
 
     p_access->p_sys = p_sys;
-    p_sys->p_server = new(std::nothrow) MediaServer( p_access );
-    if ( !p_sys->p_server )
-    {
-        delete p_sys;
-        return VLC_EGENERIC;
-    }
     p_sys->p_upnp = UpnpInstanceWrapper::get( p_this, NULL, NULL );
     if ( !p_sys->p_upnp )
     {
-        delete p_sys->p_server;
         delete p_sys;
         return VLC_EGENERIC;
     }
@@ -1115,7 +1096,6 @@ static void Close( vlc_object_t* p_this )
 {
     access_t* p_access = (access_t*)p_this;
     p_access->p_sys->p_upnp->release( false );
-    delete p_access->p_sys->p_server;
     delete p_access->p_sys;
 }
 
diff --git a/modules/services_discovery/upnp.hpp b/modules/services_discovery/upnp.hpp
index 1ef8803..26593d0 100644
--- a/modules/services_discovery/upnp.hpp
+++ b/modules/services_discovery/upnp.hpp
@@ -141,17 +141,15 @@ private:
 class MediaServer
 {
 public:
-    MediaServer( access_t* p_access );
+    MediaServer( access_t* p_access, input_item_node_t* node );
     ~MediaServer();
-    input_item_t* getNextItem();
+    bool fetchContents();
 
 private:
     MediaServer(const MediaServer&);
     MediaServer& operator=(const MediaServer&);
 
-    void fetchContents();
-    input_item_t* newItem(const char* objectID, const char* title);
-    input_item_t* newItem(const char* title, const char* psz_objectID, mtime_t duration, const char* psz_url );
+    void addItem(const char* objectID, const char* title);
 
     IXML_Document* _browseAction(const char*, const char*,
             const char*, const char*, const char* );
@@ -161,11 +159,7 @@ private:
     char* psz_root_;
     char* psz_objectId_;
     access_t* access_;
-    IXML_Document* xmlDocument_;
-    IXML_NodeList* containerNodeList_;
-    unsigned int   containerNodeIndex_;
-    IXML_NodeList* itemNodeList_;
-    unsigned int   itemNodeIndex_;
+    input_item_node_t* node_;
 };
 
 }
diff --git a/modules/stream_filter/inflate.c b/modules/stream_filter/inflate.c
index 543d563..fd9a902 100644
--- a/modules/stream_filter/inflate.c
+++ b/modules/stream_filter/inflate.c
@@ -101,10 +101,10 @@ static ssize_t Read(stream_t *stream, void *buf, size_t buflen)
     return -1;
 }
 
-static input_item_t *ReadDir(stream_t *stream)
+static int ReadDir(stream_t *stream, input_item_node_t *node)
 {
-    (void) stream;
-    return NULL;
+    (void) stream; (void) node;
+    return VLC_EGENERIC;
 }
 
 static int Seek(stream_t *stream, uint64_t offset)
diff --git a/modules/stream_filter/prefetch.c b/modules/stream_filter/prefetch.c
index 09b9db9..09467ad 100644
--- a/modules/stream_filter/prefetch.c
+++ b/modules/stream_filter/prefetch.c
@@ -348,10 +348,10 @@ static ssize_t Read(stream_t *stream, void *buf, size_t buflen)
     return copy;
 }
 
-static input_item_t *ReadDir(stream_t *stream)
+static int ReadDir(stream_t *stream, input_item_node_t *node)
 {
-    (void) stream;
-    return NULL;
+    (void) stream; (void) node;
+    return VLC_EGENERIC;
 }
 
 static int Control(stream_t *stream, int query, va_list args)
diff --git a/src/input/access.c b/src/input/access.c
index aac3d43..520e450 100644
--- a/src/input/access.c
+++ b/src/input/access.c
@@ -194,10 +194,10 @@ static ssize_t AStreamNoRead(stream_t *s, void *buf, size_t len)
     return -1;
 }
 
-static input_item_t *AStreamNoReadDir(stream_t *s)
+static int AStreamNoReadDir(stream_t *s, input_item_node_t *p_node)
 {
-    (void) s;
-    return NULL;
+    (void) s; (void) p_node;
+    return VLC_EGENERIC;;
 }
 
 /* Block access */
@@ -282,11 +282,11 @@ static ssize_t AStreamReadStream(stream_t *s, void *buf, size_t len)
 }
 
 /* Directory */
-static input_item_t *AStreamReadDir(stream_t *s)
+static int AStreamReadDir(stream_t *s, input_item_node_t *p_node)
 {
     stream_sys_t *sys = s->p_sys;
 
-    return sys->access->pf_readdir(sys->access);
+    return sys->access->pf_readdir(sys->access, p_node);
 }
 
 /* Common */
@@ -432,3 +432,132 @@ error:
     stream_CommonDelete(s);
     return NULL;
 }
+
+static int compar_type(input_item_t *p1, input_item_t *p2)
+{
+    if (p1->i_type != p2->i_type)
+    {
+        if (p1->i_type == ITEM_TYPE_DIRECTORY)
+            return -1;
+        if (p2->i_type == ITEM_TYPE_DIRECTORY)
+            return 1;
+    }
+    return 0;
+}
+
+static int compar_collate(input_item_t *p1, input_item_t *p2)
+{
+    int i_ret = compar_type(p1, p2);
+
+    if (i_ret != 0)
+        return i_ret;
+
+#ifdef HAVE_STRCOLL
+    /* The program's LOCAL defines if case is ignored */
+    return strcoll(p1->psz_name, p2->psz_name);
+#else
+    return strcasecmp(p1->psz_name, p2->psz_name);
+#endif
+}
+
+static int compar_version(input_item_t *p1, input_item_t *p2)
+{
+    int i_ret = compar_type(p1, p2);
+
+    if (i_ret != 0)
+        return i_ret;
+
+    return strverscmp(p1->psz_name, p2->psz_name);
+}
+
+static void fsdir_sort(struct access_fsdir *p_fsdir)
+{
+    input_item_compar_cb pf_compar = NULL;
+
+    if (p_fsdir->psz_sort != NULL)
+    {
+        if (!strcasecmp(p_fsdir->psz_sort, "version"))
+            pf_compar = compar_version;
+        else if(strcasecmp(p_fsdir->psz_sort, "none"))
+            pf_compar = compar_collate;
+
+        if (pf_compar != NULL)
+            input_item_node_Sort(p_fsdir->p_node, pf_compar);
+    }
+}
+
+/**
+ * Does the provided file name has one of the extension provided ?
+ */
+static bool fsdir_has_ext(const char *psz_filename,
+                          const char *psz_ignored_exts)
+{
+    if (psz_ignored_exts == NULL)
+        return false;
+
+    const char *ext = strrchr(psz_filename, '.');
+    if (ext == NULL)
+        return false;
+
+    size_t extlen = strlen(++ext);
+
+    for (const char *type = psz_ignored_exts, *end; type[0]; type = end + 1)
+    {
+        end = strchr(type, ',');
+        if (end == NULL)
+            end = type + strlen(type);
+
+        if (type + extlen == end && !strncasecmp(ext, type, extlen))
+            return true;
+
+        if (*end == '\0')
+            break;
+    }
+
+    return false;
+}
+
+static bool fsdir_is_ignored(struct access_fsdir *p_fsdir,
+                             const char *psz_filename)
+{
+    return (psz_filename[0] == '\0'
+         || strcmp(psz_filename, ".") == 0
+         || strcmp(psz_filename, "..") == 0
+         || (!p_fsdir->b_show_hiddenfiles && psz_filename[0] == '.')
+         || fsdir_has_ext(psz_filename, p_fsdir->psz_ignored_exts));
+}
+
+void access_fsdir_init(struct access_fsdir *p_fsdir,
+                       access_t *p_access, input_item_node_t *p_node)
+{
+    p_fsdir->p_node = p_node;
+    p_fsdir->b_show_hiddenfiles = var_InheritBool(p_access, "show-hiddenfiles");
+    p_fsdir->psz_ignored_exts = var_InheritString(p_access, "ignore-filetypes");
+    p_fsdir->psz_sort = var_InheritString(p_access, "directory-sort");
+}
+
+void access_fsdir_finish(struct access_fsdir *p_fsdir, bool b_success)
+{
+    if (b_success)
+        fsdir_sort(p_fsdir);
+    free(p_fsdir->psz_ignored_exts);
+    free(p_fsdir->psz_sort);
+}
+
+int access_fsdir_additem(struct access_fsdir *p_fsdir,
+                         const char *psz_uri, const char *psz_filename,
+                         int i_type, int i_net)
+{
+    if (fsdir_is_ignored(p_fsdir, psz_filename))
+        return VLC_SUCCESS;
+
+    input_item_t *p_item = input_item_NewExt(psz_uri, psz_filename, -1,
+                                             i_type, i_net);
+    if (p_item == NULL)
+        return VLC_ENOMEM;
+
+    input_item_CopyOptions(p_item, p_fsdir->p_node->p_item);
+    input_item_node_AppendItem(p_fsdir->p_node, p_item);
+    input_item_Release(p_item);
+    return VLC_SUCCESS;
+}
diff --git a/src/input/stream.c b/src/input/stream.c
index ea04873..c7cb4b4 100644
--- a/src/input/stream.c
+++ b/src/input/stream.c
@@ -617,11 +617,10 @@ block_t *stream_Block( stream_t *s, size_t size )
 }
 
 /**
- * Read the next input_item_t from the directory stream. It returns the next
- * input item on success or NULL in case of error or end of stream. The item
- * must be released with input_item_Release.
+ * Returns a node containing all the input_item of the directory pointer by
+ * this stream. returns VLC_SUCCESS on success.
  */
-input_item_t *stream_ReadDir( stream_t *s )
+int stream_ReadDir( stream_t *s, input_item_node_t *p_node )
 {
-    return s->pf_readdir( s );
+    return s->pf_readdir( s, p_node );
 }
diff --git a/src/input/stream_filter.c b/src/input/stream_filter.c
index b7b0fdb..c34a442 100644
--- a/src/input/stream_filter.c
+++ b/src/input/stream_filter.c
@@ -114,8 +114,8 @@ static void StreamDelete( stream_t *s )
         stream_Delete( s->p_source );
 }
 
-input_item_t *stream_FilterDefaultReadDir( stream_t *s )
+int stream_FilterDefaultReadDir( stream_t *s, input_item_node_t *p_node )
 {
     assert( s->p_source != NULL );
-    return stream_ReadDir( s->p_source );
+    return stream_ReadDir( s->p_source, p_node );
 }
diff --git a/src/libvlccore.sym b/src/libvlccore.sym
index 32dbaad..bb108d5 100644
--- a/src/libvlccore.sym
+++ b/src/libvlccore.sym
@@ -1,6 +1,9 @@
 access_vaDirectoryControlHelper
 vlc_access_NewMRL
 vlc_access_Delete
+access_fsdir_init
+access_fsdir_finish
+access_fsdir_additem
 AddMD5
 aout_BitsPerSample
 aout_ChannelExtract
-- 
2.8.1



More information about the vlc-devel mailing list