Commit 9109718b authored by Thomas Guillem's avatar Thomas Guillem

access: re-refactor pf_readdir

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 88ffe158.
parent ae738673
......@@ -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 */
......@@ -238,6 +234,11 @@ static inline void access_InitFields( access_t *p_a )
p_a->info.b_eof = false;
}
/**
* \defgroup access_helper Access Helpers
* @{
*/
/**
* Default pf_control callback for directory accesses.
*/
......@@ -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
......@@ -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
......
......@@ -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)
......
......@@ -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;
}
......@@ -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 )
......
......@@ -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 *);
......@@ -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 )
......
......@@ -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)
......
......@@ -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;
}