X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Faccess%2Fdirectory.c;h=7209978cd3f7776fc178f57cff92a10f615d2d2a;hb=4455e6d2935a498e02ac3c6ac144f0525d2e01f0;hp=1cde31422b754126ca5b4419f24208c8c169e542;hpb=7b6ad1735535ad12338cb2739f5065a829ac5469;p=vlc diff --git a/modules/access/directory.c b/modules/access/directory.c index 1cde31422b..7209978cd3 100644 --- a/modules/access/directory.c +++ b/modules/access/directory.c @@ -1,25 +1,26 @@ /***************************************************************************** - * directory.c: expands a directory (directory: access plug-in) + * directory.c: expands a directory (directory: access_browser plug-in) ***************************************************************************** - * Copyright (C) 2002-2007 the VideoLAN team + * Copyright (C) 2002-2008 VLC authors and VideoLAN * $Id$ * * Authors: Derk-Jan Hartman * Rémi Denis-Courmont + * Julien 'Lta' BALLET * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ /***************************************************************************** @@ -31,545 +32,417 @@ #endif #include -#include -#warning playlist code must not be used here. -#include -#include +#include "fs.h" #include -#include +#include -#ifdef HAVE_SYS_TYPES_H -# include -#endif -#ifdef HAVE_SYS_STAT_H -# include -#endif -#ifdef HAVE_ERRNO_H -# include -#endif -#ifdef HAVE_FCNTL_H -# include -#endif - -#ifdef HAVE_UNISTD_H -# include -#elif defined( WIN32 ) && !defined( UNDER_CE ) -# include -#elif defined( UNDER_CE ) -# define strcoll strcmp -#endif - -#ifdef HAVE_DIRENT_H -# include -#endif +#include +#include +#include +#include +#include +#include +#include +#include #include -/***************************************************************************** - * Module descriptor - *****************************************************************************/ -static int Open ( vlc_object_t * ); -static void Close( vlc_object_t * ); - -static int DemuxOpen ( vlc_object_t * ); - -#define RECURSIVE_TEXT N_("Subdirectory behavior") -#define RECURSIVE_LONGTEXT N_( \ - "Select whether subdirectories must be expanded.\n" \ - "none: subdirectories do not appear in the playlist.\n" \ - "collapse: subdirectories appear but are expanded on first play.\n" \ - "expand: all subdirectories are expanded.\n" ) - -static const char *const psz_recursive_list[] = { "none", "collapse", "expand" }; -static const char *const psz_recursive_list_text[] = { - N_("none"), N_("collapse"), N_("expand") }; - -#define IGNORE_TEXT N_("Ignored extensions") -#define IGNORE_LONGTEXT N_( \ - "Files with these extensions will not be added to playlist when " \ - "opening a directory.\n" \ - "This is useful if you add directories that contain playlist files " \ - "for instance. Use a comma-separated list of extensions." ) - -vlc_module_begin(); - set_category( CAT_INPUT ); - set_shortname( N_("Directory" ) ); - set_subcategory( SUBCAT_INPUT_ACCESS ); - set_description( N_("Standard filesystem directory input") ); - set_capability( "access", 55 ); - add_shortcut( "directory" ); - add_shortcut( "dir" ); - add_shortcut( "file" ); - add_string( "recursive", "expand" , NULL, RECURSIVE_TEXT, - RECURSIVE_LONGTEXT, false ); - change_string_list( psz_recursive_list, psz_recursive_list_text, 0 ); - add_string( "ignore-filetypes", "m3u,db,nfo,jpg,gif,sfv,txt,sub,idx,srt,cue", - NULL, IGNORE_TEXT, IGNORE_LONGTEXT, false ); - set_callbacks( Open, Close ); - - add_submodule(); - set_description( "Directory EOF"); - set_capability( "demux", 0 ); - set_callbacks( DemuxOpen, NULL ); -vlc_module_end(); - - -/***************************************************************************** - * Local prototypes, constants, structures - *****************************************************************************/ +enum +{ + ENTRY_DIR = 0, + ENTRY_ENOTDIR = -1, + ENTRY_EACCESS = -2, +}; enum { - MODE_EXPAND, + MODE_NONE, MODE_COLLAPSE, - MODE_NONE + MODE_EXPAND, }; -typedef struct stat_list_t stat_list_t; - -static ssize_t Read( access_t *, uint8_t *, size_t ); -static ssize_t ReadNull( access_t *, uint8_t *, size_t ); -static int Control( access_t *, int, va_list ); - -static int Demux( demux_t *p_demux ); -static int DemuxControl( demux_t *p_demux, int i_query, va_list args ); +typedef struct directory directory; +struct directory +{ + directory *parent; + DIR *handle; + char *uri; + char **filev; + int filec, i; +#ifdef HAVE_OPENAT + dev_t device; + ino_t inode; +#else + char *path; +#endif +}; +struct access_sys_t +{ + directory *current; + char *ignored_exts; + char mode; + int (*compar) (const char **a, const char **b); +}; -static int ReadDir( playlist_t *, const char *psz_name, int i_mode, - playlist_item_t *, input_item_t *, - DIR *handle, stat_list_t *stats ); +/* Select non-hidden files only */ +static int visible (const char *name) +{ + return name[0] != '.'; +} -static DIR *OpenDir (vlc_object_t *obj, const char *psz_name); +static int collate (const char **a, const char **b) +{ +#ifdef HAVE_STRCOLL + return strcoll (*a, *b); +#else + return strcmp (*a, *b); +#endif +} -/***************************************************************************** - * Open: open the directory - *****************************************************************************/ -static int Open( vlc_object_t *p_this ) +static int version (const char **a, const char **b) { - access_t *p_access = (access_t*)p_this; + return strverscmp (*a, *b); +} - if( !p_access->psz_path ) - return VLC_EGENERIC; +/** + * 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; - struct stat st; - if( !stat( p_access->psz_path, &st ) && !S_ISDIR( st.st_mode ) ) - return VLC_EGENERIC; + const char *ext = strrchr (psz_uri, '.'); + if (ext == NULL) + return false; - DIR *handle = OpenDir (p_this, p_access->psz_path); - if (handle == NULL) - return VLC_EGENERIC; + size_t extlen = strlen (++ext); - p_access->p_sys = (access_sys_t *)handle; + for (const char *type = psz_exts, *end; type[0]; type = end + 1) + { + end = strchr (type, ','); + if (end == NULL) + end = type + strlen (type); - p_access->pf_read = Read; - p_access->pf_block = NULL; - p_access->pf_seek = NULL; - p_access->pf_control= Control; + if (type + extlen == end && !strncasecmp (ext, type, extlen)) + return true; - /* Force a demux */ - free( p_access->psz_demux ); - p_access->psz_demux = strdup( "directory" ); + if (*end == '\0') + break; + } - return VLC_SUCCESS; + return false; } -/***************************************************************************** - * Close: close the target - *****************************************************************************/ -static void Close( vlc_object_t * p_this ) -{ - access_t *p_access = (access_t*)p_this; - DIR *handle = (DIR *)p_access->p_sys; - closedir (handle); -} -/***************************************************************************** - * ReadNull: read the directory - *****************************************************************************/ -static ssize_t ReadNull( access_t *p_access, uint8_t *p_buffer, size_t i_len) +#ifdef HAVE_OPENAT +/* Detect directories that recurse into themselves. */ +static bool has_inode_loop (const directory *dir, dev_t dev, ino_t inode) { - /* Return fake data */ - memset( p_buffer, 0, i_len ); - return i_len; + while (dir != NULL) + { + if ((dir->device == dev) && (dir->inode == inode)) + return true; + dir = dir->parent; + } + return false; } +#endif -/***************************************************************************** - * Read: read the directory - *****************************************************************************/ -static ssize_t Read( access_t *p_access, uint8_t *p_buffer, size_t i_len) +/* success -> returns ENTRY_DIR and the handle parameter is set to the handle, + * error -> return ENTRY_ENOTDIR or ENTRY_EACCESS */ +static int directory_open (directory *p_dir, char *psz_entry, DIR **handle) { - char *psz; - int i_mode; - char *psz_name = strdup (p_access->psz_path); + *handle = NULL; - if( psz_name == NULL ) - return VLC_ENOMEM; +#ifdef HAVE_OPENAT + int fd = vlc_openat (dirfd (p_dir->handle), psz_entry, + O_RDONLY | O_DIRECTORY); - playlist_t *p_playlist = pl_Yield( p_access ); - input_thread_t *p_input = (input_thread_t*)vlc_object_find( p_access, VLC_OBJECT_INPUT, FIND_PARENT ); - - playlist_item_t *p_item_in_category; - input_item_t *p_current_input; - playlist_item_t *p_current; - - if( !p_input ) + if (fd == -1) { - msg_Err( p_access, "unable to find input (internal error)" ); - vlc_object_release( p_playlist ); - return VLC_ENOOBJ; + if (errno == ENOTDIR) + return ENTRY_ENOTDIR; + else + return ENTRY_EACCESS; } - p_current_input = input_GetItem( p_input ); - p_current = playlist_ItemGetByInput( p_playlist, p_current_input, false ); - - if( !p_current ) + struct stat st; + if (fstat (fd, &st) + || has_inode_loop (p_dir, st.st_dev, st.st_ino) + || (*handle = fdopendir (fd)) == NULL) { - msg_Err( p_access, "unable to find item in playlist" ); - vlc_object_release( p_input ); - vlc_object_release( p_playlist ); - return VLC_ENOOBJ; + close (fd); + return ENTRY_EACCESS; } +#else + char *path; + if (asprintf (&path, "%s/%s", p_dir->path, psz_entry) == -1) + return ENTRY_EACCESS; - /* Remove the ending '/' char */ - if( psz_name[0] ) - { - char *ptr = psz_name + strlen (psz_name); - switch (*--ptr) - { - case '/': - case '\\': - *ptr = '\0'; - } - } + *handle = vlc_opendir (path); - /* Handle mode */ - psz = var_CreateGetString( p_access, "recursive" ); - if( *psz == '\0' || !strncmp( psz, "none" , 4 ) ) - i_mode = MODE_NONE; - else if( !strncmp( psz, "collapse", 8 ) ) - i_mode = MODE_COLLAPSE; - else - i_mode = MODE_EXPAND; - free( psz ); + free(path); - p_current->p_input->i_type = ITEM_TYPE_DIRECTORY; - p_item_in_category = playlist_ItemToNode( p_playlist, p_current, - false ); + if (*handle == NULL) { + return ENTRY_ENOTDIR; + } +#endif - ReadDir( p_playlist, psz_name, i_mode, p_item_in_category, - p_current_input, (DIR *)p_access->p_sys, NULL ); + return ENTRY_DIR; +} - playlist_Signal( p_playlist ); +static bool directory_push (access_sys_t *p_sys, DIR *handle, char *psz_uri) +{ + directory *p_dir = malloc (sizeof (*p_dir)); - free( psz_name ); - vlc_object_release( p_input ); - vlc_object_release( p_playlist ); + psz_uri = strdup (psz_uri); + if (unlikely (p_dir == NULL || psz_uri == NULL)) + goto error; - /* Return fake data forever */ - p_access->pf_read = ReadNull; - return -1; -} + p_dir->parent = p_sys->current; + p_dir->handle = handle; + p_dir->uri = psz_uri; + p_dir->filec = vlc_loaddir (handle, &p_dir->filev, visible, p_sys->compar); + if (p_dir->filec < 0) + p_dir->filev = NULL; + p_dir->i = 0; -/***************************************************************************** - * Control: - *****************************************************************************/ -static int Control( access_t *p_access, int i_query, va_list args ) -{ - bool *pb_bool; - int *pi_int; - int64_t *pi_64; +#ifdef HAVE_OPENAT + struct stat st; + if (fstat (dirfd (handle), &st)) + goto error_filev; + p_dir->device = st.st_dev; + p_dir->inode = st.st_ino; +#else + p_dir->path = make_path (psz_uri); + if (p_dir->path == NULL) + goto error_filev; +#endif - switch( i_query ) - { - /* */ - case ACCESS_CAN_SEEK: - case ACCESS_CAN_FASTSEEK: - case ACCESS_CAN_PAUSE: - case ACCESS_CAN_CONTROL_PACE: - pb_bool = (bool*)va_arg( args, bool* ); - *pb_bool = false; /* FIXME */ - break; + p_sys->current = p_dir; + return true; - /* */ - case ACCESS_GET_MTU: - pi_int = (int*)va_arg( args, int * ); - *pi_int = 0; - break; +error_filev: + for (int i = 0; i < p_dir->filec; i++) + free (p_dir->filev[i]); + free (p_dir->filev); - case ACCESS_GET_PTS_DELAY: - pi_64 = (int64_t*)va_arg( args, int64_t * ); - *pi_64 = DEFAULT_PTS_DELAY * 1000; - break; +error: + closedir (handle); + free (p_dir); + free (psz_uri); + return false; +} - /* */ - case ACCESS_SET_PAUSE_STATE: - case ACCESS_GET_TITLE_INFO: - case ACCESS_SET_TITLE: - case ACCESS_SET_SEEKPOINT: - case ACCESS_SET_PRIVATE_ID_STATE: - case ACCESS_GET_CONTENT_TYPE: - return VLC_EGENERIC; +static bool directory_pop (access_sys_t *p_sys) +{ + directory *p_old = p_sys->current; + + if (p_old == NULL) + return false; + + p_sys->current = p_old->parent; + closedir (p_old->handle); + free (p_old->uri); + for (int i = 0; i < p_old->filec; i++) + free (p_old->filev[i]); + free (p_old->filev); +#ifndef HAVE_OPENAT + free (p_old->path); +#endif + free (p_old); - default: - msg_Warn( p_access, "unimplemented query in control" ); - return VLC_EGENERIC; - } - return VLC_SUCCESS; + return p_sys->current != NULL; } + /***************************************************************************** - * DemuxOpen: + * Open: open the directory *****************************************************************************/ -static int DemuxOpen ( vlc_object_t *p_this ) +int DirOpen (vlc_object_t *p_this) { - demux_t *p_demux = (demux_t*)p_this; + access_t *p_access = (access_t*)p_this; - if( strcmp( p_demux->psz_demux, "directory" ) ) + if (!p_access->psz_filepath) return VLC_EGENERIC; - p_demux->pf_demux = Demux; - p_demux->pf_control = DemuxControl; - return VLC_SUCCESS; -} + DIR *handle = vlc_opendir (p_access->psz_filepath); + if (handle == NULL) + return VLC_EGENERIC; -/***************************************************************************** - * Demux: EOF - *****************************************************************************/ -static int Demux( demux_t *p_demux ) -{ - return 0; + return DirInit (p_access, handle); } -/***************************************************************************** - * DemuxControl: - *****************************************************************************/ -static int DemuxControl( demux_t *p_demux, int i_query, va_list args ) +int DirInit (access_t *p_access, DIR *handle) { - return demux_vaControlHelper( p_demux->s, 0, 0, 0, 1, i_query, args ); -} + access_sys_t *p_sys = malloc (sizeof (*p_sys)); + if (unlikely (p_sys == NULL)) + goto error; + + char *psz_sort = var_InheritString (p_access, "directory-sort"); + if (!psz_sort) + p_sys->compar = collate; + else if (!strcasecmp ( psz_sort, "version")) + p_sys->compar = version; + else if (!strcasecmp (psz_sort, "none")) + p_sys->compar = NULL; + else + p_sys->compar = collate; + free(psz_sort); + char *uri; + if (!strcmp (p_access->psz_access, "fd")) + { + if (asprintf (&uri, "fd://%s", p_access->psz_location) == -1) + uri = NULL; + } + else + uri = vlc_path2uri (p_access->psz_filepath, "file"); + if (unlikely (uri == NULL)) + { + closedir (handle); + goto error; + } -static int Sort (const char **a, const char **b) -{ - return strcoll (*a, *b); -} + /* "Open" the base directory */ + p_sys->current = NULL; + if (!directory_push (p_sys, handle, uri)) + { + free (uri); + goto error; + } + free (uri); -struct stat_list_t -{ - stat_list_t *parent; - struct stat st; -}; + p_access->p_sys = p_sys; + p_sys->ignored_exts = var_InheritString (p_access, "ignore-filetypes"); + + p_access->pf_readdir = DirRead; + p_access->pf_control = DirControl; + return VLC_SUCCESS; + +error: + free (p_sys); + return VLC_EGENERIC; +} /***************************************************************************** - * ReadDir: read a directory and add its content to the list + * Close: close the target *****************************************************************************/ -static int ReadDir( playlist_t *p_playlist, const char *psz_name, - int i_mode, - playlist_item_t *p_parent_category, - input_item_t *p_current_input, - DIR *handle, stat_list_t *stparent ) +void DirClose( vlc_object_t * p_this ) { - char **pp_dir_content = NULL; - int i_dir_content, i, i_return = VLC_SUCCESS; - playlist_item_t *p_node; + access_t *p_access = (access_t*)p_this; + access_sys_t *p_sys = p_access->p_sys; - if( !vlc_object_alive( p_playlist ) ) - return VLC_EGENERIC; + while (directory_pop (p_sys)) + ; - char **ppsz_extensions = NULL; - int i_extensions = 0; - char *psz_ignore; + free (p_sys->ignored_exts); + free (p_sys); +} - struct stat_list_t stself; -#ifndef WIN32 - int fd = dirfd (handle); +/* This function is a little bit too complex for what it seems to do, but the + * point is to de-recursify directory recusion to avoid overruning the stack + * in case there's a high directory depth */ +int DirRead (access_t *p_access, input_item_node_t *p_current_node) +{ + access_sys_t *p_sys = p_access->p_sys; - if ((fd == -1) || fstat (fd, &stself.st)) + while (p_sys->current != NULL + && p_sys->current->i <= p_sys->current->filec) { - msg_Err (p_playlist, "cannot stat `%s': %m", psz_name); - return VLC_EGENERIC; - } + directory *p_current = p_sys->current; - for (stat_list_t *stats = stparent; stats != NULL; stats = stats->parent) - { - if ((stself.st.st_ino == stats->st.st_ino) - && (stself.st.st_dev == stats->st.st_dev)) + /* End of the current folder, let's pop directory and node */ + if (p_current->i == p_current->filec) { - msg_Warn (p_playlist, - "ignoring infinitely recursive directory `%s'", - psz_name); - return VLC_SUCCESS; + directory_pop (p_sys); + p_current_node = p_current_node->p_parent; + continue; } - } -#else - /* Windows has st_dev (driver letter - 'A'), but it zeroes st_ino, - * so that the test above will always incorrectly succeed. - * Besides, Windows does not have dirfd(). */ -#endif - stself.parent = stparent; + char *psz_entry = p_current->filev[p_current->i++]; + char *psz_full_uri, *psz_uri; + DIR *handle; + input_item_t *p_new = NULL; + int i_res; - /* Get the first directory entry */ - i_dir_content = utf8_loaddir (handle, &pp_dir_content, NULL, Sort); - if( i_dir_content == -1 ) - { - msg_Err (p_playlist, "cannot read `%s': %m", psz_name); - return VLC_EGENERIC; - } - else if( i_dir_content <= 0 ) - { - /* directory is empty */ - msg_Dbg( p_playlist, "%s directory is empty", psz_name ); - free( pp_dir_content ); - return VLC_SUCCESS; - } + /* Check if it is a directory or even readable */ + i_res = directory_open (p_current, psz_entry, &handle); - /* Build array with ignores */ - psz_ignore = var_CreateGetString( p_playlist, "ignore-filetypes" ); - if( psz_ignore && *psz_ignore ) - { - char *psz_parser = psz_ignore; - int a; + if (i_res == ENTRY_EACCESS + || (i_res == ENTRY_ENOTDIR && has_ext (p_sys->ignored_exts, psz_entry))) + continue; - for( a = 0; psz_parser[a] != '\0'; a++ ) - { - if( psz_parser[a] == ',' ) i_extensions++; - } - ppsz_extensions = (char **)calloc (i_extensions, sizeof (char *)); + /* Create an input item for the current entry */ + psz_uri = encode_URI_component (psz_entry); + if (psz_uri == NULL + || asprintf (&psz_full_uri, "%s/%s", p_current->uri, psz_uri) == -1) + psz_full_uri = NULL; - for( a = 0; a < i_extensions; a++ ) + free (psz_uri); + if (psz_full_uri == NULL) { - char *tmp, *ptr; - - while( psz_parser[0] != '\0' && psz_parser[0] == ' ' ) psz_parser++; - ptr = strchr( psz_parser, ','); - tmp = ( ptr == NULL ) - ? strdup( psz_parser ) - : strndup( psz_parser, ptr - psz_parser ); - - ppsz_extensions[a] = tmp; - psz_parser = ptr + 1; + closedir (handle); + continue; } - } - free( psz_ignore ); - - /* While we still have entries in the directory */ - for( i = 0; i < i_dir_content; i++ ) - { - const char *entry = pp_dir_content[i]; - int i_size_entry = strlen( psz_name ) + - strlen( entry ) + 2 + 7 /* strlen("file://") */; - char psz_uri[i_size_entry]; - sprintf( psz_uri, "%s/%s", psz_name, entry); - - /* if it starts with '.' then forget it */ - if (entry[0] != '.') + int i_type = i_res == ENTRY_DIR ? ITEM_TYPE_DIRECTORY : ITEM_TYPE_FILE; + p_new = input_item_NewWithType (psz_full_uri, psz_entry, + 0, NULL, 0, 0, i_type); + if (p_new == NULL) { - DIR *subdir = (i_mode != MODE_COLLAPSE) - ? OpenDir (VLC_OBJECT (p_playlist), psz_uri) : NULL; - - if (subdir != NULL) /* Recurse into subdirectory */ - { - if( i_mode == MODE_NONE ) - { - msg_Dbg( p_playlist, "skipping subdirectory `%s'", - psz_uri ); - closedir (subdir); - continue; - } - - msg_Dbg (p_playlist, "creating subdirectory %s", psz_uri); - - PL_LOCK; - p_node = playlist_NodeCreate( p_playlist, entry, - p_parent_category, - PLAYLIST_NO_REBUILD, NULL ); - PL_UNLOCK; - - /* If we had the parent in category, the it is now node. - * Else, we still don't have */ - i_return = ReadDir( p_playlist, psz_uri , MODE_EXPAND, - p_parent_category ? p_node : NULL, - p_current_input, subdir, &stself ); - closedir (subdir); - if (i_return) - break; // error :-( - } - else - { - input_item_t *p_input; - - if( i_extensions > 0 ) - { - const char *psz_dot = strrchr (entry, '.' ); - if( psz_dot++ && *psz_dot ) - { - int a; - for( a = 0; a < i_extensions; a++ ) - { - if( !strcmp( psz_dot, ppsz_extensions[a] ) ) - break; - } - if( a < i_extensions ) - { - msg_Dbg( p_playlist, "ignoring file %s", psz_uri ); - continue; - } - } - } - - memmove (psz_uri + 7, psz_uri, sizeof (psz_uri) - 7); - memcpy (psz_uri, "file://", 7); - p_input = input_ItemNewWithType( p_playlist, - psz_uri, entry, 0, NULL, - -1, ITEM_TYPE_FILE ); - if (p_input != NULL) - { - if( p_current_input ) - input_ItemCopyOptions( p_current_input, p_input ); - int i_ret = playlist_BothAddInput( p_playlist, p_input, - p_parent_category, - PLAYLIST_APPEND|PLAYLIST_PREPARSE| - PLAYLIST_NO_REBUILD, - PLAYLIST_END, NULL, NULL, - false ); - vlc_gc_decref( p_input ); - if( i_ret != VLC_SUCCESS ) - return VLC_EGENERIC; - } - } + free (psz_full_uri); + closedir (handle); + continue; } - } - for( i = 0; i < i_extensions; i++ ) - free( ppsz_extensions[i] ); - free( ppsz_extensions ); + input_item_CopyOptions (p_current_node->p_item, p_new); + input_item_node_AppendItem (p_current_node, p_new); - for( i = 0; i < i_dir_content; i++ ) - free( pp_dir_content[i] ); - free( pp_dir_content ); + free (psz_full_uri); + input_item_Release (p_new); + } - return i_return; + return VLC_SUCCESS; } - -static DIR *OpenDir (vlc_object_t *obj, const char *path) +/***************************************************************************** + * Control: + *****************************************************************************/ +int DirControl (access_t *p_access, int i_query, va_list args) { - msg_Dbg (obj, "opening directory `%s'", path); - DIR *handle = utf8_opendir (path); - if (handle == NULL) + VLC_UNUSED (p_access); + + switch (i_query) { - int err = errno; - if (err != ENOTDIR) - msg_Err (obj, "%s: %m", path); - else - msg_Dbg (obj, "skipping non-directory `%s'", path); - errno = err; + case ACCESS_CAN_SEEK: + case ACCESS_CAN_FASTSEEK: + *va_arg (args, bool*) = false; + break; - return NULL; - } - return handle; -} + case ACCESS_CAN_PAUSE: + case ACCESS_CAN_CONTROL_PACE: + *va_arg (args, bool*) = true; + break; + + case ACCESS_GET_PTS_DELAY: + *va_arg (args, int64_t *) = DEFAULT_PTS_DELAY * 1000; + break; + + default: + return VLC_EGENERIC; + } + return VLC_SUCCESS; + }