1 /*****************************************************************************
2 * directory.c: expands a directory (directory: access_browser plug-in)
3 *****************************************************************************
4 * Copyright (C) 2002-2008 VLC authors and VideoLAN
7 * Authors: Derk-Jan Hartman <hartman at videolan dot org>
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 /*****************************************************************************
28 *****************************************************************************/
34 #include <vlc_common.h>
36 #include <vlc_access.h>
37 #include <vlc_input_item.h>
39 #include <sys/types.h>
47 #include <vlc_strings.h>
48 #include <vlc_charset.h>
64 typedef struct directory directory
;
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
)
97 return strcoll (*a
, *b
);
99 return strcmp (*a
, *b
);
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
119 static bool has_ext (const char *psz_exts
, const char *psz_uri
)
121 if (psz_exts
== NULL
)
124 const char *ext
= strrchr (psz_uri
, '.');
128 size_t extlen
= strlen (++ext
);
130 for (const char *type
= psz_exts
, *end
; type
[0]; type
= end
+ 1)
132 end
= strchr (type
, ',');
134 end
= type
+ strlen (type
);
136 if (type
+ extlen
== end
&& !strncasecmp (ext
, type
, extlen
))
148 /* Detect directories that recurse into themselves. */
149 static bool has_inode_loop (const directory
*dir
, dev_t dev
, ino_t inode
)
153 if ((dir
->device
== dev
) && (dir
->inode
== inode
))
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
)
168 int fd
= vlc_openat (dirfd (p_dir
->handle
), psz_entry
,
169 O_RDONLY
| O_DIRECTORY
);
173 if (errno
== ENOTDIR
)
174 return ENTRY_ENOTDIR
;
176 return ENTRY_EACCESS
;
181 || has_inode_loop (p_dir
, st
.st_dev
, st
.st_ino
)
182 || (*handle
= fdopendir (fd
)) == NULL
)
185 return ENTRY_EACCESS
;
189 if (asprintf (&path
, "%s/%s", p_dir
->path
, psz_entry
) == -1)
190 return ENTRY_EACCESS
;
192 *handle
= vlc_opendir (path
);
196 if (*handle
== NULL
) {
197 return ENTRY_ENOTDIR
;
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
))
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)
222 if (fstat (dirfd (handle
), &st
))
224 p_dir
->device
= st
.st_dev
;
225 p_dir
->inode
= st
.st_ino
;
227 p_dir
->path
= make_path (psz_uri
);
228 if (p_dir
->path
== NULL
)
232 p_sys
->current
= p_dir
;
236 for (int i
= 0; i
< p_dir
->filec
; i
++)
237 free (p_dir
->filev
[i
]);
247 static bool directory_pop (access_sys_t
*p_sys
)
249 directory
*p_old
= p_sys
->current
;
254 p_sys
->current
= p_old
->parent
;
255 closedir (p_old
->handle
);
257 for (int i
= 0; i
< p_old
->filec
; i
++)
258 free (p_old
->filev
[i
]);
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
)
279 DIR *handle
= vlc_opendir (p_access
->psz_filepath
);
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
))
292 char *psz_sort
= var_InheritString (p_access
, "directory-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
;
300 p_sys
->compar
= collate
;
304 if (!strcmp (p_access
->psz_access
, "fd"))
306 if (asprintf (&uri
, "fd://%s", p_access
->psz_location
) == -1)
310 uri
= vlc_path2uri (p_access
->psz_filepath
, "file");
311 if (unlikely (uri
== NULL
))
317 /* "Open" the base directory */
318 p_sys
->current
= NULL
;
319 if (!directory_push (p_sys
, handle
, uri
))
326 p_access
->p_sys
= p_sys
;
327 p_sys
->ignored_exts
= var_InheritString (p_access
, "ignore-filetypes");
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
;
337 p_sys
->mode
= MODE_EXPAND
;
340 p_access
->pf_readdir
= DirRead
;
341 p_access
->pf_control
= DirControl
;
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
);
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
;
385 char *psz_entry
= p_current
->filev
[p_current
->i
++];
386 char *psz_full_uri
, *psz_uri
;
388 input_item_t
*p_new
= NULL
;
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
)))
400 /* Create an input item for the current entry */
401 psz_uri
= encode_URI_component (psz_entry
);
403 || asprintf (&psz_full_uri
, "%s/%s", p_current
->uri
, psz_uri
) == -1)
407 if (psz_full_uri
== NULL
)
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
);
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
;
437 input_item_Release (p_new
);
443 /*****************************************************************************
445 *****************************************************************************/
446 int DirControl (access_t
*p_access
, int i_query
, va_list args
)
448 VLC_UNUSED (p_access
);
452 case ACCESS_CAN_SEEK
:
453 case ACCESS_CAN_FASTSEEK
:
454 *va_arg (args
, bool*) = false;
457 case ACCESS_CAN_PAUSE
:
458 case ACCESS_CAN_CONTROL_PACE
:
459 *va_arg (args
, bool*) = true;
462 case ACCESS_GET_PTS_DELAY
:
463 *va_arg (args
, int64_t *) = DEFAULT_PTS_DELAY
* 1000;