xref: /linux/tools/include/nolibc/stdio.h (revision 015a99fa76650e7d6efa3e36f20c0f5b346fe9ce)
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(), 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 static __attribute__((unused))
fputs(const char * s,FILE * stream)208 int fputs(const char *s, FILE *stream)
209 {
210 	return _fwrite(s, strlen(s), stream);
211 }
212 
213 static __attribute__((unused))
puts(const char * s)214 int puts(const char *s)
215 {
216 	if (fputs(s, stdout) == EOF)
217 		return EOF;
218 	return putchar('\n');
219 }
220 
221 
222 /* fgets() */
223 static __attribute__((unused))
fgets(char * s,int size,FILE * stream)224 char *fgets(char *s, int size, FILE *stream)
225 {
226 	int ofs;
227 	int c;
228 
229 	for (ofs = 0; ofs + 1 < size;) {
230 		c = fgetc(stream);
231 		if (c == EOF)
232 			break;
233 		s[ofs++] = c;
234 		if (c == '\n')
235 			break;
236 	}
237 	if (ofs < size)
238 		s[ofs] = 0;
239 	return ofs ? s : NULL;
240 }
241 
242 
243 /* minimal printf(). It supports the following formats:
244  *  - %[l*]{d,u,c,x,p}
245  *  - %s
246  *  - unknown modifiers are ignored.
247  */
248 typedef int (*__nolibc_printf_cb)(intptr_t state, const char *buf, size_t size);
249 
250 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)251 int __nolibc_printf(__nolibc_printf_cb cb, intptr_t state, size_t n, const char *fmt, va_list args)
252 {
253 	char escape, lpref, c;
254 	unsigned long long v;
255 	unsigned int written, width;
256 	size_t len, ofs, w;
257 	char tmpbuf[21];
258 	const char *outstr;
259 
260 	written = ofs = escape = lpref = 0;
261 	while (1) {
262 		c = fmt[ofs++];
263 		width = 0;
264 
265 		if (escape) {
266 			/* we're in an escape sequence, ofs == 1 */
267 			escape = 0;
268 
269 			/* width */
270 			while (c >= '0' && c <= '9') {
271 				width *= 10;
272 				width += c - '0';
273 
274 				c = fmt[ofs++];
275 			}
276 
277 			if (c == 'c' || c == 'd' || c == 'u' || c == 'x' || c == 'p') {
278 				char *out = tmpbuf;
279 
280 				if (c == 'p')
281 					v = va_arg(args, unsigned long);
282 				else if (lpref) {
283 					if (lpref > 1)
284 						v = va_arg(args, unsigned long long);
285 					else
286 						v = va_arg(args, unsigned long);
287 				} else
288 					v = va_arg(args, unsigned int);
289 
290 				if (c == 'd') {
291 					/* sign-extend the value */
292 					if (lpref == 0)
293 						v = (long long)(int)v;
294 					else if (lpref == 1)
295 						v = (long long)(long)v;
296 				}
297 
298 				switch (c) {
299 				case 'c':
300 					out[0] = v;
301 					out[1] = 0;
302 					break;
303 				case 'd':
304 					i64toa_r(v, out);
305 					break;
306 				case 'u':
307 					u64toa_r(v, out);
308 					break;
309 				case 'p':
310 					*(out++) = '0';
311 					*(out++) = 'x';
312 					__nolibc_fallthrough;
313 				default: /* 'x' and 'p' above */
314 					u64toh_r(v, out);
315 					break;
316 				}
317 				outstr = tmpbuf;
318 			}
319 			else if (c == 's') {
320 				outstr = va_arg(args, char *);
321 				if (!outstr)
322 					outstr="(null)";
323 			}
324 #ifndef NOLIBC_IGNORE_ERRNO
325 			else if (c == 'm') {
326 				outstr = strerror(errno);
327 			}
328 #endif /* NOLIBC_IGNORE_ERRNO */
329 			else if (c == '%') {
330 				/* queue it verbatim */
331 				continue;
332 			}
333 			else {
334 				/* modifiers or final 0 */
335 				if (c == 'l') {
336 					/* long format prefix, maintain the escape */
337 					lpref++;
338 				} else if (c == 'j') {
339 					lpref = 2;
340 				}
341 				escape = 1;
342 				goto do_escape;
343 			}
344 			len = strlen(outstr);
345 			goto flush_str;
346 		}
347 
348 		/* not an escape sequence */
349 		if (c == 0 || c == '%') {
350 			/* flush pending data on escape or end */
351 			escape = 1;
352 			lpref = 0;
353 			outstr = fmt;
354 			len = ofs - 1;
355 		flush_str:
356 			if (n) {
357 				w = len < n ? len : n;
358 				n -= w;
359 				while (width-- > w) {
360 					if (cb(state, " ", 1) != 0)
361 						break;
362 					written += 1;
363 				}
364 				if (cb(state, outstr, w) != 0)
365 					break;
366 			}
367 
368 			written += len;
369 		do_escape:
370 			if (c == 0)
371 				break;
372 			fmt += ofs;
373 			ofs = 0;
374 			continue;
375 		}
376 
377 		/* literal char, just queue it */
378 	}
379 	return written;
380 }
381 
__nolibc_fprintf_cb(intptr_t state,const char * buf,size_t size)382 static int __nolibc_fprintf_cb(intptr_t state, const char *buf, size_t size)
383 {
384 	return _fwrite(buf, size, (FILE *)state);
385 }
386 
387 static __attribute__((unused, format(printf, 2, 0)))
vfprintf(FILE * stream,const char * fmt,va_list args)388 int vfprintf(FILE *stream, const char *fmt, va_list args)
389 {
390 	return __nolibc_printf(__nolibc_fprintf_cb, (intptr_t)stream, SIZE_MAX, fmt, args);
391 }
392 
393 static __attribute__((unused, format(printf, 1, 0)))
vprintf(const char * fmt,va_list args)394 int vprintf(const char *fmt, va_list args)
395 {
396 	return vfprintf(stdout, fmt, args);
397 }
398 
399 static __attribute__((unused, format(printf, 2, 3)))
fprintf(FILE * stream,const char * fmt,...)400 int fprintf(FILE *stream, const char *fmt, ...)
401 {
402 	va_list args;
403 	int ret;
404 
405 	va_start(args, fmt);
406 	ret = vfprintf(stream, fmt, args);
407 	va_end(args);
408 	return ret;
409 }
410 
411 static __attribute__((unused, format(printf, 1, 2)))
printf(const char * fmt,...)412 int printf(const char *fmt, ...)
413 {
414 	va_list args;
415 	int ret;
416 
417 	va_start(args, fmt);
418 	ret = vfprintf(stdout, fmt, args);
419 	va_end(args);
420 	return ret;
421 }
422 
423 static __attribute__((unused, format(printf, 2, 0)))
vdprintf(int fd,const char * fmt,va_list args)424 int vdprintf(int fd, const char *fmt, va_list args)
425 {
426 	FILE *stream;
427 
428 	stream = fdopen(fd, NULL);
429 	if (!stream)
430 		return -1;
431 	/* Technically 'stream' is leaked, but as it's only a wrapper around 'fd' that is fine */
432 	return vfprintf(stream, fmt, args);
433 }
434 
435 static __attribute__((unused, format(printf, 2, 3)))
dprintf(int fd,const char * fmt,...)436 int dprintf(int fd, const char *fmt, ...)
437 {
438 	va_list args;
439 	int ret;
440 
441 	va_start(args, fmt);
442 	ret = vdprintf(fd, fmt, args);
443 	va_end(args);
444 
445 	return ret;
446 }
447 
__nolibc_sprintf_cb(intptr_t _state,const char * buf,size_t size)448 static int __nolibc_sprintf_cb(intptr_t _state, const char *buf, size_t size)
449 {
450 	char **state = (char **)_state;
451 
452 	memcpy(*state, buf, size);
453 	*state += size;
454 	return 0;
455 }
456 
457 static __attribute__((unused, format(printf, 3, 0)))
vsnprintf(char * buf,size_t size,const char * fmt,va_list args)458 int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
459 {
460 	char *state = buf;
461 	int ret;
462 
463 	ret = __nolibc_printf(__nolibc_sprintf_cb, (intptr_t)&state, size, fmt, args);
464 	if (ret < 0)
465 		return ret;
466 	buf[(size_t)ret < size ? (size_t)ret : size - 1] = '\0';
467 	return ret;
468 }
469 
470 static __attribute__((unused, format(printf, 3, 4)))
snprintf(char * buf,size_t size,const char * fmt,...)471 int snprintf(char *buf, size_t size, const char *fmt, ...)
472 {
473 	va_list args;
474 	int ret;
475 
476 	va_start(args, fmt);
477 	ret = vsnprintf(buf, size, fmt, args);
478 	va_end(args);
479 
480 	return ret;
481 }
482 
483 static __attribute__((unused, format(printf, 2, 0)))
vsprintf(char * buf,const char * fmt,va_list args)484 int vsprintf(char *buf, const char *fmt, va_list args)
485 {
486 	return vsnprintf(buf, SIZE_MAX, fmt, args);
487 }
488 
489 static __attribute__((unused, format(printf, 2, 3)))
sprintf(char * buf,const char * fmt,...)490 int sprintf(char *buf, const char *fmt, ...)
491 {
492 	va_list args;
493 	int ret;
494 
495 	va_start(args, fmt);
496 	ret = vsprintf(buf, fmt, args);
497 	va_end(args);
498 
499 	return ret;
500 }
501 
502 static __attribute__((unused))
vsscanf(const char * str,const char * format,va_list args)503 int vsscanf(const char *str, const char *format, va_list args)
504 {
505 	uintmax_t uval;
506 	intmax_t ival;
507 	int base;
508 	char *endptr;
509 	int matches;
510 	int lpref;
511 
512 	matches = 0;
513 
514 	while (1) {
515 		if (*format == '%') {
516 			/* start of pattern */
517 			lpref = 0;
518 			format++;
519 
520 			if (*format == 'l') {
521 				/* same as in printf() */
522 				lpref = 1;
523 				format++;
524 				if (*format == 'l') {
525 					lpref = 2;
526 					format++;
527 				}
528 			}
529 
530 			if (*format == '%') {
531 				/* literal % */
532 				if ('%' != *str)
533 					goto done;
534 				str++;
535 				format++;
536 				continue;
537 			} else if (*format == 'd') {
538 				ival = strtoll(str, &endptr, 10);
539 				if (lpref == 0)
540 					*va_arg(args, int *) = ival;
541 				else if (lpref == 1)
542 					*va_arg(args, long *) = ival;
543 				else if (lpref == 2)
544 					*va_arg(args, long long *) = ival;
545 			} else if (*format == 'u' || *format == 'x' || *format == 'X') {
546 				base = *format == 'u' ? 10 : 16;
547 				uval = strtoull(str, &endptr, base);
548 				if (lpref == 0)
549 					*va_arg(args, unsigned int *) = uval;
550 				else if (lpref == 1)
551 					*va_arg(args, unsigned long *) = uval;
552 				else if (lpref == 2)
553 					*va_arg(args, unsigned long long *) = uval;
554 			} else if (*format == 'p') {
555 				*va_arg(args, void **) = (void *)strtoul(str, &endptr, 16);
556 			} else {
557 				SET_ERRNO(EILSEQ);
558 				goto done;
559 			}
560 
561 			format++;
562 			str = endptr;
563 			matches++;
564 
565 		} else if (*format == '\0') {
566 			goto done;
567 		} else if (isspace(*format)) {
568 			/* skip spaces in format and str */
569 			while (isspace(*format))
570 				format++;
571 			while (isspace(*str))
572 				str++;
573 		} else if (*format == *str) {
574 			/* literal match */
575 			format++;
576 			str++;
577 		} else {
578 			if (!matches)
579 				matches = EOF;
580 			goto done;
581 		}
582 	}
583 
584 done:
585 	return matches;
586 }
587 
588 static __attribute__((unused, format(scanf, 2, 3)))
sscanf(const char * str,const char * format,...)589 int sscanf(const char *str, const char *format, ...)
590 {
591 	va_list args;
592 	int ret;
593 
594 	va_start(args, format);
595 	ret = vsscanf(str, format, args);
596 	va_end(args);
597 	return ret;
598 }
599 
600 static __attribute__((unused))
perror(const char * msg)601 void perror(const char *msg)
602 {
603 	fprintf(stderr, "%s%serrno=%d\n", (msg && *msg) ? msg : "", (msg && *msg) ? ": " : "", errno);
604 }
605 
606 static __attribute__((unused))
setvbuf(FILE * stream,char * buf,int mode,size_t size)607 int setvbuf(FILE *stream __attribute__((unused)),
608 	    char *buf __attribute__((unused)),
609 	    int mode,
610 	    size_t size __attribute__((unused)))
611 {
612 	/*
613 	 * nolibc does not support buffering so this is a nop. Just check mode
614 	 * is valid as required by the spec.
615 	 */
616 	switch (mode) {
617 	case _IOFBF:
618 	case _IOLBF:
619 	case _IONBF:
620 		break;
621 	default:
622 		return EOF;
623 	}
624 
625 	return 0;
626 }
627 
628 static __attribute__((unused))
strerror(int errno)629 const char *strerror(int errno)
630 {
631 	static char buf[18] = "errno=";
632 
633 	i64toa_r(errno, &buf[6]);
634 
635 	return buf;
636 }
637 
638 #endif /* _NOLIBC_STDIO_H */
639