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 /* make sure to include all global symbols */
8 #include "nolibc.h"
9
10 #ifndef _NOLIBC_STDIO_H
11 #define _NOLIBC_STDIO_H
12
13 #include "std.h"
14 #include "arch.h"
15 #include "errno.h"
16 #include "fcntl.h"
17 #include "types.h"
18 #include "sys.h"
19 #include "stdarg.h"
20 #include "stdlib.h"
21 #include "string.h"
22 #include "compiler.h"
23
24 static const char *strerror(int errnum);
25
26 #ifndef EOF
27 #define EOF (-1)
28 #endif
29
30 /* Buffering mode used by setvbuf. */
31 #define _IOFBF 0 /* Fully buffered. */
32 #define _IOLBF 1 /* Line buffered. */
33 #define _IONBF 2 /* No buffering. */
34
35 /* just define FILE as a non-empty type. The value of the pointer gives
36 * the FD: FILE=~fd for fd>=0 or NULL for fd<0. This way positive FILE
37 * are immediately identified as abnormal entries (i.e. possible copies
38 * of valid pointers to something else).
39 */
40 typedef struct FILE {
41 char dummy[1];
42 } FILE;
43
44 static __attribute__((unused)) FILE* const stdin = (FILE*)(intptr_t)~STDIN_FILENO;
45 static __attribute__((unused)) FILE* const stdout = (FILE*)(intptr_t)~STDOUT_FILENO;
46 static __attribute__((unused)) FILE* const stderr = (FILE*)(intptr_t)~STDERR_FILENO;
47
48 /* provides a FILE* equivalent of fd. The mode is ignored. */
49 static __attribute__((unused))
fdopen(int fd,const char * mode)50 FILE *fdopen(int fd, const char *mode __attribute__((unused)))
51 {
52 if (fd < 0) {
53 SET_ERRNO(EBADF);
54 return NULL;
55 }
56 return (FILE*)(intptr_t)~fd;
57 }
58
59 static __attribute__((unused))
fopen(const char * pathname,const char * mode)60 FILE *fopen(const char *pathname, const char *mode)
61 {
62 int flags, fd;
63
64 switch (*mode) {
65 case 'r':
66 flags = O_RDONLY;
67 break;
68 case 'w':
69 flags = O_WRONLY | O_CREAT | O_TRUNC;
70 break;
71 case 'a':
72 flags = O_WRONLY | O_CREAT | O_APPEND;
73 break;
74 default:
75 SET_ERRNO(EINVAL); return NULL;
76 }
77
78 if (mode[1] == '+')
79 flags = (flags & ~(O_RDONLY | O_WRONLY)) | O_RDWR;
80
81 fd = open(pathname, flags, 0666);
82 return fdopen(fd, mode);
83 }
84
85 /* provides the fd of stream. */
86 static __attribute__((unused))
fileno(FILE * stream)87 int fileno(FILE *stream)
88 {
89 intptr_t i = (intptr_t)stream;
90
91 if (i >= 0) {
92 SET_ERRNO(EBADF);
93 return -1;
94 }
95 return ~i;
96 }
97
98 /* flush a stream. */
99 static __attribute__((unused))
fflush(FILE * stream)100 int fflush(FILE *stream)
101 {
102 intptr_t i = (intptr_t)stream;
103
104 /* NULL is valid here. */
105 if (i > 0) {
106 SET_ERRNO(EBADF);
107 return -1;
108 }
109
110 /* Don't do anything, nolibc does not support buffering. */
111 return 0;
112 }
113
114 /* flush a stream. */
115 static __attribute__((unused))
fclose(FILE * stream)116 int fclose(FILE *stream)
117 {
118 intptr_t i = (intptr_t)stream;
119
120 if (i >= 0) {
121 SET_ERRNO(EBADF);
122 return -1;
123 }
124
125 if (close(~i))
126 return EOF;
127
128 return 0;
129 }
130
131 /* getc(), fgetc(), getchar() */
132
133 #define getc(stream) fgetc(stream)
134
135 static __attribute__((unused))
fgetc(FILE * stream)136 int fgetc(FILE* stream)
137 {
138 unsigned char ch;
139
140 if (read(fileno(stream), &ch, 1) <= 0)
141 return EOF;
142 return ch;
143 }
144
145 static __attribute__((unused))
getchar(void)146 int getchar(void)
147 {
148 return fgetc(stdin);
149 }
150
151
152 /* putc(), fputc(), putchar() */
153
154 #define putc(c, stream) fputc(c, stream)
155
156 static __attribute__((unused))
fputc(int c,FILE * stream)157 int fputc(int c, FILE* stream)
158 {
159 unsigned char ch = c;
160
161 if (write(fileno(stream), &ch, 1) <= 0)
162 return EOF;
163 return ch;
164 }
165
166 static __attribute__((unused))
putchar(int c)167 int putchar(int c)
168 {
169 return fputc(c, stdout);
170 }
171
172
173 /* fwrite(), fread(), puts(), fputs(). Note that puts() emits '\n' but not fputs(). */
174
175 /* internal fwrite()-like function which only takes a size and returns 0 on
176 * success or EOF on error. It automatically retries on short writes.
177 */
178 static __attribute__((unused))
_fwrite(const void * buf,size_t size,FILE * stream)179 int _fwrite(const void *buf, size_t size, FILE *stream)
180 {
181 ssize_t ret;
182 int fd = fileno(stream);
183
184 while (size) {
185 ret = write(fd, buf, size);
186 if (ret <= 0)
187 return EOF;
188 size -= ret;
189 buf += ret;
190 }
191 return 0;
192 }
193
194 static __attribute__((unused))
fwrite(const void * s,size_t size,size_t nmemb,FILE * stream)195 size_t fwrite(const void *s, size_t size, size_t nmemb, FILE *stream)
196 {
197 size_t written;
198
199 for (written = 0; written < nmemb; written++) {
200 if (_fwrite(s, size, stream) != 0)
201 break;
202 s += size;
203 }
204 return written;
205 }
206
207 /* internal fread()-like function which only takes a size and returns 0 on
208 * success or EOF on error. It automatically retries on short reads.
209 */
210 static __attribute__((unused))
_fread(void * buf,size_t size,FILE * stream)211 int _fread(void *buf, size_t size, FILE *stream)
212 {
213 int fd = fileno(stream);
214 ssize_t ret;
215
216 while (size) {
217 ret = read(fd, buf, size);
218 if (ret <= 0)
219 return EOF;
220 size -= ret;
221 buf += ret;
222 }
223 return 0;
224 }
225
226 static __attribute__((unused))
fread(void * s,size_t size,size_t nmemb,FILE * stream)227 size_t fread(void *s, size_t size, size_t nmemb, FILE *stream)
228 {
229 size_t nread;
230
231 for (nread = 0; nread < nmemb; nread++) {
232 if (_fread(s, size, stream) != 0)
233 break;
234 s += size;
235 }
236 return nread;
237 }
238
239 static __attribute__((unused))
fputs(const char * s,FILE * stream)240 int fputs(const char *s, FILE *stream)
241 {
242 return _fwrite(s, strlen(s), stream);
243 }
244
245 static __attribute__((unused))
puts(const char * s)246 int puts(const char *s)
247 {
248 if (fputs(s, stdout) == EOF)
249 return EOF;
250 return putchar('\n');
251 }
252
253
254 /* fgets() */
255 static __attribute__((unused))
fgets(char * s,int size,FILE * stream)256 char *fgets(char *s, int size, FILE *stream)
257 {
258 int ofs;
259 int c;
260
261 for (ofs = 0; ofs + 1 < size;) {
262 c = fgetc(stream);
263 if (c == EOF)
264 break;
265 s[ofs++] = c;
266 if (c == '\n')
267 break;
268 }
269 if (ofs < size)
270 s[ofs] = 0;
271 return ofs ? s : NULL;
272 }
273
274
275 /* fseek */
276 static __attribute__((unused))
fseek(FILE * stream,long offset,int whence)277 int fseek(FILE *stream, long offset, int whence)
278 {
279 int fd = fileno(stream);
280 off_t ret;
281
282 ret = lseek(fd, offset, whence);
283
284 /* lseek() and fseek() differ in that lseek returns the new
285 * position or -1, fseek() returns either 0 or -1.
286 */
287 if (ret >= 0)
288 return 0;
289
290 return -1;
291 }
292
293
294 /* minimal printf(). It supports the following formats:
295 * - %[l*]{d,u,c,x,p}
296 * - %s
297 * - unknown modifiers are ignored.
298 */
299 typedef int (*__nolibc_printf_cb)(intptr_t state, const char *buf, size_t size);
300
301 static __attribute__((unused, format(printf, 4, 0)))
__nolibc_printf(__nolibc_printf_cb cb,intptr_t state,size_t n,const char * fmt,va_list args)302 int __nolibc_printf(__nolibc_printf_cb cb, intptr_t state, size_t n, const char *fmt, va_list args)
303 {
304 char escape, lpref, c;
305 unsigned long long v;
306 unsigned int written, width;
307 size_t len, ofs, w;
308 char tmpbuf[21];
309 const char *outstr;
310
311 written = ofs = escape = lpref = 0;
312 while (1) {
313 c = fmt[ofs++];
314 width = 0;
315
316 if (escape) {
317 /* we're in an escape sequence, ofs == 1 */
318 escape = 0;
319
320 /* width */
321 while (c >= '0' && c <= '9') {
322 width *= 10;
323 width += c - '0';
324
325 c = fmt[ofs++];
326 }
327
328 if (c == 'c' || c == 'd' || c == 'u' || c == 'x' || c == 'p') {
329 char *out = tmpbuf;
330
331 if (c == 'p')
332 v = va_arg(args, unsigned long);
333 else if (lpref) {
334 if (lpref > 1)
335 v = va_arg(args, unsigned long long);
336 else
337 v = va_arg(args, unsigned long);
338 } else
339 v = va_arg(args, unsigned int);
340
341 if (c == 'd') {
342 /* sign-extend the value */
343 if (lpref == 0)
344 v = (long long)(int)v;
345 else if (lpref == 1)
346 v = (long long)(long)v;
347 }
348
349 switch (c) {
350 case 'c':
351 out[0] = v;
352 out[1] = 0;
353 break;
354 case 'd':
355 i64toa_r(v, out);
356 break;
357 case 'u':
358 u64toa_r(v, out);
359 break;
360 case 'p':
361 *(out++) = '0';
362 *(out++) = 'x';
363 __nolibc_fallthrough;
364 default: /* 'x' and 'p' above */
365 u64toh_r(v, out);
366 break;
367 }
368 outstr = tmpbuf;
369 }
370 else if (c == 's') {
371 outstr = va_arg(args, char *);
372 if (!outstr)
373 outstr="(null)";
374 }
375 else if (c == 'm') {
376 #ifdef NOLIBC_IGNORE_ERRNO
377 outstr = "unknown error";
378 #else
379 outstr = strerror(errno);
380 #endif /* NOLIBC_IGNORE_ERRNO */
381 }
382 else if (c == '%') {
383 /* queue it verbatim */
384 continue;
385 }
386 else {
387 /* modifiers or final 0 */
388 if (c == 'l') {
389 /* long format prefix, maintain the escape */
390 lpref++;
391 } else if (c == 'j') {
392 lpref = 2;
393 }
394 escape = 1;
395 goto do_escape;
396 }
397 len = strlen(outstr);
398 goto flush_str;
399 }
400
401 /* not an escape sequence */
402 if (c == 0 || c == '%') {
403 /* flush pending data on escape or end */
404 escape = 1;
405 lpref = 0;
406 outstr = fmt;
407 len = ofs - 1;
408 flush_str:
409 if (n) {
410 w = len < n ? len : n;
411 n -= w;
412 while (width-- > w) {
413 if (cb(state, " ", 1) != 0)
414 return -1;
415 written += 1;
416 }
417 if (cb(state, outstr, w) != 0)
418 return -1;
419 }
420
421 written += len;
422 do_escape:
423 if (c == 0)
424 break;
425 fmt += ofs;
426 ofs = 0;
427 continue;
428 }
429
430 /* literal char, just queue it */
431 }
432 return written;
433 }
434
__nolibc_fprintf_cb(intptr_t state,const char * buf,size_t size)435 static int __nolibc_fprintf_cb(intptr_t state, const char *buf, size_t size)
436 {
437 return _fwrite(buf, size, (FILE *)state);
438 }
439
440 static __attribute__((unused, format(printf, 2, 0)))
vfprintf(FILE * stream,const char * fmt,va_list args)441 int vfprintf(FILE *stream, const char *fmt, va_list args)
442 {
443 return __nolibc_printf(__nolibc_fprintf_cb, (intptr_t)stream, SIZE_MAX, fmt, args);
444 }
445
446 static __attribute__((unused, format(printf, 1, 0)))
vprintf(const char * fmt,va_list args)447 int vprintf(const char *fmt, va_list args)
448 {
449 return vfprintf(stdout, fmt, args);
450 }
451
452 static __attribute__((unused, format(printf, 2, 3)))
fprintf(FILE * stream,const char * fmt,...)453 int fprintf(FILE *stream, const char *fmt, ...)
454 {
455 va_list args;
456 int ret;
457
458 va_start(args, fmt);
459 ret = vfprintf(stream, fmt, args);
460 va_end(args);
461 return ret;
462 }
463
464 static __attribute__((unused, format(printf, 1, 2)))
printf(const char * fmt,...)465 int printf(const char *fmt, ...)
466 {
467 va_list args;
468 int ret;
469
470 va_start(args, fmt);
471 ret = vfprintf(stdout, fmt, args);
472 va_end(args);
473 return ret;
474 }
475
476 static __attribute__((unused, format(printf, 2, 0)))
vdprintf(int fd,const char * fmt,va_list args)477 int vdprintf(int fd, const char *fmt, va_list args)
478 {
479 FILE *stream;
480
481 stream = fdopen(fd, NULL);
482 if (!stream)
483 return -1;
484 /* Technically 'stream' is leaked, but as it's only a wrapper around 'fd' that is fine */
485 return vfprintf(stream, fmt, args);
486 }
487
488 static __attribute__((unused, format(printf, 2, 3)))
dprintf(int fd,const char * fmt,...)489 int dprintf(int fd, const char *fmt, ...)
490 {
491 va_list args;
492 int ret;
493
494 va_start(args, fmt);
495 ret = vdprintf(fd, fmt, args);
496 va_end(args);
497
498 return ret;
499 }
500
__nolibc_sprintf_cb(intptr_t _state,const char * buf,size_t size)501 static int __nolibc_sprintf_cb(intptr_t _state, const char *buf, size_t size)
502 {
503 char **state = (char **)_state;
504
505 memcpy(*state, buf, size);
506 *state += size;
507 return 0;
508 }
509
510 static __attribute__((unused, format(printf, 3, 0)))
vsnprintf(char * buf,size_t size,const char * fmt,va_list args)511 int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
512 {
513 char *state = buf;
514 int ret;
515
516 ret = __nolibc_printf(__nolibc_sprintf_cb, (intptr_t)&state, size, fmt, args);
517 if (ret < 0)
518 return ret;
519 buf[(size_t)ret < size ? (size_t)ret : size - 1] = '\0';
520 return ret;
521 }
522
523 static __attribute__((unused, format(printf, 3, 4)))
snprintf(char * buf,size_t size,const char * fmt,...)524 int snprintf(char *buf, size_t size, const char *fmt, ...)
525 {
526 va_list args;
527 int ret;
528
529 va_start(args, fmt);
530 ret = vsnprintf(buf, size, fmt, args);
531 va_end(args);
532
533 return ret;
534 }
535
536 static __attribute__((unused, format(printf, 2, 0)))
vsprintf(char * buf,const char * fmt,va_list args)537 int vsprintf(char *buf, const char *fmt, va_list args)
538 {
539 return vsnprintf(buf, SIZE_MAX, fmt, args);
540 }
541
542 static __attribute__((unused, format(printf, 2, 3)))
sprintf(char * buf,const char * fmt,...)543 int sprintf(char *buf, const char *fmt, ...)
544 {
545 va_list args;
546 int ret;
547
548 va_start(args, fmt);
549 ret = vsprintf(buf, fmt, args);
550 va_end(args);
551
552 return ret;
553 }
554
555 static __attribute__((unused))
vsscanf(const char * str,const char * format,va_list args)556 int vsscanf(const char *str, const char *format, va_list args)
557 {
558 uintmax_t uval;
559 intmax_t ival;
560 int base;
561 char *endptr;
562 int matches;
563 int lpref;
564
565 matches = 0;
566
567 while (1) {
568 if (*format == '%') {
569 /* start of pattern */
570 lpref = 0;
571 format++;
572
573 if (*format == 'l') {
574 /* same as in printf() */
575 lpref = 1;
576 format++;
577 if (*format == 'l') {
578 lpref = 2;
579 format++;
580 }
581 }
582
583 if (*format == '%') {
584 /* literal % */
585 if ('%' != *str)
586 goto done;
587 str++;
588 format++;
589 continue;
590 } else if (*format == 'd') {
591 ival = strtoll(str, &endptr, 10);
592 if (lpref == 0)
593 *va_arg(args, int *) = ival;
594 else if (lpref == 1)
595 *va_arg(args, long *) = ival;
596 else if (lpref == 2)
597 *va_arg(args, long long *) = ival;
598 } else if (*format == 'u' || *format == 'x' || *format == 'X') {
599 base = *format == 'u' ? 10 : 16;
600 uval = strtoull(str, &endptr, base);
601 if (lpref == 0)
602 *va_arg(args, unsigned int *) = uval;
603 else if (lpref == 1)
604 *va_arg(args, unsigned long *) = uval;
605 else if (lpref == 2)
606 *va_arg(args, unsigned long long *) = uval;
607 } else if (*format == 'p') {
608 *va_arg(args, void **) = (void *)strtoul(str, &endptr, 16);
609 } else {
610 SET_ERRNO(EILSEQ);
611 goto done;
612 }
613
614 format++;
615 str = endptr;
616 matches++;
617
618 } else if (*format == '\0') {
619 goto done;
620 } else if (isspace(*format)) {
621 /* skip spaces in format and str */
622 while (isspace(*format))
623 format++;
624 while (isspace(*str))
625 str++;
626 } else if (*format == *str) {
627 /* literal match */
628 format++;
629 str++;
630 } else {
631 if (!matches)
632 matches = EOF;
633 goto done;
634 }
635 }
636
637 done:
638 return matches;
639 }
640
641 static __attribute__((unused, format(scanf, 2, 3)))
sscanf(const char * str,const char * format,...)642 int sscanf(const char *str, const char *format, ...)
643 {
644 va_list args;
645 int ret;
646
647 va_start(args, format);
648 ret = vsscanf(str, format, args);
649 va_end(args);
650 return ret;
651 }
652
653 static __attribute__((unused))
perror(const char * msg)654 void perror(const char *msg)
655 {
656 #ifdef NOLIBC_IGNORE_ERRNO
657 fprintf(stderr, "%s%sunknown error\n", (msg && *msg) ? msg : "", (msg && *msg) ? ": " : "");
658 #else
659 fprintf(stderr, "%s%serrno=%d\n", (msg && *msg) ? msg : "", (msg && *msg) ? ": " : "", errno);
660 #endif
661 }
662
663 static __attribute__((unused))
setvbuf(FILE * stream,char * buf,int mode,size_t size)664 int setvbuf(FILE *stream __attribute__((unused)),
665 char *buf __attribute__((unused)),
666 int mode,
667 size_t size __attribute__((unused)))
668 {
669 /*
670 * nolibc does not support buffering so this is a nop. Just check mode
671 * is valid as required by the spec.
672 */
673 switch (mode) {
674 case _IOFBF:
675 case _IOLBF:
676 case _IONBF:
677 break;
678 default:
679 return EOF;
680 }
681
682 return 0;
683 }
684
685 static __attribute__((unused))
strerror(int errno)686 const char *strerror(int errno)
687 {
688 static char buf[18] = "errno=";
689
690 i64toa_r(errno, &buf[6]);
691
692 return buf;
693 }
694
695 #endif /* _NOLIBC_STDIO_H */
696