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
107e4346f4SWilly Tarreau #include <stdarg.h>
117e4346f4SWilly Tarreau
124e383a66SWilly Tarreau #include "std.h"
134e383a66SWilly Tarreau #include "arch.h"
1445a794bfSWilly Tarreau #include "errno.h"
154e383a66SWilly Tarreau #include "types.h"
164e383a66SWilly Tarreau #include "sys.h"
174e383a66SWilly Tarreau #include "stdlib.h"
184e383a66SWilly Tarreau #include "string.h"
194e383a66SWilly Tarreau
204e383a66SWilly Tarreau #ifndef EOF
214e383a66SWilly Tarreau #define EOF (-1)
224e383a66SWilly Tarreau #endif
234e383a66SWilly Tarreau
244893c22eSRyan Roberts /* Buffering mode used by setvbuf. */
254893c22eSRyan Roberts #define _IOFBF 0 /* Fully buffered. */
264893c22eSRyan Roberts #define _IOLBF 1 /* Line buffered. */
274893c22eSRyan Roberts #define _IONBF 2 /* No buffering. */
284893c22eSRyan Roberts
295df28c15SThomas Weißschuh /* just define FILE as a non-empty type. The value of the pointer gives
305df28c15SThomas Weißschuh * the FD: FILE=~fd for fd>=0 or NULL for fd<0. This way positive FILE
315df28c15SThomas Weißschuh * are immediately identified as abnormal entries (i.e. possible copies
325df28c15SThomas Weißschuh * of valid pointers to something else).
335df28c15SThomas Weißschuh */
3499b037cbSWilly Tarreau typedef struct FILE {
3599b037cbSWilly Tarreau char dummy[1];
3699b037cbSWilly Tarreau } FILE;
3799b037cbSWilly Tarreau
385df28c15SThomas Weißschuh static __attribute__((unused)) FILE* const stdin = (FILE*)(intptr_t)~STDIN_FILENO;
395df28c15SThomas Weißschuh static __attribute__((unused)) FILE* const stdout = (FILE*)(intptr_t)~STDOUT_FILENO;
405df28c15SThomas Weißschuh static __attribute__((unused)) FILE* const stderr = (FILE*)(intptr_t)~STDERR_FILENO;
415df28c15SThomas Weißschuh
425df28c15SThomas Weißschuh /* provides a FILE* equivalent of fd. The mode is ignored. */
435df28c15SThomas Weißschuh static __attribute__((unused))
fdopen(int fd,const char * mode)445df28c15SThomas Weißschuh FILE *fdopen(int fd, const char *mode __attribute__((unused)))
455df28c15SThomas Weißschuh {
465df28c15SThomas Weißschuh if (fd < 0) {
475df28c15SThomas Weißschuh SET_ERRNO(EBADF);
485df28c15SThomas Weißschuh return NULL;
495df28c15SThomas Weißschuh }
505df28c15SThomas Weißschuh return (FILE*)(intptr_t)~fd;
515df28c15SThomas Weißschuh }
525df28c15SThomas Weißschuh
535df28c15SThomas Weißschuh /* provides the fd of stream. */
545df28c15SThomas Weißschuh static __attribute__((unused))
fileno(FILE * stream)555df28c15SThomas Weißschuh int fileno(FILE *stream)
565df28c15SThomas Weißschuh {
575df28c15SThomas Weißschuh intptr_t i = (intptr_t)stream;
585df28c15SThomas Weißschuh
595df28c15SThomas Weißschuh if (i >= 0) {
605df28c15SThomas Weißschuh SET_ERRNO(EBADF);
615df28c15SThomas Weißschuh return -1;
625df28c15SThomas Weißschuh }
635df28c15SThomas Weißschuh return ~i;
645df28c15SThomas Weißschuh }
655df28c15SThomas Weißschuh
665df28c15SThomas Weißschuh /* flush a stream. */
675df28c15SThomas Weißschuh static __attribute__((unused))
fflush(FILE * stream)685df28c15SThomas Weißschuh int fflush(FILE *stream)
695df28c15SThomas Weißschuh {
705df28c15SThomas Weißschuh intptr_t i = (intptr_t)stream;
715df28c15SThomas Weißschuh
725df28c15SThomas Weißschuh /* NULL is valid here. */
735df28c15SThomas Weißschuh if (i > 0) {
745df28c15SThomas Weißschuh SET_ERRNO(EBADF);
755df28c15SThomas Weißschuh return -1;
765df28c15SThomas Weißschuh }
775df28c15SThomas Weißschuh
785df28c15SThomas Weißschuh /* Don't do anything, nolibc does not support buffering. */
795df28c15SThomas Weißschuh return 0;
805df28c15SThomas Weißschuh }
815df28c15SThomas Weißschuh
825df28c15SThomas Weißschuh /* flush a stream. */
835df28c15SThomas Weißschuh static __attribute__((unused))
fclose(FILE * stream)845df28c15SThomas Weißschuh int fclose(FILE *stream)
855df28c15SThomas Weißschuh {
865df28c15SThomas Weißschuh intptr_t i = (intptr_t)stream;
875df28c15SThomas Weißschuh
885df28c15SThomas Weißschuh if (i >= 0) {
895df28c15SThomas Weißschuh SET_ERRNO(EBADF);
905df28c15SThomas Weißschuh return -1;
915df28c15SThomas Weißschuh }
925df28c15SThomas Weißschuh
935df28c15SThomas Weißschuh if (close(~i))
945df28c15SThomas Weißschuh return EOF;
955df28c15SThomas Weißschuh
965df28c15SThomas Weißschuh return 0;
975df28c15SThomas Weißschuh }
9899b037cbSWilly Tarreau
9999b037cbSWilly Tarreau /* getc(), fgetc(), getchar() */
10099b037cbSWilly Tarreau
10199b037cbSWilly Tarreau #define getc(stream) fgetc(stream)
10299b037cbSWilly Tarreau
10399b037cbSWilly Tarreau static __attribute__((unused))
fgetc(FILE * stream)10499b037cbSWilly Tarreau int fgetc(FILE* stream)
10599b037cbSWilly Tarreau {
10699b037cbSWilly Tarreau unsigned char ch;
10799b037cbSWilly Tarreau
1085df28c15SThomas Weißschuh if (read(fileno(stream), &ch, 1) <= 0)
10999b037cbSWilly Tarreau return EOF;
11099b037cbSWilly Tarreau return ch;
11199b037cbSWilly Tarreau }
11299b037cbSWilly Tarreau
1134e383a66SWilly Tarreau static __attribute__((unused))
getchar(void)1144e383a66SWilly Tarreau int getchar(void)
1154e383a66SWilly Tarreau {
11699b037cbSWilly Tarreau return fgetc(stdin);
11799b037cbSWilly Tarreau }
1184e383a66SWilly Tarreau
11999b037cbSWilly Tarreau
12099b037cbSWilly Tarreau /* putc(), fputc(), putchar() */
12199b037cbSWilly Tarreau
12299b037cbSWilly Tarreau #define putc(c, stream) fputc(c, stream)
12399b037cbSWilly Tarreau
12499b037cbSWilly Tarreau static __attribute__((unused))
fputc(int c,FILE * stream)12599b037cbSWilly Tarreau int fputc(int c, FILE* stream)
12699b037cbSWilly Tarreau {
12799b037cbSWilly Tarreau unsigned char ch = c;
12899b037cbSWilly Tarreau
1295df28c15SThomas Weißschuh if (write(fileno(stream), &ch, 1) <= 0)
1304e383a66SWilly Tarreau return EOF;
1314e383a66SWilly Tarreau return ch;
1324e383a66SWilly Tarreau }
1334e383a66SWilly Tarreau
1344e383a66SWilly Tarreau static __attribute__((unused))
putchar(int c)1354e383a66SWilly Tarreau int putchar(int c)
1364e383a66SWilly Tarreau {
13799b037cbSWilly Tarreau return fputc(c, stdout);
1384e383a66SWilly Tarreau }
1394e383a66SWilly Tarreau
14099b037cbSWilly Tarreau
141e3e19052SWilly Tarreau /* fwrite(), puts(), fputs(). Note that puts() emits '\n' but not fputs(). */
14299b037cbSWilly Tarreau
143e3e19052SWilly Tarreau /* internal fwrite()-like function which only takes a size and returns 0 on
144e3e19052SWilly Tarreau * success or EOF on error. It automatically retries on short writes.
145e3e19052SWilly Tarreau */
1464e383a66SWilly Tarreau static __attribute__((unused))
_fwrite(const void * buf,size_t size,FILE * stream)147e3e19052SWilly Tarreau int _fwrite(const void *buf, size_t size, FILE *stream)
1484e383a66SWilly Tarreau {
1494e383a66SWilly Tarreau ssize_t ret;
1505df28c15SThomas Weißschuh int fd = fileno(stream);
1514e383a66SWilly Tarreau
152e3e19052SWilly Tarreau while (size) {
153e3e19052SWilly Tarreau ret = write(fd, buf, size);
1544e383a66SWilly Tarreau if (ret <= 0)
1554e383a66SWilly Tarreau return EOF;
156e3e19052SWilly Tarreau size -= ret;
157e3e19052SWilly Tarreau buf += ret;
1584e383a66SWilly Tarreau }
15999b037cbSWilly Tarreau return 0;
16099b037cbSWilly Tarreau }
16199b037cbSWilly Tarreau
16299b037cbSWilly Tarreau static __attribute__((unused))
fwrite(const void * s,size_t size,size_t nmemb,FILE * stream)163e3e19052SWilly Tarreau size_t fwrite(const void *s, size_t size, size_t nmemb, FILE *stream)
164e3e19052SWilly Tarreau {
165e3e19052SWilly Tarreau size_t written;
166e3e19052SWilly Tarreau
167e3e19052SWilly Tarreau for (written = 0; written < nmemb; written++) {
168e3e19052SWilly Tarreau if (_fwrite(s, size, stream) != 0)
169e3e19052SWilly Tarreau break;
170e3e19052SWilly Tarreau s += size;
171e3e19052SWilly Tarreau }
172e3e19052SWilly Tarreau return written;
173e3e19052SWilly Tarreau }
174e3e19052SWilly Tarreau
175e3e19052SWilly Tarreau static __attribute__((unused))
fputs(const char * s,FILE * stream)176e3e19052SWilly Tarreau int fputs(const char *s, FILE *stream)
177e3e19052SWilly Tarreau {
178e3e19052SWilly Tarreau return _fwrite(s, strlen(s), stream);
179e3e19052SWilly Tarreau }
180e3e19052SWilly Tarreau
181e3e19052SWilly Tarreau static __attribute__((unused))
puts(const char * s)18299b037cbSWilly Tarreau int puts(const char *s)
18399b037cbSWilly Tarreau {
18499b037cbSWilly Tarreau if (fputs(s, stdout) == EOF)
18599b037cbSWilly Tarreau return EOF;
1864e383a66SWilly Tarreau return putchar('\n');
1874e383a66SWilly Tarreau }
1884e383a66SWilly Tarreau
18999b037cbSWilly Tarreau
19099b037cbSWilly Tarreau /* fgets() */
19199b037cbSWilly Tarreau static __attribute__((unused))
fgets(char * s,int size,FILE * stream)19299b037cbSWilly Tarreau char *fgets(char *s, int size, FILE *stream)
19399b037cbSWilly Tarreau {
19499b037cbSWilly Tarreau int ofs;
19599b037cbSWilly Tarreau int c;
19699b037cbSWilly Tarreau
19799b037cbSWilly Tarreau for (ofs = 0; ofs + 1 < size;) {
19899b037cbSWilly Tarreau c = fgetc(stream);
19999b037cbSWilly Tarreau if (c == EOF)
20099b037cbSWilly Tarreau break;
20199b037cbSWilly Tarreau s[ofs++] = c;
20299b037cbSWilly Tarreau if (c == '\n')
20399b037cbSWilly Tarreau break;
20499b037cbSWilly Tarreau }
20599b037cbSWilly Tarreau if (ofs < size)
20699b037cbSWilly Tarreau s[ofs] = 0;
20799b037cbSWilly Tarreau return ofs ? s : NULL;
20899b037cbSWilly Tarreau }
20999b037cbSWilly Tarreau
2107e4346f4SWilly Tarreau
2117e4346f4SWilly Tarreau /* minimal vfprintf(). It supports the following formats:
212bd845a19SWilly Tarreau * - %[l*]{d,u,c,x,p}
2137e4346f4SWilly Tarreau * - %s
2147e4346f4SWilly Tarreau * - unknown modifiers are ignored.
2157e4346f4SWilly Tarreau */
2167e4346f4SWilly Tarreau static __attribute__((unused))
vfprintf(FILE * stream,const char * fmt,va_list args)2177e4346f4SWilly Tarreau int vfprintf(FILE *stream, const char *fmt, va_list args)
2187e4346f4SWilly Tarreau {
2197e4346f4SWilly Tarreau char escape, lpref, c;
2207e4346f4SWilly Tarreau unsigned long long v;
2217e4346f4SWilly Tarreau unsigned int written;
2227e4346f4SWilly Tarreau size_t len, ofs;
2237e4346f4SWilly Tarreau char tmpbuf[21];
2247e4346f4SWilly Tarreau const char *outstr;
2257e4346f4SWilly Tarreau
2267e4346f4SWilly Tarreau written = ofs = escape = lpref = 0;
2277e4346f4SWilly Tarreau while (1) {
2287e4346f4SWilly Tarreau c = fmt[ofs++];
2297e4346f4SWilly Tarreau
2307e4346f4SWilly Tarreau if (escape) {
2317e4346f4SWilly Tarreau /* we're in an escape sequence, ofs == 1 */
2327e4346f4SWilly Tarreau escape = 0;
233bd845a19SWilly Tarreau if (c == 'c' || c == 'd' || c == 'u' || c == 'x' || c == 'p') {
234bd845a19SWilly Tarreau char *out = tmpbuf;
235bd845a19SWilly Tarreau
236bd845a19SWilly Tarreau if (c == 'p')
237bd845a19SWilly Tarreau v = va_arg(args, unsigned long);
238bd845a19SWilly Tarreau else if (lpref) {
2397e4346f4SWilly Tarreau if (lpref > 1)
2407e4346f4SWilly Tarreau v = va_arg(args, unsigned long long);
2417e4346f4SWilly Tarreau else
2427e4346f4SWilly Tarreau v = va_arg(args, unsigned long);
2437e4346f4SWilly Tarreau } else
2447e4346f4SWilly Tarreau v = va_arg(args, unsigned int);
2457e4346f4SWilly Tarreau
2467e4346f4SWilly Tarreau if (c == 'd') {
2477e4346f4SWilly Tarreau /* sign-extend the value */
2487e4346f4SWilly Tarreau if (lpref == 0)
2497e4346f4SWilly Tarreau v = (long long)(int)v;
2507e4346f4SWilly Tarreau else if (lpref == 1)
2517e4346f4SWilly Tarreau v = (long long)(long)v;
2527e4346f4SWilly Tarreau }
2537e4346f4SWilly Tarreau
2547e4346f4SWilly Tarreau switch (c) {
255bd845a19SWilly Tarreau case 'c':
256bd845a19SWilly Tarreau out[0] = v;
257bd845a19SWilly Tarreau out[1] = 0;
258bd845a19SWilly Tarreau break;
2597e4346f4SWilly Tarreau case 'd':
260bd845a19SWilly Tarreau i64toa_r(v, out);
2617e4346f4SWilly Tarreau break;
2627e4346f4SWilly Tarreau case 'u':
263bd845a19SWilly Tarreau u64toa_r(v, out);
2647e4346f4SWilly Tarreau break;
265bd845a19SWilly Tarreau case 'p':
266bd845a19SWilly Tarreau *(out++) = '0';
267bd845a19SWilly Tarreau *(out++) = 'x';
268bd845a19SWilly Tarreau /* fall through */
269bd845a19SWilly Tarreau default: /* 'x' and 'p' above */
270bd845a19SWilly Tarreau u64toh_r(v, out);
2717e4346f4SWilly Tarreau break;
2727e4346f4SWilly Tarreau }
2737e4346f4SWilly Tarreau outstr = tmpbuf;
2747e4346f4SWilly Tarreau }
2757e4346f4SWilly Tarreau else if (c == 's') {
2767e4346f4SWilly Tarreau outstr = va_arg(args, char *);
277170b230dSWilly Tarreau if (!outstr)
278170b230dSWilly Tarreau outstr="(null)";
2797e4346f4SWilly Tarreau }
2807e4346f4SWilly Tarreau else if (c == '%') {
2817e4346f4SWilly Tarreau /* queue it verbatim */
2827e4346f4SWilly Tarreau continue;
2837e4346f4SWilly Tarreau }
2847e4346f4SWilly Tarreau else {
2857e4346f4SWilly Tarreau /* modifiers or final 0 */
2867e4346f4SWilly Tarreau if (c == 'l') {
2877e4346f4SWilly Tarreau /* long format prefix, maintain the escape */
2887e4346f4SWilly Tarreau lpref++;
2897e4346f4SWilly Tarreau }
2907e4346f4SWilly Tarreau escape = 1;
2917e4346f4SWilly Tarreau goto do_escape;
2927e4346f4SWilly Tarreau }
2937e4346f4SWilly Tarreau len = strlen(outstr);
2947e4346f4SWilly Tarreau goto flush_str;
2957e4346f4SWilly Tarreau }
2967e4346f4SWilly Tarreau
2977e4346f4SWilly Tarreau /* not an escape sequence */
2987e4346f4SWilly Tarreau if (c == 0 || c == '%') {
2997e4346f4SWilly Tarreau /* flush pending data on escape or end */
3007e4346f4SWilly Tarreau escape = 1;
3017e4346f4SWilly Tarreau lpref = 0;
3027e4346f4SWilly Tarreau outstr = fmt;
3037e4346f4SWilly Tarreau len = ofs - 1;
3047e4346f4SWilly Tarreau flush_str:
3057e4346f4SWilly Tarreau if (_fwrite(outstr, len, stream) != 0)
3067e4346f4SWilly Tarreau break;
3077e4346f4SWilly Tarreau
3087e4346f4SWilly Tarreau written += len;
3097e4346f4SWilly Tarreau do_escape:
3107e4346f4SWilly Tarreau if (c == 0)
3117e4346f4SWilly Tarreau break;
3127e4346f4SWilly Tarreau fmt += ofs;
3137e4346f4SWilly Tarreau ofs = 0;
3147e4346f4SWilly Tarreau continue;
3157e4346f4SWilly Tarreau }
3167e4346f4SWilly Tarreau
3177e4346f4SWilly Tarreau /* literal char, just queue it */
3187e4346f4SWilly Tarreau }
3197e4346f4SWilly Tarreau return written;
3207e4346f4SWilly Tarreau }
3217e4346f4SWilly Tarreau
322322759f9SMark Brown static __attribute__((unused))
vprintf(const char * fmt,va_list args)323322759f9SMark Brown int vprintf(const char *fmt, va_list args)
324322759f9SMark Brown {
325322759f9SMark Brown return vfprintf(stdout, fmt, args);
326322759f9SMark Brown }
327322759f9SMark Brown
3284f2c9703SAlviro Iskandar Setiawan static __attribute__((unused, format(printf, 2, 3)))
fprintf(FILE * stream,const char * fmt,...)3297e4346f4SWilly Tarreau int fprintf(FILE *stream, const char *fmt, ...)
3307e4346f4SWilly Tarreau {
3317e4346f4SWilly Tarreau va_list args;
3327e4346f4SWilly Tarreau int ret;
3337e4346f4SWilly Tarreau
3347e4346f4SWilly Tarreau va_start(args, fmt);
3357e4346f4SWilly Tarreau ret = vfprintf(stream, fmt, args);
3367e4346f4SWilly Tarreau va_end(args);
3377e4346f4SWilly Tarreau return ret;
3387e4346f4SWilly Tarreau }
3397e4346f4SWilly Tarreau
3404f2c9703SAlviro Iskandar Setiawan static __attribute__((unused, format(printf, 1, 2)))
printf(const char * fmt,...)3417e4346f4SWilly Tarreau int printf(const char *fmt, ...)
3427e4346f4SWilly Tarreau {
3437e4346f4SWilly Tarreau va_list args;
3447e4346f4SWilly Tarreau int ret;
3457e4346f4SWilly Tarreau
3467e4346f4SWilly Tarreau va_start(args, fmt);
3477e4346f4SWilly Tarreau ret = vfprintf(stdout, fmt, args);
3487e4346f4SWilly Tarreau va_end(args);
3497e4346f4SWilly Tarreau return ret;
3507e4346f4SWilly Tarreau }
3517e4346f4SWilly Tarreau
352acab7bcdSWilly Tarreau static __attribute__((unused))
perror(const char * msg)353acab7bcdSWilly Tarreau void perror(const char *msg)
354acab7bcdSWilly Tarreau {
355acab7bcdSWilly Tarreau fprintf(stderr, "%s%serrno=%d\n", (msg && *msg) ? msg : "", (msg && *msg) ? ": " : "", errno);
356acab7bcdSWilly Tarreau }
357acab7bcdSWilly Tarreau
3584893c22eSRyan Roberts static __attribute__((unused))
setvbuf(FILE * stream,char * buf,int mode,size_t size)359*809145f8SThomas Weißschuh int setvbuf(FILE *stream __attribute__((unused)),
360*809145f8SThomas Weißschuh char *buf __attribute__((unused)),
361*809145f8SThomas Weißschuh int mode,
362*809145f8SThomas Weißschuh size_t size __attribute__((unused)))
3634893c22eSRyan Roberts {
3644893c22eSRyan Roberts /*
3654893c22eSRyan Roberts * nolibc does not support buffering so this is a nop. Just check mode
3664893c22eSRyan Roberts * is valid as required by the spec.
3674893c22eSRyan Roberts */
3684893c22eSRyan Roberts switch (mode) {
3694893c22eSRyan Roberts case _IOFBF:
3704893c22eSRyan Roberts case _IOLBF:
3714893c22eSRyan Roberts case _IONBF:
3724893c22eSRyan Roberts break;
3734893c22eSRyan Roberts default:
3744893c22eSRyan Roberts return EOF;
3754893c22eSRyan Roberts }
3764893c22eSRyan Roberts
3774893c22eSRyan Roberts return 0;
3784893c22eSRyan Roberts }
3794893c22eSRyan Roberts
38055abdd1fSWilly Tarreau /* make sure to include all global symbols */
38155abdd1fSWilly Tarreau #include "nolibc.h"
38255abdd1fSWilly Tarreau
3834e383a66SWilly Tarreau #endif /* _NOLIBC_STDIO_H */
384