xref: /openbmc/linux/tools/include/nolibc/stdio.h (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
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