Main Page | Modules | Class Hierarchy | Class List | Directories | File List | Class Members | File Members | Related Pages

directory.c

00001 /*****************************************************************************
00002  * directory.c: expands a directory (directory: access plug-in)
00003  *****************************************************************************
00004  * Copyright (C) 2002-2004 the VideoLAN team
00005  * $Id: directory.c 12869 2005-10-17 01:14:04Z hartman $
00006  *
00007  * Authors: Derk-Jan Hartman <hartman at videolan dot org>
00008  *
00009  * This program is free software; you can redistribute it and/or modify
00010  * it under the terms of the GNU General Public License as published by
00011  * the Free Software Foundation; either version 2 of the License, or
00012  * (at your option) any later version.
00013  *
00014  * This program is distributed in the hope that it will be useful,
00015  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00016  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017  * GNU General Public License for more details.
00018  *
00019  * You should have received a copy of the GNU General Public License
00020  * along with this program; if not, write to the Free Software
00021  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
00022  *****************************************************************************/
00023 
00024 /*****************************************************************************
00025  * Preamble
00026  *****************************************************************************/
00027 
00028 #include <vlc/vlc.h>
00029 #include <vlc/input.h>
00030 #include <vlc_playlist.h>
00031 
00032 #include <stdlib.h>
00033 #include <string.h>
00034 #ifdef HAVE_SYS_TYPES_H
00035 #   include <sys/types.h>
00036 #endif
00037 #ifdef HAVE_SYS_STAT_H
00038 #   include <sys/stat.h>
00039 #endif
00040 #ifdef HAVE_ERRNO_H
00041 #   include <errno.h>
00042 #endif
00043 #ifdef HAVE_FCNTL_H
00044 #   include <fcntl.h>
00045 #endif
00046 
00047 #ifdef HAVE_UNISTD_H
00048 #   include <unistd.h>
00049 #elif defined( WIN32 ) && !defined( UNDER_CE )
00050 #   include <io.h>
00051 #elif defined( UNDER_CE )
00052 #   define strcoll strcmp
00053 #endif
00054 
00055 #ifdef HAVE_DIRENT_H
00056 #   include <dirent.h>
00057 #endif
00058 
00059 #include "charset.h"
00060 
00061 /*****************************************************************************
00062  * Module descriptor
00063  *****************************************************************************/
00064 static int  Open ( vlc_object_t * );
00065 static void Close( vlc_object_t * );
00066 
00067 static int  DemuxOpen ( vlc_object_t * );
00068 
00069 #define RECURSIVE_TEXT N_("Subdirectory behavior")
00070 #define RECURSIVE_LONGTEXT N_( \
00071         "Select whether subdirectories must be expanded.\n" \
00072         "none: subdirectories do not appear in the playlist.\n" \
00073         "collapse: subdirectories appear but are expanded on first play.\n" \
00074         "expand: all subdirectories are expanded.\n" )
00075 
00076 static char *psz_recursive_list[] = { "none", "collapse", "expand" };
00077 static char *psz_recursive_list_text[] = { N_("none"), N_("collapse"),
00078                                            N_("expand") };
00079 
00080 #define IGNORE_TEXT N_("Ignore files with these extensions")
00081 #define IGNORE_LONGTEXT N_( \
00082         "Specify a comma seperated list of file extensions. " \
00083         "Files with these extensions will not be added to playlist when opening a directory. " \
00084         "This is useful if you add directories that contain mp3 albums for instance." )
00085 
00086 vlc_module_begin();
00087     set_category( CAT_INPUT );
00088     set_shortname( _("Directory" ) );
00089     set_subcategory( SUBCAT_INPUT_ACCESS );
00090     set_description( _("Standard filesystem directory input") );
00091     set_capability( "access2", 55 );
00092     add_shortcut( "directory" );
00093     add_shortcut( "dir" );
00094     add_string( "recursive", "expand" , NULL, RECURSIVE_TEXT,
00095                 RECURSIVE_LONGTEXT, VLC_FALSE );
00096       change_string_list( psz_recursive_list, psz_recursive_list_text, 0 );
00097     add_string( "ignore-filetypes", "m3u,db,nfo,jpg,gif,sfv,txt,sub,idx,srt,cue",
00098                 NULL, IGNORE_TEXT, IGNORE_LONGTEXT, VLC_FALSE );
00099     set_callbacks( Open, Close );
00100 
00101     add_submodule();
00102         set_description( "Directory EOF");
00103         set_capability( "demux2", 0 );
00104         add_shortcut( "directory" );
00105         set_callbacks( DemuxOpen, NULL );
00106 vlc_module_end();
00107 
00108 
00109 /*****************************************************************************
00110  * Local prototypes, constants, structures
00111  *****************************************************************************/
00112 
00113 #define MODE_EXPAND 0
00114 #define MODE_COLLAPSE 1
00115 #define MODE_NONE 2
00116 
00117 static int Read( access_t *, uint8_t *, int );
00118 static int ReadNull( access_t *, uint8_t *, int );
00119 static int Control( access_t *, int, va_list );
00120 
00121 static int Demux( demux_t *p_demux );
00122 static int DemuxControl( demux_t *p_demux, int i_query, va_list args );
00123 
00124 
00125 static int ReadDir( playlist_t *, char *psz_name, int i_mode, int *pi_pos,
00126                     playlist_item_t * );
00127 
00128 /*****************************************************************************
00129  * Open: open the directory
00130  *****************************************************************************/
00131 static int Open( vlc_object_t *p_this )
00132 {
00133     access_t *p_access = (access_t*)p_this;
00134 
00135 #ifdef HAVE_SYS_STAT_H
00136     struct stat stat_info;
00137     char *psz_path = ToLocale( p_access->psz_path );
00138 
00139     if( ( stat( psz_path, &stat_info ) == -1 ) ||
00140         !S_ISDIR( stat_info.st_mode ) )
00141 #elif defined(WIN32)
00142     int i_ret;
00143 
00144 #   ifdef UNICODE
00145     wchar_t psz_path[MAX_PATH];
00146     mbstowcs( psz_path, p_access->psz_path, MAX_PATH );
00147     psz_path[MAX_PATH-1] = 0;
00148 #   else
00149     char *psz_path = p_access->psz_path;
00150 #   endif /* UNICODE */
00151 
00152     i_ret = GetFileAttributes( psz_path );
00153     if( i_ret == -1 || !(i_ret & FILE_ATTRIBUTE_DIRECTORY) )
00154 
00155 #else
00156     if( strcmp( p_access->psz_access, "dir") &&
00157         strcmp( p_access->psz_access, "directory") )
00158 #endif
00159     {
00160         LocaleFree( psz_path );
00161         return VLC_EGENERIC;
00162     }
00163 
00164     LocaleFree( psz_path );
00165     p_access->pf_read  = Read;
00166     p_access->pf_block = NULL;
00167     p_access->pf_seek  = NULL;
00168     p_access->pf_control= Control;
00169 
00170     /* Force a demux */
00171     p_access->psz_demux = strdup( "directory" );
00172 
00173     return VLC_SUCCESS;
00174 }
00175 
00176 /*****************************************************************************
00177  * Close: close the target
00178  *****************************************************************************/
00179 static void Close( vlc_object_t * p_this )
00180 {
00181 }
00182 
00183 /*****************************************************************************
00184  * ReadNull: read the directory
00185  *****************************************************************************/
00186 static int ReadNull( access_t *p_access, uint8_t *p_buffer, int i_len)
00187 {
00188     /* Return fake data */
00189     memset( p_buffer, 0, i_len );
00190     return i_len;
00191 }
00192 
00193 /*****************************************************************************
00194  * Read: read the directory
00195  *****************************************************************************/
00196 static int Read( access_t *p_access, uint8_t *p_buffer, int i_len)
00197 {
00198     char *psz_name = NULL;
00199     char *psz;
00200     int  i_mode, i_pos;
00201 
00202     playlist_item_t *p_item;
00203     vlc_bool_t b_play = VLC_FALSE;
00204 
00205     playlist_t *p_playlist =
00206         (playlist_t *) vlc_object_find( p_access,
00207                                         VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
00208 
00209     if( !p_playlist )
00210     {
00211         msg_Err( p_access, "can't find playlist" );
00212         goto end;
00213     }
00214     else
00215     {
00216         char *ptr;
00217 
00218         psz_name = ToLocale( p_access->psz_path );
00219         ptr = strdup( psz_name );
00220         LocaleFree( psz_name );
00221         if( ptr == NULL )
00222             goto end;
00223 
00224         psz_name = ptr;
00225 
00226         /* Remove the ending '/' char */
00227         ptr += strlen( ptr );
00228         if( ( ptr > psz_name ) )
00229         {
00230             switch( *--ptr )
00231             {
00232                 case '/':
00233                 case '\\':
00234                     *ptr = '\0';
00235             }
00236         }
00237     }
00238 
00239     /* Initialize structure */
00240     psz = var_CreateGetString( p_access, "recursive" );
00241     if( *psz == '\0' || !strncmp( psz, "none" , 4 )  )
00242     {
00243         i_mode = MODE_NONE;
00244     }
00245     else if( !strncmp( psz, "collapse", 8 )  )
00246     {
00247         i_mode = MODE_COLLAPSE;
00248     }
00249     else
00250     {
00251         i_mode = MODE_EXPAND;
00252     }
00253     free( psz );
00254 
00255     /* Make sure we are deleted when we are done */
00256     /* The playlist position we will use for the add */
00257     i_pos = p_playlist->i_index + 1;
00258 
00259     msg_Dbg( p_access, "opening directory `%s'", p_access->psz_path );
00260 
00261     if( &p_playlist->status.p_item->input ==
00262         ((input_thread_t *)p_access->p_parent)->input.p_item )
00263     {
00264         p_item = p_playlist->status.p_item;
00265         b_play = VLC_TRUE;
00266         msg_Dbg( p_access, "starting directory playback");
00267     }
00268     else
00269     {
00270         input_item_t *p_current = ( (input_thread_t*)p_access->p_parent)->
00271                                                         input.p_item;
00272         p_item = playlist_LockItemGetByInput( p_playlist, p_current );
00273         msg_Dbg( p_access, "not starting directory playback");
00274         if( !p_item )
00275         {
00276             msg_Dbg( p_playlist, "unable to find item in playlist");
00277             return -1;
00278         }
00279         b_play = VLC_FALSE;
00280     }
00281 
00282     p_item->input.i_type = ITEM_TYPE_DIRECTORY;
00283     if( ReadDir( p_playlist, psz_name , i_mode, &i_pos,
00284                  p_item ) != VLC_SUCCESS )
00285     {
00286     }
00287 end:
00288 
00289     /* Begin to read the directory */
00290     if( b_play )
00291     {
00292         playlist_Control( p_playlist, PLAYLIST_VIEWPLAY,
00293                           p_playlist->status.i_view,
00294                           p_playlist->status.p_item, NULL );
00295     }
00296     if( psz_name ) free( psz_name );
00297     vlc_object_release( p_playlist );
00298 
00299     /* Return fake data forever */
00300     p_access->pf_read = ReadNull;
00301     return ReadNull( p_access, p_buffer, i_len );
00302 }
00303 
00304 /*****************************************************************************
00305  * DemuxOpen:
00306  *****************************************************************************/
00307 static int Control( access_t *p_access, int i_query, va_list args )
00308 {
00309     vlc_bool_t   *pb_bool;
00310     int          *pi_int;
00311     int64_t      *pi_64;
00312 
00313     switch( i_query )
00314     {
00315         /* */
00316         case ACCESS_CAN_SEEK:
00317         case ACCESS_CAN_FASTSEEK:
00318         case ACCESS_CAN_PAUSE:
00319         case ACCESS_CAN_CONTROL_PACE:
00320             pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
00321             *pb_bool = VLC_FALSE;    /* FIXME */
00322             break;
00323 
00324         /* */
00325         case ACCESS_GET_MTU:
00326             pi_int = (int*)va_arg( args, int * );
00327             *pi_int = 0;
00328             break;
00329 
00330         case ACCESS_GET_PTS_DELAY:
00331             pi_64 = (int64_t*)va_arg( args, int64_t * );
00332             *pi_64 = DEFAULT_PTS_DELAY * 1000;
00333             break;
00334 
00335         /* */
00336         case ACCESS_SET_PAUSE_STATE:
00337         case ACCESS_GET_TITLE_INFO:
00338         case ACCESS_SET_TITLE:
00339         case ACCESS_SET_SEEKPOINT:
00340         case ACCESS_SET_PRIVATE_ID_STATE:
00341             return VLC_EGENERIC;
00342 
00343         default:
00344             msg_Warn( p_access, "unimplemented query in control" );
00345             return VLC_EGENERIC;
00346     }
00347     return VLC_SUCCESS;
00348 }
00349 
00350 /*****************************************************************************
00351  * DemuxOpen:
00352  *****************************************************************************/
00353 static int DemuxOpen ( vlc_object_t *p_this )
00354 {
00355     demux_t *p_demux = (demux_t*)p_this;
00356 
00357     if( strcmp( p_demux->psz_demux, "directory" ) )
00358         return VLC_EGENERIC;
00359 
00360     p_demux->pf_demux   = Demux;
00361     p_demux->pf_control = DemuxControl;
00362     return VLC_SUCCESS;
00363 }
00364 
00365 /*****************************************************************************
00366  * Demux: EOF
00367  *****************************************************************************/
00368 static int Demux( demux_t *p_demux )
00369 {
00370     return 0;
00371 }
00372 /*****************************************************************************
00373  * DemuxControl:
00374  *****************************************************************************/
00375 static int DemuxControl( demux_t *p_demux, int i_query, va_list args )
00376 {
00377     return demux2_vaControlHelper( p_demux->s, 0, 0, 0, 1, i_query, args );
00378 }
00379 
00380 static int Filter( const struct dirent *foo )
00381 {
00382     return VLC_TRUE;
00383 }
00384 /*****************************************************************************
00385  * ReadDir: read a directory and add its content to the list
00386  *****************************************************************************/
00387 static int ReadDir( playlist_t *p_playlist,
00388                     char *psz_name, int i_mode, int *pi_position,
00389                     playlist_item_t *p_parent )
00390 {
00391     struct dirent   **pp_dir_content;
00392     int             i_dir_content, i;
00393     playlist_item_t *p_node;
00394 
00395     /* Build array with ignores */
00396     char **ppsz_extensions = 0;
00397     int i_extensions = 0;
00398     char *psz_ignore = var_CreateGetString( p_playlist, "ignore-filetypes" );
00399     if( psz_ignore && *psz_ignore )
00400     {
00401         char *psz_parser = psz_ignore;
00402         int a;
00403 
00404         for( a = 0; psz_parser[a] != '\0'; a++ )
00405         {
00406             if( psz_parser[a] == ',' ) i_extensions++;
00407         }
00408 
00409         ppsz_extensions = (char **)malloc( sizeof( char * ) * i_extensions );
00410 
00411         for( a = 0; a < i_extensions; a++ )
00412         {
00413             int b;
00414             char *tmp;
00415             
00416             while( psz_parser[0] != '\0' && psz_parser[0] == ' ' ) psz_parser++;
00417             for( b = 0; psz_parser[b] != '\0'; b++ )
00418             {
00419                 if( psz_parser[b] == ',' ) break;
00420             }
00421             tmp = malloc( b + 1 );
00422             strncpy( tmp, psz_parser, b );
00423             tmp[b] = 0;
00424             ppsz_extensions[a] = tmp;
00425             psz_parser += b+1;
00426         }
00427     }
00428 
00429     /* Change the item to a node */
00430     if( p_parent->i_children == -1 )
00431     {
00432         playlist_LockItemToNode( p_playlist,p_parent );
00433     }
00434 
00435     /* get the first directory entry */
00436     i_dir_content = scandir( psz_name, &pp_dir_content, Filter, alphasort );
00437     if( i_dir_content == -1 )
00438     {
00439         msg_Warn( p_playlist, "Failed to read directory" );
00440         return VLC_EGENERIC;
00441     }
00442     else if( i_dir_content <= 0 )
00443     {
00444         /* directory is empty */
00445         return VLC_SUCCESS;
00446     }
00447 
00448     /* While we still have entries in the directory */
00449     for( i = 0; i < i_dir_content; i++ )
00450     {
00451         struct dirent *p_dir_content = pp_dir_content[i];
00452         int i_size_entry = strlen( psz_name ) +
00453                            strlen( p_dir_content->d_name ) + 2;
00454         char *psz_uri = (char *)malloc( sizeof(char) * i_size_entry );
00455 
00456         sprintf( psz_uri, "%s/%s", psz_name, p_dir_content->d_name );
00457 
00458         /* if it starts with '.' then forget it */
00459         if( p_dir_content->d_name[0] != '.' )
00460         {
00461 #if defined( S_ISDIR )
00462             struct stat stat_data;
00463 
00464             if( !stat( psz_uri, &stat_data )
00465              && S_ISDIR(stat_data.st_mode) && i_mode != MODE_COLLAPSE )
00466 #elif defined( DT_DIR )
00467             if( ( p_dir_content->d_type & DT_DIR ) && i_mode != MODE_COLLAPSE )
00468 #else
00469             if( 0 )
00470 #endif
00471             {
00472                 if( i_mode == MODE_NONE )
00473                 {
00474                     msg_Dbg( p_playlist, "Skipping subdirectory %s", psz_uri );
00475                     free( psz_uri );
00476                     continue;
00477                 }
00478                 else if( i_mode == MODE_EXPAND )
00479                 {
00480                     char *psz_newname, *psz_tmp;
00481                     msg_Dbg(p_playlist, "Reading subdirectory %s", psz_uri );
00482 
00483                     psz_tmp = FromLocale( p_dir_content->d_name );
00484                     psz_newname = vlc_fix_readdir_charset(
00485                                                 p_playlist, psz_tmp );
00486                     LocaleFree( psz_tmp );
00487 
00488                     p_node = playlist_NodeCreate( p_playlist,
00489                                        p_parent->pp_parents[0]->i_view,
00490                                        psz_newname, p_parent );
00491 
00492                     playlist_CopyParents(  p_parent, p_node );
00493 
00494                     p_node->input.i_type = ITEM_TYPE_DIRECTORY;
00495 
00496                     if( ReadDir( p_playlist, psz_uri , MODE_EXPAND,
00497                                  pi_position, p_node ) != VLC_SUCCESS )
00498                     {
00499                         return VLC_EGENERIC;
00500                     }
00501 
00502                     /* an strdup() just because of Mac OS X */
00503                     free( psz_newname );
00504                 }
00505             }
00506             else
00507             {
00508                 playlist_item_t *p_item;
00509                 char *psz_tmp1, *psz_tmp2, *psz_loc;
00510 
00511                 if( i_extensions > 0 )
00512                 {
00513                     char *psz_dot = strrchr( p_dir_content->d_name, '.' );
00514                     if( psz_dot++ && *psz_dot )
00515                     {
00516                         int a;
00517                         for( a = 0; a < i_extensions; a++ )
00518                         {
00519                             if( !strcmp( psz_dot, ppsz_extensions[a] ) )
00520                                 break;
00521                         }
00522                         if( a < i_extensions )
00523                         {
00524                             msg_Dbg( p_playlist, "Ignoring file %s", psz_uri );
00525                             free( psz_uri );
00526                             continue;
00527                         }
00528                     }
00529                 }
00530 
00531                 psz_loc = FromLocale( psz_uri );
00532                 psz_tmp1 = vlc_fix_readdir_charset( VLC_OBJECT(p_playlist),
00533                                                     psz_loc );
00534                 LocaleFree( psz_loc );
00535 
00536                 psz_loc = FromLocale( p_dir_content->d_name );
00537                 psz_tmp2 = vlc_fix_readdir_charset( VLC_OBJECT(p_playlist),
00538                                                     psz_loc );
00539                 LocaleFree( psz_loc );
00540 
00541                 p_item = playlist_ItemNewWithType( VLC_OBJECT(p_playlist),
00542                         psz_tmp1, psz_tmp2, ITEM_TYPE_VFILE );
00543                 playlist_NodeAddItem( p_playlist,p_item,
00544                                       p_parent->pp_parents[0]->i_view,
00545                                       p_parent,
00546                                       PLAYLIST_APPEND, PLAYLIST_END );
00547 
00548                 playlist_CopyParents( p_parent, p_item );
00549             }
00550         }
00551         free( psz_uri );
00552     }
00553 
00554     for( i = 0; i < i_extensions; i++ )
00555     {
00556         if( ppsz_extensions[i] )
00557             free( ppsz_extensions[i] );
00558     }
00559     if( ppsz_extensions ) free( ppsz_extensions );
00560     if( psz_ignore ) free( psz_ignore );
00561 
00562     free( pp_dir_content );
00563     return VLC_SUCCESS;
00564 }

Generated on Tue Dec 20 10:14:23 2005 for vlc-0.8.4a by  doxygen 1.4.2