#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; if (!fc->iofuncs.read) goto bail; ret = fc->iofuncs.read(fc->cookie, (char *) buf, len > 1 ? (len - 1) : 1); if (ret <= 0) goto bail; readlen += ret; remain -= ret; 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; if (remain > 0) { if (remain > f->buf_size) remain = f->buf_size; memcpy(buf + readlen, f->rpos, remain); readlen += remain; f->rpos += remain; } return readlen; bail: f->flags |= F_EOF ^ ((F_ERR^F_EOF) & ret); 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 > 2) { 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, 0, sizeof(FILE)); /* 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.read = iofuncs.read; f->fc.iofuncs.write = iofuncs.write; f->fc.iofuncs.seek = iofuncs.seek; f->fc.iofuncs.close = iofuncs.close; f->f.fd = -1; f->f.cookie = &f->fc; f->f.buf = f->buf; f->f.buf_size = BUFSIZ; f->f.lbf = EOF; /* enable opportunistic stdio locking */ f->f.lock = 0; /* 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); }