directory.c 9.32 KB
Newer Older
1
2
3
/*****************************************************************************
 * directory.c: expands a directory (directory: access plug-in)
 *****************************************************************************
4
 * Copyright (C) 2002-2004 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
 * 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
 *****************************************************************************/
27

28
29
#include <vlc/vlc.h>
#include <vlc/input.h>
30
#include <vlc_playlist.h>
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
59
60

#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
61
62
63
#define MODE_EXPAND 0
#define MODE_COLLAPSE 1
#define MODE_NONE 2
64
65
66
67
68
69
70
71

/*****************************************************************************
 * 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 );
72
int ReadDir( playlist_t *p_playlist, char *psz_name , int i_mode, int *pi_pos );
73
74
75
76

/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
77
#define RECURSIVE_TEXT N_("Subdirectory behaviour")
zorglub's avatar
zorglub committed
78
79
#define RECURSIVE_LONGTEXT N_( \
        "Select whether subdirectories must be expanded.\n" \
Carlo Calabrò's avatar
Carlo Calabrò committed
80
81
82
        "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
83
84
85
86

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

vlc_module_begin();
gbazin's avatar
   
gbazin committed
89
    set_description( _("Standard filesystem directory input") );
90
91
92
    set_capability( "access", 55 );
    add_shortcut( "directory" );
    add_shortcut( "dir" );
zorglub's avatar
zorglub committed
93
94
    add_string( "recursive", "expand" , NULL, RECURSIVE_TEXT,
                RECURSIVE_LONGTEXT, VLC_FALSE );
gbazin's avatar
gbazin committed
95
      change_string_list( psz_recursive_list, psz_recursive_list_text, 0 );
96
97
98
99
100
101
102
103
104
    set_callbacks( Open, Close );
vlc_module_end();


/*****************************************************************************
 * Open: open the directory
 *****************************************************************************/
static int Open( vlc_object_t *p_this )
{
gbazin's avatar
   
gbazin committed
105
    input_thread_t *            p_input = (input_thread_t *)p_this;
106
#ifdef HAVE_SYS_STAT_H
gbazin's avatar
   
gbazin committed
107
    struct stat                 stat_info;
108
#endif
zorglub's avatar
zorglub committed
109

110
111
112
113
114
115
116
117
118
119
120
121
    /* 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
122
#ifdef HAVE_SYS_STAT_H
123
    if( ( stat( p_input->psz_name, &stat_info ) == -1 ) ||
gbazin's avatar
   
gbazin committed
124
125
126
127
        !S_ISDIR( stat_info.st_mode ) )
#else
    if( !p_input->psz_access || strcmp(p_input->psz_access, "dir") )
#endif
128
    {
zorglub's avatar
zorglub committed
129
        return VLC_EGENERIC;
130
    }
gbazin's avatar
   
gbazin committed
131

132
133
    /* Force a demux */
    p_input->psz_demux = "dummy";
gbazin's avatar
   
gbazin committed
134

135
136
137
138
139
140
141
142
143
144
    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;

zorglub's avatar
zorglub committed
145
    msg_Info( p_input, "closing `%s/%s://%s'",
146
147
148
149
              p_input->psz_access, p_input->psz_demux, p_input->psz_name );
}

/*****************************************************************************
150
 * Read: read the directory
151
152
153
 *****************************************************************************/
static ssize_t Read( input_thread_t * p_input, byte_t * p_buffer, size_t i_len )
{
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
    char *                      psz_name;
    char *                      psz_mode;
    int                         i_mode, i_pos;

    playlist_t * p_playlist = (playlist_t *) vlc_object_find(
                        p_input, VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );

    if( !p_playlist )
    {
        msg_Err( p_input, "can't find playlist" );
        goto end;
    }
    
    /* Remove the ending '/' char */
    psz_name = strdup( p_input->psz_name );
    if( psz_name == NULL )
        goto end;

    if( (psz_name[strlen(psz_name)-1] == '/') ||
        (psz_name[strlen(psz_name)-1] == '\\') )
    {
        psz_name[strlen(psz_name)-1] = '\0';
    }
    
    /* Initialize structure */
    psz_mode = config_GetPsz( p_input , "recursive" );
    if( !psz_mode || !strncmp( psz_mode, "none" , 4 )  )
    {
        i_mode = MODE_NONE;
    }
    else if( !strncmp( psz_mode, "collapse", 8 )  )
    {
        i_mode = MODE_COLLAPSE;
    }
    else
    {
        i_mode = MODE_EXPAND;
    }
    
    /* Make sure we are deleted when we are done */
    p_playlist->pp_items[p_playlist->i_index]->b_autodeletion = VLC_TRUE;
    /* The playlist position we will use for the add */
    i_pos = p_playlist->i_index + 1;
gbazin's avatar
   
gbazin committed
197

198
199
    msg_Dbg( p_input, "opening directory `%s'", psz_name );
    if( ReadDir( p_playlist, psz_name , i_mode, &i_pos ) != VLC_SUCCESS )
200
    {
201
202
        free( psz_name );
        goto end;
203
    }
204
205
206
207

end:
    vlc_object_release( p_playlist );
    p_input->b_eof = 1;
208
209
    return 0;
}
zorglub's avatar
zorglub committed
210
211
212
213
214
215

