aboutsummaryrefslogtreecommitdiffstats
path: root/src/gfile/gfile.c
diff options
context:
space:
mode:
authorinmarket <andrewh@inmarket.com.au>2014-08-12 16:43:45 +1000
committerinmarket <andrewh@inmarket.com.au>2014-08-12 16:43:45 +1000
commit10902154aec652a3fcdf028b2c6ff16743464973 (patch)
tree2941d4f7ed14450ee5731da2e106f824e3435ee8 /src/gfile/gfile.c
parent6ff7d90500bf5939252eb39272558706da1b8f44 (diff)
downloaduGFX-10902154aec652a3fcdf028b2c6ff16743464973.tar.gz
uGFX-10902154aec652a3fcdf028b2c6ff16743464973.tar.bz2
uGFX-10902154aec652a3fcdf028b2c6ff16743464973.zip
GFILE: restructure files, add File Listing, add C String files
Fix compile error for ChibiOS BaseStreamFile based GFILES'.
Diffstat (limited to 'src/gfile/gfile.c')
-rw-r--r--src/gfile/gfile.c788
1 files changed, 108 insertions, 680 deletions
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.
@@ -119,6 +132,34 @@ GFILE *gfileStdErr;
#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) {
- 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) {
+ // 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_READ; break;
- case 'b': flags |= GFILEFLG_BINARY; break;
- case 'x': flags |= GFILEFLG_MUSTNOTEXIST; 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;
}
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,578 +484,34 @@ bool_t gfileSync(GFILE *f) {
return f->vmt->sync(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) {
- (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 <stdarg.h>
-
- #define MAX_FILLER 11
- #define FLOAT_PRECISION 100000
-
- int fnprintg(GFILE *f, int maxlen, const char *fmt, ...) {
- int res;
- va_list ap;
+#if GFILE_NEED_FILELISTS
+ gfileList *gfileOpenFileList(char fs, const char *path, bool_t dirs) {
+ const GFILEVMT *p;
+ gfileList * pfl;
- 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_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;
+ // 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;
+ return pfl;
}
- 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;
+ return 0;
}
- 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;
+ const char *gfileReadFileList(gfileList *pfl) {
+ return pfl->vmt->flread ? pfl->vmt->flread(pfl) : 0;
}
- #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;
+ void gfileCloseFileList(gfileList *pfl) {
+ if (pfl->vmt->flclose)
+ pfl->vmt->flclose(pfl);
}
#endif