Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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
24static 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 */
40typedef struct FILE {
41 char dummy[1];
42} FILE;
43
44static __attribute__((unused)) FILE* const stdin = (FILE*)(intptr_t)~STDIN_FILENO;
45static __attribute__((unused)) FILE* const stdout = (FILE*)(intptr_t)~STDOUT_FILENO;
46static __attribute__((unused)) FILE* const stderr = (FILE*)(intptr_t)~STDERR_FILENO;
47
48/* provides a FILE* equivalent of fd. The mode is ignored. */
49static __attribute__((unused))
50FILE *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
59static __attribute__((unused))
60FILE *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. */
86static __attribute__((unused))
87int 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. */
99static __attribute__((unused))
100int 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. */
115static __attribute__((unused))
116int 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
135static __attribute__((unused))
136int 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
145static __attribute__((unused))
146int getchar(void)
147{
148 return fgetc(stdin);
149}
150
151
152/* putc(), fputc(), putchar() */
153
154#define putc(c, stream) fputc(c, stream)
155
156static __attribute__((unused))
157int 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
166static __attribute__((unused))
167int putchar(int c)
168{
169 return fputc(c, stdout);
170}
171
172
173/* fwrite(), 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 */
178static __attribute__((unused))
179int _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
194static __attribute__((unused))
195size_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
207static __attribute__((unused))
208int fputs(const char *s, FILE *stream)
209{
210 return _fwrite(s, strlen(s), stream);
211}
212
213static __attribute__((unused))
214int puts(const char *s)
215{
216 if (fputs(s, stdout) == EOF)
217 return EOF;
218 return putchar('\n');
219}
220
221
222/* fgets() */
223static __attribute__((unused))
224char *fgets(char *s, int size, FILE *stream)
225{
226 int ofs;
227 int c;
228
229 for (ofs = 0; ofs + 1 < size;) {
230 c = fgetc(stream);
231 if (c == EOF)
232 break;
233 s[ofs++] = c;
234 if (c == '\n')
235 break;
236 }
237 if (ofs < size)
238 s[ofs] = 0;
239 return ofs ? s : NULL;
240}
241
242
243/* minimal printf(). It supports the following formats:
244 * - %[l*]{d,u,c,x,p}
245 * - %s
246 * - unknown modifiers are ignored.
247 */
248typedef int (*__nolibc_printf_cb)(intptr_t state, const char *buf, size_t size);
249
250static __attribute__((unused, format(printf, 4, 0)))
251int __nolibc_printf(__nolibc_printf_cb cb, intptr_t state, size_t n, const char *fmt, va_list args)
252{
253 char escape, lpref, c;
254 unsigned long long v;
255 unsigned int written, width;
256 size_t len, ofs, w;
257 char tmpbuf[21];
258 const char *outstr;
259
260 written = ofs = escape = lpref = 0;
261 while (1) {
262 c = fmt[ofs++];
263 width = 0;
264
265 if (escape) {
266 /* we're in an escape sequence, ofs == 1 */
267 escape = 0;
268
269 /* width */
270 while (c >= '0' && c <= '9') {
271 width *= 10;
272 width += c - '0';
273
274 c = fmt[ofs++];
275 }
276
277 if (c == 'c' || c == 'd' || c == 'u' || c == 'x' || c == 'p') {
278 char *out = tmpbuf;
279
280 if (c == 'p')
281 v = va_arg(args, unsigned long);
282 else if (lpref) {
283 if (lpref > 1)
284 v = va_arg(args, unsigned long long);
285 else
286 v = va_arg(args, unsigned long);
287 } else
288 v = va_arg(args, unsigned int);
289
290 if (c == 'd') {
291 /* sign-extend the value */
292 if (lpref == 0)
293 v = (long long)(int)v;
294 else if (lpref == 1)
295 v = (long long)(long)v;
296 }
297
298 switch (c) {
299 case 'c':
300 out[0] = v;
301 out[1] = 0;
302 break;
303 case 'd':
304 i64toa_r(v, out);
305 break;
306 case 'u':
307 u64toa_r(v, out);
308 break;
309 case 'p':
310 *(out++) = '0';
311 *(out++) = 'x';
312 __nolibc_fallthrough;
313 default: /* 'x' and 'p' above */
314 u64toh_r(v, out);
315 break;
316 }
317 outstr = tmpbuf;
318 }
319 else if (c == 's') {
320 outstr = va_arg(args, char *);
321 if (!outstr)
322 outstr="(null)";
323 }
324 else if (c == 'm') {
325#ifdef NOLIBC_IGNORE_ERRNO
326 outstr = "unknown error";
327#else
328 outstr = strerror(errno);
329#endif /* NOLIBC_IGNORE_ERRNO */
330 }
331 else if (c == '%') {
332 /* queue it verbatim */
333 continue;
334 }
335 else {
336 /* modifiers or final 0 */
337 if (c == 'l') {
338 /* long format prefix, maintain the escape */
339 lpref++;
340 } else if (c == 'j') {
341 lpref = 2;
342 }
343 escape = 1;
344 goto do_escape;
345 }
346 len = strlen(outstr);
347 goto flush_str;
348 }
349
350 /* not an escape sequence */
351 if (c == 0 || c == '%') {
352 /* flush pending data on escape or end */
353 escape = 1;
354 lpref = 0;
355 outstr = fmt;
356 len = ofs - 1;
357 flush_str:
358 if (n) {
359 w = len < n ? len : n;
360 n -= w;
361 while (width-- > w) {
362 if (cb(state, " ", 1) != 0)
363 return -1;
364 written += 1;
365 }
366 if (cb(state, outstr, w) != 0)
367 return -1;
368 }
369
370 written += len;
371 do_escape:
372 if (c == 0)
373 break;
374 fmt += ofs;
375 ofs = 0;
376 continue;
377 }
378
379 /* literal char, just queue it */
380 }
381 return written;
382}
383
384static int __nolibc_fprintf_cb(intptr_t state, const char *buf, size_t size)
385{
386 return _fwrite(buf, size, (FILE *)state);
387}
388
389static __attribute__((unused, format(printf, 2, 0)))
390int vfprintf(FILE *stream, const char *fmt, va_list args)
391{
392 return __nolibc_printf(__nolibc_fprintf_cb, (intptr_t)stream, SIZE_MAX, fmt, args);
393}
394
395static __attribute__((unused, format(printf, 1, 0)))
396int vprintf(const char *fmt, va_list args)
397{
398 return vfprintf(stdout, fmt, args);
399}
400
401static __attribute__((unused, format(printf, 2, 3)))
402int fprintf(FILE *stream, const char *fmt, ...)
403{
404 va_list args;
405 int ret;
406
407 va_start(args, fmt);
408 ret = vfprintf(stream, fmt, args);
409 va_end(args);
410 return ret;
411}
412
413static __attribute__((unused, format(printf, 1, 2)))
414int printf(const char *fmt, ...)
415{
416 va_list args;
417 int ret;
418
419 va_start(args, fmt);
420 ret = vfprintf(stdout, fmt, args);
421 va_end(args);
422 return ret;
423}
424
425static __attribute__((unused, format(printf, 2, 0)))
426int vdprintf(int fd, const char *fmt, va_list args)
427{
428 FILE *stream;
429
430 stream = fdopen(fd, NULL);
431 if (!stream)
432 return -1;
433 /* Technically 'stream' is leaked, but as it's only a wrapper around 'fd' that is fine */
434 return vfprintf(stream, fmt, args);
435}
436
437static __attribute__((unused, format(printf, 2, 3)))
438int dprintf(int fd, const char *fmt, ...)
439{
440 va_list args;
441 int ret;
442
443 va_start(args, fmt);
444 ret = vdprintf(fd, fmt, args);
445 va_end(args);
446
447 return ret;
448}
449
450static int __nolibc_sprintf_cb(intptr_t _state, const char *buf, size_t size)
451{
452 char **state = (char **)_state;
453
454 memcpy(*state, buf, size);
455 *state += size;
456 return 0;
457}
458
459static __attribute__((unused, format(printf, 3, 0)))
460int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
461{
462 char *state = buf;
463 int ret;
464
465 ret = __nolibc_printf(__nolibc_sprintf_cb, (intptr_t)&state, size, fmt, args);
466 if (ret < 0)
467 return ret;
468 buf[(size_t)ret < size ? (size_t)ret : size - 1] = '\0';
469 return ret;
470}
471
472static __attribute__((unused, format(printf, 3, 4)))
473int snprintf(char *buf, size_t size, const char *fmt, ...)
474{
475 va_list args;
476 int ret;
477
478 va_start(args, fmt);
479 ret = vsnprintf(buf, size, fmt, args);
480 va_end(args);
481
482 return ret;
483}
484
485static __attribute__((unused, format(printf, 2, 0)))
486int vsprintf(char *buf, const char *fmt, va_list args)
487{
488 return vsnprintf(buf, SIZE_MAX, fmt, args);
489}
490
491static __attribute__((unused, format(printf, 2, 3)))
492int sprintf(char *buf, const char *fmt, ...)
493{
494 va_list args;
495 int ret;
496
497 va_start(args, fmt);
498 ret = vsprintf(buf, fmt, args);
499 va_end(args);
500
501 return ret;
502}
503
504static __attribute__((unused))
505int vsscanf(const char *str, const char *format, va_list args)
506{
507 uintmax_t uval;
508 intmax_t ival;
509 int base;
510 char *endptr;
511 int matches;
512 int lpref;
513
514 matches = 0;
515
516 while (1) {
517 if (*format == '%') {
518 /* start of pattern */
519 lpref = 0;
520 format++;
521
522 if (*format == 'l') {
523 /* same as in printf() */
524 lpref = 1;
525 format++;
526 if (*format == 'l') {
527 lpref = 2;
528 format++;
529 }
530 }
531
532 if (*format == '%') {
533 /* literal % */
534 if ('%' != *str)
535 goto done;
536 str++;
537 format++;
538 continue;
539 } else if (*format == 'd') {
540 ival = strtoll(str, &endptr, 10);
541 if (lpref == 0)
542 *va_arg(args, int *) = ival;
543 else if (lpref == 1)
544 *va_arg(args, long *) = ival;
545 else if (lpref == 2)
546 *va_arg(args, long long *) = ival;
547 } else if (*format == 'u' || *format == 'x' || *format == 'X') {
548 base = *format == 'u' ? 10 : 16;
549 uval = strtoull(str, &endptr, base);
550 if (lpref == 0)
551 *va_arg(args, unsigned int *) = uval;
552 else if (lpref == 1)
553 *va_arg(args, unsigned long *) = uval;
554 else if (lpref == 2)
555 *va_arg(args, unsigned long long *) = uval;
556 } else if (*format == 'p') {
557 *va_arg(args, void **) = (void *)strtoul(str, &endptr, 16);
558 } else {
559 SET_ERRNO(EILSEQ);
560 goto done;
561 }
562
563 format++;
564 str = endptr;
565 matches++;
566
567 } else if (*format == '\0') {
568 goto done;
569 } else if (isspace(*format)) {
570 /* skip spaces in format and str */
571 while (isspace(*format))
572 format++;
573 while (isspace(*str))
574 str++;
575 } else if (*format == *str) {
576 /* literal match */
577 format++;
578 str++;
579 } else {
580 if (!matches)
581 matches = EOF;
582 goto done;
583 }
584 }
585
586done:
587 return matches;
588}
589
590static __attribute__((unused, format(scanf, 2, 3)))
591int sscanf(const char *str, const char *format, ...)
592{
593 va_list args;
594 int ret;
595
596 va_start(args, format);
597 ret = vsscanf(str, format, args);
598 va_end(args);
599 return ret;
600}
601
602static __attribute__((unused))
603void perror(const char *msg)
604{
605#ifdef NOLIBC_IGNORE_ERRNO
606 fprintf(stderr, "%s%sunknown error\n", (msg && *msg) ? msg : "", (msg && *msg) ? ": " : "");
607#else
608 fprintf(stderr, "%s%serrno=%d\n", (msg && *msg) ? msg : "", (msg && *msg) ? ": " : "", errno);
609#endif
610}
611
612static __attribute__((unused))
613int setvbuf(FILE *stream __attribute__((unused)),
614 char *buf __attribute__((unused)),
615 int mode,
616 size_t size __attribute__((unused)))
617{
618 /*
619 * nolibc does not support buffering so this is a nop. Just check mode
620 * is valid as required by the spec.
621 */
622 switch (mode) {
623 case _IOFBF:
624 case _IOLBF:
625 case _IONBF:
626 break;
627 default:
628 return EOF;
629 }
630
631 return 0;
632}
633
634static __attribute__((unused))
635const char *strerror(int errno)
636{
637 static char buf[18] = "errno=";
638
639 i64toa_r(errno, &buf[6]);
640
641 return buf;
642}
643
644#endif /* _NOLIBC_STDIO_H */