1 /*
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Copyright © 2019 Keith Packard
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following
15 * disclaimer in the documentation and/or other materials provided
16 * with the distribution.
17 *
18 * 3. Neither the name of the copyright holder nor the names of its
19 * contributors may be used to endorse or promote products derived
20 * from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
27 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
29 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
31 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
33 * OF THE POSSIBILITY OF SUCH DAMAGE.
34 */
35
36 #include <stdio-bufio.h>
37 #include <stdlib.h>
38 #include <unistd.h>
39 #include <stdbool.h>
40
41 /* Buffered I/O routines for tiny stdio */
42
43 static int
__bufio_flush_locked(FILE * f)44 __bufio_flush_locked(FILE *f)
45 {
46 struct __file_bufio *bf = (struct __file_bufio *) f;
47 char *buf;
48 int ret = 0;
49 off_t backup;
50
51 switch (bf->dir) {
52 case __SWR:
53 /* Flush everything, drop contents if that doesn't work */
54 buf = bf->buf;
55 while (bf->len) {
56 ssize_t this = (bf->write) (bf->fd, buf, bf->len);
57 if (this <= 0) {
58 bf->len = 0;
59 ret = -1;
60 break;
61 }
62 bf->pos += this;
63 bf->len -= this;
64 }
65 break;
66 case __SRD:
67 /* Move the FD back to the current read position */
68 backup = bf->len - bf->off;
69 if (backup) {
70 bf->pos -= backup;
71 if (bf->lseek)
72 (void) (bf->lseek)(bf->fd, bf->pos, SEEK_SET);
73 }
74 bf->len = 0;
75 bf->off = 0;
76 break;
77 default:
78 break;
79 }
80 return ret;
81 }
82
83 int
__bufio_flush(FILE * f)84 __bufio_flush(FILE *f)
85 {
86 int ret;
87
88 __bufio_lock(f);
89 ret = __bufio_flush_locked(f);
90 __bufio_unlock(f);
91 return ret;
92 }
93
94 /* Set I/O direction, flushing when it changes */
95 static int
__bufio_setdir_locked(FILE * f,uint8_t dir)96 __bufio_setdir_locked(FILE *f, uint8_t dir)
97 {
98 struct __file_bufio *bf = (struct __file_bufio *) f;
99 int ret = 0;
100
101 if (bf->dir != dir) {
102 ret = __bufio_flush_locked(f);
103 bf->dir = dir;
104 }
105 return ret;
106 }
107
108 int
__bufio_put(char c,FILE * f)109 __bufio_put(char c, FILE *f)
110 {
111 struct __file_bufio *bf = (struct __file_bufio *) f;
112 int ret = (unsigned char) c;
113
114 __bufio_lock(f);
115 if (__bufio_setdir_locked(f, __SWR) < 0) {
116 ret = _FDEV_ERR;
117 goto bail;
118 }
119
120 bf->buf[bf->len++] = c;
121
122 /* flush if full, or if sending newline when linebuffered */
123 if (bf->len >= bf->size || (c == '\n' && (bf->bflags & __BLBF)))
124 if (__bufio_flush_locked(f) < 0)
125 ret = _FDEV_ERR;
126
127 bail:
128 __bufio_unlock(f);
129 return ret;
130 }
131
132 int
__bufio_get(FILE * f)133 __bufio_get(FILE *f)
134 {
135 struct __file_bufio *bf = (struct __file_bufio *) f;
136 int ret;
137 bool flushed = false;
138
139 again:
140 __bufio_lock(f);
141 if (__bufio_setdir_locked(f, __SRD) < 0) {
142 ret = _FDEV_ERR;
143 goto bail;
144 }
145
146 if (bf->off >= bf->len) {
147
148 /* Flush stdout if reading from stdin */
149 if (f == stdin && !flushed) {
150 flushed = true;
151 __bufio_unlock(f);
152 fflush(stdout);
153 goto again;
154 }
155
156 /* Reset read pointer, read some data */
157 bf->off = 0;
158 bf->len = (bf->read)(bf->fd, bf->buf, bf->size);
159
160 if (bf->len <= 0) {
161 bf->len = 0;
162 ret = _FDEV_EOF;
163 goto bail;
164 }
165
166 /* Update FD pos */
167 bf->pos += bf->len;
168 }
169
170 /*
171 * Cast to unsigned avoids sign-extending chars with high-bit
172 * set
173 */
174 ret = (unsigned char) bf->buf[bf->off++];
175 bail:
176 __bufio_unlock(f);
177 return ret;
178 }
179
180 off_t
__bufio_seek(FILE * f,off_t offset,int whence)181 __bufio_seek(FILE *f, off_t offset, int whence)
182 {
183 struct __file_bufio *bf = (struct __file_bufio *) f;
184 off_t ret;
185
186 __bufio_lock(f);
187 if (__bufio_setdir_locked(f, 0) < 0)
188 return _FDEV_ERR;
189 if (bf->lseek) {
190 if (whence == SEEK_CUR) {
191 whence = SEEK_SET;
192 offset += bf->pos;
193 }
194 ret = (bf->lseek)(bf->fd, offset, whence);
195 } else
196 ret = _FDEV_ERR;
197 if (ret >= 0)
198 bf->pos = ret;
199 __bufio_unlock(f);
200 return ret;
201 }
202
203 int
__bufio_setvbuf(FILE * f,char * buf,int mode,size_t size)204 __bufio_setvbuf(FILE *f, char *buf, int mode, size_t size)
205 {
206 struct __file_bufio *bf = (struct __file_bufio *) f;
207 int ret = 0;
208
209 __bufio_lock(f);
210 bf->bflags &= ~__BLBF;
211 switch (mode) {
212 case _IONBF:
213 buf = NULL;
214 size = 1;
215 break;
216 case _IOLBF:
217 bf->bflags |= __BLBF;
218 break;
219 case _IOFBF:
220 break;
221 default:
222 ret = -1;
223 goto bail;
224 }
225 if (bf->bflags & __BALL) {
226 /*
227 * Handling allocation failures here is a bit tricky;
228 * we don't want to lose the existing buffer. Instead,
229 * we try to reallocate it
230 */
231 if (!buf) {
232 buf = realloc(bf->buf, size);
233 if (!buf)
234 ret = -1;
235 else
236 bf->size = size;
237 goto bail;
238 }
239 free(bf->buf);
240 bf->bflags &= ~__BALL;
241 }
242 if (!buf) {
243 buf = malloc(size);
244 if (!buf) {
245 ret = -1;
246 goto bail;
247 }
248 bf->bflags |= __BALL;
249 }
250 bf->buf = buf;
251 bf->size = size;
252 bail:
253 __bufio_unlock(f);
254 return ret;
255 }
256
257 int
__bufio_close(FILE * f)258 __bufio_close(FILE *f)
259 {
260 struct __file_bufio *bf = (struct __file_bufio *) f;
261 int ret = 0;
262
263 __bufio_lock(f);
264 ret = __bufio_flush_locked(f);
265
266 if (bf->bflags & __BALL)
267 free(bf->buf);
268
269 __bufio_lock_close(f);
270 /* Don't close stdin/stdout/stderr fds */
271 if (bf->fd > 2)
272 (bf->close)(bf->fd);
273 free(f);
274 return ret;
275 }
276
277