/* Local functions */

/*****************************************************************************
 * ReadDir: read a directory and add its content to the list
 *****************************************************************************/
216
int ReadDir( playlist_t *p_playlist, char *psz_name , int i_mode, int *pi_position )
zorglub's avatar
zorglub committed
217
218
219
220
{
    DIR *                       p_current_dir;
    struct dirent *             p_dir_content;

221
    /* Open the dir */
zorglub's avatar
zorglub committed
222
223
224
225
226
227
    p_current_dir = opendir( psz_name );

    if( p_current_dir == NULL )
    {
        /* something went bad, get out of here ! */
#   ifdef HAVE_ERRNO_H
228
        msg_Warn( p_playlist, "cannot open directory `%s' (%s)",
zorglub's avatar
zorglub committed
229
230
                  psz_name, strerror(errno));
#   else
231
        msg_Warn( p_playlist, "cannot open directory `%s'", psz_name );
zorglub's avatar
zorglub committed
232
233
234
235
#   endif
        return VLC_EGENERIC;
    }

236
    /* get the first directory entry */
zorglub's avatar
zorglub committed
237
238
239
    p_dir_content = readdir( p_current_dir );

    /* while we still have entries in the directory */
240
    while( p_dir_content != NULL )
zorglub's avatar
zorglub committed
241
242
    {
        int i_size_entry = strlen( psz_name ) +
243
244
                           p_dir_content->d_namlen + 2;
        char *psz_uri = (char *)malloc( sizeof(char)*i_size_entry);
zorglub's avatar
zorglub committed
245

246
        sprintf( psz_uri, "%s/%s", psz_name, p_dir_content->d_name );
zorglub's avatar
zorglub committed
247

248
249
        /* if it starts with '.' then forget it */
        if( p_dir_content->d_name[0] != '.' )
zorglub's avatar
zorglub committed
250
        {
251
#if defined( S_ISDIR )
gbazin's avatar
gbazin committed
252
            struct stat stat_data;
253
254
            stat( psz_uri, &stat_data );
            if( S_ISDIR(stat_data.st_mode) && i_mode != MODE_COLLAPSE )
255
#elif defined( DT_DIR )
256
            if( p_dir_content->d_type == DT_DIR && i_mode != MODE_COLLAPSE )
gbazin's avatar
gbazin committed
257
258
259
#else
            if( 0 )
#endif
zorglub's avatar
zorglub committed
260
261
262
            {
                if( i_mode == MODE_NONE )
                {
263
                    msg_Dbg( p_playlist, "Skipping subdirectory %s", psz_uri );
zorglub's avatar
zorglub committed
264
265
266
267
268
                    p_dir_content = readdir( p_current_dir );
                    continue;
                }
                else if(i_mode == MODE_EXPAND )
                {
269
270
                    msg_Dbg(p_playlist, "Reading subdirectory %s", psz_uri );
                    if( ReadDir( p_playlist, psz_uri , MODE_EXPAND, pi_position )
zorglub's avatar
zorglub committed
271
272
273
274
275
276
277
278
                                 != VLC_SUCCESS )
                    {
                        return VLC_EGENERIC;
                    }
                }
            }
            else
            {
279
280
281
                playlist_Add( p_playlist, psz_uri, p_dir_content->d_name,
                          PLAYLIST_INSERT, *pi_position );
                (*pi_position)++;
zorglub's avatar
zorglub committed
282
283
            }
        }
284
        free( psz_uri );
zorglub's avatar
zorglub committed
285
286
287
288
289
        p_dir_content = readdir( p_current_dir );
    }
    closedir( p_current_dir );
    return VLC_SUCCESS;
}