1 /*****************************************************************************
2 * directory.c: expands a directory (directory: access plug-in)
3 *****************************************************************************
4 * Copyright (C) 2002-2008 VLC authors and VideoLAN
7 * Authors: Derk-Jan Hartman <hartman at videolan dot org>
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU Lesser General Public License as published by
12 * the Free Software Foundation; either version 2.1 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public License
21 * along with this program; if not, write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
25 /*****************************************************************************
27 *****************************************************************************/
33 #include <vlc_common.h>
35 #include <vlc_access.h>
37 #include <sys/types.h>
45 #include <vlc_strings.h>
46 #include <vlc_charset.h>
55 typedef struct directory_t directory_t
;
79 int (*compar
)(const char **a
, const char **b
);
82 /* Select non-hidden files only */
83 static int visible (const char *name
)
85 return name
[0] != '.';
88 static int collate (const char **a
, const char **b
)
91 return strcoll (*a
, *b
);
93 return strcmp (*a
, *b
);
97 static int version (const char **a
, const char **b
)
99 return strverscmp (*a
, *b
);
102 /*****************************************************************************
103 * Open: open the directory
104 *****************************************************************************/
105 int DirOpen( vlc_object_t
*p_this
)
107 access_t
*p_access
= (access_t
*)p_this
;
109 if( !p_access
->psz_filepath
)
112 DIR *handle
= vlc_opendir (p_access
->psz_filepath
);
116 return DirInit (p_access
, handle
);
119 int DirInit (access_t
*p_access
, DIR *handle
)
121 access_sys_t
*p_sys
= malloc (sizeof (*p_sys
));
122 if (unlikely(p_sys
== NULL
))
126 if (!strcmp (p_access
->psz_access
, "fd"))
128 if (asprintf (&uri
, "fd://%s", p_access
->psz_location
) == -1)
132 uri
= vlc_path2uri (p_access
->psz_filepath
, "file");
133 if (unlikely(uri
== NULL
))
136 /* "Open" the base directory */
137 directory_t
*root
= malloc (sizeof (*root
));
138 if (unlikely(root
== NULL
))
144 char *psz_sort
= var_InheritString (p_access
, "directory-sort");
146 p_sys
->compar
= collate
;
147 else if (!strcasecmp (psz_sort
, "version"))
148 p_sys
->compar
= version
;
149 else if (!strcasecmp (psz_sort
, "none"))
150 p_sys
->compar
= NULL
;
152 p_sys
->compar
= collate
;
156 root
->handle
= handle
;
158 root
->filec
= vlc_loaddir (handle
, &root
->filev
, visible
, p_sys
->compar
);
164 if (fstat (dirfd (handle
), &st
))
170 root
->device
= st
.st_dev
;
171 root
->inode
= st
.st_ino
;
173 root
->path
= strdup (p_access
->psz_filepath
);
176 p_access
->p_sys
= p_sys
;
177 p_sys
->current
= root
;
178 p_sys
->ignored_exts
= var_InheritString (p_access
, "ignore-filetypes");
179 p_sys
->header
= true;
180 p_sys
->i_item_count
= 0;
181 p_sys
->xspf_ext
= strdup ("");
184 char *psz
= var_InheritString (p_access
, "recursive");
185 if (psz
== NULL
|| !strcasecmp (psz
, "none"))
186 p_sys
->mode
= MODE_NONE
;
187 else if( !strcasecmp( psz
, "collapse" ) )
188 p_sys
->mode
= MODE_COLLAPSE
;
190 p_sys
->mode
= MODE_EXPAND
;
193 access_InitFields(p_access
);
194 p_access
->pf_read
= NULL
;
195 p_access
->pf_block
= DirBlock
;
196 p_access
->pf_seek
= NULL
;
197 p_access
->pf_control
= DirControl
;
206 /*****************************************************************************
207 * Close: close the target
208 *****************************************************************************/
209 void DirClose( vlc_object_t
* p_this
)
211 access_t
*p_access
= (access_t
*)p_this
;
212 access_sys_t
*p_sys
= p_access
->p_sys
;
214 while (p_sys
->current
)
216 directory_t
*current
= p_sys
->current
;
218 p_sys
->current
= current
->parent
;
219 closedir (current
->handle
);
221 while (current
->i
< current
->filec
)
222 free (current
->filev
[current
->i
++]);
223 free (current
->filev
);
225 free (current
->path
);
230 free (p_sys
->xspf_ext
);
231 free (p_sys
->ignored_exts
);
236 /* Detect directories that recurse into themselves. */
237 static bool has_inode_loop (const directory_t
*dir
, dev_t dev
, ino_t inode
)
241 if ((dir
->device
== dev
) && (dir
->inode
== inode
))
249 block_t
*DirBlock (access_t
*p_access
)
251 access_sys_t
*p_sys
= p_access
->p_sys
;
252 directory_t
*current
= p_sys
->current
;
254 if (p_access
->info
.b_eof
)
258 { /* Startup: send the XSPF header */
259 static const char header
[] =
260 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
261 "<playlist version=\"1\" xmlns=\"http://xspf.org/ns/0/\" xmlns:vlc=\"http://www.videolan.org/vlc/playlist/ns/0/\">\n"
263 block_t
*block
= block_Alloc (sizeof (header
) - 1);
266 memcpy (block
->p_buffer
, header
, sizeof (header
) - 1);
267 p_sys
->header
= false;
271 if (current
->i
>= current
->filec
)
272 { /* End of directory, go back to parent */
273 closedir (current
->handle
);
274 p_sys
->current
= current
->parent
;
276 free (current
->filev
);
278 free (current
->path
);
282 if (p_sys
->current
== NULL
)
283 { /* End of XSPF playlist */
285 int len
= asprintf (&footer
, " </trackList>\n"
286 " <extension application=\"http://www.videolan.org/"
287 "vlc/playlist/0\">\n"
290 "</playlist>\n", p_sys
->xspf_ext
? p_sys
->xspf_ext
: "");
291 if (unlikely(len
== -1))
294 block_t
*block
= block_heap_Alloc (footer
, len
);
295 p_access
->info
.b_eof
= true;
300 /* This was the end of a "subnode" */
301 /* Write the ID to the extension */
302 char *old_xspf_ext
= p_sys
->xspf_ext
;
303 if (old_xspf_ext
!= NULL
304 && asprintf (&p_sys
->xspf_ext
, "%s </vlc:node>\n",
305 old_xspf_ext
? old_xspf_ext
: "") == -1)
306 p_sys
->xspf_ext
= NULL
;
312 char *entry
= current
->filev
[current
->i
++];
314 /* Handle recursion */
315 if (p_sys
->mode
!= MODE_COLLAPSE
)
319 int fd
= vlc_openat (dirfd (current
->handle
), entry
,
320 O_RDONLY
| O_DIRECTORY
);
323 if (errno
== ENOTDIR
)
325 goto skip
; /* File cannot be opened... forget it */
330 || p_sys
->mode
== MODE_NONE
331 || has_inode_loop (current
, st
.st_dev
, st
.st_ino
)
332 || (handle
= fdopendir (fd
)) == NULL
)
339 if (asprintf (&path
, "%s/%s", current
->path
, entry
) == -1)
341 if ((handle
= vlc_opendir (path
)) == NULL
)
343 if (p_sys
->mode
== MODE_NONE
)
346 directory_t
*sub
= malloc (sizeof (*sub
));
347 if (unlikely(sub
== NULL
))
355 sub
->parent
= current
;
356 sub
->handle
= handle
;
357 sub
->filec
= vlc_loaddir (handle
, &sub
->filev
, visible
, p_sys
->compar
);
362 sub
->device
= st
.st_dev
;
363 sub
->inode
= st
.st_ino
;
367 p_sys
->current
= sub
;
369 char *encoded
= encode_URI_component (entry
);
371 || (asprintf (&sub
->uri
, "%s/%s", current
->uri
, encoded
) == -1))
374 if (unlikely(sub
->uri
== NULL
))
380 /* Add node to XSPF extension */
381 char *old_xspf_ext
= p_sys
->xspf_ext
;
383 char *title
= convert_xml_special_chars (entry
);
384 if (old_xspf_ext
!= NULL
385 && asprintf (&p_sys
->xspf_ext
, "%s <vlc:node title=\"%s\">\n",
386 old_xspf_ext
, title
? title
: "?") == -1)
387 p_sys
->xspf_ext
= NULL
;
394 /* Skip files with ignored extensions */
395 if (p_sys
->ignored_exts
!= NULL
)
397 const char *ext
= strrchr (entry
, '.');
400 size_t extlen
= strlen (++ext
);
401 for (const char *type
= p_sys
->ignored_exts
, *end
;
402 type
[0]; type
= end
+ 1)
404 end
= strchr (type
, ',');
406 end
= type
+ strlen (type
);
408 if (type
+ extlen
== end
409 && !strncasecmp (ext
, type
, extlen
))
421 char *encoded
= encode_URI_component (entry
);
425 int len
= asprintf (&entry
,
426 " <track><location>%s/%s</location>\n" \
427 " <extension application=\"http://www.videolan.org/vlc/playlist/0\">\n" \
428 " <vlc:id>%d</vlc:id>\n" \
431 current
->uri
, encoded
, p_sys
->i_item_count
++);
436 /* Write the ID to the extension */
437 char *old_xspf_ext
= p_sys
->xspf_ext
;
438 if (old_xspf_ext
!= NULL
439 && asprintf (&p_sys
->xspf_ext
, "%s <vlc:item tid=\"%i\" />\n",
440 old_xspf_ext
, p_sys
->i_item_count
- 1) == -1)
441 p_sys
->xspf_ext
= NULL
;
444 block_t
*block
= block_heap_Alloc (entry
, len
);
445 if (unlikely(block
== NULL
))
450 p_access
->info
.b_eof
= true;
458 /*****************************************************************************
460 *****************************************************************************/
461 int DirControl( access_t
*p_access
, int i_query
, va_list args
)
465 case ACCESS_CAN_SEEK
:
466 case ACCESS_CAN_FASTSEEK
:
467 *va_arg( args
, bool* ) = false;
470 case ACCESS_CAN_PAUSE
:
471 case ACCESS_CAN_CONTROL_PACE
:
472 *va_arg( args
, bool* ) = true;
475 case ACCESS_GET_PTS_DELAY
:
476 *va_arg( args
, int64_t * ) = DEFAULT_PTS_DELAY
* 1000;
479 case ACCESS_GET_CONTENT_TYPE
:
480 *va_arg( args
, char** ) = strdup("application/xspf+xml");