nativewindowpriv: use only one call to lock/unlock sw buffers
[vlc.git] / modules / access / directory.c
blob5c471a299ee75aef5f234541163411de61837ece
1 /*****************************************************************************
2 * directory.c: expands a directory (directory: access_browser plug-in)
3 *****************************************************************************
4 * Copyright (C) 2002-2008 VLC authors and VideoLAN
5 * $Id$
7 * Authors: Derk-Jan Hartman <hartman at videolan dot org>
8 * RĂ©mi Denis-Courmont
9 * Julien 'Lta' BALLET <contact # lta.io>
11 * This program is free software; you can redistribute it and/or modify it
12 * under the terms of the GNU Lesser General Public License as published by
13 * the Free Software Foundation; either version 2.1 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public License
22 * along with this program; if not, write to the Free Software Foundation,
23 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24 *****************************************************************************/
26 /*****************************************************************************
27 * Preamble
28 *****************************************************************************/
30 #ifdef HAVE_CONFIG_H
31 # include "config.h"
32 #endif
34 #include <vlc_common.h>
35 #include "fs.h"
36 #include <vlc_access.h>
37 #include <vlc_input_item.h>
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <errno.h>
42 #include <unistd.h>
43 #include <fcntl.h>
45 #include <vlc_fs.h>
46 #include <vlc_url.h>
47 #include <vlc_strings.h>
48 #include <vlc_charset.h>
50 enum
52 ENTRY_DIR = 0,
53 ENTRY_ENOTDIR = -1,
54 ENTRY_EACCESS = -2,
57 enum
59 MODE_NONE,
60 MODE_COLLAPSE,
61 MODE_EXPAND,
64 typedef struct directory directory;
65 struct directory
67 directory *parent;
68 DIR *handle;
69 char *uri;
70 char **filev;
71 int filec, i;
72 #ifdef HAVE_OPENAT
73 dev_t device;
74 ino_t inode;
75 #else
76 char *path;
77 #endif
80 struct access_sys_t
82 directory *current;
83 char *ignored_exts;
84 char mode;
85 int (*compar) (const char **a, const char **b);
88 /* Select non-hidden files only */
89 static int visible (const char *name)
91 return name[0] != '.';
94 static int collate (const char **a, const char **b)
96 #ifdef HAVE_STRCOLL
97 return strcoll (*a, *b);
98 #else
99 return strcmp (*a, *b);
100 #endif
103 static int version (const char **a, const char **b)
105 return strverscmp (*a, *b);
109 * Does the provided URI/path/stuff has one of the extension provided ?
111 * \param psz_exts A comma separated list of extension without dot, or only
112 * one ext (ex: "avi,mkv,webm")
113 * \param psz_uri The uri/path to check (ex: "file:///home/foo/bar.avi"). If
114 * providing an URI, it must not contain a query string.
116 * \return true if the uri/path has one of the provided extension
117 * false otherwise.
119 static bool has_ext (const char *psz_exts, const char *psz_uri)
121 if (psz_exts == NULL)
122 return false;
124 const char *ext = strrchr (psz_uri, '.');
125 if (ext == NULL)
126 return false;
128 size_t extlen = strlen (++ext);
130 for (const char *type = psz_exts, *end; type[0]; type = end + 1)
132 end = strchr (type, ',');
133 if (end == NULL)
134 end = type + strlen (type);
136 if (type + extlen == end && !strncasecmp (ext, type, extlen))
137 return true;
139 if (*end == '\0')
140 break;
143 return false;
147 #ifdef HAVE_OPENAT
148 /* Detect directories that recurse into themselves. */
149 static bool has_inode_loop (const directory *dir, dev_t dev, ino_t inode)
151 while (dir != NULL)
153 if ((dir->device == dev) && (dir->inode == inode))
154 return true;
155 dir = dir->parent;
157 return false;
159 #endif
161 /* success -> returns ENTRY_DIR and the handle parameter is set to the handle,
162 * error -> return ENTRY_ENOTDIR or ENTRY_EACCESS */
163 static int directory_open (directory *p_dir, char *psz_entry, DIR **handle)
165 *handle = NULL;
167 #ifdef HAVE_OPENAT
168 int fd = vlc_openat (dirfd (p_dir->handle), psz_entry,
169 O_RDONLY | O_DIRECTORY);
171 if (fd == -1)
173 if (errno == ENOTDIR)
174 return ENTRY_ENOTDIR;
175 else
176 return ENTRY_EACCESS;
179 struct stat st;
180 if (fstat (fd, &st)
181 || has_inode_loop (p_dir, st.st_dev, st.st_ino)
182 || (*handle = fdopendir (fd)) == NULL)
184 close (fd);
185 return ENTRY_EACCESS;
187 #else
188 char *path;
189 if (asprintf (&path, "%s/%s", p_dir->path, psz_entry) == -1)
190 return ENTRY_EACCESS;
192 *handle = vlc_opendir (path);
194 free(path);
196 if (*handle == NULL) {
197 return ENTRY_ENOTDIR;
199 #endif
201 return ENTRY_DIR;
204 static bool directory_push (access_sys_t *p_sys, DIR *handle, char *psz_uri)
206 directory *p_dir = malloc (sizeof (*p_dir));
208 psz_uri = strdup (psz_uri);
209 if (unlikely (p_dir == NULL || psz_uri == NULL))
210 goto error;
212 p_dir->parent = p_sys->current;
213 p_dir->handle = handle;
214 p_dir->uri = psz_uri;
215 p_dir->filec = vlc_loaddir (handle, &p_dir->filev, visible, p_sys->compar);
216 if (p_dir->filec < 0)
217 p_dir->filev = NULL;
218 p_dir->i = 0;
220 #ifdef HAVE_OPENAT
221 struct stat st;
222 if (fstat (dirfd (handle), &st))
223 goto error_filev;
224 p_dir->device = st.st_dev;
225 p_dir->inode = st.st_ino;
226 #else
227 p_dir->path = make_path (psz_uri);
228 if (p_dir->path == NULL)
229 goto error_filev;
230 #endif
232 p_sys->current = p_dir;
233 return true;
235 error_filev:
236 for (int i = 0; i < p_dir->filec; i++)
237 free (p_dir->filev[i]);
238 free (p_dir->filev);
240 error:
241 closedir (handle);
242 free (p_dir);
243 free (psz_uri);
244 return false;
247 static bool directory_pop (access_sys_t *p_sys)
249 directory *p_old = p_sys->current;
251 if (p_old == NULL)
252 return false;
254 p_sys->current = p_old->parent;
255 closedir (p_old->handle);
256 free (p_old->uri);
257 for (int i = 0; i < p_old->filec; i++)
258 free (p_old->filev[i]);
259 free (p_old->filev);
260 #ifndef HAVE_OPENAT
261 free (p_old->path);
262 #endif
263 free (p_old);
265 return p_sys->current != NULL;
269 /*****************************************************************************
270 * Open: open the directory
271 *****************************************************************************/
272 int DirOpen (vlc_object_t *p_this)
274 access_t *p_access = (access_t*)p_this;
276 if (!p_access->psz_filepath)
277 return VLC_EGENERIC;
279 DIR *handle = vlc_opendir (p_access->psz_filepath);
280 if (handle == NULL)
281 return VLC_EGENERIC;
283 return DirInit (p_access, handle);
286 int DirInit (access_t *p_access, DIR *handle)
288 access_sys_t *p_sys = malloc (sizeof (*p_sys));
289 if (unlikely (p_sys == NULL))
290 goto error;
292 char *psz_sort = var_InheritString (p_access, "directory-sort");
293 if (!psz_sort)
294 p_sys->compar = collate;
295 else if (!strcasecmp ( psz_sort, "version"))
296 p_sys->compar = version;
297 else if (!strcasecmp (psz_sort, "none"))
298 p_sys->compar = NULL;
299 else
300 p_sys->compar = collate;
301 free(psz_sort);
303 char *uri;
304 if (!strcmp (p_access->psz_access, "fd"))
306 if (asprintf (&uri, "fd://%s", p_access->psz_location) == -1)
307 uri = NULL;
309 else
310 uri = vlc_path2uri (p_access->psz_filepath, "file");
311 if (unlikely (uri == NULL))
313 closedir (handle);
314 goto error;
317 /* "Open" the base directory */
318 p_sys->current = NULL;
319 if (!directory_push (p_sys, handle, uri))
321 free (uri);
322 goto error;
324 free (uri);
326 p_access->p_sys = p_sys;
327 p_sys->ignored_exts = var_InheritString (p_access, "ignore-filetypes");
330 /* Handle mode */
331 char *psz_rec = var_InheritString (p_access, "recursive");
332 if (psz_rec == NULL || !strcasecmp (psz_rec, "none"))
333 p_sys->mode = MODE_NONE;
334 else if (!strcasecmp (psz_rec, "collapse"))
335 p_sys->mode = MODE_COLLAPSE;
336 else
337 p_sys->mode = MODE_EXPAND;
338 free (psz_rec);
340 p_access->pf_readdir = DirRead;
341 p_access->pf_control = DirControl;
343 return VLC_SUCCESS;
345 error:
346 free (p_sys);
347 return VLC_EGENERIC;
350 /*****************************************************************************
351 * Close: close the target
352 *****************************************************************************/
353 void DirClose( vlc_object_t * p_this )
355 access_t *p_access = (access_t*)p_this;
356 access_sys_t *p_sys = p_access->p_sys;
358 while (directory_pop (p_sys))
361 free (p_sys->ignored_exts);
362 free (p_sys);
365 /* This function is a little bit too complex for what it seems to do, but the
366 * point is to de-recursify directory recusion to avoid overruning the stack
367 * in case there's a high directory depth */
368 int DirRead (access_t *p_access, input_item_node_t *p_current_node)
370 access_sys_t *p_sys = p_access->p_sys;
372 while (p_sys->current != NULL
373 && p_sys->current->i <= p_sys->current->filec)
375 directory *p_current = p_sys->current;
377 /* End of the current folder, let's pop directory and node */
378 if (p_current->i == p_current->filec)
380 directory_pop (p_sys);
381 p_current_node = p_current_node->p_parent;
382 continue;
385 char *psz_entry = p_current->filev[p_current->i++];
386 char *psz_full_uri, *psz_uri;
387 DIR *handle;
388 input_item_t *p_new = NULL;
389 int i_res;
391 /* Check if it is a directory or even readable */
392 i_res = directory_open (p_current, psz_entry, &handle);
394 if (i_res == ENTRY_EACCESS
395 || (i_res == ENTRY_DIR && p_sys->mode == MODE_NONE)
396 || (i_res == ENTRY_ENOTDIR && has_ext (p_sys->ignored_exts, psz_entry)))
397 continue;
400 /* Create an input item for the current entry */
401 psz_uri = encode_URI_component (psz_entry);
402 if (psz_uri == NULL
403 || asprintf (&psz_full_uri, "%s/%s", p_current->uri, psz_uri) == -1)
404 psz_full_uri = NULL;
406 free (psz_uri);
407 if (psz_full_uri == NULL)
409 closedir (handle);
410 continue;
413 int i_type = i_res == ENTRY_DIR ? ITEM_TYPE_DIRECTORY : ITEM_TYPE_FILE;
414 p_new = input_item_NewWithType (psz_full_uri, psz_entry,
415 0, NULL, 0, 0, i_type);
416 if (p_new == NULL)
418 free (psz_full_uri);
419 closedir (handle);
420 continue;
423 input_item_CopyOptions (p_current_node->p_item, p_new);
424 input_item_node_t *p_new_node = input_item_node_AppendItem (p_current_node, p_new);
426 /* Handle directory flags and recursion if in EXPAND mode */
427 if (i_res == ENTRY_DIR)
429 if (p_sys->mode == MODE_EXPAND
430 && directory_push (p_sys, handle, psz_full_uri))
432 p_current_node = p_new_node;
436 free (psz_full_uri);
437 input_item_Release (p_new);
440 return VLC_SUCCESS;
443 /*****************************************************************************
444 * Control:
445 *****************************************************************************/
446 int DirControl (access_t *p_access, int i_query, va_list args)
448 VLC_UNUSED (p_access);
450 switch (i_query)
452 case ACCESS_CAN_SEEK:
453 case ACCESS_CAN_FASTSEEK:
454 *va_arg (args, bool*) = false;
455 break;
457 case ACCESS_CAN_PAUSE:
458 case ACCESS_CAN_CONTROL_PACE:
459 *va_arg (args, bool*) = true;
460 break;
462 case ACCESS_GET_PTS_DELAY:
463 *va_arg (args, int64_t *) = DEFAULT_PTS_DELAY * 1000;
464 break;
466 default:
467 return VLC_EGENERIC;
469 return VLC_SUCCESS;