1 /*****************************************************************************
2 * directory.c: expands a directory (directory: access plug-in)
3 *****************************************************************************
4 * Copyright (C) 2002-2008 the VideoLAN team
7 * Authors: Derk-Jan Hartman <hartman at videolan dot org>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 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 General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, 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>
38 #ifdef HAVE_SYS_STAT_H
39 # include <sys/stat.h>
45 #elif defined( WIN32 ) && !defined( UNDER_CE )
50 static inline int dirfd (DIR *dir
)
58 #include <vlc_strings.h>
67 typedef struct directory_t directory_t
;
89 char *psz_xspf_extension
;
92 /*****************************************************************************
93 * Open: open the directory
94 *****************************************************************************/
95 int DirOpen( vlc_object_t
*p_this
)
97 access_t
*p_access
= (access_t
*)p_this
;
99 if( !p_access
->psz_filepath
)
102 DIR *handle
= vlc_opendir (p_access
->psz_filepath
);
106 return DirInit (p_access
, handle
);
109 int DirInit (access_t
*p_access
, DIR *handle
)
111 access_sys_t
*p_sys
= malloc (sizeof (*p_sys
));
112 if (unlikely(p_sys
== NULL
))
116 if (!strcmp (p_access
->psz_access
, "fd"))
118 if (asprintf (&uri
, "fd://%s", p_access
->psz_location
) == -1)
122 uri
= make_URI (p_access
->psz_filepath
, "file");
123 if (unlikely(uri
== NULL
))
126 p_access
->p_sys
= p_sys
;
127 p_sys
->current
= NULL
;
128 p_sys
->handle
= handle
;
130 p_sys
->ignored_exts
= var_InheritString (p_access
, "ignore-filetypes");
131 p_sys
->i_item_count
= 0;
132 p_sys
->psz_xspf_extension
= strdup( "" );
135 char *psz
= var_InheritString (p_access
, "recursive");
136 if (psz
== NULL
|| !strcasecmp (psz
, "none"))
137 p_sys
->mode
= MODE_NONE
;
138 else if( !strcasecmp( psz
, "collapse" ) )
139 p_sys
->mode
= MODE_COLLAPSE
;
141 p_sys
->mode
= MODE_EXPAND
;
144 access_InitFields(p_access
);
145 p_access
->pf_read
= NULL
;
146 p_access
->pf_block
= DirBlock
;
147 p_access
->pf_seek
= NULL
;
148 p_access
->pf_control
= DirControl
;
149 free (p_access
->psz_demux
);
150 p_access
->psz_demux
= strdup ("xspf-open");
160 /*****************************************************************************
161 * Close: close the target
162 *****************************************************************************/
163 void DirClose( vlc_object_t
* p_this
)
165 access_t
*p_access
= (access_t
*)p_this
;
166 access_sys_t
*p_sys
= p_access
->p_sys
;
168 while (p_sys
->current
)
170 directory_t
*current
= p_sys
->current
;
172 p_sys
->current
= current
->parent
;
173 closedir (current
->handle
);
176 free (current
->path
);
181 /* corner case: Block() not called ever */
182 if (p_sys
->handle
!= NULL
)
183 closedir (p_sys
->handle
);
186 free (p_sys
->psz_xspf_extension
);
187 free (p_sys
->ignored_exts
);
191 /* Detect directories that recurse into themselves. */
192 static bool has_inode_loop (const directory_t
*dir
)
195 dev_t dev
= dir
->st
.st_dev
;
196 ino_t inode
= dir
->st
.st_ino
;
198 while ((dir
= dir
->parent
) != NULL
)
199 if ((dir
->st
.st_dev
== dev
) && (dir
->st
.st_ino
== inode
))
203 # define fstat( fd, st ) (0)
209 block_t
*DirBlock (access_t
*p_access
)
211 access_sys_t
*p_sys
= p_access
->p_sys
;
212 directory_t
*current
= p_sys
->current
;
214 if (p_access
->info
.b_eof
)
218 { /* Startup: send the XSPF header */
219 static const char header
[] =
220 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
221 "<playlist version=\"1\" xmlns=\"http://xspf.org/ns/0/\" xmlns:vlc=\"http://www.videolan.org/vlc/playlist/ns/0/\">\n"
223 block_t
*block
= block_Alloc (sizeof (header
) - 1);
226 memcpy (block
->p_buffer
, header
, sizeof (header
) - 1);
228 /* "Open" the base directory */
229 current
= malloc (sizeof (*current
));
232 block_Release (block
);
235 current
->parent
= NULL
;
236 current
->handle
= p_sys
->handle
;
238 current
->path
= strdup (p_access
->psz_filepath
);
240 current
->uri
= p_sys
->uri
;
241 if (fstat (dirfd (current
->handle
), ¤t
->st
))
244 block_Release (block
);
248 p_sys
->handle
= NULL
;
250 p_sys
->current
= current
;
254 char *entry
= vlc_readdir (current
->handle
);
256 { /* End of directory, go back to parent */
257 closedir (current
->handle
);
258 p_sys
->current
= current
->parent
;
261 free (current
->path
);
265 if (p_sys
->current
== NULL
)
266 { /* End of XSPF playlist */
268 int len
= asprintf( &footer
, " </trackList>\n" \
269 " <extension application=\"http://www.videolan.org/vlc/playlist/0\">\n" \
272 "</playlist>\n", p_sys
->psz_xspf_extension
);
273 if (unlikely(len
== -1))
276 block_t
*block
= block_heap_Alloc (footer
, footer
, len
);
277 if (unlikely(block
== NULL
))
279 p_access
->info
.b_eof
= true;
284 /* This was the end of a "subnode" */
285 /* Write the ID to the extension */
286 char *old_xspf_extension
= p_sys
->psz_xspf_extension
;
287 if (old_xspf_extension
== NULL
)
290 int len2
= asprintf( &p_sys
->psz_xspf_extension
, "%s </vlc:node>\n", old_xspf_extension
);
293 free( old_xspf_extension
);
298 /* Skip current, parent and hidden directories */
304 /* Handle recursion */
305 if (p_sys
->mode
!= MODE_COLLAPSE
)
307 directory_t
*sub
= malloc (sizeof (*sub
));
316 int fd
= vlc_openat (dirfd (current
->handle
), entry
, O_RDONLY
);
319 handle
= fdopendir (fd
);
326 if (asprintf (&sub
->path
, "%s/%s", current
->path
, entry
) != -1)
327 handle
= vlc_opendir (sub
->path
);
333 sub
->parent
= current
;
334 sub
->handle
= handle
;
336 char *encoded
= encode_URI_component (entry
);
337 if ((encoded
== NULL
)
338 || (asprintf (&sub
->uri
, "%s/%s", current
->uri
, encoded
) == -1))
342 if ((p_sys
->mode
== MODE_NONE
)
343 || fstat (dirfd (handle
), &sub
->st
)
344 || has_inode_loop (sub
)
345 || (sub
->uri
== NULL
))
353 p_sys
->current
= sub
;
355 /* Add node to xspf extension */
356 char *old_xspf_extension
= p_sys
->psz_xspf_extension
;
357 if (old_xspf_extension
== NULL
)
363 char *title
= convert_xml_special_chars (entry
);
366 || asprintf (&p_sys
->psz_xspf_extension
, "%s"
367 " <vlc:node title=\"%s\">\n", old_xspf_extension
,
374 free (old_xspf_extension
);
381 /* Skip files with ignored extensions */
382 if (p_sys
->ignored_exts
!= NULL
)
384 const char *ext
= strrchr (entry
, '.');
387 size_t extlen
= strlen (++ext
);
388 for (const char *type
= p_sys
->ignored_exts
, *end
;
389 type
[0]; type
= end
+ 1)
391 end
= strchr (type
, ',');
393 end
= type
+ strlen (type
);
395 if (type
+ extlen
== end
396 && !strncasecmp (ext
, type
, extlen
))
408 char *encoded
= encode_URI_component (entry
);
412 int len
= asprintf (&entry
,
413 " <track><location>%s/%s</location>\n" \
414 " <extension application=\"http://www.videolan.org/vlc/playlist/0\">\n" \
415 " <vlc:id>%d</vlc:id>\n" \
418 current
->uri
, encoded
, p_sys
->i_item_count
++);
423 /* Write the ID to the extension */
424 char *old_xspf_extension
= p_sys
->psz_xspf_extension
;
425 if (old_xspf_extension
== NULL
)
428 int len2
= asprintf( &p_sys
->psz_xspf_extension
, "%s <vlc:item tid=\"%i\" />\n",
429 old_xspf_extension
, p_sys
->i_item_count
-1 );
432 free( old_xspf_extension
);
434 block_t
*block
= block_heap_Alloc (entry
, entry
, len
);
435 if (unlikely(block
== NULL
))
443 p_access
->info
.b_eof
= true;
447 /*****************************************************************************
449 *****************************************************************************/
450 int DirControl( access_t
*p_access
, int i_query
, va_list args
)
455 case ACCESS_CAN_SEEK
:
456 case ACCESS_CAN_FASTSEEK
:
457 *va_arg( args
, bool* ) = false;
460 case ACCESS_CAN_PAUSE
:
461 case ACCESS_CAN_CONTROL_PACE
:
462 *va_arg( args
, bool* ) = true;
466 case ACCESS_GET_PTS_DELAY
:
467 *va_arg( args
, int64_t * ) = DEFAULT_PTS_DELAY
* 1000;
471 case ACCESS_SET_PAUSE_STATE
:
472 case ACCESS_GET_TITLE_INFO
:
473 case ACCESS_SET_TITLE
:
474 case ACCESS_SET_SEEKPOINT
:
475 case ACCESS_SET_PRIVATE_ID_STATE
:
476 case ACCESS_GET_CONTENT_TYPE
:
477 case ACCESS_GET_META
:
481 msg_Warn( p_access
, "unimplemented query in control" );