1 /*
2   This file is part of Deadbeef Player source code
3   http://deadbeef.sourceforge.net
4 
5   standard file vfs implementation
6 
7   Copyright (C) 2009-2015 Alexey Yakovenko
8 
9   This software is provided 'as-is', without any express or implied
10   warranty.  In no event will the authors be held liable for any damages
11   arising from the use of this software.
12 
13   Permission is granted to anyone to use this software for any purpose,
14   including commercial applications, and to alter it and redistribute it
15   freely, subject to the following restrictions:
16 
17   1. The origin of this software must not be misrepresented; you must not
18      claim that you wrote the original software. If you use this software
19      in a product, an acknowledgment in the product documentation would be
20      appreciated but is not required.
21   2. Altered source versions must be plainly marked as such, and must not be
22      misrepresented as being the original software.
23   3. This notice may not be removed or altered from any source distribution.
24 
25   Alexey Yakovenko waker@users.sourceforge.net
26 */
27 #include "deadbeef.h"
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <assert.h>
31 #include <string.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <fcntl.h>
35 #include <unistd.h>
36 
37 #ifndef __linux__
38 #define off64_t off_t
39 #define lseek64 lseek
40 #define O_LARGEFILE 0
41 #endif
42 
43 //#define USE_STDIO
44 
45 #ifndef USE_STDIO
46 #define BUFSIZE 1024
47 #endif
48 
49 static DB_functions_t *deadbeef;
50 typedef struct {
51     DB_vfs_t *vfs;
52 #ifdef USE_STDIO
53     FILE *stream;
54 #else
55     int stream;
56     int64_t offs;
57     uint8_t buffer[BUFSIZE];
58     uint8_t *bufptr;
59     int bufremaining;
60     int have_size;
61     size_t size;
62 #endif
63 } STDIO_FILE;
64 
65 static DB_vfs_t plugin;
66 
67 static DB_FILE *
stdio_open(const char * fname)68 stdio_open (const char *fname) {
69     if (!memcmp (fname, "file://", 7)) {
70         fname += 7;
71     }
72 #ifdef USE_STDIO
73     FILE *file = fopen (fname, "rb");
74     if (!file) {
75         return NULL;
76     }
77 #else
78     int file = open (fname, O_LARGEFILE);
79     if (file == -1) {
80         return NULL;
81     }
82 #endif
83     STDIO_FILE *fp = malloc (sizeof (STDIO_FILE));
84     memset (fp, 0, sizeof (STDIO_FILE));
85     fp->vfs = &plugin;
86     fp->stream = file;
87     return (DB_FILE*)fp;
88 }
89 
90 static void
stdio_close(DB_FILE * stream)91 stdio_close (DB_FILE *stream) {
92     assert (stream);
93 #ifdef USE_STDIO
94     fclose (((STDIO_FILE *)stream)->stream);
95 #else
96     close (((STDIO_FILE *)stream)->stream);
97 #endif
98     free (stream);
99 }
100 
101 static int
fillbuffer(STDIO_FILE * f)102 fillbuffer (STDIO_FILE *f) {
103     assert (f->bufremaining >= 0);
104     if (f->bufremaining == 0) {
105         f->bufremaining = (int)read (f->stream, f->buffer, BUFSIZE);
106         if (f->bufremaining < 0) {
107             f->bufremaining = 0;
108             return -1;
109         }
110         f->bufptr = f->buffer;
111     }
112     return f->bufremaining;
113 }
114 
115 static size_t
stdio_read(void * ptr,size_t size,size_t nmemb,DB_FILE * stream)116 stdio_read (void *ptr, size_t size, size_t nmemb, DB_FILE *stream) {
117     assert (stream);
118     assert (ptr);
119 #ifdef USE_STDIO
120     return fread (ptr, size, nmemb, ((STDIO_FILE*)stream)->stream);
121 #else
122     STDIO_FILE *f = (STDIO_FILE*)stream;
123     size_t nb = size * nmemb;
124     while (nb > 0) {
125         if (fillbuffer (f) <= 0) {
126             break;
127         }
128         int r = f->bufremaining;
129         if (r > nb) {
130             r = (int)nb;
131         }
132         memcpy (ptr, f->bufptr, r);
133         f->bufremaining -= r;
134         f->bufptr += r;
135         ptr += r;
136         f->offs += r;
137         nb -= r;
138     }
139     size_t ret = ((size * nmemb) - nb) / size;
140     return ret;
141 #endif
142 }
143 
144 static int
stdio_seek(DB_FILE * stream,int64_t offset,int whence)145 stdio_seek (DB_FILE *stream, int64_t offset, int whence) {
146     assert (stream);
147 #ifdef USE_STDIO
148     return fseek (((STDIO_FILE *)stream)->stream, offset, whence);
149 #else
150     // convert offset to absolute
151     if (whence == SEEK_CUR) {
152         whence = SEEK_SET;
153         offset = ((STDIO_FILE*)stream)->offs + offset;
154     }
155     off64_t res = lseek64 (((STDIO_FILE *)stream)->stream, offset, whence);
156     if (res == -1) {
157         return -1;
158     }
159 //    printf ("lseek res: %lld (%lld, %d, prev=%lld)\n", res, offset, whence,  ((STDIO_FILE*)stream)->offs);
160     ((STDIO_FILE*)stream)->offs = res;
161     ((STDIO_FILE*)stream)->bufremaining = 0;
162 #endif
163     return 0;
164 }
165 
166 static int64_t
stdio_tell(DB_FILE * stream)167 stdio_tell (DB_FILE *stream) {
168     assert (stream);
169 #ifdef USE_STDIO
170     return ftell (((STDIO_FILE*)stream)->stream);
171 #else
172     return ((STDIO_FILE*)stream)->offs;
173 #endif
174 }
175 
176 static void
stdio_rewind(DB_FILE * stream)177 stdio_rewind (DB_FILE *stream) {
178     assert (stream);
179 #ifdef USE_STDIO
180     rewind (((STDIO_FILE*)stream)->stream);
181 #else
182     stdio_seek (stream, 0, SEEK_SET);
183 #endif
184 }
185 
186 static int64_t
stdio_getlength(DB_FILE * stream)187 stdio_getlength (DB_FILE *stream) {
188     assert (stream);
189     STDIO_FILE *f = (STDIO_FILE *)stream;
190 #ifdef USE_STDIO
191     size_t offs = ftell (f->stream);
192     fseek (f->stream, 0, SEEK_END);
193     size_t l = ftell (f->stream);
194     fseek (f->stream, offs, SEEK_SET);
195     return l;
196 #else
197     if (!f->have_size) {
198         int64_t size = lseek64 (f->stream, 0, SEEK_END);
199         lseek64 (f->stream, f->offs, SEEK_SET);
200         f->bufremaining = 0;
201         f->have_size = 1;
202         f->size = size;
203     }
204     return f->size;
205 #endif
206 }
207 
208 const char *
stdio_get_content_type(DB_FILE * stream)209 stdio_get_content_type (DB_FILE *stream) {
210     return NULL;
211 }
212 
213 int
stdio_is_streaming(void)214 stdio_is_streaming (void) {
215     return 0;
216 }
217 
218 // standard stdio vfs
219 static DB_vfs_t plugin = {
220     DB_PLUGIN_SET_API_VERSION
221     .plugin.version_major = 1,
222     .plugin.version_minor = 0,
223     .plugin.type = DB_PLUGIN_VFS,
224     .plugin.name = "stdio vfs",
225     .plugin.id = "vfs_stdio",
226     .plugin.descr = "Standard IO plugin\nUsed for reading normal local files\nIt is statically linked, so you can't delete it.",
227     .plugin.copyright =
228         "standard file vfs implementation\n"
229         "\n"
230         "Copyright (C) 2009-2015 Alexey Yakovenko\n"
231         "\n"
232         "This software is provided 'as-is', without any express or implied\n"
233         "warranty.  In no event will the authors be held liable for any damages\n"
234         "arising from the use of this software.\n"
235         "\n"
236         "Permission is granted to anyone to use this software for any purpose,\n"
237         "including commercial applications, and to alter it and redistribute it\n"
238         "freely, subject to the following restrictions:\n"
239         "\n"
240         "1. The origin of this software must not be misrepresented; you must not\n"
241         " claim that you wrote the original software. If you use this software\n"
242         " in a product, an acknowledgment in the product documentation would be\n"
243         " appreciated but is not required.\n"
244         "2. Altered source versions must be plainly marked as such, and must not be\n"
245         " misrepresented as being the original software.\n"
246         "3. This notice may not be removed or altered from any source distribution.\n"
247         "\n"
248         "Alexey Yakovenko waker@users.sourceforge.net\n"
249     ,
250     .plugin.website = "http://deadbeef.sf.net",
251     .open = stdio_open,
252     .close = stdio_close,
253     .read = stdio_read,
254     .seek = stdio_seek,
255     .tell = stdio_tell,
256     .rewind = stdio_rewind,
257     .getlength = stdio_getlength,
258     .get_content_type = stdio_get_content_type,
259     .is_streaming = stdio_is_streaming
260 };
261 
262 DB_plugin_t *
stdio_load(DB_functions_t * api)263 stdio_load (DB_functions_t *api) {
264     deadbeef = api;
265     return DB_PLUGIN (&plugin);
266 }
267 
268