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}