X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Faccess%2Fdirectory.c;h=5c471a299ee75aef5f234541163411de61837ece;hb=1dcc337d1f41bb760d72118849d53c3856d030ca;hp=8c4950f491eb629db1552e48207615e0dc990f3e;hpb=6d4bc50f38f446d21a9d8eabf983e7b6584d9474;p=vlc diff --git a/modules/access/directory.c b/modules/access/directory.c index 8c4950f491..5c471a299e 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-2008 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. *****************************************************************************/ /***************************************************************************** @@ -33,32 +34,25 @@ #include #include "fs.h" #include +#include #include -#ifdef HAVE_SYS_STAT_H -# include -#endif - -#ifdef HAVE_UNISTD_H -# include -# include -#elif defined( WIN32 ) && !defined( UNDER_CE ) -# include -#endif - -#ifdef HAVE_DIRENT_H -# include -#endif -#ifdef __sun__ -static inline int dirfd (DIR *dir) -{ - return dir->dd_fd; -} -#endif +#include +#include +#include +#include #include #include #include +#include + +enum +{ + ENTRY_DIR = 0, + ENTRY_ENOTDIR = -1, + ENTRY_EACCESS = -2, +}; enum { @@ -67,39 +61,219 @@ enum MODE_EXPAND, }; -typedef struct directory_t directory_t; -struct directory_t +typedef struct directory directory; +struct directory { - directory_t *parent; + directory *parent; DIR *handle; char *uri; -#ifndef WIN32 - struct stat st; -#endif -#ifndef HAVE_OPENAT + char **filev; + int filec, i; +#ifdef HAVE_OPENAT + dev_t device; + ino_t inode; +#else char *path; #endif }; struct access_sys_t { - directory_t *current; - DIR *handle; - char *uri; - char *ignored_exts; - int mode; - int i_item_count; - char *psz_xspf_extension; + directory *current; + char *ignored_exts; + char mode; + int (*compar) (const char **a, const char **b); }; +/* Select non-hidden files only */ +static int visible (const char *name) +{ + return name[0] != '.'; +} + +static int collate (const char **a, const char **b) +{ +#ifdef HAVE_STRCOLL + return strcoll (*a, *b); +#else + return strcmp (*a, *b); +#endif +} + +static int version (const char **a, const char **b) +{ + return strverscmp (*a, *b); +} + +/** + * 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; +} + + +#ifdef HAVE_OPENAT +/* Detect directories that recurse into themselves. */ +static bool has_inode_loop (const directory *dir, dev_t dev, ino_t inode) +{ + while (dir != NULL) + { + if ((dir->device == dev) && (dir->inode == inode)) + return true; + dir = dir->parent; + } + return false; +} +#endif + +/* 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) +{ + *handle = NULL; + +#ifdef HAVE_OPENAT + int fd = vlc_openat (dirfd (p_dir->handle), psz_entry, + O_RDONLY | O_DIRECTORY); + + if (fd == -1) + { + if (errno == ENOTDIR) + return ENTRY_ENOTDIR; + else + return ENTRY_EACCESS; + } + + struct stat st; + if (fstat (fd, &st) + || has_inode_loop (p_dir, st.st_dev, st.st_ino) + || (*handle = fdopendir (fd)) == NULL) + { + close (fd); + return ENTRY_EACCESS; + } +#else + char *path; + if (asprintf (&path, "%s/%s", p_dir->path, psz_entry) == -1) + return ENTRY_EACCESS; + + *handle = vlc_opendir (path); + + free(path); + + if (*handle == NULL) { + return ENTRY_ENOTDIR; + } +#endif + + return ENTRY_DIR; +} + +static bool directory_push (access_sys_t *p_sys, DIR *handle, char *psz_uri) +{ + directory *p_dir = malloc (sizeof (*p_dir)); + + psz_uri = strdup (psz_uri); + if (unlikely (p_dir == NULL || psz_uri == NULL)) + goto error; + + 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; + +#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 + + p_sys->current = p_dir; + return true; + +error_filev: + for (int i = 0; i < p_dir->filec; i++) + free (p_dir->filev[i]); + free (p_dir->filev); + +error: + closedir (handle); + free (p_dir); + free (psz_uri); + return false; +} + +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); + + return p_sys->current != NULL; +} + + /***************************************************************************** * Open: open the directory *****************************************************************************/ -int DirOpen( vlc_object_t *p_this ) +int DirOpen (vlc_object_t *p_this) { access_t *p_access = (access_t*)p_this; - if( !p_access->psz_filepath ) + if (!p_access->psz_filepath) return VLC_EGENERIC; DIR *handle = vlc_opendir (p_access->psz_filepath); @@ -112,9 +286,20 @@ int DirOpen( vlc_object_t *p_this ) int DirInit (access_t *p_access, DIR *handle) { access_sys_t *p_sys = malloc (sizeof (*p_sys)); - if (unlikely(p_sys == NULL)) + 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")) { @@ -122,40 +307,42 @@ int DirInit (access_t *p_access, DIR *handle) uri = NULL; } else - uri = make_URI (p_access->psz_filepath); - if (unlikely(uri == NULL)) + uri = vlc_path2uri (p_access->psz_filepath, "file"); + if (unlikely (uri == NULL)) + { + closedir (handle); goto error; + } - p_access->p_sys = p_sys; + /* "Open" the base directory */ p_sys->current = NULL; - p_sys->handle = handle; - p_sys->uri = uri; + if (!directory_push (p_sys, handle, uri)) + { + free (uri); + goto error; + } + free (uri); + + p_access->p_sys = p_sys; p_sys->ignored_exts = var_InheritString (p_access, "ignore-filetypes"); - p_sys->i_item_count = 0; - p_sys->psz_xspf_extension = strdup( "" ); + /* Handle mode */ - char *psz = var_InheritString (p_access, "recursive"); - if (psz == NULL || !strcasecmp (psz, "none")) + char *psz_rec = var_InheritString (p_access, "recursive"); + if (psz_rec == NULL || !strcasecmp (psz_rec, "none")) p_sys->mode = MODE_NONE; - else if( !strcasecmp( psz, "collapse" ) ) + else if (!strcasecmp (psz_rec, "collapse")) p_sys->mode = MODE_COLLAPSE; else p_sys->mode = MODE_EXPAND; - free( psz ); + free (psz_rec); - access_InitFields(p_access); - p_access->pf_read = NULL; - p_access->pf_block = DirBlock; - p_access->pf_seek = NULL; - p_access->pf_control= DirControl; - free (p_access->psz_demux); - p_access->psz_demux = strdup ("xspf-open"); + p_access->pf_readdir = DirRead; + p_access->pf_control = DirControl; return VLC_SUCCESS; error: - closedir (handle); free (p_sys); return VLC_EGENERIC; } @@ -168,321 +355,116 @@ void DirClose( vlc_object_t * p_this ) access_t *p_access = (access_t*)p_this; access_sys_t *p_sys = p_access->p_sys; - while (p_sys->current) - { - directory_t *current = p_sys->current; + while (directory_pop (p_sys)) + ; - p_sys->current = current->parent; - closedir (current->handle); - free (current->uri); -#ifndef HAVE_OPENAT - free (current->path); -#endif - free (current); - } - - /* corner case: Block() not called ever */ - if (p_sys->handle != NULL) - closedir (p_sys->handle); - free (p_sys->uri); - - free (p_sys->psz_xspf_extension); free (p_sys->ignored_exts); free (p_sys); } -/* Detect directories that recurse into themselves. */ -static bool has_inode_loop (const directory_t *dir) +/* 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) { -#ifndef WIN32 - dev_t dev = dir->st.st_dev; - ino_t inode = dir->st.st_ino; + access_sys_t *p_sys = p_access->p_sys; - while ((dir = dir->parent) != NULL) - if ((dir->st.st_dev == dev) && (dir->st.st_ino == inode)) - return true; -#else -# undef fstat -# define fstat( fd, st ) (0) - VLC_UNUSED( dir ); -#endif - return false; -} + while (p_sys->current != NULL + && p_sys->current->i <= p_sys->current->filec) + { + directory *p_current = p_sys->current; -block_t *DirBlock (access_t *p_access) -{ - access_sys_t *p_sys = p_access->p_sys; - directory_t *current = p_sys->current; - - if (p_access->info.b_eof) - return NULL; - - if (current == NULL) - { /* Startup: send the XSPF header */ - static const char header[] = - "\n" - "\n" - " \n"; - block_t *block = block_Alloc (sizeof (header) - 1); - if (!block) - goto fatal; - memcpy (block->p_buffer, header, sizeof (header) - 1); - - /* "Open" the base directory */ - current = malloc (sizeof (*current)); - if (current == NULL) - { - block_Release (block); - goto fatal; - } - current->parent = NULL; - current->handle = p_sys->handle; -#ifndef HAVE_OPENAT - current->path = strdup (p_access->psz_filepath); -#endif - current->uri = p_sys->uri; - if (fstat (dirfd (current->handle), ¤t->st)) + /* End of the current folder, let's pop directory and node */ + if (p_current->i == p_current->filec) { - free (current); - block_Release (block); - goto fatal; + directory_pop (p_sys); + p_current_node = p_current_node->p_parent; + continue; } - p_sys->handle = NULL; - p_sys->uri = NULL; - p_sys->current = current; - return block; - } + 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; - char *entry = vlc_readdir (current->handle); - if (entry == NULL) - { /* End of directory, go back to parent */ - closedir (current->handle); - p_sys->current = current->parent; - free (current->uri); -#ifndef HAVE_OPENAT - free (current->path); -#endif - free (current); - - if (p_sys->current == NULL) - { /* End of XSPF playlist */ - char *footer; - int len = asprintf( &footer, " \n" \ - " \n" \ - "%s" \ - " \n" \ - "\n", p_sys->psz_xspf_extension ); - if (unlikely(len == -1)) - goto fatal; - - block_t *block = block_heap_Alloc (footer, footer, len); - if (unlikely(block == NULL)) - free (footer); - p_access->info.b_eof = true; - return block; - } - else - { - /* This was the end of a "subnode" */ - /* Write the ID to the extension */ - char *old_xspf_extension = p_sys->psz_xspf_extension; - if (old_xspf_extension == NULL) - goto fatal; - - int len2 = asprintf( &p_sys->psz_xspf_extension, "%s \n", old_xspf_extension ); - if (len2 == -1) - goto fatal; - free( old_xspf_extension ); - } - return NULL; - } + /* Check if it is a directory or even readable */ + i_res = directory_open (p_current, psz_entry, &handle); - /* Skip current, parent and hidden directories */ - if (entry[0] == '.') - { - free (entry); - return NULL; - } - /* Handle recursion */ - if (p_sys->mode != MODE_COLLAPSE) - { - directory_t *sub = malloc (sizeof (*sub)); - if (sub == NULL) + if (i_res == ENTRY_EACCESS + || (i_res == ENTRY_DIR && p_sys->mode == MODE_NONE) + || (i_res == ENTRY_ENOTDIR && has_ext (p_sys->ignored_exts, psz_entry))) + continue; + + + /* 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; + + free (psz_uri); + if (psz_full_uri == NULL) { - free (entry); - return NULL; + closedir (handle); + continue; } - DIR *handle; -#ifdef HAVE_OPENAT - int fd = vlc_openat (dirfd (current->handle), entry, O_RDONLY); - if (fd != -1) + 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) { - handle = fdopendir (fd); - if (handle == NULL) - close (fd); + free (psz_full_uri); + closedir (handle); + continue; } - else - handle = NULL; -#else - if (asprintf (&sub->path, "%s/%s", current->path, entry) != -1) - handle = vlc_opendir (sub->path); - else - handle = NULL; -#endif - if (handle != NULL) - { - sub->parent = current; - sub->handle = handle; - - char *encoded = encode_URI_component (entry); - if ((encoded == NULL) - || (asprintf (&sub->uri, "%s/%s", current->uri, encoded) == -1)) - sub->uri = NULL; - free (encoded); - - if ((p_sys->mode == MODE_NONE) - || fstat (dirfd (handle), &sub->st) - || has_inode_loop (sub) - || (sub->uri == NULL)) - { - free (entry); - closedir (handle); - free (sub->uri); - free (sub); - return NULL; - } - p_sys->current = sub; - - /* Add node to xspf extension */ - char *old_xspf_extension = p_sys->psz_xspf_extension; - if (old_xspf_extension == NULL) - { - free (entry); - goto fatal; - } - char *title = convert_xml_special_chars (entry); - free (entry); - if (title == NULL - || asprintf (&p_sys->psz_xspf_extension, "%s" - " \n", old_xspf_extension, - title) == -1) - { - free (title); - goto fatal; - } - free (title); - free (old_xspf_extension); - return NULL; - } - else - free (sub); - } + input_item_CopyOptions (p_current_node->p_item, p_new); + input_item_node_t *p_new_node = input_item_node_AppendItem (p_current_node, p_new); - /* Skip files with ignored extensions */ - if (p_sys->ignored_exts != NULL) - { - const char *ext = strrchr (entry, '.'); - if (ext != NULL) + /* Handle directory flags and recursion if in EXPAND mode */ + if (i_res == ENTRY_DIR) { - size_t extlen = strlen (++ext); - for (const char *type = p_sys->ignored_exts, *end; - type[0]; type = end + 1) + if (p_sys->mode == MODE_EXPAND + && directory_push (p_sys, handle, psz_full_uri)) { - end = strchr (type, ','); - if (end == NULL) - end = type + strlen (type); - - if (type + extlen == end - && !strncasecmp (ext, type, extlen)) - { - free (entry); - return NULL; - } - - if (*end == '\0') - break; + p_current_node = p_new_node; } } - } - char *encoded = encode_URI_component (entry); - free (entry); - if (encoded == NULL) - goto fatal; - int len = asprintf (&entry, - " %s/%s\n" \ - " \n" \ - " %d\n" \ - " \n" \ - " \n", - current->uri, encoded, p_sys->i_item_count++); - free (encoded); - if (len == -1) - goto fatal; - - /* Write the ID to the extension */ - char *old_xspf_extension = p_sys->psz_xspf_extension; - if (old_xspf_extension == NULL) - goto fatal; - - int len2 = asprintf( &p_sys->psz_xspf_extension, "%s \n", - old_xspf_extension, p_sys->i_item_count-1 ); - if (len2 == -1) - goto fatal; - free( old_xspf_extension ); - - block_t *block = block_heap_Alloc (entry, entry, len); - if (unlikely(block == NULL)) - { - free (entry); - goto fatal; + free (psz_full_uri); + input_item_Release (p_new); } - return block; -fatal: - p_access->info.b_eof = true; - return NULL; + return VLC_SUCCESS; } /***************************************************************************** * Control: *****************************************************************************/ -int DirControl( access_t *p_access, int i_query, va_list args ) +int DirControl (access_t *p_access, int i_query, va_list args) { - switch( i_query ) + VLC_UNUSED (p_access); + + switch (i_query) { - /* */ case ACCESS_CAN_SEEK: case ACCESS_CAN_FASTSEEK: - *va_arg( args, bool* ) = false; + *va_arg (args, bool*) = false; break; case ACCESS_CAN_PAUSE: case ACCESS_CAN_CONTROL_PACE: - *va_arg( args, bool* ) = true; + *va_arg (args, bool*) = true; break; - /* */ case ACCESS_GET_PTS_DELAY: - *va_arg( args, int64_t * ) = DEFAULT_PTS_DELAY * 1000; + *va_arg (args, int64_t *) = DEFAULT_PTS_DELAY * 1000; break; - /* */ - 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: - case ACCESS_GET_META: - return VLC_EGENERIC; - default: - msg_Warn( p_access, "unimplemented query in control" ); return VLC_EGENERIC; - } - return VLC_SUCCESS; -} + } + return VLC_SUCCESS; + }