#ifdef __linux__ #define _GNU_SOURCE /* !! */ #include #include #include #include typedef struct blob BLOB; struct blob { void *cookie; int (*rfn)(void *, char *, int); int (*wfn)(void *, const char *, int); fpos_t (*sfn)(void *, fpos_t, int); int (*cfn)(void *); } ; static ssize_t funopen_read(void *bv, char *buf, size_t size) { BLOB *b; b = bv; return(b->rfn?(*b->rfn)(b->cookie,buf,size):0); } static ssize_t funopen_write(void *bv, const char *buf, size_t size) { BLOB *b; b = bv; if (b->wfn) return((*b->wfn)(b->cookie,buf,size)); errno = EIO; return(-1); } static int funopen_seek(void *bv, off64_t *offset, int whence) { #if 0 /* I've been unable to figure out how the fsck a code author is supposed to do anything useful with an fpos_t. It's a struct type including not only an off_t but some kind of multibyte character blah. This makes it impossible to do arithmetic on them or even obtain one any way other than ftell(). The manpage warns that fpos_t may be a non-simple type on non-Unix systems - but doesn't indicate that Linux is such a system. So we punt utterly. */ BLOB *b; off_t o; b = bv; if (b->sfn) { o = (*b->sfn)(b->cookie,(fpos_t)*offset,whence); if (o == -1) return(-1); *offset = o; return(0); } #endif bv = bv; offset = offset; whence = whence; errno = EINVAL; /* XXX is this reasonable? */ return(-1); } static int funopen_close(void *bv) { BLOB *b; int rv; b = bv; rv = b->cfn ? (*b->cfn)(b->cookie) : 0; free(b); return(rv); } static cookie_io_functions_t funopen_funcs = { .read = &funopen_read, .write = &funopen_write, .seek = &funopen_seek, .close = &funopen_close }; FILE *funopen( void *cookie, int (*rfn)(void *, char *, int), int (*wfn)(void *, const char *, int), fpos_t (*sfn)(void *, fpos_t, int), int (*cfn)(void *) ) { BLOB *b; FILE *f; const char *mode; int e; b = malloc(sizeof(BLOB)); if (! b) return(0); b->cookie = cookie; b->rfn = rfn; b->wfn = wfn; b->sfn = sfn; b->cfn = cfn; switch ((rfn?1:0) + (wfn?2:0)) { case 0: free(b); errno = EINVAL; return(0); break; case 1: mode = "r"; break; case 2: mode = "w"; break; case 3: mode = "r+"; break; } f = fopencookie(b,mode,funopen_funcs); if (! f) { e = errno; free(b); errno = e; } return(f); } #endif