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,o,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', 'o', '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 } else { 483 /* "#o" requires that the output always starts with a '0'. 484 * This needs another check after any zero padding to avoid 485 * adding an extra leading '0'. 486 */ 487 if (_NOLIBC_PF_FLAGS_CONTAIN(ch_flag, 'o') && 488 _NOLIBC_PF_FLAGS_CONTAIN(ch_flag, '#' - 1)) 489 sign_prefix = '0'; 490 } 491 492 /* The value is converted offset into the buffer so that 493 * 31 zero pad characters and the sign/prefix can be added in front. 494 * The longest digit string is 22 + 1 for octal conversions. 495 */ 496 out = outbuf + 2 + 31; 497 498 if (v == 0) { 499 /* There are special rules for zero. */ 500 if (_NOLIBC_PF_FLAGS_CONTAIN(ch_flag, 'p')) { 501 /* "%p" match glibc, precision is ignored */ 502 outstr = "(nil)"; 503 len = 5; 504 goto do_output; 505 } 506 if (!precision) { 507 /* Explicit %nn.0d, no digits output (except for %#.0o) */ 508 len = 0; 509 goto prepend_sign; 510 } 511 /* All other formats (including "%#x") just output "0". */ 512 out[0] = '0'; 513 len = 1; 514 } else { 515 /* Convert the number to ascii in the required base. */ 516 unsigned long long recip; 517 unsigned int base; 518 if (_NOLIBC_PF_FLAGS_CONTAIN(ch_flag, 'd', 'i', 'u')) { 519 base = 10; 520 recip = _NOLIBC_U64TOA_RECIP(10); 521 } else if (_NOLIBC_PF_FLAGS_CONTAIN(ch_flag, 'o')) { 522 base = 8; 523 recip = _NOLIBC_U64TOA_RECIP(8); 524 } else { 525 base = 16; 526 recip = _NOLIBC_U64TOA_RECIP(16); 527 if (_NOLIBC_PF_FLAGS_CONTAIN(ch_flag, 'p', '#' - 1)) { 528 /* "%p" and "%#x" need "0x" prepending. */ 529 sign_prefix = '0' << 8 | 'x'; 530 } 531 } 532 len = _nolibc_u64toa_base(v, out, base, recip); 533 } 534 535 /* Add zero padding */ 536 if (_NOLIBC_PF_FLAGS_CONTAIN(flags, '0', '.')) { 537 if (!_NOLIBC_PF_FLAGS_CONTAIN(flags, '.')) { 538 if (_NOLIBC_PF_FLAGS_CONTAIN(flags, '-')) 539 /* Left justify overrides zero pad */ 540 goto prepend_sign; 541 /* eg "%05d", Zero pad to field width less sign. 542 * Note that precision can end up negative so all 543 * the variables have to be 'signed int'. 544 */ 545 precision = width; 546 if (sign_prefix) { 547 precision--; 548 if (sign_prefix >= 256) 549 precision--; 550 } 551 } 552 if (precision > 31) 553 /* Don't run off the start of outbuf[], arbitrary limit 554 * longer than the longest number field. */ 555 precision = 31; 556 for (; len < precision; len++) { 557 /* Stop gcc generating horrid code and memset(). */ 558 _NOLIBC_OPTIMIZER_HIDE_VAR(len); 559 *--out = '0'; 560 } 561 } 562 563 /* %#o has set sign_prefix to '0', but we don't want so add an extra 564 * leading zero here. 565 * Since the only other byte values of sign_prefix are ' ', '+' and '-' 566 * it is enough to check that out[] doesn't already start with sign_prefix. 567 */ 568 if (sign_prefix - *out) { 569 prepend_sign: 570 /* Add the 0, 1 or 2 ("0x") sign/prefix characters at the front. */ 571 for (; sign_prefix; sign_prefix >>= 8) { 572 /* Force gcc to increment len inside the loop. */ 573 _NOLIBC_OPTIMIZER_HIDE_VAR(len); 574 len++; 575 *--out = sign_prefix; 576 } 577 } 578 outstr = out; 579 goto do_output; 580 } 581 582 if (ch == 'm') { 583 #ifdef NOLIBC_IGNORE_ERRNO 584 outstr = "unknown error"; 585 #else 586 outstr = strerror(errno); 587 #endif /* NOLIBC_IGNORE_ERRNO */ 588 goto do_strlen_output; 589 } 590 591 if (ch != '%') { 592 /* Invalid format: back up to output the format characters */ 593 fmt = outstr + 1; 594 /* and output a '%' now. */ 595 } 596 /* %% is documented as a 'conversion specifier'. 597 * Any flags, precision or length modifier are ignored. 598 */ 599 len = 1; 600 width = 0; 601 outstr = fmt - 1; 602 goto do_output; 603 604 do_strlen_output: 605 /* Open coded strnlen() (slightly smaller). */ 606 for (len = 0; len < precision; len++) 607 if (!outstr[len]) 608 break; 609 610 do_output: 611 written += len; 612 613 /* Stop gcc back-merging this code into one of the conditionals above. */ 614 _NOLIBC_OPTIMIZER_HIDE_VAR(len); 615 616 /* Output the characters on the required side of any padding. */ 617 width -= len; 618 flags = _NOLIBC_PF_FLAGS_CONTAIN(flags, '-'); 619 if (flags && cb(state, outstr, len) != 0) 620 return -1; 621 while (width > 0) { 622 /* Output pad in 16 byte blocks with the small block first. */ 623 int pad_len = ((width - 1) & 15) + 1; 624 width -= pad_len; 625 written += pad_len; 626 if (cb(state, " ", pad_len) != 0) 627 return -1; 628 } 629 if (!flags && cb(state, outstr, len) != 0) 630 return -1; 631 } 632 633 /* Request a final '\0' be added to the snprintf() output. 634 * This may be the only call of the cb() function. 635 */ 636 if (cb(state, NULL, 0) != 0) 637 return -1; 638 639 return written; 640 } 641 642 static int __nolibc_fprintf_cb(void *stream, const char *buf, size_t size) 643 { 644 return _fwrite(buf, size, stream); 645 } 646 647 static __attribute__((unused, format(printf, 2, 0))) 648 int vfprintf(FILE *stream, const char *fmt, va_list args) 649 { 650 return __nolibc_printf(__nolibc_fprintf_cb, stream, fmt, args); 651 } 652 653 static __attribute__((unused, format(printf, 1, 0))) 654 int vprintf(const char *fmt, va_list args) 655 { 656 return vfprintf(stdout, fmt, args); 657 } 658 659 static __attribute__((unused, format(printf, 2, 3))) 660 int fprintf(FILE *stream, const char *fmt, ...) 661 { 662 va_list args; 663 int ret; 664 665 va_start(args, fmt); 666 ret = vfprintf(stream, fmt, args); 667 va_end(args); 668 return ret; 669 } 670 671 static __attribute__((unused, format(printf, 1, 2))) 672 int printf(const char *fmt, ...) 673 { 674 va_list args; 675 int ret; 676 677 va_start(args, fmt); 678 ret = vfprintf(stdout, fmt, args); 679 va_end(args); 680 return ret; 681 } 682 683 static __attribute__((unused, format(printf, 2, 0))) 684 int vdprintf(int fd, const char *fmt, va_list args) 685 { 686 FILE *stream; 687 688 stream = fdopen(fd, NULL); 689 if (!stream) 690 return -1; 691 /* Technically 'stream' is leaked, but as it's only a wrapper around 'fd' that is fine */ 692 return vfprintf(stream, fmt, args); 693 } 694 695 static __attribute__((unused, format(printf, 2, 3))) 696 int dprintf(int fd, const char *fmt, ...) 697 { 698 va_list args; 699 int ret; 700 701 va_start(args, fmt); 702 ret = vdprintf(fd, fmt, args); 703 va_end(args); 704 705 return ret; 706 } 707 708 struct __nolibc_sprintf_cb_state { 709 char *buf; 710 size_t space; 711 }; 712 713 static int __nolibc_sprintf_cb(void *v_state, const char *buf, size_t size) 714 { 715 struct __nolibc_sprintf_cb_state *state = v_state; 716 size_t space = state->space; 717 char *tgt; 718 719 /* Truncate the request to fit in the output buffer space. 720 * The last byte is reserved for the terminating '\0'. 721 * state->space can only be zero for snprintf(NULL, 0, fmt, args) 722 * so this normally lets through calls with 'size == 0'. 723 */ 724 if (size >= space) { 725 if (space <= 1) 726 return 0; 727 size = space - 1; 728 } 729 tgt = state->buf; 730 731 /* __nolibc_printf() ends with cb(state, NULL, 0) to request the output 732 * buffer be '\0' terminated. 733 * That will be the only cb() call for, eg, snprintf(buf, sz, ""). 734 * Zero lengths can occur at other times (eg "%s" for an empty string). 735 * Unconditionally write the '\0' byte to reduce code size, it is 736 * normally overwritten by the data being output. 737 * There is no point adding a '\0' after copied data - there is always 738 * another call. 739 */ 740 *tgt = '\0'; 741 if (size) { 742 state->space = space - size; 743 state->buf = tgt + size; 744 memcpy(tgt, buf, size); 745 } 746 747 return 0; 748 } 749 750 static __attribute__((unused, format(printf, 3, 0))) 751 int vsnprintf(char *buf, size_t size, const char *fmt, va_list args) 752 { 753 struct __nolibc_sprintf_cb_state state = { .buf = buf, .space = size }; 754 755 return __nolibc_printf(__nolibc_sprintf_cb, &state, fmt, args); 756 } 757 758 static __attribute__((unused, format(printf, 3, 4))) 759 int snprintf(char *buf, size_t size, const char *fmt, ...) 760 { 761 va_list args; 762 int ret; 763 764 va_start(args, fmt); 765 ret = vsnprintf(buf, size, fmt, args); 766 va_end(args); 767 768 return ret; 769 } 770 771 static __attribute__((unused, format(printf, 2, 0))) 772 int vsprintf(char *buf, const char *fmt, va_list args) 773 { 774 return vsnprintf(buf, SIZE_MAX, fmt, args); 775 } 776 777 static __attribute__((unused, format(printf, 2, 3))) 778 int sprintf(char *buf, const char *fmt, ...) 779 { 780 va_list args; 781 int ret; 782 783 va_start(args, fmt); 784 ret = vsprintf(buf, fmt, args); 785 va_end(args); 786 787 return ret; 788 } 789 790 static __attribute__((unused)) 791 int vsscanf(const char *str, const char *format, va_list args) 792 { 793 uintmax_t uval; 794 intmax_t ival; 795 int base; 796 char *endptr; 797 int matches; 798 int lpref; 799 800 matches = 0; 801 802 while (1) { 803 if (*format == '%') { 804 /* start of pattern */ 805 lpref = 0; 806 format++; 807 808 if (*format == 'l') { 809 /* same as in printf() */ 810 lpref = 1; 811 format++; 812 if (*format == 'l') { 813 lpref = 2; 814 format++; 815 } 816 } 817 818 if (*format == '%') { 819 /* literal % */ 820 if ('%' != *str) 821 goto done; 822 str++; 823 format++; 824 continue; 825 } else if (*format == 'd') { 826 ival = strtoll(str, &endptr, 10); 827 if (lpref == 0) 828 *va_arg(args, int *) = ival; 829 else if (lpref == 1) 830 *va_arg(args, long *) = ival; 831 else if (lpref == 2) 832 *va_arg(args, long long *) = ival; 833 } else if (*format == 'u' || *format == 'x' || *format == 'X') { 834 base = *format == 'u' ? 10 : 16; 835 uval = strtoull(str, &endptr, base); 836 if (lpref == 0) 837 *va_arg(args, unsigned int *) = uval; 838 else if (lpref == 1) 839 *va_arg(args, unsigned long *) = uval; 840 else if (lpref == 2) 841 *va_arg(args, unsigned long long *) = uval; 842 } else if (*format == 'p') { 843 *va_arg(args, void **) = (void *)strtoul(str, &endptr, 16); 844 } else { 845 SET_ERRNO(EILSEQ); 846 goto done; 847 } 848 849 format++; 850 str = endptr; 851 matches++; 852 853 } else if (*format == '\0') { 854 goto done; 855 } else if (isspace(*format)) { 856 /* skip spaces in format and str */ 857 while (isspace(*format)) 858 format++; 859 while (isspace(*str)) 860 str++; 861 } else if (*format == *str) { 862 /* literal match */ 863 format++; 864 str++; 865 } else { 866 if (!matches) 867 matches = EOF; 868 goto done; 869 } 870 } 871 872 done: 873 return matches; 874 } 875 876 static __attribute__((unused, format(scanf, 2, 3))) 877 int sscanf(const char *str, const char *format, ...) 878 { 879 va_list args; 880 int ret; 881 882 va_start(args, format); 883 ret = vsscanf(str, format, args); 884 va_end(args); 885 return ret; 886 } 887 888 static __attribute__((unused)) 889 void perror(const char *msg) 890 { 891 #ifdef NOLIBC_IGNORE_ERRNO 892 fprintf(stderr, "%s%sunknown error\n", (msg && *msg) ? msg : "", (msg && *msg) ? ": " : ""); 893 #else 894 fprintf(stderr, "%s%serrno=%d\n", (msg && *msg) ? msg : "", (msg && *msg) ? ": " : "", errno); 895 #endif 896 } 897 898 static __attribute__((unused)) 899 int setvbuf(FILE *stream __attribute__((unused)), 900 char *buf __attribute__((unused)), 901 int mode, 902 size_t size __attribute__((unused))) 903 { 904 /* 905 * nolibc does not support buffering so this is a nop. Just check mode 906 * is valid as required by the spec. 907 */ 908 switch (mode) { 909 case _IOFBF: 910 case _IOLBF: 911 case _IONBF: 912 break; 913 default: 914 return EOF; 915 } 916 917 return 0; 918 } 919 920 static __attribute__((unused)) 921 int strerror_r(int errnum, char *buf, size_t buflen) 922 { 923 if (buflen < 18) 924 return ERANGE; 925 926 __builtin_memcpy(buf, "errno=", 6); 927 i64toa_r(errnum, buf + 6); 928 return 0; 929 } 930 931 static __attribute__((unused)) 932 const char *strerror(int errnum) 933 { 934 static char buf[18]; 935 char *b = buf; 936 937 /* Force gcc to use 'register offset' to access buf[]. */ 938 _NOLIBC_OPTIMIZER_HIDE_VAR(b); 939 940 /* Use strerror_r() to avoid having the only .data in small programs. */ 941 strerror_r(errnum, b, sizeof(buf)); 942 943 return b; 944 } 945 946 #endif /* _NOLIBC_STDIO_H */ 947