]> git.sesse.net Git - vlc/blob - modules/access/file.c
- access/file.c: fixed stat on win32, make sure the right stat structure is used...
[vlc] / modules / access / file.c
1 /*****************************************************************************
2  * file.c: file input (file: access plug-in)
3  *****************************************************************************
4  * Copyright (C) 2001-2006 the VideoLAN team
5  * Copyright © 2006 Rémi Denis-Courmont
6  * $Id$
7  *
8  * Authors: Christophe Massiot <massiot@via.ecp.fr>
9  *          Rémi Denis-Courmont <rem # videolan # org>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24  *****************************************************************************/
25
26 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29 #include <vlc/vlc.h>
30 #include <vlc/input.h>
31 #include <vlc_interaction.h>
32
33 #include <stdlib.h>
34 #include <string.h>
35 #include <errno.h>
36 #ifdef HAVE_SYS_TYPES_H
37 #   include <sys/types.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 #if defined( WIN32 ) && !defined( UNDER_CE )
47 #   include <io.h>
48 #else
49 #   include <unistd.h>
50 #   include <poll.h>
51 #endif
52
53 #if defined( WIN32 ) && !defined( UNDER_CE )
54 /* fstat() support for large files on win32 */
55 #   define fstat(a,b) _fstati64(a,b)
56 #   define FILESTAT _stati64 
57 #   ifdef lseek
58 #      undef lseek
59 #   endif
60 #   define lseek _lseeki64
61 #elif defined( UNDER_CE )
62 #   ifdef read
63 #      undef read
64 #   endif
65 #   define read(a,b,c) fread(b,1,c,a)
66 #   define close(a) fclose(a)
67 #   ifdef lseek
68 #      undef lseek
69 #   endif
70 #   define lseek fseek
71 #else
72 #   define FILESTAT stat
73 #endif
74
75 #include "charset.h"
76
77 /*****************************************************************************
78  * Module descriptor
79  *****************************************************************************/
80 static int  Open ( vlc_object_t * );
81 static void Close( vlc_object_t * );
82
83 #define CACHING_TEXT N_("Caching value in ms")
84 #define CACHING_LONGTEXT N_( \
85     "Caching value for files. This " \
86     "value should be set in milliseconds." )
87 #define CAT_TEXT N_("Concatenate with additional files")
88 #define CAT_LONGTEXT N_( \
89     "Play split files as if they were part of a unique file. " \
90     "You need to specify a comma-separated list of files." )
91
92 vlc_module_begin();
93     set_description( _("File input") );
94     set_shortname( _("File") );
95     set_category( CAT_INPUT );
96     set_subcategory( SUBCAT_INPUT_ACCESS );
97     add_integer( "file-caching", DEFAULT_PTS_DELAY / 1000, NULL, CACHING_TEXT, CACHING_LONGTEXT, VLC_TRUE );
98     add_deprecated( "file-cat", VLC_TRUE );
99     set_capability( "access2", 50 );
100     add_shortcut( "file" );
101     add_shortcut( "stream" );
102     add_shortcut( "kfir" );
103     set_callbacks( Open, Close );
104 vlc_module_end();
105
106
107 /*****************************************************************************
108  * Exported prototypes
109  *****************************************************************************/
110 static int  Seek( access_t *, int64_t );
111 static int  Read( access_t *, uint8_t *, int );
112 static int  Control( access_t *, int, va_list );
113
114 static int  open_file( access_t *, const char * );
115
116 struct access_sys_t
117 {
118     unsigned int i_nb_reads;
119     vlc_bool_t   b_kfir;
120
121     int fd;
122
123     /* */
124     vlc_bool_t b_seekable;
125     vlc_bool_t b_pace_control;
126 };
127
128 /*****************************************************************************
129  * Open: open the file
130  *****************************************************************************/
131 static int Open( vlc_object_t *p_this )
132 {
133     access_t     *p_access = (access_t*)p_this;
134     access_sys_t *p_sys;
135
136     vlc_bool_t    b_stdin = !strcmp (p_access->psz_path, "-");
137
138     /* Update default_pts to a suitable value for file access */
139     var_Create( p_access, "file-caching", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
140
141     STANDARD_READ_ACCESS_INIT;
142     p_sys->i_nb_reads = 0;
143     p_sys->b_kfir = VLC_FALSE;
144     int fd = p_sys->fd = -1;
145
146     if (!strcasecmp (p_access->psz_access, "stream"))
147     {
148         p_sys->b_seekable = VLC_FALSE;
149         p_sys->b_pace_control = VLC_FALSE;
150     }
151     else if (!strcasecmp (p_access->psz_access, "kfir"))
152     {
153         p_sys->b_seekable = VLC_FALSE;
154         p_sys->b_pace_control = VLC_FALSE;
155         p_sys->b_kfir = VLC_TRUE;
156     }
157     else
158     {
159         p_sys->b_seekable = VLC_TRUE;
160         p_sys->b_pace_control = VLC_TRUE;
161     }
162
163     /* Open file */
164     msg_Dbg (p_access, "opening file `%s'", p_access->psz_path);
165
166     if (b_stdin)
167         fd = dup (0);
168     else
169         fd = open_file (p_access, p_access->psz_path);
170
171 #ifdef HAVE_SYS_STAT_H
172     struct FILESTAT st;
173
174     while (fd != -1)
175     {
176         if (fstat (fd, &st))
177             msg_Err (p_access, "fstat(%d): %s", fd, strerror (errno));
178         else
179         if (S_ISDIR (st.st_mode))
180             /* The directory plugin takes care of that */
181             msg_Dbg (p_access, "file is a directory, aborting");
182         else
183             break; // success
184
185         close (fd);
186         fd = -1;
187     }
188 #endif
189
190     if (fd == -1)
191     {
192         free (p_sys);
193         return VLC_EGENERIC;
194     }
195     p_sys->fd = fd;
196
197 #ifdef HAVE_SYS_STAT_H
198     p_access->info.i_size = st.st_size;
199     if (!S_ISREG (st.st_mode) && !S_ISBLK (st.st_mode)
200      && (!S_ISCHR (st.st_mode) || (st.st_size == 0)))
201         p_sys->b_seekable = VLC_FALSE;
202 #else
203     p_sys->b_seekable = !b_stdin;
204 # warning File size not known!
205 #endif
206
207     if (p_sys->b_seekable && (p_access->info.i_size == 0))
208     {
209         /* FIXME that's bad because all others access will be probed */
210         msg_Err (p_access, "file is empty, aborting");
211         Close (p_this);
212         return VLC_EGENERIC;
213     }
214     msg_Dbg (p_access, "opened file of size "I64Fd" (FIXME: remove this)",
215              p_access->info.i_size);
216
217     return VLC_SUCCESS;
218 }
219
220 /*****************************************************************************
221  * Close: close the target
222  *****************************************************************************/
223 static void Close (vlc_object_t * p_this)
224 {
225     access_t     *p_access = (access_t*)p_this;
226     access_sys_t *p_sys = p_access->p_sys;
227
228     close (p_sys->fd);
229     free (p_sys);
230 }
231
232 /*****************************************************************************
233  * Read: standard read on a file descriptor.
234  *****************************************************************************/
235 static int Read( access_t *p_access, uint8_t *p_buffer, int i_len )
236 {
237     access_sys_t *p_sys = p_access->p_sys;
238     int i_ret;
239     int fd = p_sys->fd;
240
241 #if !defined(WIN32) && !defined(UNDER_CE)
242     if( !p_sys->b_pace_control )
243     {
244         if( !p_sys->b_kfir )
245         {
246             /* Find if some data is available. This won't work under Windows. */
247             do
248             {
249                 struct pollfd ufd;
250
251                 if( p_access->b_die )
252                     return 0;
253
254                 memset (&ufd, 0, sizeof (ufd));
255                 ufd.fd = fd;
256                 ufd.events = POLLIN;
257
258                 i_ret = poll (&ufd, 1, 500);
259             }
260             while (i_ret <= 0);
261
262             i_ret = read (fd, p_buffer, i_len);
263         }
264         else
265         {
266             /* b_kfir ; work around a buggy poll() driver implementation */
267             while (((i_ret = read (fd, p_buffer, i_len)) == 0)
268                 && !p_access->b_die)
269             {
270                 msleep( INPUT_ERROR_SLEEP );
271             }
272         }
273     }
274     else
275 #endif /* WIN32 || UNDER_CE */
276         /* b_pace_control || WIN32 */
277         i_ret = read( fd, p_buffer, i_len );
278
279     if( i_ret < 0 )
280     {
281         switch (errno)
282         {
283             case EINTR:
284             case EAGAIN:
285                 break;
286
287             default:
288                 msg_Err (p_access, "read failed (%s)", strerror (errno));
289                 intf_UserFatal (p_access, VLC_FALSE, _("File reading failed"),
290                                 _("VLC could not read file \"%s\"."),
291                                 strerror (errno));
292         }
293
294         /* Delay a bit to avoid consuming all the CPU. This is particularly
295          * useful when reading from an unconnected FIFO. */
296         msleep( INPUT_ERROR_SLEEP );
297     }
298
299     p_sys->i_nb_reads++;
300
301 #ifdef HAVE_SYS_STAT_H
302     if( p_access->info.i_size != 0 &&
303         (p_sys->i_nb_reads % INPUT_FSTAT_NB_READS) == 0 )
304     {
305         struct FILESTAT st;
306
307         if ((fstat (fd, &st) == 0)
308          && (p_access->info.i_size != st.st_size))
309         {
310             p_access->info.i_size = st.st_size;
311             p_access->info.i_update |= INPUT_UPDATE_SIZE;
312         }
313     }
314 #endif
315
316     if( i_ret > 0 )
317         p_access->info.i_pos += i_ret;
318     else if( i_ret == 0 )
319         p_access->info.b_eof = VLC_TRUE;
320
321     return i_ret;
322 }
323
324 /*****************************************************************************
325  * Seek: seek to a specific location in a file
326  *****************************************************************************/
327 static int Seek (access_t *p_access, int64_t i_pos)
328 {
329     if (i_pos > p_access->info.i_size)
330     {
331         msg_Err (p_access, "seeking too far");
332         i_pos = p_access->info.i_size;
333     }
334     else if (i_pos < 0)
335     {
336         msg_Err (p_access, "seeking too early");
337         i_pos = 0;
338     }
339
340     p_access->info.i_pos = i_pos;
341     p_access->info.b_eof = VLC_FALSE;
342
343     /* Determine which file we need to access */
344     lseek (p_access->p_sys->fd, i_pos, SEEK_SET);
345     return VLC_SUCCESS;
346 }
347
348 /*****************************************************************************
349  * Control:
350  *****************************************************************************/
351 static int Control( access_t *p_access, int i_query, va_list args )
352 {
353     access_sys_t *p_sys = p_access->p_sys;
354     vlc_bool_t   *pb_bool;
355     int          *pi_int;
356     int64_t      *pi_64;
357
358     switch( i_query )
359     {
360         /* */
361         case ACCESS_CAN_SEEK:
362         case ACCESS_CAN_FASTSEEK:
363             pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
364             *pb_bool = p_sys->b_seekable;
365             break;
366
367         case ACCESS_CAN_PAUSE:
368         case ACCESS_CAN_CONTROL_PACE:
369             pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
370             *pb_bool = p_sys->b_pace_control;
371             break;
372
373         /* */
374         case ACCESS_GET_MTU:
375             pi_int = (int*)va_arg( args, int * );
376             *pi_int = 0;
377             break;
378
379         case ACCESS_GET_PTS_DELAY:
380             pi_64 = (int64_t*)va_arg( args, int64_t * );
381             *pi_64 = var_GetInteger( p_access, "file-caching" ) * I64C(1000);
382             break;
383
384         /* */
385         case ACCESS_SET_PAUSE_STATE:
386             /* Nothing to do */
387             break;
388
389         case ACCESS_GET_TITLE_INFO:
390         case ACCESS_SET_TITLE:
391         case ACCESS_SET_SEEKPOINT:
392         case ACCESS_SET_PRIVATE_ID_STATE:
393         case ACCESS_GET_META:
394             return VLC_EGENERIC;
395
396         default:
397             msg_Warn( p_access, "unimplemented query in control" );
398             return VLC_EGENERIC;
399
400     }
401     return VLC_SUCCESS;
402 }
403
404
405 static char *expand_path (const access_t *p_access, const char *path)
406 {
407     if (strncmp (path, "~/", 2) == 0)
408     {
409         char *res;
410
411          // TODO: we should also support the ~cmassiot/ syntax
412          if (asprintf (&res, "%s/%s", p_access->p_libvlc->psz_homedir, path + 2) == -1)
413              return NULL;
414          return res;
415     }
416
417 #if defined(WIN32)
418     if (!strcasecmp (p_access->psz_access, "file")
419       && ('/' == path[0]) && path[1] && (':' == path[2]) && ('/' == path[3]))
420         // Explorer can open path such as file:/C:/ or file:///C:/
421         // hence remove leading / if found
422         return strdup (path + 1);
423 #endif
424
425     return strdup (path);
426 }
427
428
429 /*****************************************************************************
430  * open_file: Opens a specific file
431  *****************************************************************************/
432 static int open_file (access_t *p_access, const char *psz_name)
433 {
434     char *path = expand_path (p_access, psz_name);
435
436 #ifdef UNDER_CE
437     p_sys->fd = utf8_fopen( path, "rb" );
438     if ( !p_sys->fd )
439     {
440         msg_Err( p_access, "cannot open file %s", psz_name );
441         intf_UserFatal( p_access, VLC_FALSE, _("File reading failed"), 
442                         _("VLC could not open file \"%s\"."), psz_name );
443         free (path);
444         return VLC_EGENERIC;
445     }
446
447     fseek( p_sys->fd, 0, SEEK_END );
448     p_access->info.i_size = ftell( p_sys->fd );
449     p_access->info.i_update |= INPUT_UPDATE_SIZE;
450     fseek( p_sys->fd, 0, SEEK_SET );
451 #else
452     int fd = utf8_open (path, O_RDONLY | O_NONBLOCK /* O_LARGEFILE*/, 0666);
453     if (fd == -1)
454     {
455         msg_Err (p_access, "cannot open file %s (%s)", psz_name,
456                  strerror (errno));
457         intf_UserFatal (p_access, VLC_FALSE, _("File reading failed"), 
458                         _("VLC could not open file \"%s\" (%s)."),
459                         psz_name, strerror (errno));
460         return -1;
461     }
462
463 # if defined(HAVE_FCNTL_H) && defined(F_FDAHEAD) && defined(F_NOCACHE)
464     /* We'd rather use any available memory for reading ahead
465      * than for caching what we've already seen/heard */
466     fcntl (fd, F_RDAHEAD, 1);
467     fcntl (fd, F_NOCACHE, 1);
468 # endif
469 #endif
470
471     return fd;
472 }