playlist/directory: add missing return check
[vlc.git] / modules / demux / playlist / directory.c
1 /*****************************************************************************
2  * directory.c : Use access readdir to output folder content to playlist
3  *****************************************************************************
4  * Copyright (C) 2014 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Authors: Julien 'Lta' BALLET <contact # lta . io >
8  *
9  * This program is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #include <vlc_common.h>
32 #include <vlc_demux.h>
33
34 #include "playlist.h"
35
36 struct demux_sys_t
37 {
38     bool b_dir_sorted;
39     bool b_dir_can_loop;
40 };
41
42 /*****************************************************************************
43  * Local prototypes
44  *****************************************************************************/
45 static int Demux( demux_t *p_demux );
46
47
48 int Import_Dir ( vlc_object_t *p_this)
49 {
50     demux_t  *p_demux = (demux_t *)p_this;
51     bool b_dir_sorted, b_dir_can_loop;
52
53     if( stream_Control( p_demux->s, STREAM_IS_DIRECTORY,
54                         &b_dir_sorted, &b_dir_can_loop ) )
55         return VLC_EGENERIC;
56
57     STANDARD_DEMUX_INIT_MSG( "reading directory content" );
58     p_demux->p_sys->b_dir_sorted = b_dir_sorted;
59     p_demux->p_sys->b_dir_can_loop = b_dir_can_loop;
60
61     return VLC_SUCCESS;
62 }
63
64 void Close_Dir ( vlc_object_t *p_this )
65 {
66     demux_t *p_demux = (demux_t *)p_this;
67     free( p_demux->p_sys );
68 }
69
70 /**
71  * Does the provided URI/path/stuff has one of the extension provided ?
72  *
73  * \param psz_exts A comma separated list of extension without dot, or only
74  * one ext (ex: "avi,mkv,webm")
75  * \param psz_uri The uri/path to check (ex: "file:///home/foo/bar.avi"). If
76  * providing an URI, it must not contain a query string.
77  *
78  * \return true if the uri/path has one of the provided extension
79  * false otherwise.
80  */
81 static bool has_ext( const char *psz_exts, const char *psz_uri )
82 {
83     if( psz_exts == NULL )
84         return false;
85
86     const char *ext = strrchr( psz_uri, '.' );
87     if( ext == NULL )
88         return false;
89
90     size_t extlen = strlen( ++ext );
91
92     for( const char *type = psz_exts, *end; type[0]; type = end + 1 )
93     {
94         end = strchr( type, ',' );
95         if( end == NULL )
96             end = type + strlen( type );
97
98         if( type + extlen == end && !strncasecmp( ext, type, extlen ) )
99             return true;
100
101         if( *end == '\0' )
102             break;
103     }
104
105     return false;
106 }
107
108 static int compar_type( input_item_t *p1, input_item_t *p2 )
109 {
110     if( p1->i_type != p2->i_type )
111     {
112         if( p1->i_type == ITEM_TYPE_DIRECTORY )
113             return -1;
114         if( p2->i_type == ITEM_TYPE_DIRECTORY )
115             return 1;
116     }
117     return 0;
118 }
119
120 static int compar_collate( input_item_t *p1, input_item_t *p2 )
121 {
122     int i_ret = compar_type( p1, p2 );
123
124     if( i_ret != 0 )
125         return i_ret;
126
127 #ifdef HAVE_STRCOLL
128     /* The program's LOCAL defines if case is ignored */
129     return strcoll( p1->psz_name, p2->psz_name );
130 #else
131     return strcasecmp( p1->psz_name, p2->psz_name );
132 #endif
133 }
134
135 static int compar_version( input_item_t *p1, input_item_t *p2 )
136 {
137     int i_ret = compar_type( p1, p2 );
138
139     if( i_ret != 0 )
140         return i_ret;
141
142     return strverscmp( p1->psz_name, p2->psz_name );
143 }
144
145 static int Demux( demux_t *p_demux )
146 {
147     int i_ret = VLC_SUCCESS;
148     input_item_t *p_input;
149     input_item_node_t *p_node;
150     input_item_t *p_item;
151     char *psz_ignored_exts;
152     bool b_show_hiddenfiles;
153
154     p_input = GetCurrentItem( p_demux );
155     p_node = input_item_node_Create( p_input );
156     if( p_node == NULL )
157     {
158         input_item_Release( p_input );
159         return VLC_ENOMEM;
160     }
161     p_node->b_can_loop = p_demux->p_sys->b_dir_can_loop;
162     input_item_Release(p_input);
163
164     b_show_hiddenfiles = var_InheritBool( p_demux, "show-hiddenfiles" );
165     psz_ignored_exts = var_InheritString( p_demux, "ignore-filetypes" );
166
167     while( !i_ret && ( p_item = stream_ReadDir( p_demux->s ) ) )
168     {
169         int i_name_len = p_item->psz_name ? strlen( p_item->psz_name ) : 0;
170
171         /* skip null, "." and ".." and hidden files if option is activated */
172         if( !i_name_len || strcmp( p_item->psz_name, "." ) == 0
173          || strcmp( p_item->psz_name, ".." ) == 0
174          || ( !b_show_hiddenfiles && p_item->psz_name[0] == '.' ) )
175             goto skip_item;
176         /* skip ignored files */
177         if( has_ext( psz_ignored_exts, p_item->psz_name ) )
178             goto skip_item;
179
180         input_item_CopyOptions( p_node->p_item, p_item );
181         if( !input_item_node_AppendItem( p_node, p_item ) )
182             i_ret = VLC_ENOMEM;
183 skip_item:
184         input_item_Release( p_item );
185     }
186     free( psz_ignored_exts );
187
188     if( i_ret )
189     {
190         msg_Warn( p_demux, "unable to read directory" );
191         input_item_node_Delete( p_node );
192         return i_ret;
193     }
194
195     if( !p_demux->p_sys->b_dir_sorted )
196     {
197         input_item_compar_cb compar_cb = NULL;
198         char *psz_sort = var_InheritString( p_demux, "directory-sort" );
199
200         if( psz_sort )
201         {
202             if( !strcasecmp( psz_sort, "version" ) )
203                 compar_cb = compar_version;
204             else if( strcasecmp( psz_sort, "none" ) )
205                 compar_cb = compar_collate;
206         }
207         free( psz_sort );
208         if( compar_cb )
209             input_item_node_Sort( p_node, compar_cb );
210     }
211
212     input_item_node_PostAndDelete( p_node );
213     return VLC_SUCCESS;
214 }