directory.c 11.1 KB
Newer Older
1
2
3
4
/*****************************************************************************
 * directory.c: expands a directory (directory: access plug-in)
 *****************************************************************************
 * Copyright (C) 2001, 2002 VideoLAN
Carlo Calabrò's avatar
Carlo Calabrò committed
5
 * $Id$
6
7
8
9
10
11
12
 *
 * Authors: Derk-Jan Hartman <thedj@users.sourceforge.net>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
zorglub's avatar
zorglub committed
13
 *
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
#include <vlc/vlc.h>
#include <vlc/input.h>

#include <stdlib.h>
#include <string.h>
#ifdef HAVE_SYS_TYPES_H
#   include <sys/types.h>
#endif
#ifdef HAVE_SYS_STAT_H
#   include <sys/stat.h>
#endif
#ifdef HAVE_ERRNO_H
#   include <errno.h>
#endif
#ifdef HAVE_FCNTL_H
#   include <fcntl.h>
#endif

#ifdef HAVE_UNISTD_H
#   include <unistd.h>
#elif defined( WIN32 ) && !defined( UNDER_CE )
#   include <io.h>
#endif

#if (!defined( WIN32 ) || defined(__MINGW32__))
/* Mingw has its own version of dirent */
#   include <dirent.h>
#endif

/*****************************************************************************
 * Constants and structures
 *****************************************************************************/
zorglub's avatar
zorglub committed
59
60
61
62
63
#define MAX_DIR_SIZE 100000

#define MODE_EXPAND 0
#define MODE_COLLAPSE 1
#define MODE_NONE 2
64

gbazin's avatar
   
gbazin committed
65
typedef struct input_directory_s
66
{
gbazin's avatar
   
gbazin committed
67
68
69
    char   p_dir_buffer[MAX_DIR_SIZE];
    int    i_buf_pos;
    int    i_buf_length;
zorglub's avatar
zorglub committed
70
    int    i_pos;
gbazin's avatar
   
gbazin committed
71
} input_directory_t;
72
73
74
75
76
77
78
79
80


/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
static int     Open   ( vlc_object_t * );
static void    Close  ( vlc_object_t * );

static ssize_t Read   ( input_thread_t *, byte_t *, size_t );
zorglub's avatar
zorglub committed
81
int ReadDir( input_thread_t *p_input, char *psz_name , int i_mode );
82
83
84
85

/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
86
#define RECURSIVE_TEXT N_("Subdirectory behaviour")
zorglub's avatar
zorglub committed
87
88
#define RECURSIVE_LONGTEXT N_( \
        "Select whether subdirectories must be expanded.\n" \
Carlo Calabrò's avatar
Carlo Calabrò committed
89
90
91
        "none: subdirectories do not appear in the playlist.\n" \
        "collapse: subdirectories appear but are expanded on first play.\n" \
        "expand: all subdirectories are expanded.\n" )
zorglub's avatar
zorglub committed
92
93
94
95

static char *psz_recursive_list[] = { "none", "collapse", "expand" };
static char *psz_recursive_list_text[] = { N_("none"), N_("collapse"),
                                           N_("expand") };
96
97

vlc_module_begin();
gbazin's avatar
   
gbazin committed
98
    set_description( _("Standard filesystem directory input") );
99
100
101
    set_capability( "access", 55 );
    add_shortcut( "directory" );
    add_shortcut( "dir" );
zorglub's avatar
zorglub committed
102
103
    add_string( "recursive", "expand" , NULL, RECURSIVE_TEXT,
                RECURSIVE_LONGTEXT, VLC_FALSE );
gbazin's avatar
gbazin committed
104
      change_string_list( psz_recursive_list, psz_recursive_list_text, 0 );
105
106
107
108
109
110
111
112
113
    set_callbacks( Open, Close );
vlc_module_end();


/*****************************************************************************
 * Open: open the directory
 *****************************************************************************/
