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