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