14e383a66SWilly Tarreau /* SPDX-License-Identifier: LGPL-2.1 OR MIT */
24e383a66SWilly Tarreau /*
34e383a66SWilly Tarreau * minimal stdio function definitions for NOLIBC
44e383a66SWilly Tarreau * Copyright (C) 2017-2021 Willy Tarreau <w@1wt.eu>
54e383a66SWilly Tarreau */
64e383a66SWilly Tarreau
74e383a66SWilly Tarreau #ifndef _NOLIBC_STDIO_H
84e383a66SWilly Tarreau #define _NOLIBC_STDIO_H
94e383a66SWilly Tarreau
104e383a66SWilly Tarreau #include "std.h"
114e383a66SWilly Tarreau #include "arch.h"
1245a794bfSWilly Tarreau #include "errno.h"
134e383a66SWilly Tarreau #include "types.h"
144e383a66SWilly Tarreau #include "sys.h"
15b56a9492SThomas Weißschuh #include "stdarg.h"
164e383a66SWilly Tarreau #include "stdlib.h"
174e383a66SWilly Tarreau #include "string.h"
184e383a66SWilly Tarreau
194e383a66SWilly Tarreau #ifndef EOF
204e383a66SWilly Tarreau #define EOF (-1)
214e383a66SWilly Tarreau #endif
224e383a66SWilly Tarreau
234893c22eSRyan Roberts /* Buffering mode used by setvbuf. */
244893c22eSRyan Roberts #define _IOFBF 0 /* Fully buffered. */
254893c22eSRyan Roberts #define _IOLBF 1 /* Line buffered. */
264893c22eSRyan Roberts #define _IONBF 2 /* No buffering. */
274893c22eSRyan Roberts
285df28c15SThomas Weißschuh /* just define FILE as a non-empty type. The value of the pointer gives
295df28c15SThomas Weißschuh * the FD: FILE=~fd for fd>=0 or NULL for fd<0. This way positive FILE
305df28c15SThomas Weißschuh * are immediately identified as abnormal entries (i.e. possible copies
315df28c15SThomas Weißschuh * of valid pointers to something else).
325df28c15SThomas Weißschuh */
3399b037cbSWilly Tarreau typedef struct FILE {
3499b037cbSWilly Tarreau char dummy[1];
3599b037cbSWilly Tarreau } FILE;
3699b037cbSWilly Tarreau
375df28c15SThomas Weißschuh static __attribute__((unused)) FILE* const stdin = (FILE*)(intptr_t)~STDIN_FILENO;
385df28c15SThomas Weißschuh static __attribute__((unused)) FILE* const stdout = (FILE*)(intptr_t)~STDOUT_FILENO;
395df28c15SThomas Weißschuh static __attribute__((unused)) FILE* const stderr = (FILE*)(intptr_t)~STDERR_FILENO;
405df28c15SThomas Weißschuh
415df28c15SThomas Weißschuh /* provides a FILE* equivalent of fd. The mode is ignored. */
425df28c15SThomas Weißschuh static __attribute__((unused))
fdopen(int fd,const char * mode)435df28c15SThomas Weißschuh FILE *fdopen(int fd, const char *mode __attribute__((unused)))
445df28c15SThomas Weißschuh {
455df28c15SThomas Weißschuh if (fd < 0) {
465df28c15SThomas Weißschuh SET_ERRNO(EBADF);
475df28c15SThomas Weißschuh return NULL;
485df28c15SThomas Weißschuh }
495df28c15SThomas Weißschuh return (FILE*)(intptr_t)~fd;
505df28c15SThomas Weißschuh }
515df28c15SThomas Weißschuh
525df28c15SThomas Weißschuh /* provides the fd of stream. */
535df28c15SThomas Weißschuh static __attribute__((unused))
fileno(FILE * stream)545df28c15SThomas Weißschuh int fileno(FILE *stream)
555df28c15SThomas Weißschuh {
565df28c15SThomas Weißschuh intptr_t i = (intptr_t)stream;
575df28c15SThomas Weißschuh
585df28c15SThomas Weißschuh if (i >= 0) {
595df28c15SThomas Weißschuh SET_ERRNO(EBADF);
605df28c15SThomas Weißschuh return -1;
615df28c15SThomas Weißschuh }
625df28c15SThomas Weißschuh return ~i;
635df28c15SThomas Weißschuh }
645df28c15SThomas Weißschuh
655df28c15SThomas Weißschuh /* flush a stream. */
665df28c15SThomas Weißschuh static __attribute__((unused))
fflush(FILE * stream)675df28c15SThomas Weißschuh int fflush(FILE *stream)
685df28c15SThomas Weißschuh {
695df28c15SThomas Weißschuh intptr_t i = (intptr_t)stream;
705df28c15SThomas Weißschuh
715df28c15SThomas Weißschuh /* NULL is valid here. */
725df28c15SThomas Weißschuh if (i > 0) {
735df28c15SThomas Weißschuh SET_ERRNO(EBADF);
745df28c15SThomas Weißschuh return -1;
755df28c15SThomas Weißschuh }
765df28c15SThomas Weißschuh
775df28c15SThomas Weißschuh /* Don't do anything, nolibc does not support buffering. */
785df28c15SThomas Weißschuh return 0;
795df28c15SThomas Weißschuh }
805df28c15SThomas Weißschuh
815df28c15SThomas Weißschuh /* flush a stream. */
825df28c15SThomas Weißschuh static __attribute__((unused))
fclose(FILE * stream)835df28c15SThomas Weißschuh int fclose(FILE *stream)
845df28c15SThomas Weißschuh {
855df28c15SThomas Weißschuh intptr_t i = (intptr_t)stream;
865df28c15SThomas Weißschuh
875df28c15SThomas Weißschuh if (i >= 0) {
885df28c15SThomas Weißschuh SET_ERRNO(EBADF);
895df28c15SThomas Weißschuh return -1;
905df28c15SThomas Weißschuh }
915df28c15SThomas Weißschuh
925df28c15SThomas Weißschuh if (close(~i))
935df28c15SThomas Weißschuh return EOF;
945df28c15SThomas Weißschuh
955df28c15SThomas Weißschuh return 0;
965df28c15SThomas Weißschuh }
9799b037cbSWilly Tarreau
9899b037cbSWilly Tarreau /* getc(), fgetc(), getchar() */
9999b037cbSWilly Tarreau
10099b037cbSWilly Tarreau #define getc(stream) fgetc(stream)
10199b037cbSWilly Tarreau
10299b037cbSWilly Tarreau static __attribute__((unused))
fgetc(FILE * stream)10399b037cbSWilly Tarreau int fgetc(FILE* stream)
10499b037cbSWilly Tarreau {
10599b037cbSWilly Tarreau unsigned char ch;
10699b037cbSWilly Tarreau
1075df28c15SThomas Weißschuh if (read(fileno(stream), &ch, 1) <= 0)
10899b037cbSWilly Tarreau return EOF;
10999b037cbSWilly Tarreau return ch;
11099b037cbSWilly Tarreau }
11199b037cbSWilly Tarreau
1124e383a66SWilly Tarreau static __attribute__((unused))
getchar(void)1134e383a66SWilly Tarreau int getchar(void)
1144e383a66SWilly Tarreau {
11599b037cbSWilly Tarreau return fgetc(stdin);
11699b037cbSWilly Tarreau }
1174e383a66SWilly Tarreau
11899b037cbSWilly Tarreau
11999b037cbSWilly Tarreau /* putc(), fputc(), putchar() */
12099b037cbSWilly Tarreau
12199b037cbSWilly Tarreau #define putc(c, stream) fputc(c, stream)
12299b037cbSWilly Tarreau
12399b037cbSWilly Tarreau static __attribute__((unused))
fputc(int c,FILE * stream)12499b037cbSWilly Tarreau int fputc(int c, FILE* stream)
12599b037cbSWilly Tarreau {
12699b037cbSWilly Tarreau unsigned char ch = c;
12799b037cbSWilly Tarreau
1285df28c15SThomas Weißschuh if (write(fileno(stream), &ch, 1) <= 0)
1294e383a66SWilly Tarreau return EOF;
1304e383a66SWilly Tarreau return ch;
1314e383a66SWilly Tarreau }
1324e383a66SWilly Tarreau
1334e383a66SWilly Tarreau static __attribute__((unused))
putchar(int c)1344e383a66SWilly Tarreau int putchar(int c)
1354e383a66SWilly Tarreau {
13699b037cbSWilly Tarreau return fputc(c, stdout);
1374e383a66SWilly Tarreau }
1384e383a66SWilly Tarreau
13999b037cbSWilly Tarreau
140e3e19052SWilly Tarreau /* fwrite(), puts(), fputs(). Note that puts() emits '\n' but not fputs(). */
14199b037cbSWilly Tarreau
142e3e19052SWilly Tarreau /* internal fwrite()-like function which only takes a size and returns 0 on
143e3e19052SWilly Tarreau * success or EOF on error. It automatically retries on short writes.
144e3e19052SWilly Tarreau */
1454e383a66SWilly Tarreau static __attribute__((unused))
_fwrite(const void * buf,size_t size,FILE * stream)146e3e19052SWilly Tarreau int _fwrite(const void *buf, size_t size, FILE *stream)
1474e383a66SWilly Tarreau {
1484e383a66SWilly Tarreau ssize_t ret;
1495df28c15SThomas Weißschuh int fd = fileno(stream);
1504e383a66SWilly Tarreau
151e3e19052SWilly Tarreau while (size) {
152e3e19052SWilly Tarreau ret = write(fd, buf, size);
1534e383a66SWilly Tarreau if (ret <= 0)
1544e383a66SWilly Tarreau return EOF;
155e3e19052SWilly Tarreau size -= ret;
156e3e19052SWilly Tarreau buf += ret;
1574e383a66SWilly Tarreau }
15899b037cbSWilly Tarreau return 0;
15999b037cbSWilly Tarreau }
16099b037cbSWilly Tarreau
16199b037cbSWilly Tarreau static __attribute__((unused))
fwrite(const void * s,size_t size,size_t nmemb,FILE * stream)162e3e19052SWilly Tarreau size_t fwrite(const void *s, size_t size, size_t nmemb, FILE *stream)
163e3e19052SWilly Tarreau {
164e3e19052SWilly Tarreau size_t written;
165e3e19052SWilly Tarreau
166e3e19052SWilly Tarreau for (written = 0; written < nmemb; written++) {
167e3e19052SWilly Tarreau if (_fwrite(s, size, stream) != 0)
168e3e19052SWilly Tarreau break;
169e3e19052SWilly Tarreau s += size;
170e3e19052SWilly Tarreau }
171e3e19052SWilly Tarreau return written;
172e3e19052SWilly Tarreau }
173e3e19052SWilly Tarreau
174e3e19052SWilly Tarreau static __attribute__((unused))
fputs(const char * s,FILE * stream)175e3e19052SWilly Tarreau int fputs(const char *s, FILE *stream)
176e3e19052SWilly Tarreau {
177e3e19052SWilly Tarreau return _fwrite(s, strlen(s), stream);
178e3e19052SWilly Tarreau }
179e3e19052SWilly Tarreau
180e3e19052SWilly Tarreau static __attribute__((unused))
puts(const char * s)18199b037cbSWilly Tarreau int puts(const char *s)
18299b037cbSWilly Tarreau {
18399b037cbSWilly Tarreau if (fputs(s, stdout) == EOF)
18499b037cbSWilly Tarreau return EOF;
1854e383a66SWilly Tarreau return putchar('\n');
1864e383a66SWilly Tarreau }
1874e383a66SWilly Tarreau
18899b037cbSWilly Tarreau
18999b037cbSWilly Tarreau /* fgets() */
19099b037cbSWilly Tarreau static __attribute__((unused))
fgets(char * s,int size,FILE * stream)19199b037cbSWilly Tarreau char *fgets(char *s, int size, FILE *stream)
19299b037cbSWilly Tarreau {
19399b037cbSWilly Tarreau int ofs;
19499b037cbSWilly Tarreau int c;
19599b037cbSWilly Tarreau
19699b037cbSWilly Tarreau for (ofs = 0; ofs + 1 < size;) {
19799b037cbSWilly Tarreau c = fgetc(stream);
19899b037cbSWilly Tarreau if (c == EOF)
19999b037cbSWilly Tarreau break;
20099b037cbSWilly Tarreau s[ofs++] = c;
20199b037cbSWilly Tarreau if (c == '\n')
20299b037cbSWilly Tarreau break;
20399b037cbSWilly Tarreau }
20499b037cbSWilly Tarreau if (ofs < size)
20599b037cbSWilly Tarreau s[ofs] = 0;
20699b037cbSWilly Tarreau return ofs ? s : NULL;
20799b037cbSWilly Tarreau }
20899b037cbSWilly Tarreau
2097e4346f4SWilly Tarreau
2107e4346f4SWilly Tarreau /* minimal vfprintf(). It supports the following formats:
211bd845a19SWilly Tarreau * - %[l*]{d,u,c,x,p}
2127e4346f4SWilly Tarreau * - %s
2137e4346f4SWilly Tarreau * - unknown modifiers are ignored.
2147e4346f4SWilly Tarreau */
215dece8476SThomas Weißschuh static __attribute__((unused, format(printf, 2, 0)))
vfprintf(FILE * stream,const char * fmt,va_list args)2167e4346f4SWilly Tarreau int vfprintf(FILE *stream, const char *fmt, va_list args)
2177e4346f4SWilly Tarreau {
2187e4346f4SWilly Tarreau char escape, lpref, c;
2197e4346f4SWilly Tarreau unsigned long long v;
2207e4346f4SWilly Tarreau unsigned int written;
2217e4346f4SWilly Tarreau size_t len, ofs;
2227e4346f4SWilly Tarreau char tmpbuf[21];
2237e4346f4SWilly Tarreau const char *outstr;
2247e4346f4SWilly Tarreau
2257e4346f4SWilly Tarreau written = ofs = escape = lpref = 0;
2267e4346f4SWilly Tarreau while (1) {
2277e4346f4SWilly Tarreau c = fmt[ofs++];
2287e4346f4SWilly Tarreau
2297e4346f4SWilly Tarreau if (escape) {
2307e4346f4SWilly Tarreau /* we're in an escape sequence, ofs == 1 */
2317e4346f4SWilly Tarreau escape = 0;
232bd845a19SWilly Tarreau if (c == 'c' || c == 'd' || c == 'u' || c == 'x' || c == 'p') {
233bd845a19SWilly Tarreau char *out = tmpbuf;
234bd845a19SWilly Tarreau
235bd845a19SWilly Tarreau if (c == 'p')
236bd845a19SWilly Tarreau v = va_arg(args, unsigned long);
237bd845a19SWilly Tarreau else if (lpref) {
2387e4346f4SWilly Tarreau if (lpref > 1)
2397e4346f4SWilly Tarreau v = va_arg(args, unsigned long long);
2407e4346f4SWilly Tarreau else
2417e4346f4SWilly Tarreau v = va_arg(args, unsigned long);
2427e4346f4SWilly Tarreau } else
2437e4346f4SWilly Tarreau v = va_arg(args, unsigned int);
2447e4346f4SWilly Tarreau
2457e4346f4SWilly Tarreau if (c == 'd') {
2467e4346f4SWilly Tarreau /* sign-extend the value */
2477e4346f4SWilly Tarreau if (lpref == 0)
2487e4346f4SWilly Tarreau v = (long long)(int)v;
2497e4346f4SWilly Tarreau else if (lpref == 1)
2507e4346f4SWilly Tarreau v = (long long)(long)v;
2517e4346f4SWilly Tarreau }
2527e4346f4SWilly Tarreau
2537e4346f4SWilly Tarreau switch (c) {
254bd845a19SWilly Tarreau case 'c':
255bd845a19SWilly Tarreau out[0] = v;
256bd845a19SWilly Tarreau out[1] = 0;
257bd845a19SWilly Tarreau break;
2587e4346f4SWilly Tarreau case 'd':
259bd845a19SWilly Tarreau i64toa_r(v, out);
2607e4346f4SWilly Tarreau break;
2617e4346f4SWilly Tarreau case 'u':
262bd845a19SWilly Tarreau u64toa_r(v, out);
2637e4346f4SWilly Tarreau break;
264bd845a19SWilly Tarreau case 'p':
265bd845a19SWilly Tarreau *(out++) = '0';
266bd845a19SWilly Tarreau *(out++) = 'x';
267bd845a19SWilly Tarreau /* fall through */
268bd845a19SWilly Tarreau default: /* 'x' and 'p' above */
269bd845a19SWilly Tarreau u64toh_r(v, out);
2707e4346f4SWilly Tarreau break;
2717e4346f4SWilly Tarreau }
2727e4346f4SWilly Tarreau outstr = tmpbuf;
2737e4346f4SWilly Tarreau }
2747e4346f4SWilly Tarreau else if (c == 's') {
2757e4346f4SWilly Tarreau outstr = va_arg(args, char *);
276170b230dSWilly Tarreau if (!outstr)
277170b230dSWilly Tarreau outstr="(null)";
2787e4346f4SWilly Tarreau }
2797e4346f4SWilly Tarreau else if (c == '%') {
2807e4346f4SWilly Tarreau /* queue it verbatim */
2817e4346f4SWilly Tarreau continue;
2827e4346f4SWilly Tarreau }
2837e4346f4SWilly Tarreau else {
2847e4346f4SWilly Tarreau /* modifiers or final 0 */
2857e4346f4SWilly Tarreau if (c == 'l') {
2867e4346f4SWilly Tarreau /* long format prefix, maintain the escape */
2877e4346f4SWilly Tarreau lpref++;
2887e4346f4SWilly Tarreau }
2897e4346f4SWilly Tarreau escape = 1;
2907e4346f4SWilly Tarreau goto do_escape;
2917e4346f4SWilly Tarreau }
2927e4346f4SWilly Tarreau len = strlen(outstr);
2937e4346f4SWilly Tarreau goto flush_str;
2947e4346f4SWilly Tarreau }
2957e4346f4SWilly Tarreau
2967e4346f4SWilly Tarreau /* not an escape sequence */
2977e4346f4SWilly Tarreau if (c == 0 || c == '%') {
2987e4346f4SWilly Tarreau /* flush pending data on escape or end */
2997e4346f4SWilly Tarreau escape = 1;
3007e4346f4SWilly Tarreau lpref = 0;
3017e4346f4SWilly Tarreau outstr = fmt;
3027e4346f4SWilly Tarreau len = ofs - 1;
3037e4346f4SWilly Tarreau flush_str:
3047e4346f4SWilly Tarreau if (_fwrite(outstr, len, stream) != 0)
3057e4346f4SWilly Tarreau break;
3067e4346f4SWilly Tarreau
3077e4346f4SWilly Tarreau written += len;
3087e4346f4SWilly Tarreau do_escape:
3097e4346f4SWilly Tarreau if (c == 0)
3107e4346f4SWilly Tarreau break;
3117e4346f4SWilly Tarreau fmt += ofs;
3127e4346f4SWilly Tarreau ofs = 0;
3137e4346f4SWilly Tarreau continue;
3147e4346f4SWilly Tarreau }
3157e4346f4SWilly Tarreau
3167e4346f4SWilly Tarreau /* literal char, just queue it */
3177e4346f4SWilly Tarreau }
3187e4346f4SWilly Tarreau return written;
3197e4346f4SWilly Tarreau }
3207e4346f4SWilly Tarreau
321dece8476SThomas Weißschuh static __attribute__((unused, format(printf, 1, 0)))
vprintf(const char * fmt,va_list args)322322759f9SMark Brown int vprintf(const char *fmt, va_list args)
323322759f9SMark Brown {
324322759f9SMark Brown return vfprintf(stdout, fmt, args);
325322759f9SMark Brown }
326322759f9SMark Brown
3274f2c9703SAlviro Iskandar Setiawan static __attribute__((unused, format(printf, 2, 3)))
fprintf(FILE * stream,const char * fmt,...)3287e4346f4SWilly Tarreau int fprintf(FILE *stream, const char *fmt, ...)
3297e4346f4SWilly Tarreau {
3307e4346f4SWilly Tarreau va_list args;
3317e4346f4SWilly Tarreau int ret;
3327e4346f4SWilly Tarreau
3337e4346f4SWilly Tarreau va_start(args, fmt);
3347e4346f4SWilly Tarreau ret = vfprintf(stream, fmt, args);
3357e4346f4SWilly Tarreau va_end(args);
3367e4346f4SWilly Tarreau return ret;
3377e4346f4SWilly Tarreau }
3387e4346f4SWilly Tarreau
3394f2c9703SAlviro Iskandar Setiawan static __attribute__((unused, format(printf, 1, 2)))
printf(const char * fmt,...)3407e4346f4SWilly Tarreau int printf(const char *fmt, ...)
3417e4346f4SWilly Tarreau {
3427e4346f4SWilly Tarreau va_list args;
3437e4346f4SWilly Tarreau int ret;
3447e4346f4SWilly Tarreau
3457e4346f4SWilly Tarreau va_start(args, fmt);
3467e4346f4SWilly Tarreau ret = vfprintf(stdout, fmt, args);
3477e4346f4SWilly Tarreau va_end(args);
3487e4346f4SWilly Tarreau return ret;
3497e4346f4SWilly Tarreau }
3507e4346f4SWilly Tarreau
351acab7bcdSWilly Tarreau static __attribute__((unused))
perror(const char * msg)352acab7bcdSWilly Tarreau void perror(const char *msg)
353acab7bcdSWilly Tarreau {
354acab7bcdSWilly Tarreau fprintf(stderr, "%s%serrno=%d\n", (msg && *msg) ? msg : "", (msg && *msg) ? ": " : "", errno);
355acab7bcdSWilly Tarreau }
356acab7bcdSWilly Tarreau
3574893c22eSRyan Roberts static __attribute__((unused))
setvbuf(FILE * stream,char * buf,int mode,size_t size)358809145f8SThomas Weißschuh int setvbuf(FILE *stream __attribute__((unused)),
359809145f8SThomas Weißschuh char *buf __attribute__((unused)),
360809145f8SThomas Weißschuh int mode,
361809145f8SThomas Weißschuh size_t size __attribute__((unused)))
3624893c22eSRyan Roberts {
3634893c22eSRyan Roberts /*
3644893c22eSRyan Roberts * nolibc does not support buffering so this is a nop. Just check mode
3654893c22eSRyan Roberts * is valid as required by the spec.
3664893c22eSRyan Roberts */
3674893c22eSRyan Roberts switch (mode) {
3684893c22eSRyan Roberts case _IOFBF:
3694893c22eSRyan Roberts case _IOLBF:
3704893c22eSRyan Roberts case _IONBF:
3714893c22eSRyan Roberts break;
3724893c22eSRyan Roberts default:
3734893c22eSRyan Roberts return EOF;
3744893c22eSRyan Roberts }
3754893c22eSRyan Roberts
3764893c22eSRyan Roberts return 0;
3774893c22eSRyan Roberts }
3784893c22eSRyan Roberts
379*d20d0b10SThomas Weißschuh static __attribute__((unused))
strerror(int errno)380*d20d0b10SThomas Weißschuh const char *strerror(int errno)
381*d20d0b10SThomas Weißschuh {
382*d20d0b10SThomas Weißschuh static char buf[18] = "errno=";
383*d20d0b10SThomas Weißschuh
384*d20d0b10SThomas Weißschuh i64toa_r(errno, &buf[6]);
385*d20d0b10SThomas Weißschuh
386*d20d0b10SThomas Weißschuh return buf;
387*d20d0b10SThomas Weißschuh }
388*d20d0b10SThomas Weißschuh
38955abdd1fSWilly Tarreau /* make sure to include all global symbols */
39055abdd1fSWilly Tarreau #include "nolibc.h"
39155abdd1fSWilly Tarreau
3924e383a66SWilly Tarreau #endif /* _NOLIBC_STDIO_H */
393