static int Open( vlc_object_t *p_this )
{
gbazin's avatar
   
gbazin committed
114
115
116
    input_thread_t *            p_input = (input_thread_t *)p_this;
    char *                      psz_name;
    input_directory_t *         p_access_data;
zorglub's avatar
zorglub committed
117
118
    char *                      psz_mode;
    int                         i_mode;
119
#ifdef HAVE_SYS_STAT_H
gbazin's avatar
   
gbazin committed
120
    struct stat                 stat_info;
121
#endif
zorglub's avatar
zorglub committed
122

123
124
125
126
127
128
129
130
131
132
133
134
    /* Initialize access plug-in structures. */
    if( p_input->i_mtu == 0 )
    {
        /* Improve speed. */
        p_input->i_bufsize = INPUT_DEFAULT_BUFSIZE;
    }

    p_input->pf_read = Read;
    p_input->pf_set_program = NULL;
    p_input->pf_set_area = NULL;
    p_input->pf_seek = NULL;

gbazin's avatar
   
gbazin committed
135
136
137
    /* Remove the ending '/' char */
    psz_name = strdup( p_input->psz_name );
    if( psz_name == NULL )
138
        return VLC_EGENERIC;
gbazin's avatar
   
gbazin committed
139
140
141
142
143

    if( (psz_name[strlen(psz_name)-1] == '/') ||
        (psz_name[strlen(psz_name)-1] == '\\') )
    {
        psz_name[strlen(psz_name)-1] = '\0';
144
145
    }

gbazin's avatar
   
gbazin committed
146
147
148
149
150
151
#ifdef HAVE_SYS_STAT_H
    if( ( stat( psz_name, &stat_info ) == -1 ) ||
        !S_ISDIR( stat_info.st_mode ) )
#else
    if( !p_input->psz_access || strcmp(p_input->psz_access, "dir") )
#endif
152
    {
gbazin's avatar
   
gbazin committed
153
154
        free( psz_name );
        return VLC_EGENERIC;
155
156
    }

zorglub's avatar
zorglub committed
157
    /* Initialize structure */
158
    msg_Dbg( p_input, "opening directory `%s'", psz_name );
gbazin's avatar
   
gbazin committed
159
    p_access_data = malloc( sizeof(input_directory_t) );
160
161
162
163
    p_input->p_access_data = (void *)p_access_data;
    if( p_access_data == NULL )
    {
        msg_Err( p_input, "out of memory" );
gbazin's avatar
   
gbazin committed
164
        free( psz_name );
165
166
        return VLC_ENOMEM;
    }
zorglub's avatar
zorglub committed
167
    p_access_data->i_pos = 0;
gbazin's avatar
   
gbazin committed
168

zorglub's avatar
zorglub committed
169
    psz_mode = config_GetPsz( p_input , "recursive" );
170
    if( !psz_mode || !strncmp( psz_mode, "none" , 4 )  )
171
    {
zorglub's avatar
zorglub committed
172
173
174
175
176
177
178
179
180
181
        i_mode = MODE_NONE;
    }
    else if( !strncmp( psz_mode, "collapse", 8 )  )
    {
        i_mode = MODE_COLLAPSE;
    }
    else
    {
        i_mode = MODE_EXPAND;
    }
182

zorglub's avatar
zorglub committed
183
184
185
186
187
    if( ReadDir( p_input, psz_name , i_mode ) != VLC_SUCCESS )
    {
        free( p_access_data );
        free( psz_name );
        return VLC_EGENERIC;
188
    }
gbazin's avatar
   
gbazin committed
189

zorglub's avatar
zorglub committed
190
191
192
193
194
195
196
    msg_Dbg(p_input,"Directory read complete. Read %i bytes",
                    p_access_data->i_pos);

    p_access_data->p_dir_buffer[p_access_data->i_pos] = '\0';
    p_access_data->i_pos++;
    p_access_data->i_buf_length = p_access_data->i_pos;
    p_access_data->i_buf_pos = 0;
gbazin's avatar
   
gbazin committed
197
198
199
200

    /* Force m3u demuxer */
    p_input->psz_demux = "m3u";

201
202
203
204
205
206
207
208
209
    return VLC_SUCCESS;
}

/*****************************************************************************
 * Close: close the target
 *****************************************************************************/
static void Close( vlc_object_t * p_this )
{
    input_thread_t * p_input = (input_thread_t *)p_this;
gbazin's avatar
   
gbazin committed
210
211
    input_directory_t * p_access_data =
        (input_directory_t *)p_input->p_access_data;
212

zorglub's avatar
zorglub committed
213
    msg_Info( p_input, "closing `%s/%s://%s'",
214
215
216
217
218
219
220
221
222
223
              p_input->psz_access, p_input->psz_demux, p_input->psz_name );

    free( p_access_data );
}

/*****************************************************************************
 * Read: read directory and output to demux.
 *****************************************************************************/
