]> git.sesse.net Git - vlc/blob - modules/access/directory.c
* configure.ac, modules/access/directory.c: enabled the directory access module on...
[vlc] / modules / access / directory.c
1 /*****************************************************************************
2  * directory.c: expands a directory (directory: access plug-in)
3  *****************************************************************************
4  * Copyright (C) 2002-2004 VideoLAN
5  * $Id$
6  *
7  * Authors: Derk-Jan Hartman <thedj@users.sourceforge.net>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27
28 #include <vlc/vlc.h>
29 #include <vlc/input.h>
30 #include <vlc_playlist.h>
31
32 #include <stdlib.h>
33 #include <string.h>
34 #ifdef HAVE_SYS_TYPES_H
35 #   include <sys/types.h>
36 #endif
37 #ifdef HAVE_SYS_STAT_H
38 #   include <sys/stat.h>
39 #endif
40 #ifdef HAVE_ERRNO_H
41 #   include <errno.h>
42 #endif
43 #ifdef HAVE_FCNTL_H
44 #   include <fcntl.h>
45 #endif
46
47 #ifdef HAVE_UNISTD_H
48 #   include <unistd.h>
49 #elif defined( WIN32 ) && !defined( UNDER_CE )
50 #   include <io.h>
51 #elif defined( UNDER_CE )
52 #   define strcoll strcmp
53 #endif
54
55 #ifdef HAVE_DIRENT_H
56 #   include <dirent.h>
57 #endif
58
59 /*****************************************************************************
60  * Module descriptor
61  *****************************************************************************/
62 static int  Open ( vlc_object_t * );
63 static void Close( vlc_object_t * );
64
65 static int  DemuxOpen ( vlc_object_t * );
66
67 #define RECURSIVE_TEXT N_("Subdirectory behavior")
68 #define RECURSIVE_LONGTEXT N_( \
69         "Select whether subdirectories must be expanded.\n" \
70         "none: subdirectories do not appear in the playlist.\n" \
71         "collapse: subdirectories appear but are expanded on first play.\n" \
72         "expand: all subdirectories are expanded.\n" )
73
74 static char *psz_recursive_list[] = { "none", "collapse", "expand" };
75 static char *psz_recursive_list_text[] = { N_("none"), N_("collapse"),
76                                            N_("expand") };
77
78 vlc_module_begin();
79     set_category( CAT_INPUT );
80     set_shortname( _("Directory" ) );
81     set_subcategory( SUBCAT_INPUT_ACCESS );
82     set_description( _("Standard filesystem directory input") );
83     set_capability( "access2", 55 );
84     add_shortcut( "directory" );
85     add_shortcut( "dir" );
86     add_string( "recursive", "expand" , NULL, RECURSIVE_TEXT,
87                 RECURSIVE_LONGTEXT, VLC_FALSE );
88       change_string_list( psz_recursive_list, psz_recursive_list_text, 0 );
89     set_callbacks( Open, Close );
90
91     add_submodule();
92         set_description( "Directory EOF");
93         set_capability( "demux2", 0 );
94         add_shortcut( "directory" );
95         set_callbacks( DemuxOpen, NULL );
96 vlc_module_end();
97
98
99 /*****************************************************************************
100  * Local prototypes, constants, structures
101  *****************************************************************************/
102
103 #define MODE_EXPAND 0
104 #define MODE_COLLAPSE 1
105 #define MODE_NONE 2
106
107 static int Read( access_t *, uint8_t *, int );
108 static int ReadNull( access_t *, uint8_t *, int );
109 static int Control( access_t *, int, va_list );
110
111 static int Demux( demux_t *p_demux );
112 static int DemuxControl( demux_t *p_demux, int i_query, va_list args );
113
114
115 static int ReadDir( playlist_t *, char *psz_name, int i_mode, int *pi_pos,
116                     playlist_item_t * );
117
118 /*****************************************************************************
119  * Open: open the directory
120  *****************************************************************************/
121 static int Open( vlc_object_t *p_this )
122 {
123     access_t *p_access = (access_t*)p_this;
124
125 #ifdef HAVE_SYS_STAT_H
126     struct stat stat_info;
127
128     if( ( stat( p_access->psz_path, &stat_info ) == -1 ) ||
129         !S_ISDIR( stat_info.st_mode ) )
130
131 #elif defined(WIN32)
132 #   ifdef UNICODE
133     wchar_t pwsz_path[MAX_PATH];
134     mbstowcs( pwsz_path, p_access->psz_path, MAX_PATH );
135     pwsz_path[MAX_PATH-1] = 0;
136     if( !(GetFileAttributes( pwsz_path ) & FILE_ATTRIBUTE_DIRECTORY) )
137 #   else
138     if( !(GetFileAttributes( p_access->psz_path ) & FILE_ATTRIBUTE_DIRECTORY) )
139 #   endif
140
141 #else
142     if( strcmp( p_access->psz_access, "dir") &&
143         strcmp( p_access->psz_access, "directory") )
144 #endif
145     {
146         return VLC_EGENERIC;
147     }
148
149     p_access->pf_read  = Read;
150     p_access->pf_block = NULL;
151     p_access->pf_seek  = NULL;
152     p_access->pf_control= Control;
153
154     /* Force a demux */
155     p_access->psz_demux = strdup( "directory" );
156
157     return VLC_SUCCESS;
158 }
159
160 /*****************************************************************************
161  * Close: close the target
162  *****************************************************************************/
163 static void Close( vlc_object_t * p_this )
164 {
165 }
166
167 /*****************************************************************************
168  * ReadNull: read the directory
169  *****************************************************************************/
170 static int ReadNull( access_t *p_access, uint8_t *p_buffer, int i_len)
171 {
172     /* Return fake data */
173     memset( p_buffer, 0, i_len );
174     return i_len;
175 }
176
177 /*****************************************************************************
178  * Read: read the directory
179  *****************************************************************************/
180 static int Read( access_t *p_access, uint8_t *p_buffer, int i_len)
181 {
182     char *psz_name = NULL;
183     char *psz;
184     int  i_mode, i_pos;
185
186     playlist_item_t *p_item;
187     vlc_bool_t b_play = VLC_FALSE;
188
189     playlist_t *p_playlist =
190         (playlist_t *) vlc_object_find( p_access,
191                                         VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
192
193     if( !p_playlist )
194     {
195         msg_Err( p_access, "can't find playlist" );
196         goto end;
197     }
198
199     /* Remove the ending '/' char */
200     psz_name = strdup( p_access->psz_path );
201     if( psz_name == NULL )
202         goto end;
203
204     if( (psz_name[strlen(psz_name)-1] == '/') ||
205         (psz_name[strlen(psz_name)-1] == '\\') )
206     {
207         psz_name[strlen(psz_name)-1] = '\0';
208     }
209
210     /* Initialize structure */
211     psz = var_CreateGetString( p_access, "recursive" );
212     if( *psz == '\0' || !strncmp( psz, "none" , 4 )  )
213     {
214         i_mode = MODE_NONE;
215     }
216     else if( !strncmp( psz, "collapse", 8 )  )
217     {
218         i_mode = MODE_COLLAPSE;
219     }
220     else
221     {
222         i_mode = MODE_EXPAND;
223     }
224     free( psz );
225
226     /* Make sure we are deleted when we are done */
227     /* The playlist position we will use for the add */
228     i_pos = p_playlist->i_index + 1;
229
230     msg_Dbg( p_access, "opening directory `%s'", psz_name );
231
232     if( &p_playlist->status.p_item->input ==
233         ((input_thread_t *)p_access->p_parent)->input.p_item )
234     {
235         p_item = p_playlist->status.p_item;
236         b_play = VLC_TRUE;
237         msg_Dbg( p_access, "starting directory playback");
238     }
239     else
240     {
241         input_item_t *p_current = ( (input_thread_t*)p_access->p_parent)->
242                                                         input.p_item;
243         p_item = playlist_LockItemGetByInput( p_playlist, p_current );
244         msg_Dbg( p_access, "not starting directory playback");
245         if( !p_item )
246         {
247             msg_Dbg( p_playlist, "unable to find item in playlist");
248             return -1;
249         }
250         b_play = VLC_FALSE;
251     }
252     p_item->input.i_type = ITEM_TYPE_DIRECTORY;
253     if( ReadDir( p_playlist, psz_name , i_mode, &i_pos,
254                  p_item ) != VLC_SUCCESS )
255     {
256     }
257 end:
258
259     /* Begin to read the directory */
260     if( b_play )
261     {
262         playlist_Control( p_playlist, PLAYLIST_VIEWPLAY,
263                           p_playlist->status.i_view,
264                           p_playlist->status.p_item, NULL );
265     }
266     if( psz_name ) free( psz_name );
267     vlc_object_release( p_playlist );
268
269     /* Return fake data forever */
270     p_access->pf_read = ReadNull;
271     return ReadNull( p_access, p_buffer, i_len );
272 }
273
274 /*****************************************************************************
275  * DemuxOpen:
276  *****************************************************************************/
277 static int Control( access_t *p_access, int i_query, va_list args )
278 {
279     vlc_bool_t   *pb_bool;
280     int          *pi_int;
281     int64_t      *pi_64;
282
283     switch( i_query )
284     {
285         /* */
286         case ACCESS_CAN_SEEK:
287         case ACCESS_CAN_FASTSEEK:
288         case ACCESS_CAN_PAUSE:
289         case ACCESS_CAN_CONTROL_PACE:
290             pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
291             *pb_bool = VLC_FALSE;    /* FIXME */
292             break;
293
294         /* */
295         case ACCESS_GET_MTU:
296             pi_int = (int*)va_arg( args, int * );
297             *pi_int = 0;
298             break;
299
300         case ACCESS_GET_PTS_DELAY:
301             pi_64 = (int64_t*)va_arg( args, int64_t * );
302             *pi_64 = DEFAULT_PTS_DELAY * 1000;
303             break;
304
305         /* */
306         case ACCESS_SET_PAUSE_STATE:
307         case ACCESS_GET_TITLE_INFO:
308         case ACCESS_SET_TITLE:
309         case ACCESS_SET_SEEKPOINT:
310         case ACCESS_SET_PRIVATE_ID_STATE:
311             return VLC_EGENERIC;
312
313         default:
314             msg_Warn( p_access, "unimplemented query in control" );
315             return VLC_EGENERIC;
316     }
317     return VLC_SUCCESS;
318 }
319
320 /*****************************************************************************
321  * DemuxOpen:
322  *****************************************************************************/
323 static int DemuxOpen ( vlc_object_t *p_this )
324 {
325     demux_t *p_demux = (demux_t*)p_this;
326
327     if( strcmp( p_demux->psz_demux, "directory" ) )
328         return VLC_EGENERIC;
329
330     p_demux->pf_demux   = Demux;
331     p_demux->pf_control = DemuxControl;
332     return VLC_SUCCESS;
333 }
334
335 /*****************************************************************************
336  * Demux: EOF
337  *****************************************************************************/
338 static int Demux( demux_t *p_demux )
339 {
340     return 0;
341 }
342 /*****************************************************************************
343  * DemuxControl:
344  *****************************************************************************/
345 static int DemuxControl( demux_t *p_demux, int i_query, va_list args )
346 {
347     return demux2_vaControlHelper( p_demux->s, 0, 0, 0, 1, i_query, args );
348 }
349
350 #if defined(SYS_BEOS) || defined(WIN32)
351 /* BeOS doesn't have scandir/alphasort/versionsort */
352 static int alphasort( const struct dirent **a, const struct dirent **b )
353 {
354     return strcoll( (*a)->d_name, (*b)->d_name );
355 }
356
357 static int scandir( const char *name, struct dirent ***namelist,
358                     int (*filter) ( const struct dirent * ),
359                     int (*compar) ( const struct dirent **,
360                                     const struct dirent ** ) )
361 {
362     DIR            * p_dir;
363     struct dirent  * p_content;
364     struct dirent ** pp_list;
365     int              ret, size;
366
367     if( !namelist || !( p_dir = opendir( name ) ) ) return -1;
368
369     ret     = 0;
370     pp_list = NULL;
371     while( ( p_content = readdir( p_dir ) ) )
372     {
373         if( filter && !filter( p_content ) )
374         {
375             continue;
376         }
377         pp_list = realloc( pp_list, ( ret + 1 ) * sizeof( struct dirent * ) );
378         size = sizeof( struct dirent ) + strlen( p_content->d_name ) + 1;
379         pp_list[ret] = malloc( size );
380         memcpy( pp_list[ret], p_content, size );
381         ret++;
382     }
383
384     closedir( p_dir );
385
386     if( compar )
387     {
388         qsort( pp_list, ret, sizeof( struct dirent * ),
389                (int (*)(const void *, const void *)) compar );
390     }
391
392     *namelist = pp_list;
393     return ret;
394 }
395 #endif
396
397 static int Filter( const struct dirent *foo )
398 {
399     return VLC_TRUE;
400 }
401 /*****************************************************************************
402  * ReadDir: read a directory and add its content to the list
403  *****************************************************************************/
404 static int ReadDir( playlist_t *p_playlist,
405                     char *psz_name , int i_mode, int *pi_position,
406                     playlist_item_t *p_parent )
407 {
408     struct dirent   *p_dir_content;
409     struct dirent   **pp_dir_content;
410     int             i_dir_content, i = 0;
411     playlist_item_t *p_node;
412
413     /* Change the item to a node */
414     if( p_parent->i_children == -1 )
415     {
416         playlist_LockItemToNode( p_playlist,p_parent );
417     }
418
419     /* get the first directory entry */
420     i_dir_content = scandir( psz_name, &pp_dir_content, Filter, alphasort );
421     if( i_dir_content == -1 )
422     {
423         msg_Warn( p_playlist, "Failed to read directory" );
424         return VLC_EGENERIC;
425     }
426     else if( i_dir_content <= 0 )
427     {
428         /* directory is empty */
429         return VLC_SUCCESS;
430     }
431     p_dir_content = pp_dir_content[0];
432
433     /* while we still have entries in the directory */
434     while( i < i_dir_content )
435     {
436         int i_size_entry = strlen( psz_name ) +
437                            strlen( p_dir_content->d_name ) + 2;
438         char *psz_uri = (char *)malloc( sizeof(char)*i_size_entry);
439
440         sprintf( psz_uri, "%s/%s", psz_name, p_dir_content->d_name );
441
442         /* if it starts with '.' then forget it */
443         if( p_dir_content->d_name[0] != '.' )
444         {
445 #if defined( S_ISDIR )
446             struct stat stat_data;
447             stat( psz_uri, &stat_data );
448             if( S_ISDIR(stat_data.st_mode) && i_mode != MODE_COLLAPSE )
449 #elif defined( DT_DIR )
450             if( ( p_dir_content->d_type & DT_DIR ) && i_mode != MODE_COLLAPSE )
451 #else
452             if( 0 )
453 #endif
454             {
455                 if( i_mode == MODE_NONE )
456                 {
457                     msg_Dbg( p_playlist, "Skipping subdirectory %s", psz_uri );
458                     i++;
459                     p_dir_content = pp_dir_content[i];
460                     continue;
461                 }
462                 else if(i_mode == MODE_EXPAND )
463                 {
464                     char *psz_newname;
465                     msg_Dbg(p_playlist, "Reading subdirectory %s", psz_uri );
466
467                     if( !strncmp( psz_uri, psz_name, strlen( psz_name ) ) )
468                     {
469                         char *psz_subdir = psz_uri;
470                         /* Skip the parent path + the separator */
471                         psz_subdir += strlen( psz_name ) + 1;
472                         psz_newname = strdup( psz_subdir );
473                     }
474                     else
475                     {
476                         psz_newname = strdup( psz_uri );
477                     }
478                     p_node = playlist_NodeCreate( p_playlist,
479                                        p_parent->pp_parents[0]->i_view,
480                                        psz_newname, p_parent );
481
482                     playlist_CopyParents(  p_parent, p_node );
483
484                     p_node->input.i_type = ITEM_TYPE_DIRECTORY;
485
486                     if( ReadDir( p_playlist, psz_uri , MODE_EXPAND,
487                                  pi_position, p_node ) != VLC_SUCCESS )
488                     {
489                         return VLC_EGENERIC;
490                     }
491
492                     free( psz_newname );
493                 }
494             }
495             else
496             {
497                 playlist_item_t *p_item = playlist_ItemNewWithType(
498                                                 p_playlist,
499                                                 psz_uri,
500                                                 p_dir_content->d_name,
501                                                 ITEM_TYPE_VFILE );
502                 playlist_NodeAddItem( p_playlist,p_item,
503                                       p_parent->pp_parents[0]->i_view,
504                                       p_parent,
505                                       PLAYLIST_APPEND, PLAYLIST_END );
506
507                 playlist_CopyParents( p_parent, p_item );
508             }
509         }
510         free( psz_uri );
511         i++;
512         p_dir_content = pp_dir_content[i];
513     }
514     free( pp_dir_content );
515     return VLC_SUCCESS;
516 }