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