cc.c (12708B)
1 /* 2 * Scc is a pure C99 source code, except this file that is 3 * intended for a POSIX.2008 environment. This situation creates 4 * a problem where some systems require the macro _ANSI_SOURCE to 5 * limit the namespace to a pure ISO namespace. NetBSD had a bug 6 * and using POSIX_C_SOURCE and _ANSI_SOURCE at the same time 7 * generates a syntax error in the system headers. The bug was 8 * already fixed in NetBSD but keeping the workaround here does 9 * not hurt and it helps to keep back compatibility. 10 */ 11 #undef _POSIX_C_SOURCE 12 #undef _ANSI_SOURCE 13 #define _POSIX_C_SOURCE 200809L 14 15 #include <fcntl.h> 16 #include <sys/types.h> 17 #include <sys/wait.h> 18 #include <unistd.h> 19 20 #include <assert.h> 21 #include <errno.h> 22 #include <limits.h> 23 #include <signal.h> 24 #include <stdio.h> 25 #include <stdlib.h> 26 #include <string.h> 27 28 #include <scc/arg.h> 29 #include <scc/config.h> 30 #include <scc/scc.h> 31 #include <scc/sys.h> 32 33 enum { 34 CC1, 35 TEEIR, 36 CC2, 37 TEEQBE, 38 QBE, 39 TEEAS, 40 AS, 41 LD, 42 LAST_TOOL, 43 }; 44 45 static struct tool { 46 char cmd[PATH_MAX]; 47 char bin[32]; 48 char *outfile; 49 struct items args; 50 int in, out; 51 pid_t pid; 52 } tools[] = { 53 [CC1] = {.bin = "cc1"}, 54 [TEEIR] = {.bin = "tee"}, 55 [CC2] = {.bin = "cc2"}, 56 [TEEQBE] = {.bin = "tee"}, 57 [QBE] = {.bin = "qbe"}, 58 [TEEAS] = {.bin = "tee"}, 59 [AS] = {.bin = ASBIN}, 60 [LD] = {.bin = LDBIN}, 61 }; 62 63 char *argv0; 64 static char *arch, *sys, *abi, *format; 65 static char *objfile, *outfile; 66 static char *prefix, *libprefix; 67 static char *tmpdir; 68 static size_t tmpdirln; 69 static struct items objtmp, objout; 70 static struct items linkargs, cc1args; 71 72 static int Mflag, Eflag, Sflag, Wflag, 73 cflag, dflag, kflag, sflag, Qflag = 1, /* TODO: Remove Qflag */ 74 gflag; 75 76 static int devnullfd = -1; 77 78 extern int failure; 79 80 static void 81 terminate(void) 82 { 83 unsigned i; 84 85 if (!kflag) { 86 for (i = 0; i < objtmp.n; ++i) 87 unlink(objtmp.s[i]); 88 } 89 } 90 91 static void 92 sighandler(int sig) 93 { 94 terminate(); 95 _exit(1); 96 } 97 98 static void 99 addarg(int tool, char *arg) 100 { 101 struct tool *t = &tools[tool]; 102 char *p, buff[FILENAME_MAX]; 103 size_t len, cnt; 104 int n; 105 106 if (!arg) { 107 newitem(&t->args, NULL); 108 return; 109 } 110 111 if (arg[0] == '-') { 112 newitem(&t->args, xstrdup(arg)); 113 return; 114 } 115 116 if (!strcmp(arg, "%c")) { 117 for (n = 0; n < linkargs.n; ++n) 118 newitem(&t->args, xstrdup(linkargs.s[n])); 119 return; 120 } 121 122 for (cnt = 0 ; *arg && cnt < FILENAME_MAX; ++arg) { 123 if (*arg != '%') { 124 buff[cnt++] = *arg; 125 continue; 126 } 127 128 switch (*++arg) { 129 case 'a': 130 p = arch; 131 break; 132 case 's': 133 p = sys; 134 break; 135 case 'p': 136 p = libprefix; 137 break; 138 case 'b': 139 p = abi; 140 break; 141 case 'o': 142 p = t->outfile; 143 break; 144 default: 145 buff[cnt++] = *arg; 146 continue; 147 } 148 149 len = strlen(p); 150 if (len + cnt >= FILENAME_MAX) 151 die("scc-cc: pathname too long"); 152 memcpy(buff+cnt, p, len); 153 cnt += len; 154 } 155 156 if (cnt >= FILENAME_MAX) 157 abort(); 158 159 buff[cnt] = '\0'; 160 newitem(&t->args, xstrdup(buff)); 161 } 162 163 static char * 164 cc2fmt(int tool) 165 { 166 if (Qflag) 167 return "%s/libexec/scc/%s-qbe_%s-%s"; 168 return "%s/libexec/scc/%s-%s-%s"; 169 } 170 171 static char * 172 outfname(char *path, char *type) 173 { 174 char *new, sep, *p; 175 size_t newsz, pathln; 176 int tmpfd, n; 177 178 if (path) { 179 sep = '.'; 180 if (p = strrchr(path, '/')) 181 path = p + 1; 182 pathln = strlen(path); 183 if (p = strrchr(path, '.')) 184 pathln -= strlen(p); 185 } else { 186 sep = '/'; 187 type = "scc-XXXXXX"; 188 path = tmpdir; 189 pathln = tmpdirln; 190 } 191 192 newsz = pathln + 1 + strlen(type) + 1; 193 new = xmalloc(newsz); 194 n = snprintf(new, newsz, "%.*s%c%s", (int)pathln, path, sep, type); 195 if (n < 0 || n >= newsz) 196 die("scc-cc: wrong output filename"); 197 if (sep == '/') { 198 if ((tmpfd = mkstemp(new)) < 0) 199 die("scc-cc: could not create output file '%s': %s", 200 new, strerror(errno)); 201 close(tmpfd); 202 } 203 204 return new; 205 } 206 207 static int 208 settool(int tool, char *infile, int nexttool) 209 { 210 struct tool *t = &tools[tool]; 211 char *fmt; 212 int n, fds[2]; 213 static int fdin = -1; 214 215 strcpy(t->cmd, t->bin); 216 assert(t->args.n == 0); 217 newitem(&t->args, xstrdup(t->bin)); 218 219 switch (tool) { 220 case CC1: 221 if (Eflag) 222 addarg(tool, "-E"); 223 if (Mflag) 224 addarg(tool, "-M"); 225 if (Wflag) 226 addarg(tool, "-w"); 227 for (n = 0; n < cc1args.n; ++n) 228 addarg(tool, cc1args.s[n]); 229 for (n = 0; sysincludes[n]; ++n) { 230 addarg(tool, "-I"); 231 addarg(tool, sysincludes[n]); 232 } 233 fmt = "%s/libexec/scc/%s"; 234 goto local_tool; 235 case CC2: 236 fmt = cc2fmt(tool); 237 local_tool: 238 n = snprintf(t->cmd, sizeof(t->cmd), 239 fmt, prefix, t->bin, arch, abi); 240 if (n < 0 || n >= sizeof(t->cmd)) 241 die("scc-cc: target tool path is too long"); 242 break; 243 case QBE: 244 strcpy(t->cmd, "qbe"); 245 246 if (!strcmp(arch, "amd64") && !strcmp(abi, "sysv")) 247 fmt = "amd64_sysv"; 248 else if (!strcmp(arch, "arm64") && !strcmp(abi, "sysv")) 249 fmt = "arm64"; 250 else if (!strcmp(arch, "riscv64") && !strcmp(abi, "sysv")) 251 fmt = "rv64"; 252 else 253 die("not supported arch-abi (%s-%s) for qbe", arch, abi); 254 255 addarg(tool, "-t"); 256 addarg(tool, fmt); 257 break; 258 case LD: 259 if (!outfile) 260 outfile = "a.out"; 261 t->outfile = xstrdup(outfile); 262 if (gflag) 263 addarg(tool, "-g"); 264 if (sflag) 265 addarg(tool, "-s"); 266 for (n = 0; ldcmd[n]; n++) 267 addarg(tool, ldcmd[n]); 268 break; 269 case TEEIR: 270 if (Eflag && outfile) 271 t->outfile = xstrdup(outfile); 272 else 273 t->outfile = outfname(infile, "ir"); 274 addarg(tool, t->outfile); 275 break; 276 case TEEQBE: 277 t->outfile = outfname(infile, "qbe"); 278 addarg(tool, t->outfile); 279 break; 280 case TEEAS: 281 t->outfile = outfname(infile, "s"); 282 addarg(tool, t->outfile); 283 break; 284 case AS: 285 if (cflag && outfile) { 286 objfile = xstrdup(outfile); 287 } else { 288 objfile = (cflag || kflag) ? infile : NULL; 289 objfile = outfname(objfile, "o"); 290 } 291 t->outfile = xstrdup(objfile); 292 if (gflag) 293 addarg(tool, "-g"); 294 for (n = 0; ascmd[n]; n++) 295 addarg(tool, ascmd[n]); 296 break; 297 break; 298 default: 299 break; 300 } 301 302 if (fdin > -1) { 303 t->in = fdin; 304 fdin = -1; 305 } else { 306 t->in = -1; 307 if (infile) 308 addarg(tool, infile); 309 } 310 311 if (nexttool < LAST_TOOL) { 312 if (pipe(fds)) 313 die("scc-cc: pipe: %s", strerror(errno)); 314 t->out = fds[1]; 315 fdin = fds[0]; 316 } else { 317 t->out = -1; 318 } 319 320 addarg(tool, NULL); 321 322 return tool; 323 } 324 325 /* 326 * We cannot call exit() because it would call the atexit() 327 * handlers. If it finishes correctly we already called 328 * fflush() in the output buffers so it is not a problem 329 * not following the normal exit path. 330 */ 331 static void 332 tee(char *fname) 333 { 334 FILE *fp; 335 int ch; 336 337 if ((fp = fopen(fname, "w")) == NULL) 338 goto err; 339 340 while ((ch = getchar()) != EOF) { 341 putc(ch, stdout); 342 putc(ch, fp); 343 } 344 345 fflush(stdout); 346 fflush(fp); 347 348 if (ferror(stdin) || ferror(stdout) || ferror(fp)) 349 goto err; 350 _exit(0); 351 352 err: 353 fprintf(stderr, 354 "tee: teeing %s: %s\n", 355 fname, 356 strerror(errno)); 357 358 _exit(1); 359 } 360 361 static void 362 spawn(int tool) 363 { 364 int i; 365 char **ap; 366 struct tool *t = &tools[tool]; 367 368 switch (t->pid = fork()) { 369 case -1: 370 die("scc-cc: %s: %s", t->bin, strerror(errno)); 371 case 0: 372 if (t->out > -1) 373 dup2(t->out, 1); 374 if (t->in > -1) 375 dup2(t->in, 0); 376 377 if (tool == TEEAS && Sflag) 378 dup2(devnullfd, 1); 379 if (!dflag && tool != CC1 && tool != LD) 380 dup2(devnullfd, 2); 381 382 if (dflag) { 383 fprintf(stderr, "%s", t->cmd); 384 for (ap = t->args.s+1; *ap; ap++) 385 fprintf(stderr, " %s", *ap); 386 putc('\n', stderr); 387 } 388 if (strcmp(t->cmd, "tee") == 0) 389 tee(t->outfile); 390 execvp(t->cmd, t->args.s); 391 if (dflag) { 392 fprintf(stderr, 393 "scc-cc: execvp %s: %s\n", 394 t->cmd, 395 strerror(errno)); 396 } 397 abort(); 398 default: 399 if (t->in > -1) 400 close(t->in); 401 if (t->out > -1) 402 close(t->out); 403 404 for (i = 0; i < t->args.n; i++) 405 free(t->args.s[i]); 406 t->args.n = 0; 407 408 break; 409 } 410 } 411 412 static int 413 toolfor(char *file) 414 { 415 char *dot = strrchr(file, '.'); 416 417 if (Eflag) 418 return CC1; 419 420 if (dot) { 421 if (!strcmp(dot, ".c")) 422 return CC1; 423 if (!strcmp(dot, ".ir")) 424 return CC2; 425 if (!strcmp(dot, ".qbe")) 426 return QBE; 427 if (!strcmp(dot, ".s")) 428 return AS; 429 if (!strcmp(dot, ".o")) 430 return LD; 431 if (!strcmp(dot, ".a")) 432 return LD; 433 } else if (!strcmp(file, "-")) { 434 return CC1; 435 } 436 437 die("scc-cc: unrecognized filetype of %s", file); 438 } 439 440 static int 441 valid(int tool, struct tool *t) 442 { 443 int st; 444 445 if (waitpid(t->pid, &st, 0) == -1 || WIFSIGNALED(st)) 446 goto internal; 447 if (WIFEXITED(st) && WEXITSTATUS(st) == 0) 448 return 1; 449 if (!failure && (tool == CC1 || tool == LD)) 450 goto fail; 451 452 internal: 453 if (!failure) 454 fprintf(stderr, "scc-cc:%s: internal error\n", t->bin); 455 fail: 456 failure = 1; 457 return 0; 458 } 459 460 static int 461 validatetools(void) 462 { 463 struct tool *t; 464 unsigned i; 465 int tool, st, failed = LAST_TOOL; 466 467 for (tool = 0; tool < LAST_TOOL; ++tool) { 468 t = &tools[tool]; 469 if (!t->pid) 470 continue; 471 if (!valid(tool, t)) 472 failed = tool; 473 if (tool >= failed && t->outfile) 474 unlink(t->outfile); 475 free(t->outfile); 476 t->pid = 0; 477 } 478 if (failed < LAST_TOOL) { 479 unlink(objfile); 480 return 0; 481 } 482 483 return 1; 484 } 485 486 static int 487 buildfile(char *file, int tool) 488 { 489 int nexttool; 490 491 for (; tool < LAST_TOOL; tool = nexttool) { 492 switch (tool) { 493 case CC1: 494 if (Eflag && outfile) 495 nexttool = TEEIR; 496 else if (Eflag || Mflag) 497 nexttool = LAST_TOOL; 498 else 499 nexttool = kflag ? TEEIR : CC2; 500 break; 501 case TEEIR: 502 nexttool = (Eflag) ? LAST_TOOL : CC2; 503 break; 504 case CC2: 505 if (Qflag) 506 nexttool = kflag ? TEEQBE : QBE; 507 else 508 nexttool = (Sflag || kflag) ? TEEAS : AS; 509 break; 510 case TEEQBE: 511 nexttool = QBE; 512 break; 513 case QBE: 514 nexttool = (Sflag || kflag) ? TEEAS : AS; 515 break; 516 case TEEAS: 517 nexttool = Sflag ? LAST_TOOL : AS; 518 break; 519 case AS: 520 nexttool = LAST_TOOL; 521 break; 522 default: 523 nexttool = LAST_TOOL; 524 continue; 525 } 526 527 spawn(settool(tool, file, nexttool)); 528 } 529 530 return validatetools(); 531 } 532 533 static void 534 build(struct items *chain, int link) 535 { 536 int i, tool; 537 538 for (i = 0; i < chain->n; ++i) { 539 if (!strcmp(chain->s[i], "-l")) { 540 newitem(&linkargs, chain->s[i++]); 541 newitem(&linkargs, chain->s[i]); 542 continue; 543 } 544 tool = toolfor(chain->s[i]); 545 if (tool == LD) { 546 newitem(&linkargs, chain->s[i]); 547 continue; 548 } 549 if (buildfile(chain->s[i], tool)) { 550 newitem(&linkargs, objfile); 551 newitem((!link || kflag) ? &objout : &objtmp, objfile); 552 } 553 } 554 } 555 556 static void 557 usage(void) 558 { 559 fputs("usage: cc [options] file...\n", stderr); 560 exit(1); 561 } 562 563 int 564 main(int argc, char *argv[]) 565 { 566 struct items linkchain = { .n = 0, }; 567 int link, n; 568 569 atexit(terminate); 570 signal(SIGHUP, sighandler); 571 signal(SIGINT, sighandler); 572 signal(SIGTERM, sighandler); 573 574 if (!(arch = getenv("ARCH"))) 575 arch = ARCH; 576 if (!(sys = getenv("SYS"))) 577 sys = SYS; 578 if (!(abi = getenv("ABI"))) 579 abi = ABI; 580 if (!(format = getenv("FORMAT"))) 581 format = FORMAT; 582 if (!(prefix = getenv("SCCPREFIX"))) 583 prefix = PREFIX; 584 if (!(libprefix = getenv("SCCLIBPREFIX"))) 585 libprefix = LIBPREFIX; 586 587 ARGBEGIN { 588 case 'D': 589 newitem(&cc1args, "-D"); 590 newitem(&cc1args, EARGF(usage())); 591 break; 592 case 'M': 593 Mflag = 1; 594 break; 595 case 'E': 596 Eflag = 1; 597 break; 598 case 'I': 599 newitem(&cc1args, "-I"); 600 newitem(&cc1args, EARGF(usage())); 601 break; 602 case 'L': 603 newitem(&linkargs, "-L"); 604 newitem(&linkargs, EARGF(usage())); 605 break; 606 case 'O': 607 EARGF(usage()); 608 break; 609 case 'S': 610 Sflag = 1; 611 break; 612 case 'U': 613 newitem(&cc1args, "-U"); 614 newitem(&cc1args, EARGF(usage())); 615 break; 616 case 'c': 617 cflag = 1; 618 break; 619 case 'd': 620 dflag = 1; 621 break; 622 case 'g': 623 gflag = 1; 624 break; 625 case 'k': 626 kflag = 1; 627 break; 628 case 'l': 629 newitem(&linkchain, "-l"); 630 newitem(&linkchain, EARGF(usage())); 631 break; 632 case 'm': 633 arch = EARGF(usage()); 634 break; 635 case 'o': 636 outfile = EARGF(usage()); 637 break; 638 case 's': 639 sflag = 1; 640 break; 641 case 't': 642 sys = EARGF(usage()); 643 break; 644 case 'w': 645 Wflag = 0; 646 break; 647 case 'W': 648 Wflag = 1; 649 break; 650 case 'q': 651 Qflag = 0; 652 break; 653 case 'Q': 654 Qflag = 1; 655 break; 656 case '-': 657 fprintf(stderr, 658 "scc-cc: ignored parameter --%s\n", EARGF(usage())); 659 break; 660 default: 661 usage(); 662 } ARGOPERAND { 663 operand: 664 newitem(&linkchain, ARGOP()); 665 } ARGEND 666 667 for (; *argv; --argc, ++argv) 668 goto operand; 669 670 if (Eflag && linkchain.n == 0) 671 newitem(&linkchain, "-"); 672 673 if (Eflag && Mflag || 674 (Eflag || Mflag) && (Sflag || kflag) || 675 linkchain.n == 0 || 676 linkchain.n > 1 && cflag && outfile) 677 usage(); 678 679 if (!dflag) { 680 if ((devnullfd = open("/dev/null", O_WRONLY)) < 0) 681 fputs("scc-cc: could not open /dev/null\n", stderr); 682 } 683 684 if (!(tmpdir = getenv("TMPDIR")) || !tmpdir[0]) 685 tmpdir = "."; 686 tmpdirln = strlen(tmpdir); 687 688 build(&linkchain, (link = !(Mflag || Eflag || Sflag || cflag))); 689 690 if (!(link || cflag)) 691 return failure; 692 693 if (link && !failure) { 694 spawn(settool(LD, NULL, LAST_TOOL)); 695 validatetools(); 696 } 697 698 return failure; 699 }