diff --git a/docs/releases.txt b/docs/releases.txt index 6814fd0b..2908535e 100644 --- a/docs/releases.txt +++ b/docs/releases.txt @@ -22,6 +22,8 @@ FEATURE: Added support for the RawOS real time operating system FEATURE: Operating System initialisation is now optional FEATURE: Added optional transparency to container FEATURE: Prevent mouse events going to obscured widgets +FEATURE: Add GFILE support for file lists +FEATURE: Add GFILE support for C strings as files *** Release 2.1 *** diff --git a/gfxconf.example.h b/gfxconf.example.h index 8163b6f5..2abcf0b4 100644 --- a/gfxconf.example.h +++ b/gfxconf.example.h @@ -224,10 +224,8 @@ //#define GFILE_NEED_PRINTG FALSE //#define GFILE_NEED_SCANG FALSE //#define GFILE_NEED_STRINGS FALSE +//#define GFILE_NEED_FILELISTS FALSE //#define GFILE_NEED_STDIO FALSE -// #define GFILE_ALLOW_FLOATS FALSE -// #define GFILE_ALLOW_DEVICESPECIFIC FALSE -// #define GFILE_MAX_GFILES 3 //#define GFILE_NEED_NOAUTOMOUNT FALSE //#define GFILE_NEED_NOAUTOSYNC FALSE @@ -238,6 +236,9 @@ //#define GFILE_NEED_NATIVEFS FALSE //#define GFILE_NEED_CHBIOSFS FALSE +//#define GFILE_ALLOW_FLOATS FALSE +//#define GFILE_ALLOW_DEVICESPECIFIC FALSE +//#define GFILE_MAX_GFILES 3 /////////////////////////////////////////////////////////////////////////// // GADC // diff --git a/src/gfile/gfile.c b/src/gfile/gfile.c index abffd410..caf7f22f 100644 --- a/src/gfile/gfile.c +++ b/src/gfile/gfile.c @@ -35,6 +35,11 @@ struct GFILE { long int pos; }; +struct gfileList { + const struct GFILEVMT * vmt; + bool_t dirs; +}; + typedef struct GFILEVMT { const struct GFILEVMT * next; uint8_t flags; @@ -59,6 +64,11 @@ typedef struct GFILEVMT { bool_t (*mount) (const char *drive); bool_t (*unmount) (const char *drive); bool_t (*sync) (GFILE *f); + #if GFILE_NEED_FILELISTS + gfileList * (*flopen) (const char *path, bool_t dirs); + const char *(*flread) (gfileList *pfl); + void (*flclose) (gfileList *pfl); + #endif } GFILEVMT; // The chain of FileSystems @@ -70,6 +80,9 @@ GFILE *gfileStdIn; GFILE *gfileStdOut; GFILE *gfileStdErr; +// Forward definition used by some special open calls +static GFILE *findemptyfile(const char *mode); + /** * The order of the file-systems below determines the order * that they are searched to find a file. @@ -118,6 +131,34 @@ GFILE *gfileStdErr; #include "src/gfile/inc_romfs.c" #endif +/******************************************************** + * The virtual string file VMT + ********************************************************/ +#if GFILE_NEED_STRINGS + #include "src/gfile/inc_strings.c" +#endif + +/******************************************************** + * Printg Routines + ********************************************************/ +#if GFILE_NEED_PRINTG + #include "src/gfile/inc_printg.c" +#endif + +/******************************************************** + * Scang Routines + ********************************************************/ +#if GFILE_NEED_SCANG + #include "src/gfile/inc_scang.c" +#endif + +/******************************************************** + * Stdio Emulation Routines + ********************************************************/ +#if GFILE_NEED_STDIO + #include "src/gfile/inc_stdio.c" +#endif + /******************************************************** * IO routines ********************************************************/ @@ -251,39 +292,48 @@ bool_t gfileRename(const char *oldname, const char *newname) { return FALSE; } -static uint16_t mode2flags(const char *mode) { - uint16_t flags; +static GFILE *findemptyfile(const char *mode) { + GFILE * f; - switch(mode[0]) { - case 'r': - flags = GFILEFLG_READ|GFILEFLG_MUSTEXIST; - while (*++mode) { + // First find an available GFILE slot. + for (f = gfileArr; f < &gfileArr[GFILE_MAX_GFILES]; f++) { + if (!(f->flags & GFILEFLG_OPEN)) { + // Get the flags switch(mode[0]) { - case '+': flags |= GFILEFLG_WRITE; break; - case 'b': flags |= GFILEFLG_BINARY; break; + case 'r': + f->flags = GFILEFLG_READ|GFILEFLG_MUSTEXIST; + while (*++mode) { + switch(mode[0]) { + case '+': f->flags |= GFILEFLG_WRITE; break; + case 'b': f->flags |= GFILEFLG_BINARY; break; + } + } + break; + case 'w': + f->flags = GFILEFLG_WRITE|GFILEFLG_TRUNC; + while (*++mode) { + switch(mode[0]) { + case '+': f->flags |= GFILEFLG_READ; break; + case 'b': f->flags |= GFILEFLG_BINARY; break; + case 'x': f->flags |= GFILEFLG_MUSTNOTEXIST; break; + } + } + break; + case 'a': + f->flags = GFILEFLG_WRITE|GFILEFLG_APPEND; + while (*++mode) { + switch(mode[0]) { + case '+': f->flags |= GFILEFLG_READ; break; + case 'b': f->flags |= GFILEFLG_BINARY; break; + case 'x': f->flags |= GFILEFLG_MUSTNOTEXIST; break; + } + } + break; + default: + return 0; } + return f; } - return flags; - case 'w': - flags = GFILEFLG_WRITE|GFILEFLG_TRUNC; - while (*++mode) { - switch(mode[0]) { - case '+': flags |= GFILEFLG_READ; break; - case 'b': flags |= GFILEFLG_BINARY; break; - case 'x': flags |= GFILEFLG_MUSTNOTEXIST; break; - } - } - return flags; - case 'a': - flags = GFILEFLG_WRITE|GFILEFLG_APPEND; - while (*++mode) { - switch(mode[0]) { - case '+': flags |= GFILEFLG_READ; break; - case 'b': flags |= GFILEFLG_BINARY; break; - case 'x': flags |= GFILEFLG_MUSTNOTEXIST; break; - } - } - return flags; } return 0; } @@ -307,112 +357,34 @@ static bool_t testopen(const GFILEVMT *p, GFILE *f, const char *fname) { } GFILE *gfileOpen(const char *fname, const char *mode) { - uint16_t flags; GFILE * f; const GFILEVMT *p; - // Get the requested mode - if (!(flags = mode2flags(mode))) + // Get an empty file and set the flags + if (!(f = findemptyfile(mode))) return 0; #if GFILE_ALLOW_DEVICESPECIFIC if (fname[0] && fname[1] == '|') { - // First find an available GFILE slot. - for (f = gfileArr; f < &gfileArr[GFILE_MAX_GFILES]; f++) { - if (!(f->flags & GFILEFLG_OPEN)) { - // Try to open the file - f->flags = flags; - for(p = FsChain; p; p = p->next) { - if (p->prefix == fname[0]) - return testopen(p, f, fname+2) ? f : 0; - } - // File not found - break; - } + for(p = FsChain; p; p = p->next) { + if (p->prefix == fname[0]) + return testopen(p, f, fname+2) ? f : 0; } - // No available slot + // File not found return 0; } #endif - // First find an available GFILE slot. - for (f = gfileArr; f < &gfileArr[GFILE_MAX_GFILES]; f++) { - if (!(f->flags & GFILEFLG_OPEN)) { - - // Try to open the file - f->flags = flags; - for(p = FsChain; p; p = p->next) { - if (testopen(p, f, fname)) - return f; - } - // File not found - break; - } + for(p = FsChain; p; p = p->next) { + if (testopen(p, f, fname)) + return f; } - // No available slot + // File not found return 0; } -#if GFILE_NEED_CHIBIOSFS && GFX_USE_OS_CHIBIOS - GFILE * gfileOpenBaseFileStream(void *BaseFileStreamPtr, const char *mode) { - GFILE * f; - - // First find an available GFILE slot. - for (f = gfileArr; f < &gfileArr[GFILE_MAX_GFILES]; f++) { - if (!(f->flags & GFILEFLG_OPEN)) { - // Get the flags - if (!(f->flags = mode2flags(mode))) - return 0; - - // If we want write but the fs doesn't allow it then return - if ((f->flags & GFILEFLG_WRITE) && !(FsCHIBIOSVMT.flags & GFSFLG_WRITEABLE)) - return 0; - - // File is open - fill in all the details - f->vmt = &FsCHIBIOSVMT; - f->obj = BaseFileStreamPtr; - f->pos = 0; - f->flags |= GFILEFLG_OPEN|GFILEFLG_CANSEEK; - return f; - } - } - - // No available slot - return 0; - } -#endif - -#if GFILE_NEED_MEMFS - GFILE * gfileOpenMemory(void *memptr, const char *mode) { - GFILE * f; - - // First find an available GFILE slot. - for (f = gfileArr; f < &gfileArr[GFILE_MAX_GFILES]; f++) { - if (!(f->flags & GFILEFLG_OPEN)) { - // Get the flags - if (!(f->flags = mode2flags(mode))) - return 0; - - // If we want write but the fs doesn't allow it then return - if ((f->flags & GFILEFLG_WRITE) && !(FsMemVMT.flags & GFSFLG_WRITEABLE)) - return 0; - - // File is open - fill in all the details - f->vmt = &FsMemVMT; - f->obj = memptr; - f->pos = 0; - f->flags |= GFILEFLG_OPEN|GFILEFLG_CANSEEK; - return f; - } - } - - // No available slot - return 0; - } -#endif - void gfileClose(GFILE *f) { if (!f || !(f->flags & GFILEFLG_OPEN)) return; @@ -512,579 +484,35 @@ bool_t gfileSync(GFILE *f) { return f->vmt->sync(f); } -/******************************************************** - * String VMT routines - ********************************************************/ -#if GFILE_NEED_STRINGS && (GFILE_NEED_PRINTG || GFILE_NEED_SCANG) - #include +#if GFILE_NEED_FILELISTS + gfileList *gfileOpenFileList(char fs, const char *path, bool_t dirs) { + const GFILEVMT *p; + gfileList * pfl; - // Special String VMT - static int StringRead(GFILE *f, void *buf, int size) { - (void) size; - - // size must be 1 for a complete read - if (!((char *)f->obj)[f->pos]) - return 0; - ((char *)buf)[0] = ((char *)f->obj)[f->pos]; - return 1; - } - static int StringWrite(GFILE *f, const void *buf, int size) { - (void) size; - - // size must be 1 for a complete write - ((char *)f->obj)[f->pos] = ((char *)buf)[0]; - return 1; - } - static const GFILEVMT StringVMT = { - 0, // next - 0, // flags - '_', // prefix - 0, 0, 0, 0, - 0, 0, StringRead, StringWrite, - 0, 0, 0, - 0, 0, 0 - }; -#endif - -/******************************************************** - * printg routines - ********************************************************/ -#if GFILE_NEED_PRINTG - #include - - #define MAX_FILLER 11 - #define FLOAT_PRECISION 100000 - - int fnprintg(GFILE *f, int maxlen, const char *fmt, ...) { - int res; - va_list ap; - - va_start(ap, fmt); - res = vfnprintg(f, maxlen, fmt, ap); - va_end(ap); - return res; - } - - static char *ltoa_wd(char *p, long num, unsigned radix, long divisor) { - int i; - char * q; - - if (!divisor) divisor = num; - - q = p + MAX_FILLER; - do { - i = (int)(num % radix); - i += '0'; - if (i > '9') - i += 'A' - '0' - 10; - *--q = i; - num /= radix; - } while ((divisor /= radix) != 0); - - i = (int)(p + MAX_FILLER - q); - do { - *p++ = *q++; - } while (--i); - - return p; - } - - int vfnprintg(GFILE *f, int maxlen, const char *fmt, va_list arg) { - int ret; - char *p, *s, c, filler; - int i, precision, width; - bool_t is_long, left_align; - long l; - #if GFILE_ALLOW_FLOATS - float f; - char tmpbuf[2*MAX_FILLER + 1]; - #else - char tmpbuf[MAX_FILLER + 1]; - #endif - - ret = 0; - if (maxlen < 0) - return 0; - if (!maxlen) - maxlen = -1; - - while (*fmt) { - if (*fmt != '%') { - gfileWrite(f, fmt, 1); - ret++; if (!--maxlen) return ret; - fmt++; - continue; - } - fmt++; - - p = s = tmpbuf; - left_align = FALSE; - filler = ' '; - width = 0; - precision = 0; - - if (*fmt == '-') { - fmt++; - left_align = TRUE; - } - if (*fmt == '.') { - fmt++; - filler = '0'; - } - - while (1) { - c = *fmt++; - if (c >= '0' && c <= '9') { - c -= '0'; - width = width * 10 + c; - } else if (c == '*') - width = va_arg(arg, int); - else - break; - } - if (c == '.') { - while (1) { - c = *fmt++; - if (c >= '0' && c <= '9') { - c -= '0'; - precision = precision * 10 + c; - } else if (c == '*') - precision = va_arg(arg, int); - else - break; - } - } - /* Long modifier.*/ - if (c == 'l' || c == 'L') { - is_long = TRUE; - if (*fmt) - c = *fmt++; - } - else - is_long = (c >= 'A') && (c <= 'Z'); - - /* Command decoding.*/ - switch (c) { - case 0: - return ret; - case 'c': - filler = ' '; - *p++ = va_arg(arg, int); - break; - case 's': - filler = ' '; - if ((s = va_arg(arg, char *)) == 0) - s = "(null)"; - if (precision == 0) - precision = 32767; - for (p = s; *p && (--precision >= 0); p++); - break; - case 'D': - case 'd': - if (is_long) - l = va_arg(arg, long); - else - l = va_arg(arg, int); - if (l < 0) { - *p++ = '-'; - l = -l; - } - p = ltoa_wd(p, l, 10, 0); - break; - #if GFILE_ALLOW_FLOATS - case 'f': - f = (float) va_arg(arg, double); - if (f < 0) { - *p++ = '-'; - f = -f; - } - l = f; - p = ltoa_wd(p, l, 10, 0); - *p++ = '.'; - l = (f - l) * FLOAT_PRECISION; - p = ltoa_wd(p, l, 10, FLOAT_PRECISION / 10); - break; - #endif - case 'X': - case 'x': - c = 16; - goto unsigned_common; - case 'U': - case 'u': - c = 10; - goto unsigned_common; - case 'O': - case 'o': - c = 8; - unsigned_common: - if (is_long) - l = va_arg(arg, long); - else - l = va_arg(arg, int); - p = ltoa_wd(p, l, c, 0); - break; - default: - *p++ = c; - break; - } - - i = (int)(p - s); - if ((width -= i) < 0) - width = 0; - if (left_align == FALSE) - width = -width; - if (width < 0) { - if (*s == '-' && filler == '0') { - gfileWrite(f, s++, 1); - ret++; if (!--maxlen) return ret; - i--; - } - do { - gfileWrite(f, &filler, 1); - ret++; if (!--maxlen) return ret; - } while (++width != 0); - } - while (--i >= 0) { - gfileWrite(f, s++, 1); - ret++; if (!--maxlen) return ret; - } - while (width) { - gfileWrite(f, &filler, 1); - ret++; if (!--maxlen) return ret; - width--; - } - } - return ret; - } - - #if GFILE_NEED_STRINGS - int snprintg(char *buf, int maxlen, const char *fmt, ...) { - int res; - GFILE f; - va_list ap; - - if (maxlen <= 1) { - if (maxlen == 1) { - *buf = 0; + // Find the correct VMT + for(p = FsChain; p; p = p->next) { + if (p->prefix == fs) { + if (!p->flopen) return 0; + pfl = p->flopen(path, dirs); + if (pfl) { + pfl->vmt = p; + pfl->dirs = dirs; } - maxlen += 1; - } - f.flags = GFILEFLG_OPEN|GFILEFLG_WRITE; - f.vmt = &StringVMT; - f.pos = 0; - f.obj = buf; - va_start(ap, fmt); - res = vfnprintg(&f, maxlen-1, fmt, ap); - va_end(ap); - buf[res] = 0; - return res; - } - int vsnprintg(char *buf, int maxlen, const char *fmt, va_list arg) { - int res; - GFILE f; - - if (maxlen <= 1) { - if (maxlen == 1) { - *buf = 0; - return 0; - } - maxlen += 1; - } - f.flags = GFILEFLG_OPEN|GFILEFLG_WRITE; - f.vmt = &StringVMT; - f.pos = 0; - f.obj = buf; - res = vfnprintg(&f, maxlen-1, fmt, arg); - buf[res] = 0; - return res; - } - #endif -#endif - -/******************************************************** - * scang routines - ********************************************************/ -#if GFILE_NEED_SCANG - int fscang(GFILE *f, const char *fmt, ...) { - int res; - va_list ap; - - va_start(ap, fmt); - res = vfscang(f, fmt, ap); - va_end(ap); - return res; - } - - int vfscang(GFILE *f, const char *fmt, va_list arg) { - int res, width, size, base; - unsigned long num; - char c; - bool_t assign, negate; - char *p; - - for(res = 0; *fmt; fmt++) { - switch(*fmt) { - case ' ': case '\t': case '\r': case '\n': case '\v': case '\f': - break; - - case '%': - fmt++; - assign = TRUE; - negate = FALSE; - width = 0; - size = 1; - num = 0; - - if (*fmt == '*') { - fmt++; - assign = FALSE; - } - while(*fmt >= '0' && *fmt <= '9') - width = width * 10 + (*fmt++ - '0'); - if (*fmt == 'h') { - fmt++; - size = 0; - } else if (*fmt == 'l') { - fmt++; - size = 2; - } else if (*fmt == 'L') { - fmt++; - size = 3; - } - switch(*fmt) { - case 0: - return res; - case '%': - goto matchchar; - case 'c': - if (!width) { - while(1) { - if (!gfileRead(f, &c, 1)) return res; - switch(c) { - case ' ': case '\t': case '\r': - case '\n': case '\v': case '\f': continue; - } - break; - } - width = 1; - } else { - if (!gfileRead(f, &c, 1)) return res; - } - if (assign) { - p = va_arg(arg, char *); - res++; - *p++ = c; - } - while(--width) { - if (!gfileRead(f, &c, 1)) return res; - if (assign) *p++ = c; - } - break; - case 's': - while(1) { - if (!gfileRead(f, &c, 1)) return res; - switch(c) { - case ' ': case '\t': case '\r': - case '\n': case '\v': case '\f': continue; - } - break; - } - if (assign) { - p = va_arg(arg, char *); - res++; - *p++ = c; - } - if (width) { - while(--width) { - if (!gfileRead(f, &c, 1)) { - if (assign) *((char *)p) = 0; - return res; - } - if (assign) *p++ = c; - } - } else { - while(1) { - if (!gfileRead(f, &c, 1)) { - if (assign) *((char *)p) = 0; - return res; - } - switch(c) { - case ' ': case '\t': case '\r': - case '\n': case '\v': case '\f': break; - default: - if (assign) *p++ = c; - continue; - } - break; - } - //ungetch(c); - } - if (assign) *p = 0; - break; - case 'd': base = 10; goto getnum; - case 'i': base = -1; goto getnum; - case 'o': base = 8; goto getnum; - case 'u': base = 10; goto getnum; - case 'x': base = 16; goto getnum; - case 'b': base = 2; - getnum: - while(1) { - if (!gfileRead(f, &c, 1)) return res; - switch(c) { - case ' ': case '\t': case '\r': - case '\n': case '\v': case '\f': continue; - } - break; - } - if (c == '-' && *fmt != 'u') { - negate = TRUE; - if ((width && !--width) || !gfileRead(f, &c, 1)) return res; - } - if (base == -1) { - if (c == '0') { - if ((width && !--width) || !gfileRead(f, &c, 1)) goto assignnum; - switch(c) { - case 'x': case 'X': - base = 16; - if ((width && !--width) || !gfileRead(f, &c, 1)) return res; - break; - case 'b': case 'B': - base = 2; - if ((width && !--width) || !gfileRead(f, &c, 1)) return res; - break; - default: - base = 8; - break; - } - } else - base = 10; - } - while(1) { - if (c >= '0' && c <= '9' && c - '0' < base) - num = num * base + (c - '0'); - else if (c >= 'A' && c <= 'F' && base == 16) - num = num * base + (c - ('A'-10)); - else if (c >= 'a' && c <= 'f' && base == 16) - num = num * base + (c - ('a'-10)); - else { - // ungetch(c) - break; - } - if ((width && !--width) || !gfileRead(f, &c, 1)) - break; - } - - assignnum: - if (negate) - num = -num; - - if (assign) { - switch(size) { - case 0: // short - p = (char *)va_arg(arg, short *); - res++; - *((short *)p) = (short)num; - case 1: // int - p = (char *)va_arg(arg, int *); - res++; - *((int *)p) = (int)num; - case 2: case 3: // long - p = (char *)va_arg(arg, long *); - res++; - *((long *)p) = (long)num; - } - } - break; - - #if GFILE_ALLOW_FLOATS - case 'e': case 'f': case 'g': - // TODO - #endif - default: - return res; - } - - break; - - default: - matchchar: - while(1) { - if (!gfileRead(f, &c, 1)) return res; - switch(c) { - case ' ': case '\t': case '\r': - case '\n': case '\v': case '\f': continue; - } - break; - } - if (c != *fmt) return res; - break; + return pfl; } } - return res; - } - - #if GFILE_NEED_STRINGS - int sscang(const char *buf, const char *fmt, ...) { - int res; - GFILE f; - va_list ap; - - f.flags = GFILEFLG_OPEN|GFILEFLG_READ; - f.vmt = &StringVMT; - f.pos = 0; - f.obj = (void *)buf; - va_start(ap, fmt); - res = vfscang(&f, fmt, ap); - va_end(ap); - return res; - } - - int vsscang(const char *buf, const char *fmt, va_list arg) { - int res; - GFILE f; - - f.flags = GFILEFLG_OPEN|GFILEFLG_READ; - f.vmt = &StringVMT; - f.pos = 0; - f.obj = (void *)buf; - res = vfscang(&f, fmt, arg); - return res; - } - #endif -#endif - -/******************************************************** - * stdio emulation routines - ********************************************************/ -#if GFILE_NEED_STDIO - size_t gstdioRead(void * ptr, size_t size, size_t count, FILE *f) { - return gfileRead(f, ptr, size*count)/size; - } - size_t gstdioWrite(const void * ptr, size_t size, size_t count, FILE *f) { - return gfileWrite(f, ptr, size*count)/size; - } - int gstdioSeek(FILE *f, size_t offset, int origin) { - switch(origin) { - case SEEK_SET: - break; - case SEEK_CUR: - offset += f->pos; - break; - case SEEK_END: - offset += gfileGetSize(f); - break; - default: - return -1; - } - return gfileSetPos(f, offset) ? 0 : -1; - } - int gstdioGetpos(FILE *f, long int *pos) { - if (!(f->flags & GFILEFLG_OPEN)) - return -1; - *pos = f->pos; return 0; } + + const char *gfileReadFileList(gfileList *pfl) { + return pfl->vmt->flread ? pfl->vmt->flread(pfl) : 0; + } + + void gfileCloseFileList(gfileList *pfl) { + if (pfl->vmt->flclose) + pfl->vmt->flclose(pfl); + } #endif #endif /* GFX_USE_GFILE */ diff --git a/src/gfile/inc_chibiosfs.c b/src/gfile/inc_chibiosfs.c index 8d321b33..13ae6cac 100644 --- a/src/gfile/inc_chibiosfs.c +++ b/src/gfile/inc_chibiosfs.c @@ -27,22 +27,39 @@ static const GFILEVMT FsCHIBIOSVMT = { 0, 0, 0, 0, 0, ChibiOSBFSClose, ChibiOSBFSRead, ChibiOSBFSWrite, ChibiOSBFSSetpos, ChibiOSBFSGetsize, ChibiOSBFSEof, - 0, 0, - 0 + 0, 0, 0, + #if GFILE_NEED_FILELISTS + 0, 0, 0, + #endif }; static void ChibiOSBFSClose(GFILE *f) { - chFileStreamClose(((BaseFileStream *)f->fd)); + chFileStreamClose(((BaseFileStream *)f->obj)); } static int ChibiOSBFSRead(GFILE *f, void *buf, int size) { - return chSequentialStreamRead(((BaseFileStream *)f->fd), (uint8_t *)buf, size); + return chSequentialStreamRead(((BaseFileStream *)f->obj), (uint8_t *)buf, size); } static int ChibiOSBFSWrite(GFILE *f, const void *buf, int size) { - return chSequentialStreamWrite(((BaseFileStream *)f->fd), (uint8_t *)buf, size); + return chSequentialStreamWrite(((BaseFileStream *)f->obj), (uint8_t *)buf, size); } static bool_t ChibiOSBFSSetpos(GFILE *f, long int pos) { - chFileStreamSeek(((BaseFileStream *)f->fd), pos); + chFileStreamSeek(((BaseFileStream *)f->obj), pos); return TRUE; } -static long int ChibiOSBFSGetsize(GFILE *f) { return chFileStreamGetSize(((BaseFileStream *)f->fd)); } -static bool_t ChibiOSBFSEof(GFILE *f) { return f->pos >= chFileStreamGetSize(((BaseFileStream *)f->fd)); } +static long int ChibiOSBFSGetsize(GFILE *f) { return chFileStreamGetSize(((BaseFileStream *)f->obj)); } +static bool_t ChibiOSBFSEof(GFILE *f) { return f->pos >= chFileStreamGetSize(((BaseFileStream *)f->obj)); } + +GFILE * gfileOpenBaseFileStream(void *BaseFileStreamPtr, const char *mode) { + GFILE * f; + + // Get an empty file and set the flags + if (!(f = findemptyfile(mode))) + return 0; + + // File is open - fill in all the details + f->vmt = &FsCHIBIOSVMT; + f->obj = BaseFileStreamPtr; + f->pos = 0; + f->flags |= GFILEFLG_OPEN|GFILEFLG_CANSEEK; + return f; +} diff --git a/src/gfile/inc_fatfs.c b/src/gfile/inc_fatfs.c index 8d7233e7..c8db0e64 100644 --- a/src/gfile/inc_fatfs.c +++ b/src/gfile/inc_fatfs.c @@ -30,6 +30,11 @@ static bool_t fatfsEOF(GFILE* f); static bool_t fatfsMount(const char* drive); static bool_t fatfsUnmount(const char* drive); static bool_t fatfsSync(GFILE* f); +#if _FS_MINIMIZE <= 1 && GFILE_NEED_FILELISTS + static gfileList *fatfsFlOpen(const char *path, bool_t dirs); + static const char *fatfsFlRead(gfileList *pfl); + static void fatfsFlClose(gfileList *pfl); +#endif static const GFILEVMT FsFatFSVMT = { GFILE_CHAINHEAD, @@ -46,14 +51,29 @@ static const GFILEVMT FsFatFSVMT = { fatfsSetPos, fatfsGetSize, fatfsEOF, - fatfsMount, - fatfsUnmount, - fatfsSync + fatfsMount, fatfsUnmount, fatfsSync, + #if GFILE_NEED_FILELISTS + #if _FS_MINIMIZE <= 1 + fatfsFlOpen, fatfsFlRead, fatfsFlClose + #else + 0, 0, 0 + #endif + #endif }; #undef GFILE_CHAINHEAD #define GFILE_CHAINHEAD &FsFatFSVMT +// Our directory list structure +typedef struct fatfsList { + gfileList fl; // This must be the first element. + DIR dir; + FILINFO fno; + #if _USE_LFN + char lfn[_MAX_LFN + 1]; /* Buffer to store the LFN */ + #endif +} fatfsList; + // optimize these later on. Use an array to have multiple FatFS static bool_t fatfs_mounted = FALSE; static FATFS fatfs_fs; @@ -245,3 +265,58 @@ static bool_t fatfsSync(GFILE *f) return TRUE; } +#if _FS_MINIMIZE <= 1 && GFILE_NEED_FILELISTS + static gfileList *fatfsFlOpen(const char *path, bool_t dirs) { + fatfsList *p; + (void) dirs; + + if (!(p = gfxAlloc(sizeof(fatfsList)))) + return 0; + + if (f_opendir(&p->dir, path) != FR_OK) { + gfxFree(p); + return 0; + } + return &p->fl; + } + + static const char *fatfsFlRead(gfileList *pfl) { + #define ffl ((fatfsList *)pfl) + + while(1) { + #if _USE_LFN + ffl->fno.lfname = ffl->lfn; + ffl->fno.lfsize = sizeof(ffl->lfn); + #endif + + // Read the next entry + if (f_readdir(&ffl->dir, &ffl->fno) != FR_OK || !ffl->fno.fname[0]) + return 0; + + /* Ignore dot entries */ + if (ffl->fno.fname[0] == '.') continue; + + /* Is it a directory */ + if (ffl->fl.dirs) { + if ((ffl->fno.fattrib & AM_DIR)) + break; + } else { + if (!(ffl->fno.fattrib & AM_DIR)) + break; + } + } + + #if _USE_LFN + return ffl->fno.lfname[0] ? ffl->fno.lfname : ffl->fno.fname; + #else + return ffl->fno.fname; + #endif + #undef ffl + } + + static void fatfsFlClose(gfileList *pfl) { + f_closedir(&((fatfsList *)pfl)->dir); + gfxFree(pfl); + } + +#endif diff --git a/src/gfile/inc_memfs.c b/src/gfile/inc_memfs.c index baeb0e97..6177b7d8 100644 --- a/src/gfile/inc_memfs.c +++ b/src/gfile/inc_memfs.c @@ -26,8 +26,10 @@ static const GFILEVMT FsMemVMT = { 0, 0, 0, 0, 0, 0, MEMRead, MEMWrite, MEMSetpos, 0, 0, - 0, 0, - 0 + 0, 0, 0, + #if GFILE_NEED_FILELISTS + 0, 0, 0, + #endif }; static int MEMRead(GFILE *f, void *buf, int size) { @@ -43,3 +45,18 @@ static bool_t MEMSetpos(GFILE *f, long int pos) { (void) pos; return TRUE; } + +GFILE * gfileOpenMemory(void *memptr, const char *mode) { + GFILE *f; + + // Get an empty file and set the flags + if (!(f = findemptyfile(mode))) + return 0; + + // File is open - fill in all the details + f->vmt = &FsMemVMT; + f->obj = memptr; + f->pos = 0; + f->flags |= GFILEFLG_OPEN|GFILEFLG_CANSEEK; + return f; +} diff --git a/src/gfile/inc_nativefs.c b/src/gfile/inc_nativefs.c index 6845cb71..8c28480b 100644 --- a/src/gfile/inc_nativefs.c +++ b/src/gfile/inc_nativefs.c @@ -33,6 +33,11 @@ static int NativeWrite(GFILE *f, const void *buf, int size); static bool_t NativeSetpos(GFILE *f, long int pos); static long int NativeGetsize(GFILE *f); static bool_t NativeEof(GFILE *f); +#if GFILE_NEED_FILELISTS + static gfileList *NativeFlOpen(const char *path, bool_t dirs); + static const char *NativeFlRead(gfileList *pfl); + static void NativeFlClose(gfileList *pfl); +#endif static const GFILEVMT FsNativeVMT = { GFILE_CHAINHEAD, // next @@ -46,8 +51,10 @@ static const GFILEVMT FsNativeVMT = { NativeDel, NativeExists, NativeFilesize, NativeRen, NativeOpen, NativeClose, NativeRead, NativeWrite, NativeSetpos, NativeGetsize, NativeEof, - 0, 0, - 0 + 0, 0, 0, + #if GFILE_NEED_FILELISTS + NativeFlOpen, NativeFlRead, NativeFlClose + #endif }; #undef GFILE_CHAINHEAD #define GFILE_CHAINHEAD &FsNativeVMT @@ -102,3 +109,108 @@ static long int NativeGetsize(GFILE *f) { if (fstat(fileno((FILE *)f->obj), &st)) return -1; return st.st_size; } + +#if GFILE_NEED_FILELISTS + #if defined(WIN32) || GFX_USE_OS_WIN32 + typedef struct NativeFileList { + gfileList fl; + HANDLE d; + WIN32_FIND_DATA f; + bool_t first; + } NativeFileList; + + static gfileList *NativeFlOpen(const char *path, bool_t dirs) { + NativeFileList *p; + (void) dirs; + + if (!(p = gfxAlloc(sizeof(NativeFileList)))) + return 0; + if ((p->d = FindFirstFile(path, &p->f)) == INVALID_HANDLE_VALUE) { + gfxFree(p); + return 0; + } + p->first = TRUE; + return &p->fl; + } + + static const char *NativeFlRead(gfileList *pfl) { + #define nfl ((NativeFileList *)pfl) + while(1) { + if (!nfl->first && !FindNextFile(nfl->d, &nfl->f)) + return 0; + nfl->first = FALSE; + if (nfl->f.cFileName[0] == '.') + continue; + if (nfl->fl.dirs) { + if ((nfl->f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + break; + } else { + if (!(nfl->f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + break; + } + } + return nfl->f.cFileName; + #undef nfl + } + + static void NativeFlClose(gfileList *pfl) { + CloseHandle(((NativeFileList *)pfl)->d); + gfxFree(pfl); + } + + #else + #include + + typedef struct NativeFileList { + gfileList fl; + DIR * d; + struct dirent * f; + } NativeFileList; + + static gfileList *NativeFlOpen(const char *path, bool_t dirs) { + NativeFileList *p; + (void) dirs; + + if (!(p = gfxAlloc(sizeof(NativeFileList)))) + return 0; + if (!(p->d = opendir(path))) { + gfxFree(p); + return 0; + } + return &p->fl; + } + + static const char *NativeFlRead(gfileList *pfl) { + #define nfl ((NativeFileList *)pfl) + while(1) { + if (!(nfl->f = readdir(nfl->d))) + return 0; + if (nfl->f->d_name[0] == '.') + continue; + + #ifdef _DIRENT_HAVE_D_TYPE + if (nfl->fl.dirs) { + if (nfl->f->d_type == DT_DIR) + break; + } else { + if (nfl->f->d_type == DT_REG) + break; + } + #else + // Oops - no type field. We could use stat() here but that would mean + // concatting the supplied path to the found filename. + // That all just seems too hard. Instead we just don't + // distinguish between files and directories. + break; + #endif + } + return nfl->f->d_name; + #undef nfl + } + + static void NativeFlClose(gfileList *pfl) { + closedir(((NativeFileList *)pfl)->d); + gfxFree(pfl); + } + #endif +#endif diff --git a/src/gfile/inc_printg.c b/src/gfile/inc_printg.c new file mode 100644 index 00000000..8d24b347 --- /dev/null +++ b/src/gfile/inc_printg.c @@ -0,0 +1,261 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * This file is included by src/gfile/gfile.c + */ + +/******************************************************** + * Printg Routines + ********************************************************/ + +#include + +#define MAX_FILLER 11 +#define FLOAT_PRECISION 100000 + +int fnprintg(GFILE *f, int maxlen, const char *fmt, ...) { + int res; + va_list ap; + + va_start(ap, fmt); + res = vfnprintg(f, maxlen, fmt, ap); + va_end(ap); + return res; +} + +static char *ltoa_wd(char *p, long num, unsigned radix, long divisor) { + int i; + char * q; + + if (!divisor) divisor = num; + + q = p + MAX_FILLER; + do { + i = (int)(num % radix); + i += '0'; + if (i > '9') + i += 'A' - '0' - 10; + *--q = i; + num /= radix; + } while ((divisor /= radix) != 0); + + i = (int)(p + MAX_FILLER - q); + do { + *p++ = *q++; + } while (--i); + + return p; +} + +int vfnprintg(GFILE *f, int maxlen, const char *fmt, va_list arg) { + int ret; + char *p, *s, c, filler; + int i, precision, width; + bool_t is_long, left_align; + long l; + #if GFILE_ALLOW_FLOATS + float f; + char tmpbuf[2*MAX_FILLER + 1]; + #else + char tmpbuf[MAX_FILLER + 1]; + #endif + + ret = 0; + if (maxlen < 0) + return 0; + if (!maxlen) + maxlen = -1; + + while (*fmt) { + if (*fmt != '%') { + gfileWrite(f, fmt, 1); + ret++; if (!--maxlen) return ret; + fmt++; + continue; + } + fmt++; + + p = s = tmpbuf; + left_align = FALSE; + filler = ' '; + width = 0; + precision = 0; + + if (*fmt == '-') { + fmt++; + left_align = TRUE; + } + if (*fmt == '.') { + fmt++; + filler = '0'; + } + + while (1) { + c = *fmt++; + if (c >= '0' && c <= '9') { + c -= '0'; + width = width * 10 + c; + } else if (c == '*') + width = va_arg(arg, int); + else + break; + } + if (c == '.') { + while (1) { + c = *fmt++; + if (c >= '0' && c <= '9') { + c -= '0'; + precision = precision * 10 + c; + } else if (c == '*') + precision = va_arg(arg, int); + else + break; + } + } + /* Long modifier.*/ + if (c == 'l' || c == 'L') { + is_long = TRUE; + if (*fmt) + c = *fmt++; + } + else + is_long = (c >= 'A') && (c <= 'Z'); + + /* Command decoding.*/ + switch (c) { + case 0: + return ret; + case 'c': + filler = ' '; + *p++ = va_arg(arg, int); + break; + case 's': + filler = ' '; + if ((s = va_arg(arg, char *)) == 0) + s = "(null)"; + if (precision == 0) + precision = 32767; + for (p = s; *p && (--precision >= 0); p++); + break; + case 'D': + case 'd': + if (is_long) + l = va_arg(arg, long); + else + l = va_arg(arg, int); + if (l < 0) { + *p++ = '-'; + l = -l; + } + p = ltoa_wd(p, l, 10, 0); + break; + #if GFILE_ALLOW_FLOATS + case 'f': + f = (float) va_arg(arg, double); + if (f < 0) { + *p++ = '-'; + f = -f; + } + l = f; + p = ltoa_wd(p, l, 10, 0); + *p++ = '.'; + l = (f - l) * FLOAT_PRECISION; + p = ltoa_wd(p, l, 10, FLOAT_PRECISION / 10); + break; + #endif + case 'X': + case 'x': + c = 16; + goto unsigned_common; + case 'U': + case 'u': + c = 10; + goto unsigned_common; + case 'O': + case 'o': + c = 8; + unsigned_common: + if (is_long) + l = va_arg(arg, long); + else + l = va_arg(arg, int); + p = ltoa_wd(p, l, c, 0); + break; + default: + *p++ = c; + break; + } + + i = (int)(p - s); + if ((width -= i) < 0) + width = 0; + if (left_align == FALSE) + width = -width; + if (width < 0) { + if (*s == '-' && filler == '0') { + gfileWrite(f, s++, 1); + ret++; if (!--maxlen) return ret; + i--; + } + do { + gfileWrite(f, &filler, 1); + ret++; if (!--maxlen) return ret; + } while (++width != 0); + } + while (--i >= 0) { + gfileWrite(f, s++, 1); + ret++; if (!--maxlen) return ret; + } + while (width) { + gfileWrite(f, &filler, 1); + ret++; if (!--maxlen) return ret; + width--; + } + } + return ret; +} + +#if GFILE_NEED_STRINGS + int snprintg(char *buf, int maxlen, const char *fmt, ...) { + int res; + GFILE f; + va_list ap; + + if (maxlen <= 1) { + if (maxlen == 1) { + *buf = 0; + return 0; + } + maxlen += 1; + } + + f.flags = GFILEFLG_WRITE|GFILEFLG_TRUNC; + gfileOpenStringFromStaticGFILE(&f, buf); + + va_start(ap, fmt); + res = vfnprintg(&f, maxlen-1, fmt, ap); + va_end(ap); + return res; + } + int vsnprintg(char *buf, int maxlen, const char *fmt, va_list arg) { + GFILE f; + + if (maxlen <= 1) { + if (maxlen == 1) { + *buf = 0; + return 0; + } + maxlen += 1; + } + + f.flags = GFILEFLG_WRITE|GFILEFLG_TRUNC; + gfileOpenStringFromStaticGFILE(&f, buf); + + return vfnprintg(&f, maxlen-1, fmt, arg); + } +#endif diff --git a/src/gfile/inc_romfs.c b/src/gfile/inc_romfs.c index 167430ce..97d26239 100644 --- a/src/gfile/inc_romfs.c +++ b/src/gfile/inc_romfs.c @@ -31,11 +31,15 @@ typedef struct ROMFS_DIRENTRY { } ROMFS_DIRENTRY; #define ROMFS_DIRENTRY_HEAD 0 - #include "romfs_files.h" - static const ROMFS_DIRENTRY const *FsROMHead = ROMFS_DIRENTRY_HEAD; +typedef struct ROMFileList { + gfileList fl; + const ROMFS_DIRENTRY *pdir; +} ROMFileList; + + static bool_t ROMExists(const char *fname); static long int ROMFilesize(const char *fname); static bool_t ROMOpen(GFILE *f, const char *fname); @@ -44,6 +48,11 @@ static int ROMRead(GFILE *f, void *buf, int size); static bool_t ROMSetpos(GFILE *f, long int pos); static long int ROMGetsize(GFILE *f); static bool_t ROMEof(GFILE *f); +#if GFILE_NEED_FILELISTS + static gfileList *ROMFlOpen(const char *path, bool_t dirs); + static const char *ROMFlRead(gfileList *pfl); + static void ROMFlClose(gfileList *pfl); +#endif static const GFILEVMT FsROMVMT = { GFILE_CHAINHEAD, // next @@ -52,8 +61,10 @@ static const GFILEVMT FsROMVMT = { 0, ROMExists, ROMFilesize, 0, ROMOpen, ROMClose, ROMRead, 0, ROMSetpos, ROMGetsize, ROMEof, - 0, 0, - 0 + 0, 0, 0, + #if GFILE_NEED_FILELISTS + ROMFlOpen, ROMFlRead, ROMFlClose + #endif }; #undef GFILE_CHAINHEAD #define GFILE_CHAINHEAD &FsROMVMT @@ -122,3 +133,45 @@ static bool_t ROMEof(GFILE *f) { return f->pos >= ((const ROMFS_DIRENTRY *)f->obj)->size; } + +#if GFILE_NEED_FILELISTS + static gfileList *ROMFlOpen(const char *path, bool_t dirs) { + ROMFileList * p; + (void) path; + + // We don't support directories or path searching + if (dirs) + return 0; + + // Allocate the list buffer + if (!(p = gfxAlloc(sizeof(ROMFileList)))) + return 0; + + // Initialize it and return it. + p->pdir = 0; + return &p->fl; + } + + static const char *ROMFlRead(gfileList *pfl) { + #define rfl ((ROMFileList *)pfl) + + // Is it the first entry + if (!rfl->pdir) { + rfl->pdir = FsROMHead; + return FsROMHead->name; + } + + // Is it not the last entry + if (rfl->pdir->next) { + rfl->pdir = rfl->pdir->next; + return rfl->pdir->name; + } + + return 0; + #undef rfl + } + + static void ROMFlClose(gfileList *pfl) { + gfxFree(pfl); + } +#endif diff --git a/src/gfile/inc_scang.c b/src/gfile/inc_scang.c new file mode 100644 index 00000000..8dcc8d0f --- /dev/null +++ b/src/gfile/inc_scang.c @@ -0,0 +1,257 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * This file is included by src/gfile/gfile.c + */ + +/******************************************************** + * Scang Routines + ********************************************************/ + +int fscang(GFILE *f, const char *fmt, ...) { + int res; + va_list ap; + + va_start(ap, fmt); + res = vfscang(f, fmt, ap); + va_end(ap); + return res; +} + +int vfscang(GFILE *f, const char *fmt, va_list arg) { + int res, width, size, base; + unsigned long num; + char c; + bool_t assign, negate; + char *p; + + for(res = 0; *fmt; fmt++) { + switch(*fmt) { + case ' ': case '\t': case '\r': case '\n': case '\v': case '\f': + break; + + case '%': + fmt++; + assign = TRUE; + negate = FALSE; + width = 0; + size = 1; + num = 0; + + if (*fmt == '*') { + fmt++; + assign = FALSE; + } + while(*fmt >= '0' && *fmt <= '9') + width = width * 10 + (*fmt++ - '0'); + if (*fmt == 'h') { + fmt++; + size = 0; + } else if (*fmt == 'l') { + fmt++; + size = 2; + } else if (*fmt == 'L') { + fmt++; + size = 3; + } + switch(*fmt) { + case 0: + return res; + case '%': + goto matchchar; + case 'c': + if (!width) { + while(1) { + if (!gfileRead(f, &c, 1)) return res; + switch(c) { + case ' ': case '\t': case '\r': + case '\n': case '\v': case '\f': continue; + } + break; + } + width = 1; + } else { + if (!gfileRead(f, &c, 1)) return res; + } + if (assign) { + p = va_arg(arg, char *); + res++; + *p++ = c; + } + while(--width) { + if (!gfileRead(f, &c, 1)) return res; + if (assign) *p++ = c; + } + break; + case 's': + while(1) { + if (!gfileRead(f, &c, 1)) return res; + switch(c) { + case ' ': case '\t': case '\r': + case '\n': case '\v': case '\f': continue; + } + break; + } + if (assign) { + p = va_arg(arg, char *); + res++; + *p++ = c; + } + if (width) { + while(--width) { + if (!gfileRead(f, &c, 1)) { + if (assign) *((char *)p) = 0; + return res; + } + if (assign) *p++ = c; + } + } else { + while(1) { + if (!gfileRead(f, &c, 1)) { + if (assign) *((char *)p) = 0; + return res; + } + switch(c) { + case ' ': case '\t': case '\r': + case '\n': case '\v': case '\f': break; + default: + if (assign) *p++ = c; + continue; + } + break; + } + //ungetch(c); + } + if (assign) *p = 0; + break; + case 'd': base = 10; goto getnum; + case 'i': base = -1; goto getnum; + case 'o': base = 8; goto getnum; + case 'u': base = 10; goto getnum; + case 'x': base = 16; goto getnum; + case 'b': base = 2; + getnum: + while(1) { + if (!gfileRead(f, &c, 1)) return res; + switch(c) { + case ' ': case '\t': case '\r': + case '\n': case '\v': case '\f': continue; + } + break; + } + if (c == '-' && *fmt != 'u') { + negate = TRUE; + if ((width && !--width) || !gfileRead(f, &c, 1)) return res; + } + if (base == -1) { + if (c == '0') { + if ((width && !--width) || !gfileRead(f, &c, 1)) goto assignnum; + switch(c) { + case 'x': case 'X': + base = 16; + if ((width && !--width) || !gfileRead(f, &c, 1)) return res; + break; + case 'b': case 'B': + base = 2; + if ((width && !--width) || !gfileRead(f, &c, 1)) return res; + break; + default: + base = 8; + break; + } + } else + base = 10; + } + while(1) { + if (c >= '0' && c <= '9' && c - '0' < base) + num = num * base + (c - '0'); + else if (c >= 'A' && c <= 'F' && base == 16) + num = num * base + (c - ('A'-10)); + else if (c >= 'a' && c <= 'f' && base == 16) + num = num * base + (c - ('a'-10)); + else { + // ungetch(c) + break; + } + if ((width && !--width) || !gfileRead(f, &c, 1)) + break; + } + + assignnum: + if (negate) + num = -num; + + if (assign) { + switch(size) { + case 0: // short + p = (char *)va_arg(arg, short *); + res++; + *((short *)p) = (short)num; + case 1: // int + p = (char *)va_arg(arg, int *); + res++; + *((int *)p) = (int)num; + case 2: case 3: // long + p = (char *)va_arg(arg, long *); + res++; + *((long *)p) = (long)num; + } + } + break; + + #if GFILE_ALLOW_FLOATS + case 'e': case 'f': case 'g': + // TODO + #endif + default: + return res; + } + + break; + + default: + matchchar: + while(1) { + if (!gfileRead(f, &c, 1)) return res; + switch(c) { + case ' ': case '\t': case '\r': + case '\n': case '\v': case '\f': continue; + } + break; + } + if (c != *fmt) return res; + break; + } + } + return res; +} + +#if GFILE_NEED_STRINGS + int sscang(const char *buf, const char *fmt, ...) { + int res; + GFILE f; + va_list ap; + + f.flags = GFILEFLG_READ; + gfileOpenStringFromStaticGFILE(&f, (char *)buf); + + va_start(ap, fmt); + res = vfscang(&f, fmt, ap); + va_end(ap); + return res; + } + + int vsscang(const char *buf, const char *fmt, va_list arg) { + GFILE f; + + f.flags = GFILEFLG_READ; + gfileOpenStringFromStaticGFILE(&f, (char *)buf); + + return vfscang(&f, fmt, arg); + } +#endif diff --git a/src/gfile/inc_stdio.c b/src/gfile/inc_stdio.c new file mode 100644 index 00000000..8dc44dcb --- /dev/null +++ b/src/gfile/inc_stdio.c @@ -0,0 +1,45 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * This file is included by src/gfile/gfile.c + */ + +/******************************************************** + * Stdio Emulation Routines + ********************************************************/ + +size_t gstdioRead(void * ptr, size_t size, size_t count, FILE *f) { + return gfileRead(f, ptr, size*count)/size; +} + +size_t gstdioWrite(const void * ptr, size_t size, size_t count, FILE *f) { + return gfileWrite(f, ptr, size*count)/size; +} + +int gstdioSeek(FILE *f, size_t offset, int origin) { + switch(origin) { + case SEEK_SET: + break; + case SEEK_CUR: + offset += f->pos; + break; + case SEEK_END: + offset += gfileGetSize(f); + break; + default: + return -1; + } + return gfileSetPos(f, offset) ? 0 : -1; +} + +int gstdioGetpos(FILE *f, long int *pos) { + if (!(f->flags & GFILEFLG_OPEN)) + return -1; + *pos = f->pos; + return 0; +} diff --git a/src/gfile/inc_strings.c b/src/gfile/inc_strings.c new file mode 100644 index 00000000..692d2dd3 --- /dev/null +++ b/src/gfile/inc_strings.c @@ -0,0 +1,69 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * This file is included by src/gfile/gfile.c + */ + +/******************************************************** + * The virtual string file VMT + ********************************************************/ + +#include + +// Special String VMT +static int StringRead(GFILE *f, void *buf, int size) { + int res; + char *p; + + p = ((char *)f->obj) + f->pos; + for(res = 0; res < size && *p; res++, p++, buf = ((char *)buf)+1) + ((char *)buf)[0] = *p; + return res; +} +static int StringWrite(GFILE *f, const void *buf, int size) { + if ((f->flags & GFILEFLG_APPEND)) { + while(((char *)f->obj)[f->pos]) + f->pos++; + } + memcpy(((char *)f->obj)+f->pos, buf, size); + ((char *)f->obj)[f->pos+size] = 0; + return size; +} +static const GFILEVMT StringVMT = { + 0, // next + 0, // flags + '_', // prefix + 0, 0, 0, 0, + 0, 0, StringRead, StringWrite, + 0, 0, 0, + 0, 0, 0, + #if GFILE_NEED_FILELISTS + 0, 0, 0, + #endif +}; + +static void gfileOpenStringFromStaticGFILE(GFILE *f, char *str) { + if ((f->flags & GFILEFLG_TRUNC)) + str[0] = 0; + f->vmt = &StringVMT; + f->obj = str; + f->pos = 0; + f->flags |= GFILEFLG_OPEN|GFILEFLG_CANSEEK; +} + +GFILE *gfileOpenString(char *str, const char *mode) { + GFILE *f; + + // Get an empty file and set the flags + if (!(f = findemptyfile(mode))) + return 0; + + // File is open - fill in all the details + gfileOpenStringFromStaticGFILE(f, str); + return f; +} diff --git a/src/gfile/sys_defs.h b/src/gfile/sys_defs.h index 0c5bac0c..81d72ac8 100644 --- a/src/gfile/sys_defs.h +++ b/src/gfile/sys_defs.h @@ -33,8 +33,10 @@ #ifndef GFILE_IMPLEMENTATION typedef void GFILE; + typedef void gfileList; #else typedef struct GFILE GFILE; + typedef struct gfileList gfileList; #endif extern GFILE *gfileStdIn; @@ -98,14 +100,33 @@ extern "C" { /** * @brief Open file * @details A file must be opened before it can be accessed - * @details ToDo (document possible modes) * @details The resulting GFILE will be used for all functions that access the file. * * @param[in] fname The file name - * @param[in] mode The mode + * @param[in] mode The mode. * * @return Valid GFILE on success, 0 otherwise * + * @note The modes follow the c library fopen() standard. + * The valid modes are:
+ *
  • r - Open for read, the file must exist
  • + *
  • w - Open for write, the file is truncated if it exists
  • + *
  • wx - Open for write, the file must not exist
  • + *
  • a - Open for append, the file is truncated if it exists
  • + *
  • ax - Open for append, the file must not exists
  • + *

