]> git.sesse.net Git - vlc/blob - modules/access/file.c
Fix stat() when local charset is not UTF-8
[vlc] / modules / access / file.c
1 /*****************************************************************************
2  * file.c: file input (file: access plug-in)
3  *****************************************************************************
4  * Copyright (C) 2001-2004 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Christophe Massiot <massiot@via.ecp.fr>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 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 General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <vlc/vlc.h>
28 #include <vlc/input.h>
29
30 #include <stdlib.h>
31 #include <string.h>
32 #include <errno.h>
33 #ifdef HAVE_SYS_TYPES_H
34 #   include <sys/types.h>
35 #endif
36 #ifdef HAVE_SYS_TIME_H
37 #   include <sys/time.h>
38 #endif
39 #ifdef HAVE_SYS_STAT_H
40 #   include <sys/stat.h>
41 #endif
42 #ifdef HAVE_FCNTL_H
43 #   include <fcntl.h>
44 #endif
45
46 #ifdef HAVE_UNISTD_H
47 #   include <unistd.h>
48 #elif defined( WIN32 ) && !defined( UNDER_CE )
49 #   include <io.h>
50 #endif
51
52 #if defined( WIN32 ) && !defined( UNDER_CE )
53 /* stat() support for large files on win32 */
54 #   define stat _stati64
55 #   define fstat(a,b) _fstati64(a,b)
56 #   ifdef lseek
57 #      undef lseek
58 #   endif
59 #   define lseek _lseeki64
60 #elif defined( UNDER_CE )
61 #   ifdef read
62 #      undef read
63 #   endif
64 #   define read(a,b,c) fread(b,1,c,a)
65 #   define close(a) fclose(a)
66 #   ifdef lseek
67 #      undef lseek
68 #   endif
69 #   define lseek fseek
70 #endif
71
72 #include "charset.h"
73
74 /*****************************************************************************
75  * Module descriptor
76  *****************************************************************************/
77 static int  Open ( vlc_object_t * );
78 static void Close( vlc_object_t * );
79
80 #define CACHING_TEXT N_("Caching value in ms")
81 #define CACHING_LONGTEXT N_( \
82     "Allows you to modify the default caching value for file streams. This " \
83     "value should be set in millisecond units." )
84 #define CAT_TEXT N_("Concatenate with additional files")
85 #define CAT_LONGTEXT N_( \
86     "Allows you to play split files as if they were part of a unique file. " \
87     "Specify a comma-separated list of files." )
88
89 vlc_module_begin();
90     set_description( _("Standard filesystem file input") );
91     set_shortname( _("File") );
92     set_category( CAT_INPUT );
93     set_subcategory( SUBCAT_INPUT_ACCESS );
94     add_integer( "file-caching", DEFAULT_PTS_DELAY / 1000, NULL, CACHING_TEXT, CACHING_LONGTEXT, VLC_TRUE );
95     add_string( "file-cat", NULL, NULL, CAT_TEXT, CAT_LONGTEXT, VLC_TRUE );
96     set_capability( "access2", 50 );
97     add_shortcut( "file" );
98     add_shortcut( "stream" );
99     add_shortcut( "kfir" );
100     set_callbacks( Open, Close );
101 vlc_module_end();
102
103
104 /*****************************************************************************
105  * Exported prototypes
106  *****************************************************************************/
107 static int  Seek( access_t *, int64_t );
108 static int  Read( access_t *, uint8_t *, int );
109 static int  Control( access_t *, int, va_list );
110
111 static int  _OpenFile( access_t *, const char * );
112
113 typedef struct
114 {
115     char     *psz_name;
116     int64_t  i_size;
117
118 } file_entry_t;
119
120 struct access_sys_t
121 {
122     unsigned int i_nb_reads;
123     vlc_bool_t   b_kfir;
124
125     /* Files list */
126     int          i_file;
127     file_entry_t **file;
128
129     /* Current file */
130     int  i_index;
131 #ifndef UNDER_CE
132     int  fd;
133     int  fd_backup;
134 #else
135     FILE *fd;
136     FILE *fd_backup;
137 #endif
138
139     /* */
140     vlc_bool_t b_seekable;
141     vlc_bool_t b_pace_control;
142 };
143
144 /*****************************************************************************
145  * Open: open the file
146  *****************************************************************************/
147 static int Open( vlc_object_t *p_this )
148 {
149     access_t     *p_access = (access_t*)p_this;
150     access_sys_t *p_sys;
151     char *psz_name = p_access->psz_path;
152     char *psz;
153
154 #ifdef HAVE_SYS_STAT_H
155     struct stat         stat_info;
156 #endif
157     vlc_bool_t          b_stdin;
158
159     file_entry_t *      p_file;
160
161
162     b_stdin = psz_name[0] == '-' && psz_name[1] == '\0';
163
164 #ifdef HAVE_SYS_STAT_H
165     if( !b_stdin )
166     {
167         psz = ToLocale( psz_name );
168         if( stat( psz, &stat_info ) )
169         {
170             msg_Warn( p_access, "%s: %s", psz_name, strerror( errno ) );
171             LocaleFree( psz );
172             return VLC_EGENERIC;
173         }
174         LocaleFree( psz );
175     }
176 #endif
177
178     p_access->pf_read = Read;
179     p_access->pf_block = NULL;
180     p_access->pf_seek = Seek;
181     p_access->pf_control = Control;
182     p_access->info.i_update = 0;
183     p_access->info.i_size = 0;
184     p_access->info.i_pos = 0;
185     p_access->info.b_eof = VLC_FALSE;
186     p_access->info.i_title = 0;
187     p_access->info.i_seekpoint = 0;
188     p_access->p_sys = p_sys = malloc( sizeof( access_sys_t ) );
189     p_sys->i_nb_reads = 0;
190     p_sys->b_kfir = VLC_FALSE;
191     p_sys->file = NULL;
192     p_sys->i_file = 0;
193     p_sys->i_index = 0;
194 #ifndef UNDER_CE
195     p_sys->fd = -1;
196 #endif
197
198     if( !strcasecmp( p_access->psz_access, "stream" ) )
199     {
200         p_sys->b_seekable = VLC_FALSE;
201         p_sys->b_pace_control = VLC_FALSE;
202     }
203     else if( !strcasecmp( p_access->psz_access, "kfir" ) )
204     {
205         p_sys->b_seekable = VLC_FALSE;
206         p_sys->b_pace_control = VLC_FALSE;
207         p_sys->b_kfir = VLC_TRUE;
208     }
209     else
210     {
211         /* file:%s or %s */
212         p_sys->b_pace_control = VLC_TRUE;
213
214         if( b_stdin )
215         {
216             p_sys->b_seekable = VLC_FALSE;
217         }
218 #ifdef UNDER_CE
219         else if( VLC_TRUE )
220         {
221             /* We'll update i_size after it's been opened */
222             p_sys->b_seekable = VLC_TRUE;
223         }
224 #elif defined( HAVE_SYS_STAT_H )
225         else if( S_ISREG(stat_info.st_mode) || S_ISCHR(stat_info.st_mode) ||
226                  S_ISBLK(stat_info.st_mode) )
227         {
228             p_sys->b_seekable = VLC_TRUE;
229             p_access->info.i_size = stat_info.st_size;
230         }
231         else if( S_ISFIFO(stat_info.st_mode)
232 #   if !defined( SYS_BEOS ) && !defined( WIN32 )
233                   || S_ISSOCK(stat_info.st_mode)
234 #   endif
235                )
236         {
237             p_sys->b_seekable = VLC_FALSE;
238         }
239 #endif
240         else
241         {
242             msg_Err( p_access, "unknown file type for `%s'", psz_name );
243             return VLC_EGENERIC;
244         }
245     }
246
247     msg_Dbg( p_access, "opening file `%s'", psz_name );
248
249     if( b_stdin )
250     {
251         p_sys->fd = 0;
252     }
253     else if( _OpenFile( p_access, psz_name ) )
254     {
255         free( p_sys );
256         return VLC_EGENERIC;
257     }
258
259     if( p_sys->b_seekable && !p_access->info.i_size )
260     {
261         /* FIXME that's bad because all others access will be probed */
262         msg_Err( p_access, "file %s is empty, aborting", psz_name );
263         free( p_sys );
264         return VLC_EGENERIC;
265     }
266
267     /* Update default_pts to a suitable value for file access */
268     var_Create( p_access, "file-caching", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
269
270     /*
271      * Get the additional list of files
272      */
273     p_file = malloc( sizeof(file_entry_t) );
274     p_file->i_size = p_access->info.i_size;
275     p_file->psz_name = strdup( psz_name );
276     TAB_APPEND( p_sys->i_file, p_sys->file, p_file );
277
278     psz = var_CreateGetString( p_access, "file-cat" );
279     if( *psz )
280     {
281         char *psz_parser = psz_name = psz;
282         int64_t i_size;
283
284         while( psz_name && *psz_name )
285         {
286             psz_parser = strchr( psz_name, ',' );
287             if( psz_parser ) *psz_parser = 0;
288
289             psz_name = strdup( psz_name );
290             if( psz_name )
291             {
292                 msg_Dbg( p_access, "adding file `%s'", psz_name );
293                 i_size = 0;
294
295 #ifdef HAVE_SYS_STAT_H
296                 if( !stat( psz_name, &stat_info ) )
297                 {
298                     p_access->info.i_size += stat_info.st_size;
299                     i_size = stat_info.st_size;
300                 }
301                 else
302                 {
303                     msg_Dbg( p_access, "cannot stat() file `%s'", psz_name );
304                 }
305 #endif
306                 p_file = malloc( sizeof(file_entry_t) );
307                 p_file->i_size = i_size;
308                 p_file->psz_name = psz_name;
309
310                 TAB_APPEND( p_sys->i_file, p_sys->file, p_file );
311             }
312
313             psz_name = psz_parser;
314             if( psz_name ) psz_name++;
315         }
316     }
317     free( psz );
318
319     return VLC_SUCCESS;
320 }
321
322 /*****************************************************************************
323  * Close: close the target
324  *****************************************************************************/
325 static void Close( vlc_object_t * p_this )
326 {
327     access_t     *p_access = (access_t*)p_this;
328     access_sys_t *p_sys = p_access->p_sys;
329     int i;
330
331     close( p_sys->fd );
332
333     for( i = 0; i < p_sys->i_file; i++ )
334     {
335         free( p_sys->file[i]->psz_name );
336         free( p_sys->file[i] );
337     }
338     free( p_sys->file );
339
340     free( p_sys );
341 }
342
343 /*****************************************************************************
344  * Read: standard read on a file descriptor.
345  *****************************************************************************/
346 static int Read( access_t *p_access, uint8_t *p_buffer, int i_len )
347 {
348     access_sys_t *p_sys = p_access->p_sys;
349     int i_ret;
350
351 #if !defined(WIN32) && !defined(UNDER_CE)
352     if( !p_sys->b_pace_control )
353     {
354         if( !p_sys->b_kfir )
355         {
356             /* Find if some data is available. This won't work under Windows. */
357             struct timeval  timeout;
358             fd_set          fds;
359
360             /* Initialize file descriptor set */
361             FD_ZERO( &fds );
362             FD_SET( p_sys->fd, &fds );
363
364             /* We'll wait 0.5 second if nothing happens */
365             timeout.tv_sec = 0;
366             timeout.tv_usec = 500000;
367
368             /* Find if some data is available */
369             while( (i_ret = select( p_sys->fd + 1, &fds, NULL, NULL, &timeout )) == 0
370                     || (i_ret < 0 && errno == EINTR) )
371             {
372                 FD_ZERO( &fds );
373                 FD_SET( p_sys->fd, &fds );
374                 timeout.tv_sec = 0;
375                 timeout.tv_usec = 500000;
376
377                 if( p_access->b_die )
378                     return 0;
379             }
380
381             if( i_ret < 0 )
382             {
383                 msg_Err( p_access, "select error (%s)", strerror(errno) );
384                 return -1;
385             }
386
387             i_ret = read( p_sys->fd, p_buffer, i_len );
388         }
389         else
390         {
391             /* b_kfir ; work around a buggy poll() driver implementation */
392             while ( (i_ret = read( p_sys->fd, p_buffer, i_len )) == 0 &&
393                     !p_access->b_die )
394             {
395                 msleep( INPUT_ERROR_SLEEP );
396             }
397         }
398     }
399     else
400 #endif /* WIN32 || UNDER_CE */
401     {
402         /* b_pace_control || WIN32 */
403         i_ret = read( p_sys->fd, p_buffer, i_len );
404     }
405
406     if( i_ret < 0 )
407     {
408         if( errno != EINTR && errno != EAGAIN )
409             msg_Err( p_access, "read failed (%s)", strerror(errno) );
410
411         /* Delay a bit to avoid consuming all the CPU. This is particularly
412          * useful when reading from an unconnected FIFO. */
413         msleep( INPUT_ERROR_SLEEP );
414     }
415
416     p_sys->i_nb_reads++;
417 #ifdef HAVE_SYS_STAT_H
418     if( p_access->info.i_size != 0 &&
419         (p_sys->i_nb_reads % INPUT_FSTAT_NB_READS) == 0 )
420     {
421         struct stat stat_info;
422         int i_file = p_sys->i_index;
423
424         if ( fstat( p_sys->fd, &stat_info ) == -1 )
425         {
426             msg_Warn( p_access, "couldn't stat again the file (%s)", strerror(errno) );
427         }
428         else if ( p_sys->file[i_file]->i_size != stat_info.st_size )
429         {
430             p_access->info.i_size += (stat_info.st_size - p_sys->file[i_file]->i_size );
431             p_sys->file[i_file]->i_size = stat_info.st_size;
432             p_access->info.i_update |= INPUT_UPDATE_SIZE;
433         }
434     }
435 #endif
436
437     /* If we reached an EOF then switch to the next file in the list */
438     if ( i_ret == 0 && p_sys->i_index + 1 < p_sys->i_file )
439     {
440         char *psz_name = p_sys->file[++p_sys->i_index]->psz_name;
441         p_sys->fd_backup = p_sys->fd;
442
443         msg_Dbg( p_access, "opening file `%s'", psz_name );
444
445         if ( _OpenFile( p_access, psz_name ) )
446         {
447             p_sys->fd = p_sys->fd_backup;
448             return 0;
449         }
450
451         close( p_sys->fd_backup );
452
453         /* We have to read some data */
454         return Read( p_access, p_buffer, i_len );
455     }
456
457     if( i_ret > 0 )
458         p_access->info.i_pos += i_ret;
459     else if( i_ret == 0 )
460         p_access->info.b_eof = VLC_TRUE;
461
462     return i_ret;
463 }
464
465 /*****************************************************************************
466  * Seek: seek to a specific location in a file
467  *****************************************************************************/
468 static int Seek( access_t *p_access, int64_t i_pos )
469 {
470     access_sys_t *p_sys = p_access->p_sys;
471     int64_t i_size = 0;
472
473     /* Check which file we need to access */
474     if( p_sys->i_file > 1 )
475     {
476         int i;
477         char *psz_name;
478         p_sys->fd_backup = p_sys->fd;
479
480         for( i = 0; i < p_sys->i_file - 1; i++ )
481         {
482             if( i_pos < p_sys->file[i]->i_size + i_size )
483                 break;
484             i_size += p_sys->file[i]->i_size;
485         }
486         psz_name = p_sys->file[i]->psz_name;
487
488         msg_Dbg( p_access, "opening file `%s'", psz_name );
489
490         if ( i != p_sys->i_index && !_OpenFile( p_access, psz_name ) )
491         {
492             /* Close old file */
493             close( p_sys->fd_backup );
494             p_sys->i_index = i;
495         }
496         else
497         {
498             p_sys->fd = p_sys->fd_backup;
499         }
500     }
501
502     lseek( p_sys->fd, i_pos - i_size, SEEK_SET );
503
504     p_access->info.i_pos = i_pos;
505     if( p_access->info.i_size < p_access->info.i_pos )
506     {
507         msg_Err( p_access, "seeking too far" );
508         p_access->info.i_pos = p_access->info.i_size;
509     }
510     else if( p_access->info.i_pos < 0 )
511     {
512         msg_Err( p_access, "seeking too early" );
513         p_access->info.i_pos = 0;
514     }
515     /* Reset eof */
516     p_access->info.b_eof = VLC_FALSE;
517
518     /* FIXME */
519     return VLC_SUCCESS;
520 }
521
522 /*****************************************************************************
523  * Control:
524  *****************************************************************************/
525 static int Control( access_t *p_access, int i_query, va_list args )
526 {
527     access_sys_t *p_sys = p_access->p_sys;
528     vlc_bool_t   *pb_bool;
529     int          *pi_int;
530     int64_t      *pi_64;
531
532     switch( i_query )
533     {
534         /* */
535         case ACCESS_CAN_SEEK:
536         case ACCESS_CAN_FASTSEEK:
537             pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
538             *pb_bool = p_sys->b_seekable;
539             break;
540
541         case ACCESS_CAN_PAUSE:
542         case ACCESS_CAN_CONTROL_PACE:
543             pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
544             *pb_bool = p_sys->b_pace_control;
545             break;
546
547         /* */
548         case ACCESS_GET_MTU:
549             pi_int = (int*)va_arg( args, int * );
550             *pi_int = 0;
551             break;
552
553         case ACCESS_GET_PTS_DELAY:
554             pi_64 = (int64_t*)va_arg( args, int64_t * );
555             *pi_64 = var_GetInteger( p_access, "file-caching" ) * I64C(1000);
556             break;
557
558         /* */
559         case ACCESS_SET_PAUSE_STATE:
560             /* Nothing to do */
561             break;
562
563         case ACCESS_GET_TITLE_INFO:
564         case ACCESS_SET_TITLE:
565         case ACCESS_SET_SEEKPOINT:
566         case ACCESS_SET_PRIVATE_ID_STATE:
567         case ACCESS_GET_META:
568             return VLC_EGENERIC;
569
570         default:
571             msg_Warn( p_access, "unimplemented query in control" );
572             return VLC_EGENERIC;
573
574     }
575     return VLC_SUCCESS;
576 }
577
578
579 /*****************************************************************************
580  * OpenFile: Opens a specific file
581  *****************************************************************************/
582 static int _OpenFile( access_t * p_access, const char * psz_name )
583 {
584     access_sys_t *p_sys = p_access->p_sys;
585     const char *psz_localname;
586
587     psz_localname = ToLocale( psz_name );
588
589 #ifdef UNDER_CE
590     p_sys->fd = fopen( psz_localname, "rb" );
591     LocaleFree( psz_localname );
592     if ( !p_sys->fd )
593     {
594         msg_Err( p_access, "cannot open file %s", psz_name );
595         return VLC_EGENERIC;
596     }
597
598     fseek( p_sys->fd, 0, SEEK_END );
599     p_access->info.i_size = ftell( p_sys->fd );
600     p_access->info.i_update |= INPUT_UPDATE_SIZE;
601     fseek( p_sys->fd, 0, SEEK_SET );
602 #else
603
604     p_sys->fd = open( psz_localname, O_NONBLOCK /*| O_LARGEFILE*/ );
605     LocaleFree( psz_localname );
606     if ( p_sys->fd == -1 )
607     {
608         msg_Err( p_access, "cannot open file %s (%s)", psz_name,
609                  strerror(errno) );
610         return VLC_EGENERIC;
611     }
612 #endif
613
614     return VLC_SUCCESS;
615 }