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