master
  1#define _GNU_SOURCE
  2#include "stdio_impl.h"
  3#include <stdlib.h>
  4#include <sys/ioctl.h>
  5#include <fcntl.h>
  6#include <errno.h>
  7#include <string.h>
  8
  9struct fcookie {
 10	void *cookie;
 11	cookie_io_functions_t iofuncs;
 12};
 13
 14struct cookie_FILE {
 15	FILE f;
 16	struct fcookie fc;
 17	unsigned char buf[UNGET+BUFSIZ];
 18};
 19
 20static size_t cookieread(FILE *f, unsigned char *buf, size_t len)
 21{
 22	struct fcookie *fc = f->cookie;
 23	ssize_t ret = -1;
 24	size_t remain = len, readlen = 0;
 25	size_t len2 = len - !!f->buf_size;
 26
 27	if (!fc->iofuncs.read) goto bail;
 28
 29	if (len2) {
 30		ret = fc->iofuncs.read(fc->cookie, (char *) buf, len2);
 31		if (ret <= 0) goto bail;
 32
 33		readlen += ret;
 34		remain -= ret;
 35	}
 36
 37	if (!f->buf_size || remain > !!f->buf_size) return readlen;
 38
 39	f->rpos = f->buf;
 40	ret = fc->iofuncs.read(fc->cookie, (char *) f->rpos, f->buf_size);
 41	if (ret <= 0) goto bail;
 42	f->rend = f->rpos + ret;
 43
 44	buf[readlen++] = *f->rpos++;
 45
 46	return readlen;
 47
 48bail:
 49	f->flags |= ret == 0 ? F_EOF : F_ERR;
 50	f->rpos = f->rend = f->buf;
 51	return readlen;
 52}
 53
 54static size_t cookiewrite(FILE *f, const unsigned char *buf, size_t len)
 55{
 56	struct fcookie *fc = f->cookie;
 57	ssize_t ret;
 58	size_t len2 = f->wpos - f->wbase;
 59	if (!fc->iofuncs.write) return len;
 60	if (len2) {
 61		f->wpos = f->wbase;
 62		if (cookiewrite(f, f->wpos, len2) < len2) return 0;
 63	}
 64	ret = fc->iofuncs.write(fc->cookie, (const char *) buf, len);
 65	if (ret < 0) {
 66		f->wpos = f->wbase = f->wend = 0;
 67		f->flags |= F_ERR;
 68		return 0;
 69	}
 70	return ret;
 71}
 72
 73static off_t cookieseek(FILE *f, off_t off, int whence)
 74{
 75	struct fcookie *fc = f->cookie;
 76	int res;
 77	if (whence > 2U) {
 78		errno = EINVAL;
 79		return -1;
 80	}
 81	if (!fc->iofuncs.seek) {
 82		errno = ENOTSUP;
 83		return -1;
 84	}
 85	res = fc->iofuncs.seek(fc->cookie, &off, whence);
 86	if (res < 0)
 87		return res;
 88	return off;
 89}
 90
 91static int cookieclose(FILE *f)
 92{
 93	struct fcookie *fc = f->cookie;
 94	if (fc->iofuncs.close) return fc->iofuncs.close(fc->cookie);
 95	return 0;
 96}
 97
 98FILE *fopencookie(void *cookie, const char *mode, cookie_io_functions_t iofuncs)
 99{
100	struct cookie_FILE *f;
101
102	/* Check for valid initial mode character */
103	if (!strchr("rwa", *mode)) {
104		errno = EINVAL;
105		return 0;
106	}
107
108	/* Allocate FILE+fcookie+buffer or fail */
109	if (!(f=malloc(sizeof *f))) return 0;
110
111	/* Zero-fill only the struct, not the buffer */
112	memset(&f->f, 0, sizeof f->f);
113
114	/* Impose mode restrictions */
115	if (!strchr(mode, '+')) f->f.flags = (*mode == 'r') ? F_NOWR : F_NORD;
116
117	/* Set up our fcookie */
118	f->fc.cookie = cookie;
119	f->fc.iofuncs = iofuncs;
120
121	f->f.fd = -1;
122	f->f.cookie = &f->fc;
123	f->f.buf = f->buf + UNGET;
124	f->f.buf_size = sizeof f->buf - UNGET;
125	f->f.lbf = EOF;
126
127	/* Initialize op ptrs. No problem if some are unneeded. */
128	f->f.read = cookieread;
129	f->f.write = cookiewrite;
130	f->f.seek = cookieseek;
131	f->f.close = cookieclose;
132
133	/* Add new FILE to open file list */
134	return __ofl_add(&f->f);
135}