static ssize_t Read( input_thread_t * p_input, byte_t * p_buffer, size_t i_len )
{
gbazin's avatar
   
gbazin committed
224
225
226
227
228
    input_directory_t * p_access_data =
        (input_directory_t *)p_input->p_access_data;
    unsigned int i_remaining = p_access_data->i_buf_length -
                               p_access_data->i_buf_pos;

229
230
    if( i_remaining > 0 )
    {
gbazin's avatar
   
gbazin committed
231
232
233
234
235
236
        int i_ret;

        i_ret = __MIN( i_len, i_remaining );
        memcpy( p_buffer,
                &p_access_data->p_dir_buffer[p_access_data->i_buf_pos],
                i_ret );
237
238
239
240
241
        p_access_data->i_buf_pos += i_ret;
        return (ssize_t) i_ret;
    }
    return 0;
}
zorglub's avatar
zorglub committed
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290

/* Local functions */

/*****************************************************************************
 * ReadDir: read a directory and add its content to the list
 *****************************************************************************/
int ReadDir( input_thread_t *p_input, char *psz_name , int i_mode )
{
    DIR *                       p_current_dir;
    struct dirent *             p_dir_content;

    input_directory_t * p_access_data =
        (input_directory_t *)p_input->p_access_data;

    /* have to cd into this dir */
    p_current_dir = opendir( psz_name );

    if( p_current_dir == NULL )
    {
        /* something went bad, get out of here ! */
#   ifdef HAVE_ERRNO_H
        msg_Warn( p_input, "cannot open directory `%s' (%s)",
                  psz_name, strerror(errno));
#   else
        msg_Warn( p_input, "cannot open directory `%s'", psz_name );
#   endif
        return VLC_EGENERIC;
    }

    p_dir_content = readdir( p_current_dir );

    /* while we still have entries in the directory */
    while( p_dir_content != NULL && p_access_data->i_pos < MAX_DIR_SIZE )
    {
        int i_size_entry = strlen( psz_name ) +
                           strlen( p_dir_content->d_name ) + 2;
        char *psz_entry = (char *)malloc( sizeof(char)*i_size_entry);

        sprintf( psz_entry, "%s/%s",psz_name,p_dir_content->d_name);

#if 0 /* Disable this message, it makes too much output */
        msg_Dbg( p_input, "Entry %s",psz_entry );
#endif

        /* if it is "." or "..", forget it */
        if( strcmp( p_dir_content->d_name, "." ) &&
            strcmp( p_dir_content->d_name, ".." ) &&
            p_access_data->i_pos + i_size_entry < MAX_DIR_SIZE )
        {
291
#if defined( S_ISDIR )
gbazin's avatar
gbazin committed
292
293
294
            struct stat stat_data;
            stat( psz_entry, &stat_data );
            if( S_ISDIR(stat_data.st_mode) )
295
296
#elif defined( DT_DIR )
            if( p_dir_content->d_type == DT_DIR )
gbazin's avatar
gbazin committed
297
298
299
#else
            if( 0 )
#endif
zorglub's avatar
zorglub committed
300
301
302
            {
                if( i_mode == MODE_NONE )
                {
303
                    msg_Dbg( p_input, "Skipping subdirectory %s", psz_entry );
zorglub's avatar
zorglub committed
304
305
306
307
308
                    p_dir_content = readdir( p_current_dir );
                    continue;
                }
                else if(i_mode == MODE_EXPAND )
                {
309
                    msg_Dbg(p_input, "Reading subdirectory %s", psz_entry );
zorglub's avatar
zorglub committed
310
311
312
313
314
315
316
317
                    if( ReadDir( p_input, psz_entry , MODE_EXPAND )
                                 != VLC_SUCCESS )
                    {
                        return VLC_EGENERIC;
                    }
                }
                else
                {
318
                    msg_Dbg(p_input, "Adding subdirectory %s", psz_entry );
zorglub's avatar
zorglub committed
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
                    sprintf( &p_access_data->p_dir_buffer[p_access_data->i_pos],
                             "%s", psz_entry );
                    p_access_data->i_pos += i_size_entry -1 ;
                    p_access_data->p_dir_buffer[p_access_data->i_pos] = '\n';
                    p_access_data->i_pos++;
                }
            }
            else
            {
                sprintf( &p_access_data->p_dir_buffer[p_access_data->i_pos],
                         "%s", psz_entry );
                p_access_data->i_pos += i_size_entry - 1;
                p_access_data->p_dir_buffer[p_access_data->i_pos] = '\n';
                p_access_data->i_pos++;
            }
        }
        free( psz_entry );
        p_dir_content = readdir( p_current_dir );
    }
    closedir( p_current_dir );
    return VLC_SUCCESS;
}