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 /* printf(). Supports most of the normal integer and string formats. 295 * - %[#-+ 0][width][{l,t,z,ll,L,j,q}]{c,d,i,u,x,X,p,s,m,%} 296 * - %% generates a single % 297 * - %m outputs strerror(errno). 298 * - %X outputs a..f the same as %x. 299 * - The modifiers [#-+ 0] are currently ignored. 300 * - No support for precision or variable widths. 301 * - No support for floating point or wide characters. 302 * - Invalid formats are copied to the output buffer. 303 * 304 * Called by vfprintf() and snprintf() to do the actual formatting. 305 * The callers provide a callback function to save the formatted data. 306 * The callback function is called multiple times: 307 * - for each group of literal characters in the format string. 308 * - for field padding. 309 * - for each conversion specifier. 310 * - with (NULL, 0) at the end of the __nolibc_printf. 311 * If the callback returns non-zero __nolibc_printf() immediately returns -1. 312 */ 313 314 typedef int (*__nolibc_printf_cb)(void *state, const char *buf, size_t size); 315 316 /* This code uses 'flag' variables that are indexed by the low 6 bits 317 * of characters to optimise checks for multiple characters. 318 * 319 * _NOLIBC_PF_FLAGS_CONTAIN(flags, 'a', 'b'. ...) 320 * returns non-zero if the bit for any of the specified characters is set. 321 * 322 * _NOLIBC_PF_CHAR_IS_ONE_OF(ch, 'a', 'b'. ...) 323 * returns the flag bit for ch if it is one of the specified characters. 324 * All the characters must be in the same 32 character block (non-alphabetic, 325 * upper case, or lower case) of the ASCII character set. 326 */ 327 #define _NOLIBC_PF_FLAG(ch) (1u << ((ch) & 0x1f)) 328 #define _NOLIBC_PF_FLAG_NZ(ch) ((ch) ? _NOLIBC_PF_FLAG(ch) : 0) 329 #define _NOLIBC_PF_FLAG8(cmp_1, cmp_2, cmp_3, cmp_4, cmp_5, cmp_6, cmp_7, cmp_8, ...) \ 330 (_NOLIBC_PF_FLAG_NZ(cmp_1) | _NOLIBC_PF_FLAG_NZ(cmp_2) | \ 331 _NOLIBC_PF_FLAG_NZ(cmp_3) | _NOLIBC_PF_FLAG_NZ(cmp_4) | \ 332 _NOLIBC_PF_FLAG_NZ(cmp_5) | _NOLIBC_PF_FLAG_NZ(cmp_6) | \ 333 _NOLIBC_PF_FLAG_NZ(cmp_7) | _NOLIBC_PF_FLAG_NZ(cmp_8)) 334 #define _NOLIBC_PF_FLAGS_CONTAIN(flags, ...) \ 335 ((flags) & _NOLIBC_PF_FLAG8(__VA_ARGS__, 0, 0, 0, 0, 0, 0, 0)) 336 #define _NOLIBC_PF_CHAR_IS_ONE_OF(ch, cmp_1, ...) \ 337 ((unsigned int)(ch) - (cmp_1 & 0xe0) > 0x1f ? 0 : \ 338 _NOLIBC_PF_FLAGS_CONTAIN(_NOLIBC_PF_FLAG(ch), cmp_1, __VA_ARGS__)) 339 340 static __attribute__((unused, format(printf, 3, 0))) 341 int __nolibc_printf(__nolibc_printf_cb cb, void *state, const char *fmt, va_list args) 342 { 343 char ch; 344 unsigned long long v; 345 long long signed_v; 346 int written, width, len; 347 unsigned int flags, ch_flag; 348 char outbuf[21]; 349 char *out; 350 const char *outstr; 351 352 written = 0; 353 while (1) { 354 outstr = fmt; 355 ch = *fmt++; 356 if (!ch) 357 break; 358 359 width = 0; 360 flags = 0; 361 if (ch != '%') { 362 while (*fmt && *fmt != '%') 363 fmt++; 364 /* Output characters from the format string. */ 365 len = fmt - outstr; 366 goto do_output; 367 } 368 369 /* we're in a format sequence */ 370 371 /* Conversion flag characters */ 372 while (1) { 373 ch = *fmt++; 374 ch_flag = _NOLIBC_PF_CHAR_IS_ONE_OF(ch, ' ', '#', '+', '-', '0'); 375 if (!ch_flag) 376 break; 377 flags |= ch_flag; 378 } 379 380 /* width */ 381 while (ch >= '0' && ch <= '9') { 382 width *= 10; 383 width += ch - '0'; 384 385 ch = *fmt++; 386 } 387 388 /* Length modifier. 389 * They miss the conversion flags characters " #+-0" so can go into flags. 390 * Change both L and ll to j (all always 64bit). 391 */ 392 if (ch == 'L') 393 ch = 'j'; 394 ch_flag = _NOLIBC_PF_CHAR_IS_ONE_OF(ch, 'l', 't', 'z', 'j', 'q'); 395 if (ch_flag != 0) { 396 if (ch == 'l' && fmt[0] == 'l') { 397 fmt++; 398 ch_flag = _NOLIBC_PF_FLAG('j'); 399 } 400 flags |= ch_flag; 401 ch = *fmt++; 402 } 403 404 /* Conversion specifiers. */ 405 406 /* Numeric and pointer conversion specifiers. 407 * 408 * Use an explicit bound check (rather than _NOLIBC_PF_CHAR_IS_ONE_OF()) 409 * so that 'X' can be allowed through. 410 * 'X' gets treated and 'x' because _NOLIBC_PF_FLAG() returns the same 411 * value for both. 412 */ 413 ch_flag = _NOLIBC_PF_FLAG(ch); 414 if (((ch >= 'a' && ch <= 'z') || ch == 'X') && 415 _NOLIBC_PF_FLAGS_CONTAIN(ch_flag, 'c', 'd', 'i', 'u', 'x', 'p')) { 416 /* 'long' is needed for pointer conversions and ltz lengths. 417 * A single test can be used provided 'p' (the same bit as '0') 418 * is masked from flags. 419 */ 420 if (_NOLIBC_PF_FLAGS_CONTAIN(ch_flag | (flags & ~_NOLIBC_PF_FLAG('p')), 421 'p', 'l', 't', 'z')) { 422 v = va_arg(args, unsigned long); 423 signed_v = (long)v; 424 } else if (_NOLIBC_PF_FLAGS_CONTAIN(flags, 'j', 'q')) { 425 v = va_arg(args, unsigned long long); 426 signed_v = v; 427 } else { 428 v = va_arg(args, unsigned int); 429 signed_v = (int)v; 430 } 431 432 if (ch == 'c') { 433 /* "%c" - single character. */ 434 outbuf[0] = v; 435 len = 1; 436 outstr = outbuf; 437 goto do_output; 438 } 439 440 out = outbuf; 441 442 if (_NOLIBC_PF_FLAGS_CONTAIN(ch_flag, 'd', 'i')) { 443 /* "%d" and "%i" - signed decimal numbers. */ 444 if (signed_v < 0) { 445 *out++ = '-'; 446 v = -(signed_v + 1); 447 v++; 448 } 449 } 450 451 /* Convert the number to ascii in the required base. */ 452 if (_NOLIBC_PF_FLAGS_CONTAIN(ch_flag, 'd', 'i', 'u')) { 453 /* Base 10 */ 454 u64toa_r(v, out); 455 } else { 456 /* Base 16 */ 457 if (_NOLIBC_PF_FLAGS_CONTAIN(ch_flag, 'p')) { 458 *(out++) = '0'; 459 *(out++) = 'x'; 460 } 461 u64toh_r(v, out); 462 } 463 464 outstr = outbuf; 465 goto do_strlen_output; 466 } 467 468 if (ch == 's') { 469 outstr = va_arg(args, char *); 470 if (!outstr) 471 outstr = "(null)"; 472 goto do_strlen_output; 473 } 474 475 if (ch == 'm') { 476 #ifdef NOLIBC_IGNORE_ERRNO 477 outstr = "unknown error"; 478 #else 479 outstr = strerror(errno); 480 #endif /* NOLIBC_IGNORE_ERRNO */ 481 goto do_strlen_output; 482 } 483 484 if (ch != '%') { 485 /* Invalid format: back up to output the format characters */ 486 fmt = outstr + 1; 487 /* and output a '%' now. */ 488 } 489 /* %% is documented as a 'conversion specifier'. 490 * Any flags, precision or length modifier are ignored. 491 */ 492 len = 1; 493 width = 0; 494 outstr = fmt - 1; 495 goto do_output; 496 497 do_strlen_output: 498 /* Open coded strlen() (slightly smaller). */ 499 for (len = 0;; len++) 500 if (!outstr[len]) 501 break; 502 503 do_output: 504 written += len; 505 506 /* Stop gcc back-merging this code into one of the conditionals above. */ 507 _NOLIBC_OPTIMIZER_HIDE_VAR(len); 508 509 width -= len; 510 while (width > 0) { 511 /* Output pad in 16 byte blocks with the small block first. */ 512 int pad_len = ((width - 1) & 15) + 1; 513 width -= pad_len; 514 written += pad_len; 515 if (cb(state, " ", pad_len) != 0) 516 return -1; 517 } 518 if (cb(state, outstr, len) != 0) 519 return -1; 520 } 521 522 /* Request a final '\0' be added to the snprintf() output. 523 * This may be the only call of the cb() function. 524 */ 525 if (cb(state, NULL, 0) != 0) 526 return -1; 527 528 return written; 529 } 530 531 static int __nolibc_fprintf_cb(void *stream, const char *buf, size_t size) 532 { 533 return _fwrite(buf, size, stream); 534 } 535 536 static __attribute__((unused, format(printf, 2, 0))) 537 int vfprintf(FILE *stream, const char *fmt, va_list args) 538 { 539 return __nolibc_printf(__nolibc_fprintf_cb, stream, fmt, args); 540 } 541 542 static __attribute__((unused, format(printf, 1, 0))) 543 int vprintf(const char *fmt, va_list args) 544 { 545 return vfprintf(stdout, fmt, args); 546 } 547 548 static __attribute__((unused, format(printf, 2, 3))) 549 int fprintf(FILE *stream, const char *fmt, ...) 550 { 551 va_list args; 552 int ret; 553 554 va_start(args, fmt); 555 ret = vfprintf(stream, fmt, args); 556 va_end(args); 557 return ret; 558 } 559 560 static __attribute__((unused, format(printf, 1, 2))) 561 int printf(const char *fmt, ...) 562 { 563 va_list args; 564 int ret; 565 566 va_start(args, fmt); 567 ret = vfprintf(stdout, fmt, args); 568 va_end(args); 569 return ret; 570 } 571 572 static __attribute__((unused, format(printf, 2, 0))) 573 int vdprintf(int fd, const char *fmt, va_list args) 574 { 575 FILE *stream; 576 577 stream = fdopen(fd, NULL); 578 if (!stream) 579 return -1; 580 /* Technically 'stream' is leaked, but as it's only a wrapper around 'fd' that is fine */ 581 return vfprintf(stream, fmt, args); 582 } 583 584 static __attribute__((unused, format(printf, 2, 3))) 585 int dprintf(int fd, const char *fmt, ...) 586 { 587 va_list args; 588 int ret; 589 590 va_start(args, fmt); 591 ret = vdprintf(fd, fmt, args); 592 va_end(args); 593 594 return ret; 595 } 596 597 struct __nolibc_sprintf_cb_state { 598 char *buf; 599 size_t space; 600 }; 601 602 static int __nolibc_sprintf_cb(void *v_state, const char *buf, size_t size) 603 { 604 struct __nolibc_sprintf_cb_state *state = v_state; 605 size_t space = state->space; 606 char *tgt; 607 608 /* Truncate the request to fit in the output buffer space. 609 * The last byte is reserved for the terminating '\0'. 610 * state->space can only be zero for snprintf(NULL, 0, fmt, args) 611 * so this normally lets through calls with 'size == 0'. 612 */ 613 if (size >= space) { 614 if (space <= 1) 615 return 0; 616 size = space - 1; 617 } 618 tgt = state->buf; 619 620 /* __nolibc_printf() ends with cb(state, NULL, 0) to request the output 621 * buffer be '\0' terminated. 622 * That will be the only cb() call for, eg, snprintf(buf, sz, ""). 623 * Zero lengths can occur at other times (eg "%s" for an empty string). 624 * Unconditionally write the '\0' byte to reduce code size, it is 625 * normally overwritten by the data being output. 626 * There is no point adding a '\0' after copied data - there is always 627 * another call. 628 */ 629 *tgt = '\0'; 630 if (size) { 631 state->space = space - size; 632 state->buf = tgt + size; 633 memcpy(tgt, buf, size); 634 } 635 636 return 0; 637 } 638 639 static __attribute__((unused, format(printf, 3, 0))) 640 int vsnprintf(char *buf, size_t size, const char *fmt, va_list args) 641 { 642 struct __nolibc_sprintf_cb_state state = { .buf = buf, .space = size }; 643 644 return __nolibc_printf(__nolibc_sprintf_cb, &state, fmt, args); 645 } 646 647 static __attribute__((unused, format(printf, 3, 4))) 648 int snprintf(char *buf, size_t size, const char *fmt, ...) 649 { 650 va_list args; 651 int ret; 652 653 va_start(args, fmt); 654 ret = vsnprintf(buf, size, fmt, args); 655 va_end(args); 656 657 return ret; 658 } 659 660 static __attribute__((unused, format(printf, 2, 0))) 661 int vsprintf(char *buf, const char *fmt, va_list args) 662 { 663 return vsnprintf(buf, SIZE_MAX, fmt, args); 664 } 665 666 static __attribute__((unused, format(printf, 2, 3))) 667 int sprintf(char *buf, const char *fmt, ...) 668 { 669 va_list args; 670 int ret; 671 672 va_start(args, fmt); 673 ret = vsprintf(buf, fmt, args); 674 va_end(args); 675 676 return ret; 677 } 678 679 static __attribute__((unused)) 680 int vsscanf(const char *str, const char *format, va_list args) 681 { 682 uintmax_t uval; 683 intmax_t ival; 684 int base; 685 char *endptr; 686 int matches; 687 int lpref; 688 689 matches = 0; 690 691 while (1) { 692 if (*format == '%') { 693 /* start of pattern */ 694 lpref = 0; 695 format++; 696 697 if (*format == 'l') { 698 /* same as in printf() */ 699 lpref = 1; 700 format++; 701 if (*format == 'l') { 702 lpref = 2; 703 format++; 704 } 705 } 706 707 if (*format == '%') { 708 /* literal % */ 709 if ('%' != *str) 710 goto done; 711 str++; 712 format++; 713 continue; 714 } else if (*format == 'd') { 715 ival = strtoll(str, &endptr, 10); 716 if (lpref == 0) 717 *va_arg(args, int *) = ival; 718 else if (lpref == 1) 719 *va_arg(args, long *) = ival; 720 else if (lpref == 2) 721 *va_arg(args, long long *) = ival; 722 } else if (*format == 'u' || *format == 'x' || *format == 'X') { 723 base = *format == 'u' ? 10 : 16; 724 uval = strtoull(str, &endptr, base); 725 if (lpref == 0) 726 *va_arg(args, unsigned int *) = uval; 727 else if (lpref == 1) 728 *va_arg(args, unsigned long *) = uval; 729 else if (lpref == 2) 730 *va_arg(args, unsigned long long *) = uval; 731 } else if (*format == 'p') { 732 *va_arg(args, void **) = (void *)strtoul(str, &endptr, 16); 733 } else { 734 SET_ERRNO(EILSEQ); 735 goto done; 736 } 737 738 format++; 739 str = endptr; 740 matches++; 741 742 } else if (*format == '\0') { 743 goto done; 744 } else if (isspace(*format)) { 745 /* skip spaces in format and str */ 746 while (isspace(*format)) 747 format++; 748 while (isspace(*str)) 749 str++; 750 } else if (*format == *str) { 751 /* literal match */ 752 format++; 753 str++; 754 } else { 755 if (!matches) 756 matches = EOF; 757 goto done; 758 } 759 } 760 761 done: 762 return matches; 763 } 764 765 static __attribute__((unused, format(scanf, 2, 3))) 766 int sscanf(const char *str, const char *format, ...) 767 { 768 va_list args; 769 int ret; 770 771 va_start(args, format); 772 ret = vsscanf(str, format, args); 773 va_end(args); 774 return ret; 775 } 776 777 static __attribute__((unused)) 778 void perror(const char *msg) 779 { 780 #ifdef NOLIBC_IGNORE_ERRNO 781 fprintf(stderr, "%s%sunknown error\n", (msg && *msg) ? msg : "", (msg && *msg) ? ": " : ""); 782 #else 783 fprintf(stderr, "%s%serrno=%d\n", (msg && *msg) ? msg : "", (msg && *msg) ? ": " : "", errno); 784 #endif 785 } 786 787 static __attribute__((unused)) 788 int setvbuf(FILE *stream __attribute__((unused)), 789 char *buf __attribute__((unused)), 790 int mode, 791 size_t size __attribute__((unused))) 792 { 793 /* 794 * nolibc does not support buffering so this is a nop. Just check mode 795 * is valid as required by the spec. 796 */ 797 switch (mode) { 798 case _IOFBF: 799 case _IOLBF: 800 case _IONBF: 801 break; 802 default: 803 return EOF; 804 } 805 806 return 0; 807 } 808 809 static __attribute__((unused)) 810 int strerror_r(int errnum, char *buf, size_t buflen) 811 { 812 if (buflen < 18) 813 return ERANGE; 814 815 __builtin_memcpy(buf, "errno=", 6); 816 i64toa_r(errnum, buf + 6); 817 return 0; 818 } 819 820 static __attribute__((unused)) 821 const char *strerror(int errnum) 822 { 823 static char buf[18]; 824 char *b = buf; 825 826 /* Force gcc to use 'register offset' to access buf[]. */ 827 _NOLIBC_OPTIMIZER_HIDE_VAR(b); 828 829 /* Use strerror_r() to avoid having the only .data in small programs. */ 830 strerror_r(errnum, b, sizeof(buf)); 831 832 return b; 833 } 834 835 #endif /* _NOLIBC_STDIO_H */ 836