1 /* SPDX-License-Identifier: LGPL-2.1 OR MIT */
2 /*
3 * minimal stdio function definitions for NOLIBC
4 * Copyright (C) 2017-2021 Willy Tarreau <w@1wt.eu>
5 */
6
7 #ifndef _NOLIBC_STDIO_H
8 #define _NOLIBC_STDIO_H
9
10 #include <stdarg.h>
11
12 #include "std.h"
13 #include "arch.h"
14 #include "errno.h"
15 #include "types.h"
16 #include "sys.h"
17 #include "stdlib.h"
18 #include "string.h"
19
20 #ifndef EOF
21 #define EOF (-1)
22 #endif
23
24 /* Buffering mode used by setvbuf. */
25 #define _IOFBF 0 /* Fully buffered. */
26 #define _IOLBF 1 /* Line buffered. */
27 #define _IONBF 2 /* No buffering. */
28
29 /* just define FILE as a non-empty type. The value of the pointer gives
30 * the FD: FILE=~fd for fd>=0 or NULL for fd<0. This way positive FILE
31 * are immediately identified as abnormal entries (i.e. possible copies
32 * of valid pointers to something else).
33 */
34 typedef struct FILE {
35 char dummy[1];
36 } FILE;
37
38 static __attribute__((unused)) FILE* const stdin = (FILE*)(intptr_t)~STDIN_FILENO;
39 static __attribute__((unused)) FILE* const stdout = (FILE*)(intptr_t)~STDOUT_FILENO;
40 static __attribute__((unused)) FILE* const stderr = (FILE*)(intptr_t)~STDERR_FILENO;
41
42 /* provides a FILE* equivalent of fd. The mode is ignored. */
43 static __attribute__((unused))
fdopen(int fd,const char * mode)44 FILE *fdopen(int fd, const char *mode __attribute__((unused)))
45 {
46 if (fd < 0) {
47 SET_ERRNO(EBADF);
48 return NULL;
49 }
50 return (FILE*)(intptr_t)~fd;
51 }
52
53 /* provides the fd of stream. */
54 static __attribute__((unused))
fileno(FILE * stream)55 int fileno(FILE *stream)
56 {
57 intptr_t i = (intptr_t)stream;
58
59 if (i >= 0) {
60 SET_ERRNO(EBADF);
61 return -1;
62 }
63 return ~i;
64 }
65
66 /* flush a stream. */
67 static __attribute__((unused))
fflush(FILE * stream)68 int fflush(FILE *stream)
69 {
70 intptr_t i = (intptr_t)stream;
71
72 /* NULL is valid here. */
73 if (i > 0) {
74 SET_ERRNO(EBADF);
75 return -1;
76 }
77
78 /* Don't do anything, nolibc does not support buffering. */
79 return 0;
80 }
81
82 /* flush a stream. */
83 static __attribute__((unused))
fclose(FILE * stream)84 int fclose(FILE *stream)
85 {
86 intptr_t i = (intptr_t)stream;
87
88 if (i >= 0) {
89 SET_ERRNO(EBADF);
90 return -1;
91 }
92
93 if (close(~i))
94 return EOF;
95
96 return 0;
97 }
98
99 /* getc(), fgetc(), getchar() */
100
101 #define getc(stream) fgetc(stream)
102
103 static __attribute__((unused))
fgetc(FILE * stream)104 int fgetc(FILE* stream)
105 {
106 unsigned char ch;
107
108 if (read(fileno(stream), &ch, 1) <= 0)
109 return EOF;
110 return ch;
111 }
112
113 static __attribute__((unused))
getchar(void)114 int getchar(void)
115 {
116 return fgetc(stdin);
117 }
118
119
120 /* putc(), fputc(), putchar() */
121
122 #define putc(c, stream) fputc(c, stream)
123
124 static __attribute__((unused))
fputc(int c,FILE * stream)125 int fputc(int c, FILE* stream)
126 {
127 unsigned char ch = c;
128
129 if (write(fileno(stream), &ch, 1) <= 0)
130 return EOF;
131 return ch;
132 }
133
134 static __attribute__((unused))
putchar(int c)135 int putchar(int c)
136 {
137 return fputc(c, stdout);
138 }
139
140
141 /* fwrite(), puts(), fputs(). Note that puts() emits '\n' but not fputs(). */
142
143 /* internal fwrite()-like function which only takes a size and returns 0 on
144 * success or EOF on error. It automatically retries on short writes.
145 */
146 static __attribute__((unused))
_fwrite(const void * buf,size_t size,FILE * stream)147 int _fwrite(const void *buf, size_t size, FILE *stream)
148 {
149 ssize_t ret;
150 int fd = fileno(stream);
151
152 while (size) {
153 ret = write(fd, buf, size);
154 if (ret <= 0)
155 return EOF;
156 size -= ret;
157 buf += ret;
158 }
159 return 0;
160 }
161
162 static __attribute__((unused))
fwrite(const void * s,size_t size,size_t nmemb,FILE * stream)163 size_t fwrite(const void *s, size_t size, size_t nmemb, FILE *stream)
164 {
165 size_t written;
166
167 for (written = 0; written < nmemb; written++) {
168 if (_fwrite(s, size, stream) != 0)
169 break;
170 s += size;
171 }
172 return written;
173 }
174
175 static __attribute__((unused))
fputs(const char * s,FILE * stream)176 int fputs(const char *s, FILE *stream)
177 {
178 return _fwrite(s, strlen(s), stream);
179 }
180
181 static __attribute__((unused))
puts(const char * s)182 int puts(const char *s)
183 {
184 if (fputs(s, stdout) == EOF)
185 return EOF;
186 return putchar('\n');
187 }
188
189
190 /* fgets() */
191 static __attribute__((unused))
fgets(char * s,int size,FILE * stream)192 char *fgets(char *s, int size, FILE *stream)
193 {
194 int ofs;
195 int c;
196
197 for (ofs = 0; ofs + 1 < size;) {
198 c = fgetc(stream);
199 if (c == EOF)
200 break;
201 s[ofs++] = c;
202 if (c == '\n')
203 break;
204 }
205 if (ofs < size)
206 s[ofs] = 0;
207 return ofs ? s : NULL;
208 }
209
210
211 /* minimal vfprintf(). It supports the following formats:
212 * - %[l*]{d,u,c,x,p}
213 * - %s
214 * - unknown modifiers are ignored.
215 */
216 static __attribute__((unused))
vfprintf(FILE * stream,const char * fmt,va_list args)217 int vfprintf(FILE *stream, const char *fmt, va_list args)
218 {
219 char escape, lpref, c;
220 unsigned long long v;
221 unsigned int written;
222 size_t len, ofs;
223 char tmpbuf[21];
224 const char *outstr;
225
226 written = ofs = escape = lpref = 0;
227 while (1) {
228 c = fmt[ofs++];
229
230 if (escape) {
231 /* we're in an escape sequence, ofs == 1 */
232 escape = 0;
233 if (c == 'c' || c == 'd' || c == 'u' || c == 'x' || c == 'p') {
234 char *out = tmpbuf;
235
236 if (c == 'p')
237 v = va_arg(args, unsigned long);
238 else if (lpref) {
239 if (lpref > 1)
240 v = va_arg(args, unsigned long long);
241 else
242 v = va_arg(args, unsigned long);
243 } else
244 v = va_arg(args, unsigned int);
245
246 if (c == 'd') {
247 /* sign-extend the value */
248 if (lpref == 0)
249 v = (long long)(int)v;
250 else if (lpref == 1)
251 v = (long long)(long)v;
252 }
253
254 switch (c) {
255 case 'c':
256 out[0] = v;
257 out[1] = 0;
258 break;
259 case 'd':
260 i64toa_r(v, out);
261 break;
262 case 'u':
263 u64toa_r(v, out);
264 break;
265 case 'p':
266 *(out++) = '0';
267 *(out++) = 'x';
268 /* fall through */
269 default: /* 'x' and 'p' above */
270 u64toh_r(v, out);
271 break;
272 }
273 outstr = tmpbuf;
274 }
275 else if (c == 's') {
276 outstr = va_arg(args, char *);
277 if (!outstr)
278 outstr="(null)";
279 }
280 else if (c == '%') {
281 /* queue it verbatim */
282 continue;
283 }
284 else {
285 /* modifiers or final 0 */
286 if (c == 'l') {
287 /* long format prefix, maintain the escape */
288 lpref++;
289 }
290 escape = 1;
291 goto do_escape;
292 }
293 len = strlen(outstr);
294 goto flush_str;
295 }
296
297 /* not an escape sequence */
298 if (c == 0 || c == '%') {
299 /* flush pending data on escape or end */
300 escape = 1;
301 lpref = 0;
302 outstr = fmt;
303 len = ofs - 1;
304 flush_str:
305 if (_fwrite(outstr, len, stream) != 0)
306 break;
307
308 written += len;
309 do_escape:
310 if (c == 0)
311 break;
312 fmt += ofs;
313 ofs = 0;
314 continue;
315 }
316
317 /* literal char, just queue it */
318 }
319 return written;
320 }
321
322 static __attribute__((unused))
vprintf(const char * fmt,va_list args)323 int vprintf(const char *fmt, va_list args)
324 {
325 return vfprintf(stdout, fmt, args);
326 }
327
328 static __attribute__((unused, format(printf, 2, 3)))
fprintf(FILE * stream,const char * fmt,...)329 int fprintf(FILE *stream, const char *fmt, ...)
330 {
331 va_list args;
332 int ret;
333
334 va_start(args, fmt);
335 ret = vfprintf(stream, fmt, args);
336 va_end(args);
337 return ret;
338 }
339
340 static __attribute__((unused, format(printf, 1, 2)))
printf(const char * fmt,...)341 int printf(const char *fmt, ...)
342 {
343 va_list args;
344 int ret;
345
346 va_start(args, fmt);
347 ret = vfprintf(stdout, fmt, args);
348 va_end(args);
349 return ret;
350 }
351
352 static __attribute__((unused))
perror(const char * msg)353 void perror(const char *msg)
354 {
355 fprintf(stderr, "%s%serrno=%d\n", (msg && *msg) ? msg : "", (msg && *msg) ? ": " : "", errno);
356 }
357
358 static __attribute__((unused))
setvbuf(FILE * stream,char * buf,int mode,size_t size)359 int setvbuf(FILE *stream __attribute__((unused)),
360 char *buf __attribute__((unused)),
361 int mode,
362 size_t size __attribute__((unused)))
363 {
364 /*
365 * nolibc does not support buffering so this is a nop. Just check mode
366 * is valid as required by the spec.
367 */
368 switch (mode) {
369 case _IOFBF:
370 case _IOLBF:
371 case _IONBF:
372 break;
373 default:
374 return EOF;
375 }
376
377 return 0;
378 }
379
380 /* make sure to include all global symbols */
381 #include "nolibc.h"
382
383 #endif /* _NOLIBC_STDIO_H */
384