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 
9 struct fcookie {
10 	void *cookie;
11 	cookie_io_functions_t iofuncs;
12 };
13 
14 struct cookie_FILE {
15 	FILE f;
16 	struct fcookie fc;
17 	unsigned char buf[UNGET+BUFSIZ];
18 };
19 
cookieread(FILE * f,unsigned char * buf,size_t len)20 static 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 
48 bail:
49 	f->flags |= ret == 0 ? F_EOF : F_ERR;
50 	f->rpos = f->rend = f->buf;
51 	return readlen;
52 }
53 
cookiewrite(FILE * f,const unsigned char * buf,size_t len)54 static 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 
cookieseek(FILE * f,off_t off,int whence)73 static 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 
cookieclose(FILE * f)91 static 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 
fopencookie(void * cookie,const char * mode,cookie_io_functions_t iofuncs)98 FILE *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 }
136