directory.c 9.38 KB
Newer Older
1 2 3
/*****************************************************************************
 * directory.c: expands a directory (directory: access plug-in)
 *****************************************************************************
4
 * Copyright (C) 2002-2004 VideoLAN
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.
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
 *****************************************************************************/
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 behavior")
78 79
#define RECURSIVE_LONGTEXT N_( \
        "Select whether subdirectories must be expanded.\n" \
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" )
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();
Gildas Bazin's avatar
 
Gildas Bazin committed
89
    set_description( _("Standard filesystem directory input") );
90 91 92
    set_capability( "access", 55 );
    add_shortcut( "directory" );
    add_shortcut( "dir" );
93 94
    add_string( "recursive", "expand" , NULL, RECURSIVE_TEXT,
                RECURSIVE_LONGTEXT, VLC_FALSE );
Gildas Bazin's avatar
Gildas Bazin 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 )
{
Gildas Bazin's avatar
 
Gildas Bazin committed
105
    input_thread_t *            p_input = (input_thread_t *)p_this;
106
#ifdef HAVE_SYS_STAT_H
Gildas Bazin's avatar
 
Gildas Bazin committed
107
    struct stat                 stat_info;
108
#endif
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;

Gildas Bazin's avatar
 
Gildas Bazin committed
122
#ifdef HAVE_SYS_STAT_H
123
    if( ( stat( p_input->psz_name, &stat_info ) == -1 ) ||
Gildas Bazin's avatar
 
Gildas Bazin 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
    {
129
        return VLC_EGENERIC;
130
    }
Gildas Bazin's avatar
 
Gildas Bazin committed
131

132 133
    /* Force a demux */
    p_input->psz_demux = "dummy";
Gildas Bazin's avatar
 
Gildas Bazin 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;

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;
Gildas Bazin's avatar
 
Gildas Bazin 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
        goto end;
202
    }
203 204

end:
205 206
    if( psz_name ) free( psz_name );
    if( psz_mode ) free( psz_mode );
207 208
    vlc_object_release( p_playlist );
    p_input->b_eof = 1;
209 210
    return 0;
}
211 212 213 214 215 216

/* Local functions */

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

222
    /* Open the dir */
223 224 225 226 227 228
    p_current_dir = opendir( psz_name );

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

237
    /* get the first directory entry */
238 239 240
    p_dir_content = readdir( p_current_dir );

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

247
        sprintf( psz_uri, "%s/%s", psz_name, p_dir_content->d_name );
248

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