µGFX  2.9
version 2.9
gfile.c
1 /*
2  * This file is subject to the terms of the GFX License. If a copy of
3  * the license was not distributed with this file, you can obtain one at:
4  *
5  * http://ugfx.io/license.html
6  */
7 
8 #include "../../gfx.h"
9 
10 #if GFX_USE_GFILE
11 
12 #include "gfile_fs.h"
13 
14 /**
15  * Define the VMT's for the file-systems we want to search for files.
16  * Virtual file-systems that have special open() calls do not need to
17  * be in this list.
18  */
19 #if GFILE_NEED_USERFS
20  extern const GFILEVMT FsUSERVMT;
21 #endif
22 #if GFILE_NEED_ROMFS
23  extern const GFILEVMT FsROMVMT;
24 #endif
25 #if GFILE_NEED_NATIVEFS
26  extern const GFILEVMT FsNativeVMT;
27 #endif
28 #if GFILE_NEED_FATFS
29  extern const GFILEVMT FsFatFSVMT;
30 #endif
31 #if GFILE_NEED_RAMFS
32  extern const GFILEVMT FsRAMVMT;
33 #endif
34 
35 
36 /**
37  * The order of the file-systems below determines the order
38  * that they are searched to find a file.
39  *
40  * This should read: static const GFILEVMT const * FsArray[] = {
41  * However, some major C compilers complain about duplicate const specifiers although this is perfectly valid standard C.
42  */
43 static const GFILEVMT* FsArray[] = {
44  #if GFILE_NEED_USERFS
45  &FsUSERVMT,
46  #endif
47  #if GFILE_NEED_ROMFS
48  &FsROMVMT,
49  #endif
50  #if GFILE_NEED_NATIVEFS
51  &FsNativeVMT,
52  #endif
53  #if GFILE_NEED_FATFS
54  &FsFatFSVMT,
55  #endif
56  #if GFILE_NEED_RAMFS
57  &FsRAMVMT,
58  #endif
59 };
60 
61 /*
62  * The table of GFILE's
63  */
64 static GFILE gfileArr[GFILE_MAX_GFILES];
65 GFILE *gfileStdIn;
66 GFILE *gfileStdOut;
67 GFILE *gfileStdErr;
68 
69 /**
70  * The init routine
71  */
72 void _gfileInit(void) {
73  #if GFILE_NEED_NATIVEFS
74  extern void _gfileNativeAssignStdio(void);
75  _gfileNativeAssignStdio();
76  #endif
77 }
78 
79 void _gfileDeinit(void)
80 {
81  GFILE * f;
82  for (f = gfileArr; f < &gfileArr[GFILE_MAX_GFILES]; f++) {
83  if (f->flags & GFILEFLG_OPEN)
84  gfileClose(f);
85  }
86 }
87 
88 /**
89  * Internal routine to find an empty GFILE slot and interpret flags.
90  */
91 GFILE *_gfileFindSlot(const char *mode) {
92  GFILE * f;
93 
94  // First find an available GFILE slot.
95  for (f = gfileArr; f < &gfileArr[GFILE_MAX_GFILES]; f++) {
96  if (!(f->flags & GFILEFLG_OPEN)) {
97  // Get the flags
98  switch(mode[0]) {
99  case 'r':
100  f->flags = GFILEFLG_READ|GFILEFLG_MUSTEXIST;
101  while (*++mode) {
102  switch(mode[0]) {
103  case '+': f->flags |= GFILEFLG_WRITE; break;
104  case 'b': f->flags |= GFILEFLG_BINARY; break;
105  }
106  }
107  break;
108  case 'w':
109  f->flags = GFILEFLG_WRITE|GFILEFLG_TRUNC;
110  while (*++mode) {
111  switch(mode[0]) {
112  case '+': f->flags |= GFILEFLG_READ; break;
113  case 'b': f->flags |= GFILEFLG_BINARY; break;
114  case 'x': f->flags |= GFILEFLG_MUSTNOTEXIST; break;
115  }
116  }
117  break;
118  case 'a':
119  f->flags = GFILEFLG_WRITE|GFILEFLG_APPEND;
120  while (*++mode) {
121  switch(mode[0]) {
122  case '+': f->flags |= GFILEFLG_READ; break;
123  case 'b': f->flags |= GFILEFLG_BINARY; break;
124  case 'x': f->flags |= GFILEFLG_MUSTNOTEXIST; break;
125  }
126  }
127  break;
128  default:
129  return 0;
130  }
131  return f;
132  }
133  }
134  return 0;
135 }
136 
137 /********************************************************
138  * IO routines
139  ********************************************************/
140 
141 gBool gfileExists(const char *fname) {
142  const GFILEVMT * const *p;
143 
144  #if GFILE_ALLOW_DEVICESPECIFIC
145  if (fname[0] && fname[1] == '|') {
146  for(p = FsArray; p < &FsArray[sizeof(FsArray)/sizeof(FsArray[0])]; p++) {
147  if (p[0]->prefix == fname[0])
148  return p[0]->exists && p[0]->exists(fname+2);
149  }
150  return gFalse;
151  }
152  #endif
153 
154  for(p = FsArray; p < &FsArray[sizeof(FsArray)/sizeof(FsArray[0])]; p++) {
155  if (p[0]->exists && p[0]->exists(fname))
156  return gTrue;
157  }
158  return gFalse;
159 }
160 
161 gBool gfileDelete(const char *fname) {
162  const GFILEVMT **p;
163 
164  #if GFILE_ALLOW_DEVICESPECIFIC
165  if (fname[0] && fname[1] == '|') {
166  for(p = FsArray; p < &FsArray[sizeof(FsArray)/sizeof(FsArray[0])]; p++) {
167  if (p[0]->prefix == fname[0])
168  return p[0]->del && p[0]->del(fname+2);
169  }
170  return gFalse;
171  }
172  #endif
173 
174  for(p = FsArray; p < &FsArray[sizeof(FsArray)/sizeof(FsArray[0])]; p++) {
175  if (p[0]->del && p[0]->del(fname))
176  return gTrue;
177  }
178  return gFalse;
179 }
180 
181 gFileSize gfileGetFilesize(const char *fname) {
182  const GFILEVMT * const *p;
183  gFileSize res;
184 
185  #if GFILE_ALLOW_DEVICESPECIFIC
186  if (fname[0] && fname[1] == '|') {
187  for(p = FsArray; p < &FsArray[sizeof(FsArray)/sizeof(FsArray[0])]; p++) {
188  if (p[0]->prefix == fname[0])
189  return p[0]->filesize ? p[0]->filesize(fname+2) : -1;
190  }
191  return (gFileSize)-1;
192  }
193  #endif
194 
195  for(p = FsArray; p < &FsArray[sizeof(FsArray)/sizeof(FsArray[0])]; p++) {
196  if (p[0]->filesize && (res = p[0]->filesize(fname)) != -1)
197  return res;
198  }
199  return -1;
200 }
201 
202 gBool gfileRename(const char *oldname, const char *newname) {
203  const GFILEVMT * const *p;
204 
205  #if GFILE_ALLOW_DEVICESPECIFIC
206  if ((oldname[0] && oldname[1] == '|') || (newname[0] && newname[1] == '|')) {
207  char ch;
208 
209  if (oldname[0] && oldname[1] == '|') {
210  ch = oldname[0];
211  oldname += 2;
212  if (newname[0] && newname[1] == '|') {
213  if (newname[0] != ch)
214  // Both oldname and newname are fs specific but different ones.
215  return gFalse;
216  newname += 2;
217  }
218  } else {
219  ch = newname[0];
220  newname += 2;
221  }
222  for(p = FsArray; p < &FsArray[sizeof(FsArray)/sizeof(FsArray[0])]; p++) {
223  if (p[0]->prefix == ch)
224  return p[0]->ren && p[0]->ren(oldname, newname);
225  }
226  return gFalse;
227  }
228  #endif
229 
230  for(p = FsArray; p < &FsArray[sizeof(FsArray)/sizeof(FsArray[0])]; p++) {
231  if (p[0]->ren && p[0]->ren(oldname,newname))
232  return gTrue;
233  }
234  return gFalse;
235 }
236 
237 static gBool testopen(const GFILEVMT *p, GFILE *f, const char *fname) {
238  // If we want write but the fs doesn't allow it then return
239  if ((f->flags & GFILEFLG_WRITE) && !(p->flags & GFSFLG_WRITEABLE))
240  return gFalse;
241 
242  // Try to open
243  if (!p->open || !p->open(f, fname))
244  return gFalse;
245 
246  // File is open - fill in all the details
247  f->vmt = p;
248  f->pos = 0;
249  f->flags |= GFILEFLG_OPEN;
250  if (p->flags & GFSFLG_SEEKABLE)
251  f->flags |= GFILEFLG_CANSEEK;
252  return gTrue;
253 }
254 
255 GFILE *gfileOpen(const char *fname, const char *mode) {
256  GFILE * f;
257  const GFILEVMT * const *p;
258 
259  // Get an empty file and set the flags
260  if (!(f = _gfileFindSlot(mode)))
261  return 0;
262 
263  #if GFILE_ALLOW_DEVICESPECIFIC
264  if (fname[0] && fname[1] == '|') {
265  for(p = FsArray; p < &FsArray[sizeof(FsArray)/sizeof(FsArray[0])]; p++) {
266  if (p[0]->prefix == fname[0])
267  return testopen(p[0], f, fname+2) ? f : 0;
268  }
269 
270  // File not found
271  return 0;
272  }
273  #endif
274 
275  for(p = FsArray; p < &FsArray[sizeof(FsArray)/sizeof(FsArray[0])]; p++) {
276  if (testopen(p[0], f, fname))
277  return f;
278  }
279 
280  // File not found
281  return 0;
282 }
283 
284 void gfileClose(GFILE *f) {
285  if (!f || !(f->flags & GFILEFLG_OPEN))
286  return;
287  if (f->vmt->close)
288  f->vmt->close(f);
289  f->flags = 0;
290 }
291 
292 gMemSize gfileRead(GFILE *f, void *buf, gMemSize len) {
293  gMemSize res;
294 
295  if (!f || (f->flags & (GFILEFLG_OPEN|GFILEFLG_READ)) != (GFILEFLG_OPEN|GFILEFLG_READ))
296  return 0;
297  if (!f->vmt->read)
298  return 0;
299  if ((res = f->vmt->read(f, buf, len)) <= 0)
300  return 0;
301  f->pos += res;
302  return res;
303 }
304 
305 gMemSize gfileWrite(GFILE *f, const void *buf, gMemSize len) {
306  gMemSize res;
307 
308  if (!f || (f->flags & (GFILEFLG_OPEN|GFILEFLG_WRITE)) != (GFILEFLG_OPEN|GFILEFLG_WRITE))
309  return 0;
310  if (!f->vmt->write)
311  return 0;
312  if ((res = f->vmt->write(f, buf, len)) <= 0)
313  return 0;
314  f->pos += res;
315  return res;
316 }
317 
318 gFileSize gfileGetPos(GFILE *f) {
319  if (!f || !(f->flags & GFILEFLG_OPEN))
320  return 0;
321  return f->pos;
322 }
323 
324 gBool gfileSetPos(GFILE *f, gFileSize pos) {
325  if (!f || !(f->flags & GFILEFLG_OPEN))
326  return gFalse;
327  if (!f->vmt->setpos || !f->vmt->setpos(f, pos))
328  return gFalse;
329  f->pos = pos;
330  return gTrue;
331 }
332 
333 gFileSize gfileGetSize(GFILE *f) {
334  if (!f || !(f->flags & GFILEFLG_OPEN))
335  return 0;
336  if (!f->vmt->getsize)
337  return 0;
338  return f->vmt->getsize(f);
339 }
340 
341 gBool gfileEOF(GFILE *f) {
342  if (!f || !(f->flags & GFILEFLG_OPEN))
343  return gTrue;
344  if (!f->vmt->eof)
345  return gFalse;
346  return f->vmt->eof(f);
347 }
348 
349 gBool gfileMount(char fs, const char* drive) {
350  const GFILEVMT * const *p;
351 
352  // Find the correct VMT
353  for(p = FsArray; p < &FsArray[sizeof(FsArray)/sizeof(FsArray[0])]; p++) {
354  if (p[0]->prefix == fs) {
355  if (!p[0]->mount)
356  return gFalse;
357  return p[0]->mount(drive);
358  }
359  }
360  return gFalse;
361 }
362 
363 gBool gfileUnmount(char fs, const char* drive) {
364  const GFILEVMT * const *p;
365 
366  // Find the correct VMT
367  for(p = FsArray; p < &FsArray[sizeof(FsArray)/sizeof(FsArray[0])]; p++) {
368  if (p[0]->prefix == fs) {
369  if (!p[0]->mount)
370  return gFalse;
371  return p[0]->unmount(drive);
372  }
373  }
374  return gFalse;
375 }
376 
377 gBool gfileSync(GFILE *f) {
378  if (!f->vmt->sync)
379  return gFalse;
380  return f->vmt->sync(f);
381 }
382 
383 #if GFILE_NEED_FILELISTS
384  gfileList *gfileOpenFileList(char fs, const char *path, gBool dirs) {
385  const GFILEVMT * const *p;
386  gfileList * pfl;
387 
388  // Find the correct VMT
389  for(p = FsArray; p < &FsArray[sizeof(FsArray)/sizeof(FsArray[0])]; p++) {
390  if (p[0]->prefix == fs) {
391  if (!p[0]->flopen)
392  return 0;
393  pfl = p[0]->flopen(path, dirs);
394  if (pfl) {
395  pfl->vmt = p[0];
396  pfl->dirs = dirs;
397  }
398  return pfl;
399  }
400  }
401  return 0;
402  }
403 
404  const char *gfileReadFileList(gfileList *pfl) {
405  return pfl->vmt->flread ? pfl->vmt->flread(pfl) : 0;
406  }
407 
408  void gfileCloseFileList(gfileList *pfl) {
409  if (pfl->vmt->flclose)
410  pfl->vmt->flclose(pfl);
411  }
412 #endif
413 
414 #endif /* GFX_USE_GFILE */
GFILE file system header.
gBool gfileUnmount(char fs, const char *drive)
Unmount a logical drive (aka partition)
GFILE * gfileOpen(const char *fname, const char *mode)
Open file.
gBool gfileSetPos(GFILE *f, gFileSize pos)
Set the position of the read/write cursor.
gBool gfileMount(char fs, const char *drive)
Mount a logical drive (aka partition)
gBool gfileDelete(const char *fname)
Delete file.
gMemSize gfileRead(GFILE *f, void *buf, gMemSize len)
Read from file.
gBool gfileSync(GFILE *f)
Syncs the file object (flushes the buffer)
gBool gfileExists(const char *fname)
Check if file exists.
gfileList * gfileOpenFileList(char fs, const char *path, gBool dirs)
Open a file list.
#define GFILE_MAX_GFILES
The maximum number of open files.
void gfileClose(GFILE *f)
Close file.
const char * gfileReadFileList(gfileList *pfl)
Get the next file in a file list.
struct GFILE GFILE
A file pointer.
Definition: gfile.h:34
gFileSize gfileGetPos(GFILE *f)
Get the current position of the read/write cursor.
gMemSize gfileWrite(GFILE *f, const void *buf, gMemSize len)
Write to file.
void gfileCloseFileList(gfileList *pfl)
Close a file list.
gBool gfileRename(const char *oldname, const char *newname)
Rename file.
gFileSize gfileGetFilesize(const char *fname)
Get the size of a file.
gFileSize gfileGetSize(GFILE *f)
Get the size of file.
gBool gfileEOF(GFILE *f)
Check for EOF.