]> git.sesse.net Git - vlc/blob - modules/access/directory.c
* ALL: portability fixes.
[vlc] / modules / access / directory.c
1 /*****************************************************************************
2  * directory.c: expands a directory (directory: access plug-in)
3  *****************************************************************************
4  * Copyright (C) 2001, 2002 VideoLAN
5  * $Id: directory.c,v 1.6 2004/02/17 13:13:31 gbazin Exp $
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 #include <vlc/vlc.h>
28 #include <vlc/input.h>
29
30 #include <stdlib.h>
31 #include <string.h>
32 #ifdef HAVE_SYS_TYPES_H
33 #   include <sys/types.h>
34 #endif
35 #ifdef HAVE_SYS_STAT_H
36 #   include <sys/stat.h>
37 #endif
38 #ifdef HAVE_ERRNO_H
39 #   include <errno.h>
40 #endif
41 #ifdef HAVE_FCNTL_H
42 #   include <fcntl.h>
43 #endif
44
45 #ifdef HAVE_UNISTD_H
46 #   include <unistd.h>
47 #elif defined( WIN32 ) && !defined( UNDER_CE )
48 #   include <io.h>
49 #endif
50
51 #if (!defined( WIN32 ) || defined(__MINGW32__))
52 /* Mingw has its own version of dirent */
53 #   include <dirent.h>
54 #endif
55
56 /*****************************************************************************
57  * Constants and structures
58  *****************************************************************************/
59 #define MAX_DIR_SIZE 100000
60
61 #define MODE_EXPAND 0
62 #define MODE_COLLAPSE 1
63 #define MODE_NONE 2
64
65 typedef struct input_directory_s
66 {
67     char   p_dir_buffer[MAX_DIR_SIZE];
68     int    i_buf_pos;
69     int    i_buf_length;
70     int    i_pos;
71 } input_directory_t;
72
73
74 /*****************************************************************************
75  * Local prototypes
76  *****************************************************************************/
77 static int     Open   ( vlc_object_t * );
78 static void    Close  ( vlc_object_t * );
79
80 static ssize_t Read   ( input_thread_t *, byte_t *, size_t );
81 int ReadDir( input_thread_t *p_input, char *psz_name , int i_mode );
82
83 /*****************************************************************************
84  * Module descriptor
85  *****************************************************************************/
86 #define RECURSIVE_TEXT N_("Includes subdirectories ?")
87 #define RECURSIVE_LONGTEXT N_( \
88         "Select whether subdirectories must be expanded.\n" \
89         "none : subdirectories do not appear in the playlist.\n" \
90         "collapse : subdirectories appear but are expanded on first play.\n" \
91         "expand : all subdirectories are expanded.\n" )
92
93 static char *psz_recursive_list[] = { "none", "collapse", "expand" };
94 static char *psz_recursive_list_text[] = { N_("none"), N_("collapse"),
95                                            N_("expand") };
96
97 vlc_module_begin();
98     set_description( _("Standard filesystem directory input") );
99     set_capability( "access", 55 );
100     add_shortcut( "directory" );
101     add_shortcut( "dir" );
102     add_string( "recursive", "expand" , NULL, RECURSIVE_TEXT,
103                 RECURSIVE_LONGTEXT, VLC_FALSE );
104       change_string_list( psz_recursive_list, psz_recursive_list_text, 0 );
105     set_callbacks( Open, Close );
106 vlc_module_end();
107
108
109 /*****************************************************************************
110  * Open: open the directory
111  *****************************************************************************/
112 static int Open( vlc_object_t *p_this )
113 {
114     input_thread_t *            p_input = (input_thread_t *)p_this;
115     char *                      psz_name;
116     input_directory_t *         p_access_data;
117     char *                      psz_mode;
118     int                         i_mode;
119 #ifdef HAVE_SYS_STAT_H
120     struct stat                 stat_info;
121 #endif
122
123     /* Initialize access plug-in structures. */
124     if( p_input->i_mtu == 0 )
125     {
126         /* Improve speed. */
127         p_input->i_bufsize = INPUT_DEFAULT_BUFSIZE;
128     }
129
130     p_input->pf_read = Read;
131     p_input->pf_set_program = NULL;
132     p_input->pf_set_area = NULL;
133     p_input->pf_seek = NULL;
134
135     /* Remove the ending '/' char */
136     psz_name = strdup( p_input->psz_name );
137     if( psz_name == NULL )
138         return VLC_EGENERIC;
139
140     if( (psz_name[strlen(psz_name)-1] == '/') ||
141         (psz_name[strlen(psz_name)-1] == '\\') )
142     {
143         psz_name[strlen(psz_name)-1] = '\0';
144     }
145
146 #ifdef HAVE_SYS_STAT_H
147     if( ( stat( psz_name, &stat_info ) == -1 ) ||
148         !S_ISDIR( stat_info.st_mode ) )
149 #else
150     if( !p_input->psz_access || strcmp(p_input->psz_access, "dir") )
151 #endif
152     {
153         free( psz_name );
154         return VLC_EGENERIC;
155     }
156
157     /* Initialize structure */
158     msg_Dbg( p_input, "opening directory `%s'", psz_name );
159     p_access_data = malloc( sizeof(input_directory_t) );
160     p_input->p_access_data = (void *)p_access_data;
161     if( p_access_data == NULL )
162     {
163         msg_Err( p_input, "out of memory" );
164         free( psz_name );
165         return VLC_ENOMEM;
166     }
167     p_access_data->i_pos = 0;
168
169     psz_mode = config_GetPsz( p_input , "recursive" );
170     if( !psz_mode )
171     {
172         msg_Err( p_input, "Unable to get configuration" );
173         return VLC_EGENERIC;
174     }
175
176     if( !strncmp( psz_mode, "none" , 4 )  )
177     {
178         i_mode = MODE_NONE;
179     }
180     else if( !strncmp( psz_mode, "collapse", 8 )  )
181     {
182         i_mode = MODE_COLLAPSE;
183     }
184     else
185     {
186         i_mode = MODE_EXPAND;
187     }
188     if( ReadDir( p_input, psz_name , i_mode ) != VLC_SUCCESS )
189     {
190         free( p_access_data );
191         free( psz_name );
192         return VLC_EGENERIC;
193     }
194
195     msg_Dbg(p_input,"Directory read complete. Read %i bytes",
196                     p_access_data->i_pos);
197
198     p_access_data->p_dir_buffer[p_access_data->i_pos] = '\0';
199     p_access_data->i_pos++;
200     p_access_data->i_buf_length = p_access_data->i_pos;
201     p_access_data->i_buf_pos = 0;
202
203     /* Force m3u demuxer */
204     p_input->psz_demux = "m3u";
205
206     return VLC_SUCCESS;
207 }
208
209 /*****************************************************************************
210  * Close: close the target
211  *****************************************************************************/
212 static void Close( vlc_object_t * p_this )
213 {
214     input_thread_t * p_input = (input_thread_t *)p_this;
215     input_directory_t * p_access_data =
216         (input_directory_t *)p_input->p_access_data;
217
218     msg_Info( p_input, "closing `%s/%s://%s'",
219               p_input->psz_access, p_input->psz_demux, p_input->psz_name );
220
221     free( p_access_data );
222 }
223
224 /*****************************************************************************
225  * Read: read directory and output to demux.
226  *****************************************************************************/
227 static ssize_t Read( input_thread_t * p_input, byte_t * p_buffer, size_t i_len )
228 {
229     input_directory_t * p_access_data =
230         (input_directory_t *)p_input->p_access_data;
231     unsigned int i_remaining = p_access_data->i_buf_length -
232                                p_access_data->i_buf_pos;
233
234     if( i_remaining > 0 )
235     {
236         int i_ret;
237
238         i_ret = __MIN( i_len, i_remaining );
239         memcpy( p_buffer,
240                 &p_access_data->p_dir_buffer[p_access_data->i_buf_pos],
241                 i_ret );
242         p_access_data->i_buf_pos += i_ret;
243         return (ssize_t) i_ret;
244     }
245     return 0;
246 }
247
248 /* Local functions */
249
250 /*****************************************************************************
251  * ReadDir: read a directory and add its content to the list
252  *****************************************************************************/
253 int ReadDir( input_thread_t *p_input, char *psz_name , int i_mode )
254 {
255     DIR *                       p_current_dir;
256     struct dirent *             p_dir_content;
257
258     input_directory_t * p_access_data =
259         (input_directory_t *)p_input->p_access_data;
260
261     /* have to cd into this dir */
262     p_current_dir = opendir( psz_name );
263
264     if( p_current_dir == NULL )
265     {
266         /* something went bad, get out of here ! */
267 #   ifdef HAVE_ERRNO_H
268         msg_Warn( p_input, "cannot open directory `%s' (%s)",
269                   psz_name, strerror(errno));
270 #   else
271         msg_Warn( p_input, "cannot open directory `%s'", psz_name );
272 #   endif
273         return VLC_EGENERIC;
274     }
275
276     p_dir_content = readdir( p_current_dir );
277
278     /* while we still have entries in the directory */
279     while( p_dir_content != NULL && p_access_data->i_pos < MAX_DIR_SIZE )
280     {
281         int i_size_entry = strlen( psz_name ) +
282                            strlen( p_dir_content->d_name ) + 2;
283         char *psz_entry = (char *)malloc( sizeof(char)*i_size_entry);
284
285         sprintf( psz_entry, "%s/%s",psz_name,p_dir_content->d_name);
286
287 #if 0 /* Disable this message, it makes too much output */
288         msg_Dbg( p_input, "Entry %s",psz_entry );
289 #endif
290
291         /* if it is "." or "..", forget it */
292         if( strcmp( p_dir_content->d_name, "." ) &&
293             strcmp( p_dir_content->d_name, ".." ) &&
294             p_access_data->i_pos + i_size_entry < MAX_DIR_SIZE )
295         {
296 #if defined( DT_DIR )
297             if( p_dir_content->d_type == DT_DIR )
298 #elif defined( S_ISDIR )
299             struct stat stat_data;
300             stat( psz_entry, &stat_data );
301             if( S_ISDIR(stat_data.st_mode) )
302 #else
303             if( 0 )
304 #endif
305             {
306                 if( i_mode == MODE_NONE )
307                 {
308                     msg_Dbg( p_input, "Skipping subdirectory %s",psz_entry );
309                     p_dir_content = readdir( p_current_dir );
310                     continue;
311                 }
312                 else if(i_mode == MODE_EXPAND )
313                 {
314                     msg_Dbg(p_input, "Reading subdirectory %s",psz_entry );
315                     if( ReadDir( p_input, psz_entry , MODE_EXPAND )
316                                  != VLC_SUCCESS )
317                     {
318                         return VLC_EGENERIC;
319                     }
320                 }
321                 else
322                 {
323                     sprintf( &p_access_data->p_dir_buffer[p_access_data->i_pos],
324                              "%s", psz_entry );
325                     p_access_data->i_pos += i_size_entry -1 ;
326                     p_access_data->p_dir_buffer[p_access_data->i_pos] = '\n';
327                     p_access_data->i_pos++;
328                 }
329             }
330             else
331             {
332                 sprintf( &p_access_data->p_dir_buffer[p_access_data->i_pos],
333                          "%s", psz_entry );
334                 p_access_data->i_pos += i_size_entry - 1;
335                 p_access_data->p_dir_buffer[p_access_data->i_pos] = '\n';
336                 p_access_data->i_pos++;
337             }
338         }
339         free( psz_entry );
340         p_dir_content = readdir( p_current_dir );
341     }
342     closedir( p_current_dir );
343     return VLC_SUCCESS;
344 }