#define _GNU_SOURCE #include "stdio_impl.h" #include #include #include #include #include struct fcookie { void *cookie; cookie_io_functions_t iofuncs; }; struct cookie_FILE { FILE f; struct fcookie fc; unsigned char buf[UNGET+BUFSIZ]; }; static size_t cookieread(FILE *f, unsigned char *buf, size_t len) { struct fcookie *fc = f->cookie; ssize_t ret = -1; size_t remain = len, readlen = 0; size_t len2 = len - !!f->buf_size; if (!fc->iofuncs.read) goto bail; if (len2) { ret = fc->iofuncs.read(fc->cookie, (char *) buf, len2); if (ret <= 0) goto bail; readlen += ret; remain -= ret; } if (!f->buf_size || remain > !!f->buf_size) return readlen; f->rpos = f->buf; ret = fc->iofuncs.read(fc->cookie, (char *) f->rpos, f->buf_size); if (ret <= 0) goto bail; f->rend = f->rpos + ret; buf[readlen++] = *f->rpos++; return readlen; bail: f->flags |= ret == 0 ? F_EOF : F_ERR; f->rpos = f->rend = f->buf; return readlen; } static size_t cookiewrite(FILE *f, const unsigned char *buf, size_t len) { struct fcookie *fc = f->cookie; ssize_t ret; size_t len2 = f->wpos - f->wbase; if (!fc->iofuncs.write) return len; if (len2) { f->wpos = f->wbase; if (cookiewrite(f, f->wpos, len2) < len2) return 0; } ret = fc->iofuncs.write(fc->cookie, (const char *) buf, len); if (ret < 0) { f->wpos = f->wbase = f->wend = 0; f->flags |= F_ERR; return 0; } return ret; } static off_t cookieseek(FILE *f, off_t off, int whence) { struct fcookie *fc = f->cookie; int res; if (whence > 2U) { errno = EINVAL; return -1; } if (!fc->iofuncs.seek) { errno = ENOTSUP; return -1; } res = fc->iofuncs.seek(fc->cookie, &off, whence); if (res < 0) return res; return off; } static int cookieclose(FILE *f) { struct fcookie *fc = f->cookie; if (fc->iofuncs.close) return fc->iofuncs.close(fc->cookie); return 0; } FILE *fopencookie(void *cookie, const char *mode, cookie_io_functions_t iofuncs) { struct cookie_FILE *f; /* Check for valid initial mode character */ if (!strchr("rwa", *mode)) { errno = EINVAL; return 0; } /* Allocate FILE+fcookie+buffer or fail */ if (!(f=malloc(sizeof *f))) return 0; /* Zero-fill only the struct, not the buffer */ memset(&f->f, 0, sizeof f->f); /* Impose mode restrictions */ if (!strchr(mode, '+')) f->f.flags = (*mode == 'r') ? F_NOWR : F_NORD; /* Set up our fcookie */ f->fc.cookie = cookie; f->fc.iofuncs = iofuncs; f->f.fd = -1; f->f.cookie = &f->fc; f->f.buf = f->buf + UNGET; f->f.buf_size = sizeof f->buf - UNGET; f->f.lbf = EOF; /* Initialize op ptrs. No problem if some are unneeded. */ f->f.read = cookieread; f->f.write = cookiewrite; f->f.seek = cookieseek; f->f.close = cookieclose; /* Add new FILE to open file list */ return __ofl_add(&f->f); }