xref: /linux/tools/include/nolibc/stdio.h (revision 2337d39f7233fc3fb9d90489f0d48904eb129a2e)
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 "types.h"
17 #include "sys.h"
18 #include "stdarg.h"
19 #include "stdlib.h"
20 #include "string.h"
21 #include "compiler.h"
22 
23 static const char *strerror(int errnum);
24 
25 #ifndef EOF
26 #define EOF (-1)
27 #endif
28 
29 /* Buffering mode used by setvbuf.  */
30 #define _IOFBF 0	/* Fully buffered. */
31 #define _IOLBF 1	/* Line buffered. */
32 #define _IONBF 2	/* No buffering. */
33 
34 /* just define FILE as a non-empty type. The value of the pointer gives
35  * the FD: FILE=~fd for fd>=0 or NULL for fd<0. This way positive FILE
36  * are immediately identified as abnormal entries (i.e. possible copies
37  * of valid pointers to something else).
38  */
39 typedef struct FILE {
40 	char dummy[1];
41 } FILE;
42 
43 static __attribute__((unused)) FILE* const stdin  = (FILE*)(intptr_t)~STDIN_FILENO;
44 static __attribute__((unused)) FILE* const stdout = (FILE*)(intptr_t)~STDOUT_FILENO;
45 static __attribute__((unused)) FILE* const stderr = (FILE*)(intptr_t)~STDERR_FILENO;
46 
47 /* provides a FILE* equivalent of fd. The mode is ignored. */
48 static __attribute__((unused))
49 FILE *fdopen(int fd, const char *mode __attribute__((unused)))
50 {
51 	if (fd < 0) {
52 		SET_ERRNO(EBADF);
53 		return NULL;
54 	}
55 	return (FILE*)(intptr_t)~fd;
56 }
57 
58 /* provides the fd of stream. */
59 static __attribute__((unused))
60 int fileno(FILE *stream)
61 {
62 	intptr_t i = (intptr_t)stream;
63 
64 	if (i >= 0) {
65 		SET_ERRNO(EBADF);
66 		return -1;
67 	}
68 	return ~i;
69 }
70 
71 /* flush a stream. */
72 static __attribute__((unused))
73 int fflush(FILE *stream)
74 {
75 	intptr_t i = (intptr_t)stream;
76 
77 	/* NULL is valid here. */
78 	if (i > 0) {
79 		SET_ERRNO(EBADF);
80 		return -1;
81 	}
82 
83 	/* Don't do anything, nolibc does not support buffering. */
84 	return 0;
85 }
86 
87 /* flush a stream. */
88 static __attribute__((unused))
89 int fclose(FILE *stream)
90 {
91 	intptr_t i = (intptr_t)stream;
92 
93 	if (i >= 0) {
94 		SET_ERRNO(EBADF);
95 		return -1;
96 	}
97 
98 	if (close(~i))
99 		return EOF;
100 
101 	return 0;
102 }
103 
104 /* getc(), fgetc(), getchar() */
105 
106 #define getc(stream) fgetc(stream)
107 
108 static __attribute__((unused))
109 int fgetc(FILE* stream)
110 {
111 	unsigned char ch;
112 
113 	if (read(fileno(stream), &ch, 1) <= 0)
114 		return EOF;
115 	return ch;
116 }
117 
118 static __attribute__((unused))
119 int getchar(void)
120 {
121 	return fgetc(stdin);
122 }
123 
124 
125 /* putc(), fputc(), putchar() */
126 
127 #define putc(c, stream) fputc(c, stream)
128 
129 static __attribute__((unused))
130 int fputc(int c, FILE* stream)
131 {
132 	unsigned char ch = c;
133 
134 	if (write(fileno(stream), &ch, 1) <= 0)
135 		return EOF;
136 	return ch;
137 }
138 
139 static __attribute__((unused))
140 int putchar(int c)
141 {
142 	return fputc(c, stdout);
143 }
144 
145 
146 /* fwrite(), puts(), fputs(). Note that puts() emits '\n' but not fputs(). */
147 
148 /* internal fwrite()-like function which only takes a size and returns 0 on
149  * success or EOF on error. It automatically retries on short writes.
150  */
151 static __attribute__((unused))
152 int _fwrite(const void *buf, size_t size, FILE *stream)
153 {
154 	ssize_t ret;
155 	int fd = fileno(stream);
156 
157 	while (size) {
158 		ret = write(fd, buf, size);
159 		if (ret <= 0)
160 			return EOF;
161 		size -= ret;
162 		buf += ret;
163 	}
164 	return 0;
165 }
166 
167 static __attribute__((unused))
168 size_t fwrite(const void *s, size_t size, size_t nmemb, FILE *stream)
169 {
170 	size_t written;
171 
172 	for (written = 0; written < nmemb; written++) {
173 		if (_fwrite(s, size, stream) != 0)
174 			break;
175 		s += size;
176 	}
177 	return written;
178 }
179 
180 static __attribute__((unused))
181 int fputs(const char *s, FILE *stream)
182 {
183 	return _fwrite(s, strlen(s), stream);
184 }
185 
186 static __attribute__((unused))
187 int puts(const char *s)
188 {
189 	if (fputs(s, stdout) == EOF)
190 		return EOF;
191 	return putchar('\n');
192 }
193 
194 
195 /* fgets() */
196 static __attribute__((unused))
197 char *fgets(char *s, int size, FILE *stream)
198 {
199 	int ofs;
200 	int c;
201 
202 	for (ofs = 0; ofs + 1 < size;) {
203 		c = fgetc(stream);
204 		if (c == EOF)
205 			break;
206 		s[ofs++] = c;
207 		if (c == '\n')
208 			break;
209 	}
210 	if (ofs < size)
211 		s[ofs] = 0;
212 	return ofs ? s : NULL;
213 }
214 
215 
216 /* minimal printf(). It supports the following formats:
217  *  - %[l*]{d,u,c,x,p}
218  *  - %s
219  *  - unknown modifiers are ignored.
220  */
221 typedef int (*__nolibc_printf_cb)(intptr_t state, const char *buf, size_t size);
222 
223 static __attribute__((unused, format(printf, 4, 0)))
224 int __nolibc_printf(__nolibc_printf_cb cb, intptr_t state, size_t n, const char *fmt, va_list args)
225 {
226 	char escape, lpref, c;
227 	unsigned long long v;
228 	unsigned int written, width;
229 	size_t len, ofs, w;
230 	char tmpbuf[21];
231 	const char *outstr;
232 
233 	written = ofs = escape = lpref = 0;
234 	while (1) {
235 		c = fmt[ofs++];
236 		width = 0;
237 
238 		if (escape) {
239 			/* we're in an escape sequence, ofs == 1 */
240 			escape = 0;
241 
242 			/* width */
243 			while (c >= '0' && c <= '9') {
244 				width *= 10;
245 				width += c - '0';
246 
247 				c = fmt[ofs++];
248 			}
249 
250 			if (c == 'c' || c == 'd' || c == 'u' || c == 'x' || c == 'p') {
251 				char *out = tmpbuf;
252 
253 				if (c == 'p')
254 					v = va_arg(args, unsigned long);
255 				else if (lpref) {
256 					if (lpref > 1)
257 						v = va_arg(args, unsigned long long);
258 					else
259 						v = va_arg(args, unsigned long);
260 				} else
261 					v = va_arg(args, unsigned int);
262 
263 				if (c == 'd') {
264 					/* sign-extend the value */
265 					if (lpref == 0)
266 						v = (long long)(int)v;
267 					else if (lpref == 1)
268 						v = (long long)(long)v;
269 				}
270 
271 				switch (c) {
272 				case 'c':
273 					out[0] = v;
274 					out[1] = 0;
275 					break;
276 				case 'd':
277 					i64toa_r(v, out);
278 					break;
279 				case 'u':
280 					u64toa_r(v, out);
281 					break;
282 				case 'p':
283 					*(out++) = '0';
284 					*(out++) = 'x';
285 					__nolibc_fallthrough;
286 				default: /* 'x' and 'p' above */
287 					u64toh_r(v, out);
288 					break;
289 				}
290 				outstr = tmpbuf;
291 			}
292 			else if (c == 's') {
293 				outstr = va_arg(args, char *);
294 				if (!outstr)
295 					outstr="(null)";
296 			}
297 #ifndef NOLIBC_IGNORE_ERRNO
298 			else if (c == 'm') {
299 				outstr = strerror(errno);
300 			}
301 #endif /* NOLIBC_IGNORE_ERRNO */
302 			else if (c == '%') {
303 				/* queue it verbatim */
304 				continue;
305 			}
306 			else {
307 				/* modifiers or final 0 */
308 				if (c == 'l') {
309 					/* long format prefix, maintain the escape */
310 					lpref++;
311 				} else if (c == 'j') {
312 					lpref = 2;
313 				}
314 				escape = 1;
315 				goto do_escape;
316 			}
317 			len = strlen(outstr);
318 			goto flush_str;
319 		}
320 
321 		/* not an escape sequence */
322 		if (c == 0 || c == '%') {
323 			/* flush pending data on escape or end */
324 			escape = 1;
325 			lpref = 0;
326 			outstr = fmt;
327 			len = ofs - 1;
328 		flush_str:
329 			if (n) {
330 				w = len < n ? len : n;
331 				n -= w;
332 				while (width-- > w) {
333 					if (cb(state, " ", 1) != 0)
334 						break;
335 					written += 1;
336 				}
337 				if (cb(state, outstr, w) != 0)
338 					break;
339 			}
340 
341 			written += len;
342 		do_escape:
343 			if (c == 0)
344 				break;
345 			fmt += ofs;
346 			ofs = 0;
347 			continue;
348 		}
349 
350 		/* literal char, just queue it */
351 	}
352 	return written;
353 }
354 
355 static int __nolibc_fprintf_cb(intptr_t state, const char *buf, size_t size)
356 {
357 	return _fwrite(buf, size, (FILE *)state);
358 }
359 
360 static __attribute__((unused, format(printf, 2, 0)))
361 int vfprintf(FILE *stream, const char *fmt, va_list args)
362 {
363 	return __nolibc_printf(__nolibc_fprintf_cb, (intptr_t)stream, SIZE_MAX, fmt, args);
364 }
365 
366 static __attribute__((unused, format(printf, 1, 0)))
367 int vprintf(const char *fmt, va_list args)
368 {
369 	return vfprintf(stdout, fmt, args);
370 }
371 
372 static __attribute__((unused, format(printf, 2, 3)))
373 int fprintf(FILE *stream, const char *fmt, ...)
374 {
375 	va_list args;
376 	int ret;
377 
378 	va_start(args, fmt);
379 	ret = vfprintf(stream, fmt, args);
380 	va_end(args);
381 	return ret;
382 }
383 
384 static __attribute__((unused, format(printf, 1, 2)))
385 int printf(const char *fmt, ...)
386 {
387 	va_list args;
388 	int ret;
389 
390 	va_start(args, fmt);
391 	ret = vfprintf(stdout, fmt, args);
392 	va_end(args);
393 	return ret;
394 }
395 
396 static __attribute__((unused, format(printf, 2, 0)))
397 int vdprintf(int fd, const char *fmt, va_list args)
398 {
399 	FILE *stream;
400 
401 	stream = fdopen(fd, NULL);
402 	if (!stream)
403 		return -1;
404 	/* Technically 'stream' is leaked, but as it's only a wrapper around 'fd' that is fine */
405 	return vfprintf(stream, fmt, args);
406 }
407 
408 static __attribute__((unused, format(printf, 2, 3)))
409 int dprintf(int fd, const char *fmt, ...)
410 {
411 	va_list args;
412 	int ret;
413 
414 	va_start(args, fmt);
415 	ret = vdprintf(fd, fmt, args);
416 	va_end(args);
417 
418 	return ret;
419 }
420 
421 static int __nolibc_sprintf_cb(intptr_t _state, const char *buf, size_t size)
422 {
423 	char **state = (char **)_state;
424 
425 	memcpy(*state, buf, size);
426 	*state += size;
427 	return 0;
428 }
429 
430 static __attribute__((unused, format(printf, 3, 0)))
431 int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
432 {
433 	char *state = buf;
434 	int ret;
435 
436 	ret = __nolibc_printf(__nolibc_sprintf_cb, (intptr_t)&state, size, fmt, args);
437 	if (ret < 0)
438 		return ret;
439 	buf[(size_t)ret < size ? (size_t)ret : size - 1] = '\0';
440 	return ret;
441 }
442 
443 static __attribute__((unused, format(printf, 3, 4)))
444 int snprintf(char *buf, size_t size, const char *fmt, ...)
445 {
446 	va_list args;
447 	int ret;
448 
449 	va_start(args, fmt);
450 	ret = vsnprintf(buf, size, fmt, args);
451 	va_end(args);
452 
453 	return ret;
454 }
455 
456 static __attribute__((unused, format(printf, 2, 0)))
457 int vsprintf(char *buf, const char *fmt, va_list args)
458 {
459 	return vsnprintf(buf, SIZE_MAX, fmt, args);
460 }
461 
462 static __attribute__((unused, format(printf, 2, 3)))
463 int sprintf(char *buf, const char *fmt, ...)
464 {
465 	va_list args;
466 	int ret;
467 
468 	va_start(args, fmt);
469 	ret = vsprintf(buf, fmt, args);
470 	va_end(args);
471 
472 	return ret;
473 }
474 
475 static __attribute__((unused))
476 int vsscanf(const char *str, const char *format, va_list args)
477 {
478 	uintmax_t uval;
479 	intmax_t ival;
480 	int base;
481 	char *endptr;
482 	int matches;
483 	int lpref;
484 
485 	matches = 0;
486 
487 	while (1) {
488 		if (*format == '%') {
489 			/* start of pattern */
490 			lpref = 0;
491 			format++;
492 
493 			if (*format == 'l') {
494 				/* same as in printf() */
495 				lpref = 1;
496 				format++;
497 				if (*format == 'l') {
498 					lpref = 2;
499 					format++;
500 				}
501 			}
502 
503 			if (*format == '%') {
504 				/* literal % */
505 				if ('%' != *str)
506 					goto done;
507 				str++;
508 				format++;
509 				continue;
510 			} else if (*format == 'd') {
511 				ival = strtoll(str, &endptr, 10);
512 				if (lpref == 0)
513 					*va_arg(args, int *) = ival;
514 				else if (lpref == 1)
515 					*va_arg(args, long *) = ival;
516 				else if (lpref == 2)
517 					*va_arg(args, long long *) = ival;
518 			} else if (*format == 'u' || *format == 'x' || *format == 'X') {
519 				base = *format == 'u' ? 10 : 16;
520 				uval = strtoull(str, &endptr, base);
521 				if (lpref == 0)
522 					*va_arg(args, unsigned int *) = uval;
523 				else if (lpref == 1)
524 					*va_arg(args, unsigned long *) = uval;
525 				else if (lpref == 2)
526 					*va_arg(args, unsigned long long *) = uval;
527 			} else if (*format == 'p') {
528 				*va_arg(args, void **) = (void *)strtoul(str, &endptr, 16);
529 			} else {
530 				SET_ERRNO(EILSEQ);
531 				goto done;
532 			}
533 
534 			format++;
535 			str = endptr;
536 			matches++;
537 
538 		} else if (*format == '\0') {
539 			goto done;
540 		} else if (isspace(*format)) {
541 			/* skip spaces in format and str */
542 			while (isspace(*format))
543 				format++;
544 			while (isspace(*str))
545 				str++;
546 		} else if (*format == *str) {
547 			/* literal match */
548 			format++;
549 			str++;
550 		} else {
551 			if (!matches)
552 				matches = EOF;
553 			goto done;
554 		}
555 	}
556 
557 done:
558 	return matches;
559 }
560 
561 static __attribute__((unused, format(scanf, 2, 3)))
562 int sscanf(const char *str, const char *format, ...)
563 {
564 	va_list args;
565 	int ret;
566 
567 	va_start(args, format);
568 	ret = vsscanf(str, format, args);
569 	va_end(args);
570 	return ret;
571 }
572 
573 static __attribute__((unused))
574 void perror(const char *msg)
575 {
576 	fprintf(stderr, "%s%serrno=%d\n", (msg && *msg) ? msg : "", (msg && *msg) ? ": " : "", errno);
577 }
578 
579 static __attribute__((unused))
580 int setvbuf(FILE *stream __attribute__((unused)),
581 	    char *buf __attribute__((unused)),
582 	    int mode,
583 	    size_t size __attribute__((unused)))
584 {
585 	/*
586 	 * nolibc does not support buffering so this is a nop. Just check mode
587 	 * is valid as required by the spec.
588 	 */
589 	switch (mode) {
590 	case _IOFBF:
591 	case _IOLBF:
592 	case _IONBF:
593 		break;
594 	default:
595 		return EOF;
596 	}
597 
598 	return 0;
599 }
600 
601 static __attribute__((unused))
602 const char *strerror(int errno)
603 {
604 	static char buf[18] = "errno=";
605 
606 	i64toa_r(errno, &buf[6]);
607 
608 	return buf;
609 }
610 
611 #endif /* _NOLIBC_STDIO_H */
612