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_private.h"
37
38 int
__bufio_flush_locked(FILE * f)39 __bufio_flush_locked(FILE *f)
40 {
41 struct __file_bufio *bf = (struct __file_bufio *) f;
42 char *buf;
43 off_t backup;
44
45 switch (bf->dir) {
46 case __SWR:
47 /* Flush everything, drop contents if that doesn't work */
48 buf = bf->buf;
49 while (bf->len) {
50 ssize_t this = bufio_write(bf, buf, bf->len);
51 if (this <= 0) {
52 bf->len = 0;
53 return _FDEV_ERR;
54 break;
55 }
56 bf->pos += this;
57 bf->len -= this;
58 }
59 break;
60 case __SRD:
61 /* Move the FD back to the current read position */
62 backup = bf->len - bf->off;
63 if (backup) {
64 bf->pos -= backup;
65 (void) bufio_lseek(bf, bf->pos, SEEK_SET);
66 }
67 bf->len = 0;
68 bf->off = 0;
69 break;
70 default:
71 break;
72 }
73 return 0;
74 }
75
76
__bufio_fill_locked(FILE * f)77 int __bufio_fill_locked(FILE *f)
78 {
79 struct __file_bufio *bf = (struct __file_bufio *) f;
80 ssize_t len;
81
82 /* Reset read pointer, read some data */
83 bf->off = 0;
84 len = bufio_read (bf, bf->buf, bf->size);
85
86 if (len <= 0) {
87 bf->len = 0;
88 if (len < 0)
89 return _FDEV_ERR;
90 else
91 return _FDEV_EOF;
92 }
93
94 /* Update FD pos */
95 bf->len = len;
96 bf->pos += len;
97 return 0;
98 }
99
100 int
__bufio_flush(FILE * f)101 __bufio_flush(FILE *f)
102 {
103 int ret;
104
105 __bufio_lock(f);
106 ret = __bufio_flush_locked(f);
107 __bufio_unlock(f);
108 return ret;
109 }
110
111 /* Set I/O direction, flushing when it changes */
112 int
__bufio_setdir_locked(FILE * f,uint8_t dir)113 __bufio_setdir_locked(FILE *f, uint8_t dir)
114 {
115 struct __file_bufio *bf = (struct __file_bufio *) f;
116 int ret = 0;
117
118 if (bf->dir != dir) {
119 ret = __bufio_flush_locked(f);
120 bf->dir = dir;
121 }
122 return ret;
123 }
124
125 int
__bufio_put(char c,FILE * f)126 __bufio_put(char c, FILE *f)
127 {
128 struct __file_bufio *bf = (struct __file_bufio *) f;
129 int ret = (unsigned char) c;
130
131 __bufio_lock(f);
132 if (__bufio_setdir_locked(f, __SWR) < 0) {
133 ret = _FDEV_ERR;
134 goto bail;
135 }
136
137 bf->buf[bf->len++] = c;
138
139 /* flush if full, or if sending newline when linebuffered */
140 if (bf->len >= bf->size || (c == '\n' && (bf->bflags & __BLBF)))
141 if (__bufio_flush_locked(f) < 0)
142 ret = _FDEV_ERR;
143
144 bail:
145 __bufio_unlock(f);
146 return ret;
147 }
148
149 extern FILE *const stdin _ATTRIBUTE((__weak__));
150 extern FILE *const stdout _ATTRIBUTE((__weak__));
151
152 int
__bufio_get(FILE * f)153 __bufio_get(FILE *f)
154 {
155 struct __file_bufio *bf = (struct __file_bufio *) f;
156 int ret;
157 bool flushed = false;
158
159 again:
160 __bufio_lock(f);
161 if (__bufio_setdir_locked(f, __SRD) < 0) {
162 ret = _FDEV_ERR;
163 goto bail;
164 }
165
166 if (bf->off >= bf->len) {
167
168 /*
169 * Flush stdout if reading from stdin.
170 *
171 * The odd-looking NULL address checks along with the
172 * weak attributes for stdin and stdout above avoids
173 * pulling in stdin/stdout definitions just for this
174 * check.
175 */
176 if (!flushed) {
177 flushed = true;
178 if (&stdin != NULL && &stdout != NULL && f == stdin) {
179 __bufio_unlock(f);
180 fflush(stdout);
181 goto again;
182 }
183 }
184
185 ret = __bufio_fill_locked(f);
186 if (ret)
187 goto bail;
188 }
189
190 /*
191 * Cast to unsigned avoids sign-extending chars with high-bit
192 * set
193 */
194 ret = (unsigned char) bf->buf[bf->off++];
195 bail:
196 __bufio_unlock(f);
197 return ret;
198 }
199
200 off_t
__bufio_seek(FILE * f,off_t offset,int whence)201 __bufio_seek(FILE *f, off_t offset, int whence)
202 {
203 struct __file_bufio *bf = (struct __file_bufio *) f;
204 off_t ret;
205
206 __bufio_lock(f);
207 if (__bufio_setdir_locked(f, __SRD) < 0) {
208 ret = _FDEV_ERR;
209 } else {
210 /* compute offset of the first char in the buffer */
211 __off_t buf_pos = bf->pos - bf->len;
212
213 switch (whence) {
214 case SEEK_CUR:
215 /* Map CUR -> SET, accounting for position within buffer */
216 whence = SEEK_SET;
217 offset += buf_pos + bf->off;
218 __PICOLIBC_FALLTHROUGH;
219 case SEEK_SET:
220 /* Optimize for seeks within buffer or just past buffer */
221 if (buf_pos <= offset && offset <= buf_pos + bf->len) {
222 bf->off = offset - buf_pos;
223 ret = offset;
224 break;
225 }
226 __PICOLIBC_FALLTHROUGH;
227 default:
228 ret = bufio_lseek(bf, offset, whence);
229 if (ret >= 0)
230 bf->pos = ret;
231 /* Flush any buffered data after a real seek */
232 bf->len = 0;
233 bf->off = 0;
234 break;
235 }
236 }
237 __bufio_unlock(f);
238 return ret;
239 }
240
241 int
__bufio_setvbuf(FILE * f,char * buf,int mode,size_t size)242 __bufio_setvbuf(FILE *f, char *buf, int mode, size_t size)
243 {
244 struct __file_bufio *bf = (struct __file_bufio *) f;
245 int ret = -1;
246
247 __bufio_lock(f);
248 bf->bflags &= ~__BLBF;
249 switch (mode) {
250 case _IONBF:
251 buf = NULL;
252 size = 1;
253 break;
254 case _IOLBF:
255 bf->bflags |= __BLBF;
256 break;
257 case _IOFBF:
258 break;
259 default:
260 goto bail;
261 }
262 if (bf->bflags & __BALL) {
263 if (buf) {
264 free(bf->buf);
265 bf->bflags &= ~__BALL;
266 } else {
267 /*
268 * Handling allocation failures here is a bit tricky;
269 * we don't want to lose the existing buffer. Instead,
270 * we try to reallocate it
271 */
272 buf = realloc(bf->buf, size);
273 if (!buf)
274 goto bail;
275 }
276 } else if (!buf) {
277 buf = malloc(size);
278 if (!buf)
279 goto bail;
280 bf->bflags |= __BALL;
281 }
282 bf->buf = buf;
283 bf->size = size;
284 ret = 0;
285 bail:
286 __bufio_unlock(f);
287 return ret;
288 }
289
290 int
__bufio_close(FILE * f)291 __bufio_close(FILE *f)
292 {
293 struct __file_bufio *bf = (struct __file_bufio *) f;
294 int ret = 0;
295
296 __bufio_lock(f);
297 ret = __bufio_flush_locked(f);
298
299 if (bf->bflags & __BALL)
300 free(bf->buf);
301
302 __bufio_lock_close(f);
303
304 /*
305 * Don't close the fd or free the FILE for things not
306 * generated by fopen or fdopen. These will usually be static
307 * FILE structs defined for stdin/stdout/stderr.
308 */
309 if (bf->bflags & __BFALL) {
310 ret = bufio_close(bf);
311 free(f);
312 }
313 return ret;
314 }
315
316