aboutsummaryrefslogtreecommitdiffstats
path: root/src/gfile/gfile.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gfile/gfile.c')
-rw-r--r--src/gfile/gfile.c998
1 files changed, 998 insertions, 0 deletions
diff --git a/src/gfile/gfile.c b/src/gfile/gfile.c
new file mode 100644
index 00000000..9edafea4
--- /dev/null
+++ b/src/gfile/gfile.c
@@ -0,0 +1,998 @@
+/*
+ * 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
+ */
+
+/**
+ * @file src/gfile/gfile.c
+ * @brief GFILE code.
+ *
+ */
+
+#define GFILE_IMPLEMENTATION
+
+#include "gfx.h"
+
+#if GFX_USE_GFILE
+
+struct GFILE {
+ const struct GFILEVMT * vmt;
+ uint16_t flags;
+ #define GFILEFLG_OPEN 0x0001 // File is open
+ #define GFILEFLG_READ 0x0002 // Read the file
+ #define GFILEFLG_WRITE 0x0004 // Write the file
+ #define GFILEFLG_APPEND 0x0008 // Append on each write
+ #define GFILEFLG_BINARY 0x0010 // Treat as a binary file
+ #define GFILEFLG_DELONCLOSE 0x0020 // Delete on close
+ #define GFILEFLG_CANSEEK 0x0040 // Seek operations are valid
+ #define GFILEFLG_FAILONBLOCK 0x0080 // Fail on a blocking call
+ #define GFILEFLG_MUSTEXIST 0x0100 // On open file must exist
+ #define GFILEFLG_MUSTNOTEXIST 0x0200 // On open file must not exist
+ #define GFILEFLG_TRUNC 0x0400 // On open truncate the file
+ void * obj;
+ long int pos;
+};
+
+typedef struct GFILEVMT {
+ const struct GFILEVMT * next;
+ uint8_t flags;
+ #define GFSFLG_WRITEABLE 0x0001
+ #define GFSFLG_CASESENSITIVE 0x0002
+ #define GFSFLG_SEEKABLE 0x0004
+ #define GFSFLG_FAST 0x0010
+ #define GFSFLG_SMALL 0x0020
+ #define GFSFLG_TEXTMODES 0x0040
+ char prefix;
+ bool_t (*del) (const char *fname);
+ bool_t (*exists) (const char *fname);
+ long int (*filesize) (const char *fname);
+ bool_t (*ren) (const char *oldname, const char *newname);
+ bool_t (*open) (GFILE *f, const char *fname);
+ void (*close) (GFILE *f);
+ int (*read) (GFILE *f, void *buf, int size);
+ int (*write) (GFILE *f, const void *buf, int size);
+ bool_t (*setpos) (GFILE *f, long int pos);
+ long int (*getsize) (GFILE *f);
+ bool_t (*eof) (GFILE *f);
+} GFILEVMT;
+
+// The chain of FileSystems
+#define GFILE_CHAINHEAD 0
+
+// The table of GFILE's
+static GFILE gfileArr[GFILE_MAX_GFILES];
+GFILE *gfileStdIn;
+GFILE *gfileStdOut;
+GFILE *gfileStdErr;
+
+/**
+ * The order of the file-systems below determines the order
+ * that they are searched to find a file.
+ * The last defined is the first searched.
+ */
+
+/********************************************************
+ * The ChibiOS BaseFileStream VMT
+ ********************************************************/
+#if GFILE_NEED_CHIBIOSFS && GFX_USE_OS_CHIBIOS
+ #include "../src/gfile/inc_chibiosfs.c"
+#endif
+
+/********************************************************
+ * The Memory Pointer VMT
+ ********************************************************/
+#if GFILE_NEED_MEMFS
+ #include "../src/gfile/inc_memfs.c"
+#endif
+
+/********************************************************
+ * The RAM file-system VMT
+ ********************************************************/
+#if GFILE_NEED_RAMFS
+ #include "../src/gfile/inc_ramfs.c"
+#endif
+
+/********************************************************
+ * The FAT file-system VMT
+ ********************************************************/
+#ifndef GFILE_NEED_FATFS
+ #include "../src/gfile/inc_fatfs.c"
+#endif
+
+/********************************************************
+ * The native file-system
+ ********************************************************/
+#if GFILE_NEED_NATIVEFS
+ #include "../src/gfile/inc_nativefs.c"
+#endif
+
+/********************************************************
+ * The ROM file-system VMT
+ ********************************************************/
+#if GFILE_NEED_ROMFS
+ #include "../src/gfile/inc_romfs.c"
+#endif
+
+/********************************************************
+ * IO routines
+ ********************************************************/
+
+/**
+ * The chain of file systems.
+ */
+static const GFILEVMT const * FsChain = GFILE_CHAINHEAD;
+
+/**
+ * The init routine
+ */
+void _gfileInit(void) {
+ #if GFILE_NEED_NATIVEFS
+ NativeStdIn.flags = GFILEFLG_OPEN|GFILEFLG_READ;
+ NativeStdIn.vmt = &FsNativeVMT;
+ NativeStdIn.obj = (void *)stdin;
+ NativeStdIn.pos = 0;
+ gfileStdIn = &NativeStdIn;
+ NativeStdOut.flags = GFILEFLG_OPEN|GFILEFLG_WRITE|GFILEFLG_APPEND;
+ NativeStdOut.vmt = &FsNativeVMT;
+ NativeStdOut.obj = (void *)stdout;
+ NativeStdOut.pos = 0;
+ gfileStdOut = &NativeStdOut;
+ NativeStdErr.flags = GFILEFLG_OPEN|GFILEFLG_WRITE|GFILEFLG_APPEND;
+ NativeStdErr.vmt = &FsNativeVMT;
+ NativeStdErr.obj = (void *)stderr;
+ NativeStdErr.pos = 0;
+ gfileStdErr = &NativeStdErr;
+ #endif
+}
+
+void _gfileDeinit(void)
+{
+ /* ToDo */
+}
+
+bool_t gfileExists(const char *fname) {
+ const GFILEVMT *p;
+
+ #if GFILE_ALLOW_DEVICESPECIFIC
+ if (fname[0] && fname[1] == '|') {
+ for(p = FsChain; p; p = p->next) {
+ if (p->prefix == fname[0])
+ return p->exists && p->exists(fname+2);
+ }
+ return FALSE;
+ }
+ #endif
+
+ for(p = FsChain; p; p = p->next) {
+ if (p->exists && p->exists(fname))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+bool_t gfileDelete(const char *fname) {
+ const GFILEVMT *p;
+
+ #if GFILE_ALLOW_DEVICESPECIFIC
+ if (fname[0] && fname[1] == '|') {
+ for(p = FsChain; p; p = p->next) {
+ if (p->prefix == fname[0])
+ return p->del && p->del(fname+2);
+ }
+ return FALSE;
+ }
+ #endif
+
+ for(p = FsChain; p; p = p->next) {
+ if (p->del && p->del(fname))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+long int gfileGetFilesize(const char *fname) {
+ const GFILEVMT *p;
+ long int res;
+
+ #if GFILE_ALLOW_DEVICESPECIFIC
+ if (fname[0] && fname[1] == '|') {
+ for(p = FsChain; p; p = p->next) {
+ if (p->prefix == fname[0])
+ return p->filesize ? p->filesize(fname+2) : -1;
+ }
+ return -1;
+ }
+ #endif
+
+ for(p = FsChain; p; p = p->next) {
+ if (p->filesize && (res = p->filesize(fname)) != -1)
+ return res;
+ }
+ return -1;
+}
+
+bool_t gfileRename(const char *oldname, const char *newname) {
+ const GFILEVMT *p;
+
+ #if GFILE_ALLOW_DEVICESPECIFIC
+ if ((oldname[0] && oldname[1] == '|') || (newname[0] && newname[1] == '|')) {
+ char ch;
+
+ if (oldname[0] && oldname[1] == '|') {
+ ch = oldname[0];
+ oldname += 2;
+ if (newname[0] && newname[1] == '|') {
+ if (newname[0] != ch)
+ // Both oldname and newname are fs specific but different ones.
+ return FALSE;
+ newname += 2;
+ }
+ } else {
+ ch = newname[0];
+ newname += 2;
+ }
+ for(p = FsChain; p; p = p->next) {
+ if (p->prefix == ch)
+ return p->ren && p->ren(oldname, newname);
+ }
+ return FALSE;
+ }
+ #endif
+
+ for(p = FsChain; p; p = p->next) {
+ if (p->ren && p->ren(oldname,newname))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static uint16_t mode2flags(const char *mode) {
+ uint16_t flags;
+
+ switch(mode[0]) {
+ case 'r':
+ flags = GFILEFLG_READ|GFILEFLG_MUSTEXIST;
+ while (*++mode) {
+ switch(mode[0]) {
+ case '+': flags |= GFILEFLG_WRITE; break;
+ case 'b': flags |= GFILEFLG_BINARY; break;
+ }
+ }
+ 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;
+}
+
+static bool_t testopen(const GFILEVMT *p, GFILE *f, const char *fname) {
+ // If we want write but the fs doesn't allow it then return
+ if ((f->flags & GFILEFLG_WRITE) && !(p->flags & GFSFLG_WRITEABLE))
+ return FALSE;
+
+ // Try to open
+ if (!p->open || !p->open(f, fname))
+ return FALSE;
+
+ // File is open - fill in all the details
+ f->vmt = p;
+ f->pos = 0;
+ f->flags |= GFILEFLG_OPEN;
+ if (p->flags & GFSFLG_SEEKABLE)
+ f->flags |= GFILEFLG_CANSEEK;
+ return TRUE;
+}
+
+GFILE *gfileOpen(const char *fname, const char *mode) {
+ uint16_t flags;
+ GFILE * f;
+ const GFILEVMT *p;
+
+ // Get the requested mode
+ if (!(flags = mode2flags(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;
+ }
+ }
+
+ // No available slot
+ 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;
+ }
+ }
+
+ // No available slot
+ 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;
+ if (f->vmt->close)
+ f->vmt->close(f);
+ f->flags = 0;
+}
+
+size_t gfileRead(GFILE *f, void *buf, size_t len) {
+ size_t res;
+
+ if (!f || (f->flags & (GFILEFLG_OPEN|GFILEFLG_READ)) != (GFILEFLG_OPEN|GFILEFLG_READ))
+ return 0;
+ if (!f->vmt->read)
+ return 0;
+ if ((res = f->vmt->read(f, buf, len)) <= 0)
+ return 0;
+ f->pos += res;
+ return res;
+}
+
+size_t gfileWrite(GFILE *f, const void *buf, size_t len) {
+ size_t res;
+
+ if (!f || (f->flags & (GFILEFLG_OPEN|GFILEFLG_WRITE)) != (GFILEFLG_OPEN|GFILEFLG_WRITE))
+ return 0;
+ if (!f->vmt->write)
+ return 0;
+ if ((res = f->vmt->write(f, buf, len)) <= 0)
+ return 0;
+ f->pos += res;
+ return res;
+}
+
+long int gfileGetPos(GFILE *f) {
+ if (!f || !(f->flags & GFILEFLG_OPEN))
+ return 0;
+ return f->pos;
+}
+
+bool_t gfileSetPos(GFILE *f, long int pos) {
+ if (!f || !(f->flags & GFILEFLG_OPEN))
+ return FALSE;
+ if (!f->vmt->setpos || !f->vmt->setpos(f, pos))
+ return FALSE;
+ f->pos = pos;
+ return TRUE;
+}
+
+long int gfileGetSize(GFILE *f) {
+ if (!f || !(f->flags & GFILEFLG_OPEN))
+ return 0;
+ if (!f->vmt->getsize)
+ return 0;
+ return f->vmt->getsize(f);
+}
+
+bool_t gfileEOF(GFILE *f) {
+ if (!f || !(f->flags & GFILEFLG_OPEN))
+ return TRUE;
+ if (!f->vmt->eof)
+ return FALSE;
+ return f->vmt->eof(f);
+}
+
+/********************************************************
+ * String VMT routines
+ ********************************************************/
+#if GFILE_NEED_STRINGS && (GFILE_NEED_PRINTG || GFILE_NEED_SCANG)
+ #include <string.h>
+
+ // Special String VMT
+ static int StringRead(GFILE *f, void *buf, int 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) {
+ // 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,
+ };
+#endif
+
+/********************************************************
+ * printg routines
+ ********************************************************/
+#if GFILE_NEED_PRINTG
+ #include <stdarg.h>
+
+ #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';
+ else if (c == '*')
+ c = va_arg(ap, int);
+ else
+ break;
+ width = width * 10 + c;
+ }
+ if (c == '.') {
+ while (1) {
+ c = *fmt++;
+ if (c >= '0' && c <= '9')
+ c -= '0';
+ else if (c == '*')
+ c = va_arg(ap, int);
+ else
+ break;
+ precision = precision * 10 + c;
+ }
+ }
+ /* 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(ap, int);
+ break;
+ case 's':
+ filler = ' ';
+ if ((s = va_arg(ap, 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(ap, long);
+ else
+ l = va_arg(ap, 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(ap, 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(ap, long);
+ else
+ l = va_arg(ap, 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;
+ }
+ 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;
+ }
+ 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;
+ char c;
+ bool_t assign;
+ void *p;
+
+ for(res = 0; *fmt; fmt++) {
+ switch(*fmt) {
+ case ' ': case '\t': case '\r': case '\n': case '\v': case '\f':
+ break;
+
+ case '%':
+ fmt++;
+ assign = TRUE;
+ width = 0;
+ size = 1;
+
+ 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(ap, char *);
+ res++;
+ *((char *)p)++ = c;
+ }
+ while(--width) {
+ if (!gfileRead(f, &c, 1)) return res;
+ if (assign) *((char *)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(ap, char *);
+ res++;
+ *((char *)p)++ = c;
+ }
+ if (width) {
+ while(--width) {
+ if (!gfileRead(f, &c, 1)) {
+ if (assign) *((char *)p) = 0;
+ return res;
+ }
+ if (assign) *((char *)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) *((char *)p)++ = c;
+ continue;
+ }
+ break;
+ }
+ //ungetch(c);
+ }
+ if (assign) *((char *)p) = 0;
+ break;
+ case 'd':
+ case 'i':
+ case 'o':
+ case 'u':
+ case 'x':
+ case 'b':
+ /*
+ while (isspace (*buf))
+ buf++;
+ if (*s == 'd' || *s == 'u')
+ base = 10;
+ else if (*s == 'x')
+ base = 16;
+ else if (*s == 'o')
+ base = 8;
+ else if (*s == 'b')
+ base = 2;
+ if (!width) {
+ if (isspace (*(s + 1)) || *(s + 1) == 0)
+ width = strcspn (buf, ISSPACE);
+ else
+ width = strchr (buf, *(s + 1)) - buf;
+ }
+ strncpy (tmp, buf, width);
+ tmp[width] = '\0';
+ buf += width;
+ if (!noassign)
+ atob (va_arg (ap, u_int32_t *), tmp, base);
+ }
+ if (!noassign)
+ count++;
+ */
+
+ #if GFILE_ALLOW_FLOATS
+ case 'e': case 'f': case 'g':
+ #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_OPEN|GFILEFLG_READ;
+ f.vmt = &StringVMT;
+ f.pos = 0;
+ f.obj = 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 = 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;
+ }
+#endif
+
+#endif /* GFX_USE_GFILE */