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 * Called by vfprintf() and snprintf() to do the actual formatting. 300 * The callers provide a callback function to save the formatted data. 301 * The callback function is called multiple times: 302 * - for each group of literal characters in the format string. 303 * - for field padding. 304 * - for each conversion specifier. 305 * - with (NULL, 0) at the end of the __nolibc_printf. 306 * If the callback returns non-zero __nolibc_printf() immediately returns -1. 307 */ 308 typedef int (*__nolibc_printf_cb)(void *state, const char *buf, size_t size); 309 310 static __attribute__((unused, format(printf, 3, 0))) 311 int __nolibc_printf(__nolibc_printf_cb cb, void *state, const char *fmt, va_list args) 312 { 313 char escape, lpref, ch; 314 unsigned long long v; 315 unsigned int written, width; 316 size_t len, ofs; 317 char outbuf[21]; 318 const char *outstr; 319 320 written = ofs = escape = lpref = 0; 321 while (1) { 322 ch = fmt[ofs++]; 323 width = 0; 324 325 if (escape) { 326 /* we're in an escape sequence, ofs == 1 */ 327 escape = 0; 328 329 /* width */ 330 while (ch >= '0' && ch <= '9') { 331 width *= 10; 332 width += ch - '0'; 333 334 ch = fmt[ofs++]; 335 } 336 337 if (ch == 'c' || ch == 'd' || ch == 'u' || ch == 'x' || ch == 'p') { 338 char *out = outbuf; 339 340 if (ch == 'p') 341 v = va_arg(args, unsigned long); 342 else if (lpref) { 343 if (lpref > 1) 344 v = va_arg(args, unsigned long long); 345 else 346 v = va_arg(args, unsigned long); 347 } else 348 v = va_arg(args, unsigned int); 349 350 if (ch == 'd') { 351 /* sign-extend the value */ 352 if (lpref == 0) 353 v = (long long)(int)v; 354 else if (lpref == 1) 355 v = (long long)(long)v; 356 } 357 358 switch (ch) { 359 case 'c': 360 out[0] = v; 361 out[1] = 0; 362 break; 363 case 'd': 364 i64toa_r(v, out); 365 break; 366 case 'u': 367 u64toa_r(v, out); 368 break; 369 case 'p': 370 *(out++) = '0'; 371 *(out++) = 'x'; 372 __nolibc_fallthrough; 373 default: /* 'x' and 'p' above */ 374 u64toh_r(v, out); 375 break; 376 } 377 outstr = outbuf; 378 } 379 else if (ch == 's') { 380 outstr = va_arg(args, char *); 381 if (!outstr) 382 outstr="(null)"; 383 } 384 else if (ch == 'm') { 385 #ifdef NOLIBC_IGNORE_ERRNO 386 outstr = "unknown error"; 387 #else 388 outstr = strerror(errno); 389 #endif /* NOLIBC_IGNORE_ERRNO */ 390 } 391 else if (ch == '%') { 392 /* queue it verbatim */ 393 continue; 394 } 395 else { 396 /* modifiers or final 0 */ 397 if (ch == 'l') { 398 /* long format prefix, maintain the escape */ 399 lpref++; 400 } else if (ch == 'j') { 401 lpref = 2; 402 } 403 escape = 1; 404 goto do_escape; 405 } 406 len = strlen(outstr); 407 goto flush_str; 408 } 409 410 /* not an escape sequence */ 411 if (ch == 0 || ch == '%') { 412 /* flush pending data on escape or end */ 413 escape = 1; 414 lpref = 0; 415 outstr = fmt; 416 len = ofs - 1; 417 flush_str: 418 while (width-- > len) { 419 if (cb(state, " ", 1) != 0) 420 return -1; 421 written += 1; 422 } 423 if (cb(state, outstr, len) != 0) 424 return -1; 425 426 written += len; 427 do_escape: 428 if (ch == 0) 429 break; 430 fmt += ofs; 431 ofs = 0; 432 continue; 433 } 434 435 /* literal char, just queue it */ 436 } 437 438 /* Request a final '\0' be added to the snprintf() output. 439 * This may be the only call of the cb() function. 440 */ 441 if (cb(state, NULL, 0) != 0) 442 return -1; 443 444 return written; 445 } 446 447 static int __nolibc_fprintf_cb(void *stream, const char *buf, size_t size) 448 { 449 return _fwrite(buf, size, stream); 450 } 451 452 static __attribute__((unused, format(printf, 2, 0))) 453 int vfprintf(FILE *stream, const char *fmt, va_list args) 454 { 455 return __nolibc_printf(__nolibc_fprintf_cb, stream, fmt, args); 456 } 457 458 static __attribute__((unused, format(printf, 1, 0))) 459 int vprintf(const char *fmt, va_list args) 460 { 461 return vfprintf(stdout, fmt, args); 462 } 463 464 static __attribute__((unused, format(printf, 2, 3))) 465 int fprintf(FILE *stream, const char *fmt, ...) 466 { 467 va_list args; 468 int ret; 469 470 va_start(args, fmt); 471 ret = vfprintf(stream, fmt, args); 472 va_end(args); 473 return ret; 474 } 475 476 static __attribute__((unused, format(printf, 1, 2))) 477 int printf(const char *fmt, ...) 478 { 479 va_list args; 480 int ret; 481 482 va_start(args, fmt); 483 ret = vfprintf(stdout, fmt, args); 484 va_end(args); 485 return ret; 486 } 487 488 static __attribute__((unused, format(printf, 2, 0))) 489 int vdprintf(int fd, const char *fmt, va_list args) 490 { 491 FILE *stream; 492 493 stream = fdopen(fd, NULL); 494 if (!stream) 495 return -1; 496 /* Technically 'stream' is leaked, but as it's only a wrapper around 'fd' that is fine */ 497 return vfprintf(stream, fmt, args); 498 } 499 500 static __attribute__((unused, format(printf, 2, 3))) 501 int dprintf(int fd, const char *fmt, ...) 502 { 503 va_list args; 504 int ret; 505 506 va_start(args, fmt); 507 ret = vdprintf(fd, fmt, args); 508 va_end(args); 509 510 return ret; 511 } 512 513 struct __nolibc_sprintf_cb_state { 514 char *buf; 515 size_t space; 516 }; 517 518 static int __nolibc_sprintf_cb(void *v_state, const char *buf, size_t size) 519 { 520 struct __nolibc_sprintf_cb_state *state = v_state; 521 size_t space = state->space; 522 char *tgt; 523 524 /* Truncate the request to fit in the output buffer space. 525 * The last byte is reserved for the terminating '\0'. 526 * state->space can only be zero for snprintf(NULL, 0, fmt, args) 527 * so this normally lets through calls with 'size == 0'. 528 */ 529 if (size >= space) { 530 if (space <= 1) 531 return 0; 532 size = space - 1; 533 } 534 tgt = state->buf; 535 536 /* __nolibc_printf() ends with cb(state, NULL, 0) to request the output 537 * buffer be '\0' terminated. 538 * That will be the only cb() call for, eg, snprintf(buf, sz, ""). 539 * Zero lengths can occur at other times (eg "%s" for an empty string). 540 * Unconditionally write the '\0' byte to reduce code size, it is 541 * normally overwritten by the data being output. 542 * There is no point adding a '\0' after copied data - there is always 543 * another call. 544 */ 545 *tgt = '\0'; 546 if (size) { 547 state->space = space - size; 548 state->buf = tgt + size; 549 memcpy(tgt, buf, size); 550 } 551 552 return 0; 553 } 554 555 static __attribute__((unused, format(printf, 3, 0))) 556 int vsnprintf(char *buf, size_t size, const char *fmt, va_list args) 557 { 558 struct __nolibc_sprintf_cb_state state = { .buf = buf, .space = size }; 559 560 return __nolibc_printf(__nolibc_sprintf_cb, &state, fmt, args); 561 } 562 563 static __attribute__((unused, format(printf, 3, 4))) 564 int snprintf(char *buf, size_t size, const char *fmt, ...) 565 { 566 va_list args; 567 int ret; 568 569 va_start(args, fmt); 570 ret = vsnprintf(buf, size, fmt, args); 571 va_end(args); 572 573 return ret; 574 } 575 576 static __attribute__((unused, format(printf, 2, 0))) 577 int vsprintf(char *buf, const char *fmt, va_list args) 578 { 579 return vsnprintf(buf, SIZE_MAX, fmt, args); 580 } 581 582 static __attribute__((unused, format(printf, 2, 3))) 583 int sprintf(char *buf, const char *fmt, ...) 584 { 585 va_list args; 586 int ret; 587 588 va_start(args, fmt); 589 ret = vsprintf(buf, fmt, args); 590 va_end(args); 591 592 return ret; 593 } 594 595 static __attribute__((unused)) 596 int vsscanf(const char *str, const char *format, va_list args) 597 { 598 uintmax_t uval; 599 intmax_t ival; 600 int base; 601 char *endptr; 602 int matches; 603 int lpref; 604 605 matches = 0; 606 607 while (1) { 608 if (*format == '%') { 609 /* start of pattern */ 610 lpref = 0; 611 format++; 612 613 if (*format == 'l') { 614 /* same as in printf() */ 615 lpref = 1; 616 format++; 617 if (*format == 'l') { 618 lpref = 2; 619 format++; 620 } 621 } 622 623 if (*format == '%') { 624 /* literal % */ 625 if ('%' != *str) 626 goto done; 627 str++; 628 format++; 629 continue; 630 } else if (*format == 'd') { 631 ival = strtoll(str, &endptr, 10); 632 if (lpref == 0) 633 *va_arg(args, int *) = ival; 634 else if (lpref == 1) 635 *va_arg(args, long *) = ival; 636 else if (lpref == 2) 637 *va_arg(args, long long *) = ival; 638 } else if (*format == 'u' || *format == 'x' || *format == 'X') { 639 base = *format == 'u' ? 10 : 16; 640 uval = strtoull(str, &endptr, base); 641 if (lpref == 0) 642 *va_arg(args, unsigned int *) = uval; 643 else if (lpref == 1) 644 *va_arg(args, unsigned long *) = uval; 645 else if (lpref == 2) 646 *va_arg(args, unsigned long long *) = uval; 647 } else if (*format == 'p') { 648 *va_arg(args, void **) = (void *)strtoul(str, &endptr, 16); 649 } else { 650 SET_ERRNO(EILSEQ); 651 goto done; 652 } 653 654 format++; 655 str = endptr; 656 matches++; 657 658 } else if (*format == '\0') { 659 goto done; 660 } else if (isspace(*format)) { 661 /* skip spaces in format and str */ 662 while (isspace(*format)) 663 format++; 664 while (isspace(*str)) 665 str++; 666 } else if (*format == *str) { 667 /* literal match */ 668 format++; 669 str++; 670 } else { 671 if (!matches) 672 matches = EOF; 673 goto done; 674 } 675 } 676 677 done: 678 return matches; 679 } 680 681 static __attribute__((unused, format(scanf, 2, 3))) 682 int sscanf(const char *str, const char *format, ...) 683 { 684 va_list args; 685 int ret; 686 687 va_start(args, format); 688 ret = vsscanf(str, format, args); 689 va_end(args); 690 return ret; 691 } 692 693 static __attribute__((unused)) 694 void perror(const char *msg) 695 { 696 #ifdef NOLIBC_IGNORE_ERRNO 697 fprintf(stderr, "%s%sunknown error\n", (msg && *msg) ? msg : "", (msg && *msg) ? ": " : ""); 698 #else 699 fprintf(stderr, "%s%serrno=%d\n", (msg && *msg) ? msg : "", (msg && *msg) ? ": " : "", errno); 700 #endif 701 } 702 703 static __attribute__((unused)) 704 int setvbuf(FILE *stream __attribute__((unused)), 705 char *buf __attribute__((unused)), 706 int mode, 707 size_t size __attribute__((unused))) 708 { 709 /* 710 * nolibc does not support buffering so this is a nop. Just check mode 711 * is valid as required by the spec. 712 */ 713 switch (mode) { 714 case _IOFBF: 715 case _IOLBF: 716 case _IONBF: 717 break; 718 default: 719 return EOF; 720 } 721 722 return 0; 723 } 724 725 static __attribute__((unused)) 726 int strerror_r(int errnum, char *buf, size_t buflen) 727 { 728 if (buflen < 18) 729 return ERANGE; 730 731 __builtin_memcpy(buf, "errno=", 6); 732 i64toa_r(errnum, buf + 6); 733 return 0; 734 } 735 736 static __attribute__((unused)) 737 const char *strerror(int errno) 738 { 739 static char buf[18]; 740 char *b = buf; 741 742 /* Force gcc to use 'register offset' to access buf[]. */ 743 _NOLIBC_OPTIMIZER_HIDE_VAR(b); 744 745 /* Use strerror_r() to avoid having the only .data in small programs. */ 746 strerror_r(errno, b, sizeof(buf)); 747 748 return b; 749 } 750 751 #endif /* _NOLIBC_STDIO_H */ 752