remove useless null check before call to free in fclose
[oweals/musl.git] / src / stdio / fopencookie.c
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
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
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
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
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
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.read = iofuncs.read;
120         f->fc.iofuncs.write = iofuncs.write;
121         f->fc.iofuncs.seek = iofuncs.seek;
122         f->fc.iofuncs.close = iofuncs.close;
123
124         f->f.fd = -1;
125         f->f.cookie = &f->fc;
126         f->f.buf = f->buf + UNGET;
127         f->f.buf_size = BUFSIZ;
128         f->f.lbf = EOF;
129
130         /* Initialize op ptrs. No problem if some are unneeded. */
131         f->f.read = cookieread;
132         f->f.write = cookiewrite;
133         f->f.seek = cookieseek;
134         f->f.close = cookieclose;
135
136         /* Add new FILE to open file list */
137         return __ofl_add(&f->f);
138 }