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