+ * THe following flags can also be added to the above modes:
+ *
  • + - Open for both read and write
  • + *
  • b - Open as a binary file rather than a text file
  • + *
      + * @note Not all file-systems support all modes. For example, write + * is not available with the ROM file-system. Similarly few platforms + * distinguish between binary and text files. + * @note Even though binary vs text is relevant only for a small number of platforms + * the "b" flag should always be specified for binary files such as images. + * This ensures portability to other platforms. The extra flag will be ignored + * on platforms where it is not relevant. + * * @api */ GFILE * gfileOpen(const char *fname, const char *mode); @@ -239,15 +260,118 @@ extern "C" { */ bool_t gfileSync(GFILE *f); - #if GFILE_NEED_CHIBIOSFS && GFX_USE_OS_CHIBIOS + #if GFILE_NEED_FILELISTS || defined(__DOXYGEN__) + /** + * @brief Open a file list + * + * @param[in] fs The file system (F for FatFS) + * @param[in] path Path information to pass to the file system + * @param[in] dirs Pass TRUE to get directories only, FALSE to get files only + * + * @return A pointer to a file list on success, NULL otherwise + * + * @note The path parameter is handled in a file-system specific way. It could be + * treated as a directory name, it may be treated as a file pattern, or it + * may be ignored. Passing NULL will always return the full list of files + * in at least the top level directory. + * @note For file systems that do not support directories, passing TRUE for dirs + * will return an error. + * @note You must call @p gfileCloseFileList() when you have finished with the + * file list in order to free resources. + * + * @api + */ + gfileList *gfileOpenFileList(char fs, const char *path, bool_t dirs); + + /** + * @brief Get the next file in a file list. + * + * @param[in] pfl Pointer to a file list returned by @p gfileOpenFileList() + * + * @return A pointer to a file (or directory) name. Returns NULL if there are no more. + * + * @note The file name may contain the full directory path or may not depending + * on how the file system treats directories. + * @note The returned buffer may be destroyed by the next call to any of + * @p gfileOpenFileList(), @p gfileReadFileList() or @p gfileCloseFileList(). + * Do not use this pointer after one of those calls. + * + * @api + */ + const char *gfileReadFileList(gfileList *pfl); + + /** + * @brief Close a file list. + * + * @param[in] pfl Pointer to a file list returned by @p gfileOpenFileList() + * + * @api + */ + void gfileCloseFileList(gfileList *pfl); + #endif + + #if (GFILE_NEED_CHIBIOSFS && GFX_USE_OS_CHIBIOS) || defined(__DOXYGEN__) + /** + * @brief Open file from a ChibiOS BaseFileStream + * + * @param[in] BaseFileStreamPtr The BaseFileStream to open as a GFILE + * @param[in] mode The mode. + * + * @return Valid GFILE on success, 0 otherwise + * + * @note The modes are the same modes as in @p gfileOpen(). The + * open mode is NOT compared against the BaseFileStream capabilities. + * @note Supported operations are: read, write, getpos, setpos, eof and getsize + * + * @api + */ GFILE * gfileOpenBaseFileStream(void *BaseFileStreamPtr, const char *mode); #endif - #if GFILE_NEED_MEMFS + #if GFILE_NEED_MEMFS || defined(__DOXYGEN__) + /** + * @brief Open file from a memory pointer + * + * @param[in] memptr The pointer to the memory + * @param[in] mode The mode. + * + * @return Valid GFILE on success, 0 otherwise + * + * @note The modes are the same modes as in @p gfileOpen(). Note there is + * no concept of file-size. Be careful not to overwrite other memory or + * to read from inaccessible sections of memory. + * @note Supported operations are: read, write, getpos, setpos + * + * @api + */ GFILE * gfileOpenMemory(void *memptr, const char *mode); #endif - #if GFILE_NEED_PRINTG + #if GFILE_NEED_STRINGS || defined(__DOXYGEN__) + /** + * @brief Open file from a null terminated C string + * + * @param[in] memptr The pointer to the string or string buffer + * @param[in] mode The mode. + * + * @return Valid GFILE on success, 0 otherwise + * + * @note The modes are the same modes as in @p gfileOpen(). Note there is + * no concept of file-size. Be careful not to overwrite other memory or + * to read from inaccessible sections of memory. + * @note Reading will return EOF when the NULL character is reached. + * @note Writing will always place a NULL in the next character effectively terminating the + * string at the character just written. + * @note Supported operations are: read, write, append, getpos, setpos + * @note Be careful with setpos and getpos. They do not check for the end of the string. + * @note Reading and Writing will read/write a maximum of one character at a time. + * + * @api + */ + GFILE * gfileOpenString(char *str, const char *mode); + #endif + + #if GFILE_NEED_PRINTG || defined(__DOXYGEN__) #include int vfnprintg(GFILE *f, int maxlen, const char *fmt, va_list arg); @@ -265,7 +389,7 @@ extern "C" { #endif #endif - #if GFILE_NEED_SCANG + #if GFILE_NEED_SCANG || defined(__DOXYGEN__) #include int vfscang(GFILE *f, const char *fmt, va_list arg); diff --git a/src/gfile/sys_options.h b/src/gfile/sys_options.h index ee52298c..ff1e5d4c 100644 --- a/src/gfile/sys_options.h +++ b/src/gfile/sys_options.h @@ -46,6 +46,7 @@ /** * @brief Include printg, fprintg etc functions * @details Defaults to FALSE + * @pre To get the string sprintg functions you also need to define @p GFILE_NEED_STRINGS */ #ifndef GFILE_NEED_PRINTG #define GFILE_NEED_PRINTG FALSE @@ -53,15 +54,14 @@ /** * @brief Include scang, fscang etc functions * @details Defaults to FALSE + * @pre To get the string sscang functions you also need to define @p GFILE_NEED_STRINGS */ #ifndef GFILE_NEED_SCANG #define GFILE_NEED_SCANG FALSE #endif /** - * @brief Include the string sprintg/sscang functions + * @brief Include the string based file functions * @details Defaults to FALSE - * @pre To get sprintg functions you also need to define @p GFILE_NEED_PRINTG - * @pre To get sscang functions you also need to define @p GFILE_NEED_SCANG */ #ifndef GFILE_NEED_STRINGS #define GFILE_NEED_STRINGS FALSE @@ -146,6 +146,14 @@ #ifndef GFILE_NEED_MEMFS #define GFILE_NEED_MEMFS FALSE #endif + /** + * @brief Include support for file list functions + * @details Defaults to FALSE + * @note Adds support for @p gfileOpenFileList(), @p gfileReadFileList() and @p gfileCloseFileList(). + */ + #ifndef GFILE_NEED_FILELISTS + #define GFILE_NEED_FILELISTS FALSE + #endif /** * @} *