libc.c (24406B)
1 /* libc.c — slim libc closure for tcc-boot2. 2 * 3 * What's here: just enough libc for tcc.flat.c, the cc-libc tests, and 4 * the boot4 hello smoke binary to link. Started from mes-libc 0.24. 5 * 6 * Provides: 7 * syscalls _read _write _open3 close lseek brk unlink _exit 8 * raise abort environ getenv __libc_init 9 * ENOSYS stubs: access execve execvp fsync rmdir stat strtod 10 * I/O stdin/stdout/stderr (FILE *), FILE = long alias for fd 11 * fopen fdopen fclose fflush fseek ftell remove 12 * fread fwrite fputs fputc fgetc puts strdup 13 * fprintf printf snprintf sprintf 14 * vfprintf vsnprintf vprintf vsprintf 15 * stdlib malloc free realloc qsort exit atoi 16 * strtol strtoul strtoll strtoull strtof 17 * string strlen strcmp strcpy strncmp strncpy strchr strrchr 18 * strstr strcat strdup memmem 19 * ctype isdigit islower isnumber isspace isxdigit toupper 20 * assertions __assert_fail 21 * 22 * External: memcpy memmove memset memcmp come from libp1pp / cc/mem.c. 23 * sys_read sys_write sys_open sys_close sys_lseek sys_brk 24 * sys_unlink sys_exit are P1pp.P1pp labels. 25 * 26 * Order constraints: 27 * 1. The syscall + FILE-globals block has to come first — globals 28 * (errno, stdin/stdout/stderr, __brk, __FILEDES_MAX-sized arrays) 29 * are referenced by everything below. 30 * 2. malloc must precede the file-IO helpers because __read_cache is 31 * lazy-malloc'd on first read. 32 * 33 * License: GPLv3+ (mes upstream); see LICENSE. 34 */ 35 36 #include <fcntl.h> 37 #include <errno.h> 38 #include <stdio.h> 39 #include <unistd.h> 40 #include <sys/stat.h> 41 #include <ctype.h> 42 #include <string.h> 43 #include <stdlib.h> 44 #include <limits.h> 45 #include <stdarg.h> 46 #include <stddef.h> 47 #include <stdint.h> 48 49 #define __FILEDES_MAX 4096 50 51 /* ───────── globals ───────────────────────────────────────────────── */ 52 53 /* Our sysinclude stdio.h declares stdin/stdout/stderr as extern FILE* 54 (not macros). The #undefs below are no-ops kept for compatibility. */ 55 #undef stdin 56 #undef stdout 57 #undef stderr 58 FILE *stdin = (FILE *) 0; 59 FILE *stdout = (FILE *) 1; 60 FILE *stderr = (FILE *) 2; 61 62 int errno; 63 char *__brk = 0; 64 char **environ; 65 66 67 /* ───────── P1pp syscall labels and thin wrappers ─────────────────── */ 68 69 /* forward decl: defined in the stdio block, but _open3/close need it. */ 70 static size_t read_cache_clear_ (int fd); 71 72 extern long sys_read (long fd, long buf, long n); 73 extern long sys_write (long fd, long buf, long n); 74 extern long sys_open (long path, long flags, long mode); 75 extern long sys_close (long fd); 76 extern long sys_lseek (long fd, long off, long whence); 77 extern long sys_brk (long addr); 78 extern long sys_unlink (long path); 79 extern long sys_exit (long code); 80 81 ssize_t 82 _read (int fd, void *buf, size_t n) 83 { 84 return sys_read (fd, (long) buf, (long) n); 85 } 86 87 ssize_t 88 _write (int fd, void const *buf, size_t n) 89 { 90 return sys_write (fd, (long) buf, (long) n); 91 } 92 93 int 94 _open3 (char const *path, int flags, int mode) 95 { 96 long r = sys_open ((long) path, flags, mode); 97 if (r >= __FILEDES_MAX) { 98 errno = EMFILE; 99 return -1; 100 } 101 /* fd numbers are kernel-recycled; if we hand out a fd whose previous 102 life left bytes in the per-fd read buffer, the next read on the 103 new file picks them up and tcc_object_type sees a corrupted ELF 104 header. Mes's _open3 clears the cache for the same reason. */ 105 if (r > 2) 106 read_cache_clear_ (r); 107 return (int) r; 108 } 109 110 int 111 close (int fd) 112 { 113 long r = sys_close (fd); 114 if (r < 0) { 115 errno = -r; 116 return -1; 117 } 118 errno = 0; 119 return (int) r; 120 } 121 122 /* mes's lseek drains a per-fd read buffer before letting the kernel 123 see the seek; we replicate that in fseek directly (it owns the 124 buffer state), so this layer is just the raw syscall. */ 125 off_t 126 _lseek (int fd, off_t off, int whence) 127 { 128 return sys_lseek (fd, off, whence); 129 } 130 131 long 132 brk (void *addr) 133 { 134 return sys_brk ((long) addr); 135 } 136 137 int 138 unlink (char const *path) 139 { 140 long r = sys_unlink ((long) path); 141 if (r < 0) { 142 errno = -r; 143 return -1; 144 } 145 errno = 0; 146 return 0; 147 } 148 149 void 150 _exit (int status) 151 { 152 sys_exit (status); 153 } 154 155 /* abort() (mes's version) calls raise(SIGABRT). We don't ship signals 156 so just exit; tcc's tcc_error path goes here on hard failures. */ 157 int 158 raise (int sig) 159 { 160 sys_exit (128 + sig); 161 return -1; 162 } 163 164 void 165 abort (void) 166 { 167 sys_exit (128 + 6); 168 } 169 170 void 171 __libc_init (int argc, char **argv) 172 { 173 /* Linux exec entry stack: argc, argv[0..argc-1], NULL, envp[..], NULL. 174 :_start passes (argc, argv); environ follows argv's NULL. 175 Without it, getenv() dereferences NULL on first call. */ 176 char **p; 177 (void) argc; 178 p = argv; 179 while (*p) 180 p++; 181 environ = p + 1; 182 } 183 184 /* Stubs for libc-internal references not exercised by the tcc-boot2 185 golden path (fread/fopen/etc. include <sys/stat.h>; the closure 186 we pull in references these). If any surfaces in a real workload, 187 replace with a P1pp label + thin wrapper. */ 188 char * 189 _getcwd (char *buf, size_t size) 190 { 191 (void) buf; (void) size; errno = ENOSYS; return 0; 192 } 193 194 int 195 access (char const *path, int mode) 196 { 197 (void) path; (void) mode; errno = ENOSYS; return -1; 198 } 199 200 int 201 execve (char const *path, char *const argv[], char *const envp[]) 202 { 203 (void) path; (void) argv; (void) envp; errno = ENOSYS; return -1; 204 } 205 206 int 207 execvp (char const *file, char *const argv[]) 208 { 209 (void) file; (void) argv; errno = ENOSYS; return -1; 210 } 211 212 int 213 fsync (int fd) 214 { 215 (void) fd; return 0; 216 } 217 218 int 219 rmdir (char const *path) 220 { 221 (void) path; errno = ENOSYS; return -1; 222 } 223 224 int 225 stat (char const *path, struct stat *buf) 226 { 227 (void) path; (void) buf; errno = ENOSYS; return -1; 228 } 229 230 double 231 strtod (char const *s, char **tail) 232 { 233 /* tcc.flat.c never reaches strtod under our defines (HAVE_FLOAT off); 234 stdlib/strtof.c only forwards. cc.scm rejects FP literals, so we 235 cast a zero int — same numeric value, no `0.0` token. */ 236 if (tail) 237 *tail = (char *) s; 238 return (double) 0; 239 } 240 241 242 /* ───────── ctype ────────────────────────────────────────────────── */ 243 244 int isdigit (int c) { return c >= '0' && c <= '9'; } 245 int islower (int c) { return c >= 'a' && c <= 'z'; } 246 int isspace (int c) { return c == ' ' || (c >= '\t' && c <= '\r'); } 247 int isxdigit (int c) { return isdigit (c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); } 248 int toupper (int c) { return islower (c) ? c - 'a' + 'A' : c; } 249 250 int 251 isnumber (int c, int base) 252 { 253 if (base <= 10) 254 return c >= '0' && c < '0' + base; 255 return isdigit (c) 256 || (c >= 'a' && c < 'a' + base - 10) 257 || (c >= 'A' && c < 'A' + base - 10); 258 } 259 260 261 /* ───────── string ───────────────────────────────────────────────── */ 262 263 size_t 264 strlen (char const *s) 265 { 266 size_t n = 0; 267 while (s[n]) 268 n++; 269 return n; 270 } 271 272 int 273 strcmp (char const *a, char const *b) 274 { 275 while (*a && *a == *b) { a++; b++; } 276 return *(unsigned char *) a - *(unsigned char *) b; 277 } 278 279 int 280 strncmp (char const *a, char const *b, size_t n) 281 { 282 while (n && *a && *a == *b) { a++; b++; n--; } 283 if (!n) return 0; 284 return *(unsigned char *) a - *(unsigned char *) b; 285 } 286 287 char * 288 strcpy (char *dst, char const *src) 289 { 290 char *r = dst; 291 while ((*dst++ = *src++)) ; 292 return r; 293 } 294 295 char * 296 strncpy (char *dst, char const *src, size_t n) 297 { 298 char *r = dst; 299 while (n && (*dst++ = *src++)) n--; 300 while (n--) *dst++ = 0; 301 return r; 302 } 303 304 char * 305 strcat (char *dst, char const *src) 306 { 307 char *r = dst; 308 while (*dst) dst++; 309 while ((*dst++ = *src++)) ; 310 return r; 311 } 312 313 char * 314 strchr (char const *s, int c) 315 { 316 for (; *s; s++) 317 if (*s == (char) c) 318 return (char *) s; 319 return c ? 0 : (char *) s; 320 } 321 322 char * 323 strrchr (char const *s, int c) 324 { 325 char const *last = 0; 326 for (; *s; s++) 327 if (*s == (char) c) 328 last = s; 329 if (!c) return (char *) s; 330 return (char *) last; 331 } 332 333 char * 334 strstr (char const *hay, char const *needle) 335 { 336 size_t n = strlen (needle); 337 if (!n) return (char *) hay; 338 for (; *hay; hay++) 339 if (!strncmp (hay, needle, n)) 340 return (char *) hay; 341 return 0; 342 } 343 344 void * 345 memmem (void const *hay, int haylen, void const *needle, int needlelen) 346 { 347 char const *h = hay; 348 char const *n = needle; 349 int i; 350 if (needlelen <= 0) return (void *) hay; 351 if (needlelen > haylen) return 0; 352 for (i = 0; i + needlelen <= haylen; i++) { 353 int j = 0; 354 while (j < needlelen && h[i + j] == n[j]) 355 j++; 356 if (j == needlelen) 357 return (char *) h + i; 358 } 359 return 0; 360 } 361 362 char * 363 strdup (char const *s) 364 { 365 size_t n = strlen (s) + 1; 366 char *p = malloc (n); 367 if (p) memcpy (p, s, n); 368 return p; 369 } 370 371 372 /* ───────── malloc — bump allocator on top of brk ─────────────────── */ 373 374 void * 375 malloc (size_t size) 376 { 377 long want; 378 char *p; 379 if (!__brk) 380 __brk = (char *) brk (0); 381 /* 16-byte align — matches the largest scalar (long long / pointer / double). */ 382 __brk = (char *) (((uintptr_t) __brk + 16 - 1) & -16); 383 /* Linux brk(2) returns the (unchanged) break on failure rather than 384 -1, so compare against the requested address — a refused growth 385 surfaces here as malloc returning NULL. */ 386 want = (long) (__brk + size); 387 if (brk (__brk + size) < want) 388 return 0; 389 p = __brk; 390 __brk += size; 391 return p; 392 } 393 394 void 395 free (void *p) 396 { 397 (void) p; /* bump allocator; reclamation is not implemented. */ 398 } 399 400 void * 401 realloc (void *p, size_t size) 402 { 403 void *q = malloc (size); 404 if (p && q) 405 memcpy (q, p, size); 406 return q; 407 } 408 409 410 /* ───────── stdlib: integer parsing and exit ──────────────────────── */ 411 412 /* Internal parser shared by atoi / strtol / strtoul / strtoull and the 413 width/precision parser inside vformat_. `long long` accumulator so 414 values that don't fit in 32-bit signed don't sign-extend through the 415 parse — tcc3 needs this for `-Wl,-Ttext=0x80200000` and the like. */ 416 static long long 417 parse_int_ (char const **p, int base) 418 { 419 char const *s = *p; 420 long long n = 0; 421 int neg = 0; 422 if (base == 0) base = 10; 423 while (isspace (*s)) s++; 424 if (*s == '+') s++; 425 else if (*s == '-') { neg = 1; s++; } 426 while (isnumber (*s, base)) { 427 int d; 428 if (*s >= 'a') d = *s - 'a' + 10; 429 else if (*s >= 'A') d = *s - 'A' + 10; 430 else d = *s - '0'; 431 n = n * base + d; 432 s++; 433 } 434 *p = s; 435 return neg ? -n : n; 436 } 437 438 int 439 atoi (char const *s) 440 { 441 return (int) parse_int_ (&s, 10); 442 } 443 444 long 445 strtol (char const *s, char **tail, int base) 446 { 447 char const *p; 448 long r; 449 if (!strncmp (s, "0x", 2)) { s += 2; base = 16; } 450 p = s; 451 r = (long) parse_int_ (&p, base); 452 if (tail) *tail = (char *) p; 453 return r; 454 } 455 456 unsigned long strtoul (char const *s, char **tail, int base) { return strtol (s, tail, base); } 457 long long strtoll (char const *s, char **tail, int base) { return strtol (s, tail, base); } 458 unsigned long long strtoull (char const *s, char **tail, int base) { return strtol (s, tail, base); } 459 float strtof (char const *s, char **tail) { return strtod (s, tail); } 460 461 void (*__call_at_exit) (void); 462 463 void 464 exit (int status) 465 { 466 if (__call_at_exit) 467 __call_at_exit (); 468 _exit (status); 469 /* not reached */ 470 while (1) ; 471 } 472 473 /* qsort: lifted from mes — straightforward Lomuto-partition recursion. */ 474 static void 475 qswap_ (char *a, char *b, size_t size) 476 { 477 do { 478 char t = *a; 479 *a++ = *b; 480 *b++ = t; 481 } while (--size); 482 } 483 484 static size_t 485 qpart_ (char *base, size_t count, size_t size, int (*cmp) (void *, void *)) 486 { 487 char *pivot = base + count * size; 488 size_t i = 0; 489 size_t j; 490 int c; 491 for (j = 0; j < count; j++) { 492 c = cmp (base + j * size, pivot); 493 if (c < 0) { 494 qswap_ (base + i * size, base + j * size, size); 495 i++; 496 } else if (c == 0) { 497 i++; 498 } 499 } 500 if (cmp (pivot, base + i * size) < 0) 501 qswap_ (base + i * size, pivot, size); 502 return i; 503 } 504 505 void 506 qsort (void *base, size_t count, size_t size, int (*cmp) (void *, void *)) 507 { 508 size_t p; 509 if (count > 1) { 510 p = qpart_ (base, count - 1, size, cmp); 511 qsort (base, p, size, cmp); 512 qsort ((char *) base + p * size, count - p, size, cmp); 513 } 514 } 515 516 517 /* ───────── getenv ────────────────────────────────────────────────── */ 518 519 char * 520 getenv (char const *name) 521 { 522 size_t n = strlen (name); 523 char **p; 524 for (p = environ; p && *p; p++) { 525 if (!strncmp (*p, name, n) && (*p)[n] == '=') 526 return *p + n + 1; 527 } 528 return 0; 529 } 530 531 532 /* ───────── stdio: FILE * = (long) fd ─────────────────────────────── */ 533 534 /* Block-buffered read, per fd. Without it, tcc lexing tcc.flat.c does 535 ~500K single-byte syscalls; with a 128-byte cache it's ~4K. */ 536 #define READ_BUFFER_MAX 128 537 538 struct read_buffer { 539 ssize_t size; 540 char bytes[READ_BUFFER_MAX]; 541 }; 542 static struct read_buffer *read_cache; 543 544 static void 545 read_cache_init_ (void) 546 { 547 if (!read_cache) 548 read_cache = malloc (sizeof (struct read_buffer) * __FILEDES_MAX); 549 } 550 551 /* Returns how many buffered bytes were dropped — fseek/lseek need that 552 to back out the unconsumed portion. */ 553 static size_t 554 read_cache_clear_ (int fd) 555 { 556 size_t n; 557 read_cache_init_ (); 558 n = read_cache[fd].size; 559 read_cache[fd].size = 0; 560 return n; 561 } 562 563 static ssize_t 564 read_buffered_ (int fd, void *buf, size_t size) 565 { 566 struct read_buffer *c; 567 char *p = buf; 568 size_t todo = size; 569 read_cache_init_ (); 570 c = &read_cache[fd]; 571 if (!c->size && size > READ_BUFFER_MAX) 572 return _read (fd, buf, size); 573 while (c->size > 0 && todo) { 574 todo--; 575 *p++ = c->bytes[READ_BUFFER_MAX - c->size--]; 576 } 577 if (todo) { 578 ssize_t got; 579 if (todo > READ_BUFFER_MAX) 580 return size - todo + _read (fd, p, todo); 581 got = _read (fd, c->bytes, READ_BUFFER_MAX); 582 if (got < 0) return -1; 583 if (got > 0) { 584 c->size = got; 585 if (got < READ_BUFFER_MAX) 586 memmove (c->bytes + READ_BUFFER_MAX - got, c->bytes, got); 587 return size - todo + read_buffered_ (fd, p, todo); 588 } 589 } 590 return size - todo; 591 } 592 593 ssize_t 594 read (int fd, void *buf, size_t size) 595 { 596 return read_buffered_ (fd, buf, size); 597 } 598 599 ssize_t 600 write (int fd, void const *buf, size_t size) 601 { 602 size_t skip = read_cache_clear_ (fd); 603 ssize_t r; 604 if (skip) 605 sys_lseek (fd, -(long) skip, SEEK_CUR); 606 r = _write (fd, buf, size); 607 if (r < 0) { 608 errno = -r; 609 return -1; 610 } 611 errno = 0; 612 return r; 613 } 614 615 off_t 616 lseek (int fd, off_t off, int whence) 617 { 618 size_t skip = read_cache_clear_ (fd); 619 if (whence == SEEK_CUR) 620 off -= skip; 621 return sys_lseek (fd, off, whence); 622 } 623 624 int 625 open (char const *path, int flags, ...) 626 { 627 int mode = 0; 628 if (flags & O_CREAT) { 629 va_list ap; 630 va_start (ap, flags); 631 mode = va_arg (ap, int); 632 va_end (ap); 633 } 634 return _open3 (path, flags, mode); 635 } 636 637 FILE * 638 fopen (char const *path, char const *opentype) 639 { 640 int fd; 641 int mode = 0600; 642 if ((opentype[0] == 'a' || !strcmp (opentype, "r+")) && !access (path, O_RDONLY)) { 643 int flags = O_RDWR; 644 if (opentype[0] == 'a') flags |= O_APPEND; 645 fd = _open3 (path, flags, mode); 646 } else if (opentype[0] == 'w' || opentype[0] == 'a' || !strcmp (opentype, "r+")) { 647 int flags = strchr (opentype, '+') ? O_RDWR | O_CREAT : O_WRONLY | O_CREAT | O_TRUNC; 648 fd = _open3 (path, flags, mode); 649 } else { 650 fd = _open3 (path, O_RDONLY, 0); 651 } 652 if (fd < 0) fd = 0; 653 return (FILE *) (long) fd; 654 } 655 656 FILE *fdopen (int fd, char const *mode) { (void) mode; return (FILE *) (long) fd; } 657 658 int fclose (FILE * f) { return close ((long) f); } 659 int fflush (FILE * f) { int fd = (long) f; return fd < 3 ? 0 : fsync (fd); } 660 661 long ftell (FILE * f) { return lseek ((long) f, 0, SEEK_CUR); } 662 663 int 664 fseek (FILE * f, long off, int whence) 665 { 666 return lseek ((long) f, off, whence) >= 0 ? 0 : -1; 667 } 668 669 int 670 fgetc (FILE * f) 671 { 672 unsigned char c; 673 if (read_buffered_ ((long) f, &c, 1) != 1) 674 return -1; 675 return c; 676 } 677 678 size_t 679 fread (void *data, size_t size, size_t count, FILE * f) 680 { 681 ssize_t got; 682 if (!size || !count) return 0; 683 got = read_buffered_ ((long) f, data, size * count); 684 return got > 0 ? got / size : 0; 685 } 686 687 size_t 688 fwrite (void const *data, size_t size, size_t count, FILE * f) 689 { 690 ssize_t put; 691 if (!size || !count) return 0; 692 put = write ((long) f, data, size * count); 693 return put > 0 ? put / size : 0; 694 } 695 696 int 697 fputc (int c, FILE * f) 698 { 699 unsigned char b = c; 700 return write ((long) f, &b, 1) == 1 ? c : -1; 701 } 702 703 int 704 fputs (char const *s, FILE * f) 705 { 706 size_t n = strlen (s); 707 return (size_t) write ((long) f, s, n) == n ? (int) n : -1; 708 } 709 710 int 711 puts (char const *s) 712 { 713 if (fputs (s, stdout) < 0) return -1; 714 return fputc ('\n', stdout); 715 } 716 717 int 718 remove (char const *path) 719 { 720 return unlink (path); 721 } 722 723 724 /* ───────── printf family ─────────────────────────────────────────── */ 725 726 /* One sink type for both fd-backed (fprintf/printf) and buffer-backed 727 (snprintf/sprintf) output. emit_() drops bytes once a buffer sink 728 is full; vsnprintf still has to count them so the C99 return value 729 (bytes that would have been written) is correct. */ 730 struct sink { 731 int fd; /* -1 if buffer sink */ 732 char *buf; 733 size_t cap; /* SIZE_MAX for unbounded sprintf */ 734 size_t len; /* bytes counted (may exceed cap) */ 735 }; 736 737 static void 738 emit_ (struct sink *s, char c) 739 { 740 if (s->fd >= 0) { 741 sys_write (s->fd, (long) &c, 1); 742 } else if (s->len < s->cap) { 743 s->buf[s->len] = c; 744 } 745 s->len++; 746 } 747 748 static void 749 emit_str_ (struct sink *s, char const *p, size_t n) 750 { 751 while (n--) emit_ (s, *p++); 752 } 753 754 /* unsigned 64-bit → ASCII into `out` (at least 24 bytes). Returns 755 pointer to first character; signed prefix is added by caller. */ 756 static char * 757 utoa_ (unsigned long long u, unsigned base, char *out) 758 { 759 char *p = out + 23; 760 *p = 0; 761 do { 762 unsigned d = u % base; 763 u /= base; 764 *--p = d > 9 ? 'a' + d - 10 : '0' + d; 765 } while (u); 766 return p; 767 } 768 769 static int 770 vformat_ (struct sink *s, char const *fmt, va_list ap) 771 { 772 size_t start = s->len; 773 while (*fmt) { 774 int left, long_p, precision, width; 775 char pad; 776 char c; 777 char nbuf[24]; 778 char const *body; 779 size_t bodylen; 780 char sign; 781 782 if (*fmt != '%') { emit_ (s, *fmt++); continue; } 783 fmt++; 784 785 left = 0; long_p = 0; precision = -1; width = -1; 786 pad = ' '; 787 body = 0; 788 bodylen = 0; 789 sign = 0; 790 791 if (*fmt == '-') { left = 1; fmt++; } 792 if (*fmt == ' ') { pad = ' '; fmt++; } 793 if (*fmt == '0') { pad = '0'; fmt++; } 794 if (*fmt >= '0' && *fmt <= '9') { 795 width = (int) parse_int_ (&fmt, 10); 796 } else if (*fmt == '*') { 797 width = va_arg (ap, int); 798 fmt++; 799 } 800 if (*fmt == '.') { 801 fmt++; 802 if (*fmt >= '0' && *fmt <= '9') { 803 precision = (int) parse_int_ (&fmt, 10); 804 } else if (*fmt == '*') { 805 precision = va_arg (ap, int); 806 fmt++; 807 } 808 } 809 /* `l` length modifier; LP64 → ll == l, so collapse. Tracking it 810 matters: amd64 SysV spills an `int` arg into the low 32 bits of 811 an 8-byte slot, leaving the upper bits unspecified. va_arg(long) 812 on a `%d` would then leak garbage. */ 813 if (*fmt == 'l') { long_p = 1; fmt++; } 814 if (*fmt == 'l') fmt++; 815 816 c = *fmt; 817 818 switch (c) { 819 case '%': emit_ (s, '%'); break; 820 case 'c': emit_ (s, (char) va_arg (ap, int)); break; 821 822 case 's': { 823 body = va_arg (ap, char const *); 824 if (!body) body = "(null)"; 825 bodylen = strlen (body); 826 if (precision >= 0 && (size_t) precision < bodylen) 827 bodylen = precision; 828 goto pad_emit; 829 } 830 831 case 'd': case 'i': { 832 long long v = long_p ? va_arg (ap, long long) : (long long) va_arg (ap, int); 833 unsigned long long u = v < 0 ? -(unsigned long long) v : (unsigned long long) v; 834 if (v < 0) sign = '-'; 835 body = utoa_ (u, 10, nbuf); 836 bodylen = strlen (body); 837 goto numeric_emit; 838 } 839 840 case 'u': case 'o': case 'x': case 'X': case 'p': { 841 unsigned long long u; 842 unsigned base = c == 'o' ? 8 : (c == 'x' || c == 'X' || c == 'p') ? 16 : 10; 843 if (c == 'p') { 844 u = (unsigned long long) (uintptr_t) va_arg (ap, void *); 845 long_p = 1; 846 } else if (long_p) { 847 u = (unsigned long long) va_arg (ap, unsigned long long); 848 } else { 849 u = (unsigned long long) va_arg (ap, unsigned int); 850 } 851 body = utoa_ (u, base, nbuf); 852 bodylen = strlen (body); 853 if (c == 'X') { 854 char *t; 855 for (t = (char *) body; *t; t++) 856 *t = toupper (*t); 857 } 858 goto numeric_emit; 859 } 860 861 default: 862 /* Unsupported specifier — emit %X for diagnostics. tcc's only 863 use of an exotic specifier is %p in an unreachable error 864 path. */ 865 emit_ (s, '%'); 866 emit_ (s, c); 867 break; 868 } 869 fmt++; 870 continue; 871 872 numeric_emit: { 873 int total = (sign ? 1 : 0) + (int) bodylen; 874 int npad; 875 int zpad; 876 if (precision < 0) precision = (int) bodylen; 877 npad = width > total ? width - (precision > (int) bodylen ? precision : total) : 0; 878 zpad = precision > (int) bodylen ? precision - (int) bodylen : 0; 879 if (!left && pad == ' ') { 880 while (npad-- > 0) emit_ (s, ' '); 881 } 882 if (sign) emit_ (s, sign); 883 if (!left && pad == '0') { 884 while (npad-- > 0) emit_ (s, '0'); 885 } 886 while (zpad-- > 0) emit_ (s, '0'); 887 emit_str_ (s, body, bodylen); 888 while (left && npad-- > 0) emit_ (s, ' '); 889 fmt++; 890 continue; 891 } 892 893 pad_emit: { 894 int npad = width > (int) bodylen ? width - (int) bodylen : 0; 895 if (!left) while (npad-- > 0) emit_ (s, pad); 896 emit_str_ (s, body, bodylen); 897 if (left) while (npad-- > 0) emit_ (s, pad); 898 fmt++; 899 continue; 900 } 901 } 902 if (s->fd < 0 && s->len < s->cap) 903 s->buf[s->len] = 0; 904 else if (s->fd < 0 && s->cap) 905 s->buf[s->cap - 1] = 0; 906 return (int) (s->len - start); 907 } 908 909 int 910 vfprintf (FILE * f, char const *fmt, va_list ap) 911 { 912 struct sink s = { .fd = (int) (long) f, .buf = 0, .cap = 0, .len = 0 }; 913 return vformat_ (&s, fmt, ap); 914 } 915 916 int 917 vprintf (char const *fmt, va_list ap) 918 { 919 return vfprintf (stdout, fmt, ap); 920 } 921 922 int 923 vsnprintf (char *str, size_t size, char const *fmt, va_list ap) 924 { 925 struct sink s = { .fd = -1, .buf = str, .cap = size, .len = 0 }; 926 return vformat_ (&s, fmt, ap); 927 } 928 929 int 930 vsprintf (char *str, char const *fmt, va_list ap) 931 { 932 return vsnprintf (str, (size_t) -1, fmt, ap); 933 } 934 935 int 936 fprintf (FILE * f, char const *fmt, ...) 937 { 938 va_list ap; 939 int r; 940 va_start (ap, fmt); 941 r = vfprintf (f, fmt, ap); 942 va_end (ap); 943 return r; 944 } 945 946 int 947 printf (char const *fmt, ...) 948 { 949 va_list ap; 950 int r; 951 va_start (ap, fmt); 952 r = vfprintf (stdout, fmt, ap); 953 va_end (ap); 954 return r; 955 } 956 957 int 958 snprintf (char *str, size_t size, char const *fmt, ...) 959 { 960 va_list ap; 961 int r; 962 va_start (ap, fmt); 963 r = vsnprintf (str, size, fmt, ap); 964 va_end (ap); 965 return r; 966 } 967 968 int 969 sprintf (char *str, char const *fmt, ...) 970 { 971 va_list ap; 972 int r; 973 va_start (ap, fmt); 974 r = vsprintf (str, fmt, ap); 975 va_end (ap); 976 return r; 977 } 978 979 980 /* ───────── assertions ────────────────────────────────────────────── */ 981 982 void 983 __assert_fail (char const *msg, char const *file, unsigned line, char const *func) 984 { 985 fprintf (stderr, "%s:%u:", file, line); 986 if (func) fprintf (stderr, "%s:", func); 987 fprintf (stderr, "assert fail: %s\n", msg); 988 exit (1); 989 } 990 991 /* mes/lib.h declares assert_msg as the no-include-needed assert form 992 used inside libc. Provide a body here so anything still calling it 993 (e.g. mes-style builtins under cc.scm) links. */ 994 void 995 assert_msg (int check, char *msg) 996 { 997 if (!check) { 998 fputs (msg, stderr); 999 exit (1); 1000 } 1001 }