cc.c (12412B)
1 /* 2 * FIXME: 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 has a bug 6 * and using POSIX_C_SOURCE and _ANSI_SOURCE at the same time 7 * generates a syntax error in the system headers. A patch was 8 * sent to NetBSD, but this temporary fix is added here until the 9 * patch arrives to the stable release. 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 && !strcmp(arch, "amd64") && !strcmp(abi, "sysv")) 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 break; 246 case LD: 247 if (!outfile) 248 outfile = "a.out"; 249 t->outfile = xstrdup(outfile); 250 if (gflag) 251 addarg(tool, "-g"); 252 if (sflag) 253 addarg(tool, "-s"); 254 for (n = 0; ldcmd[n]; n++) 255 addarg(tool, ldcmd[n]); 256 break; 257 case TEEIR: 258 if (Eflag && outfile) 259 t->outfile = xstrdup(outfile); 260 else 261 t->outfile = outfname(infile, "ir"); 262 addarg(tool, t->outfile); 263 break; 264 case TEEQBE: 265 t->outfile = outfname(infile, "qbe"); 266 addarg(tool, t->outfile); 267 break; 268 case TEEAS: 269 t->outfile = outfname(infile, "s"); 270 addarg(tool, t->outfile); 271 break; 272 case AS: 273 if (cflag && outfile) { 274 objfile = xstrdup(outfile); 275 } else { 276 objfile = (cflag || kflag) ? infile : NULL; 277 objfile = outfname(objfile, "o"); 278 } 279 t->outfile = xstrdup(objfile); 280 if (gflag) 281 addarg(tool, "-g"); 282 for (n = 0; ascmd[n]; n++) 283 addarg(tool, ascmd[n]); 284 break; 285 break; 286 default: 287 break; 288 } 289 290 if (fdin > -1) { 291 t->in = fdin; 292 fdin = -1; 293 } else { 294 t->in = -1; 295 if (infile) 296 addarg(tool, infile); 297 } 298 299 if (nexttool < LAST_TOOL) { 300 if (pipe(fds)) 301 die("scc-cc: pipe: %s", strerror(errno)); 302 t->out = fds[1]; 303 fdin = fds[0]; 304 } else { 305 t->out = -1; 306 } 307 308 addarg(tool, NULL); 309 310 return tool; 311 } 312 313 /* 314 * We cannot call exit() because it would call the atexit() 315 * handlers. If it finishes correctly we already called 316 * fflush() in the output buffers so it is not a problem 317 * not following the normal exit path. 318 */ 319 static void 320 tee(char *fname) 321 { 322 FILE *fp; 323 int ch; 324 325 if ((fp = fopen(fname, "w")) == NULL) 326 goto err; 327 328 while ((ch = getchar()) != EOF) { 329 putc(ch, stdout); 330 putc(ch, fp); 331 } 332 333 fflush(stdout); 334 fflush(fp); 335 336 if (ferror(stdin) || ferror(stdout) || ferror(fp)) 337 goto err; 338 _exit(0); 339 340 err: 341 fprintf(stderr, 342 "tee: teeing %s: %s\n", 343 fname, 344 strerror(errno)); 345 346 _exit(1); 347 } 348 349 static void 350 spawn(int tool) 351 { 352 int i; 353 char **ap; 354 struct tool *t = &tools[tool]; 355 356 switch (t->pid = fork()) { 357 case -1: 358 die("scc-cc: %s: %s", t->bin, strerror(errno)); 359 case 0: 360 if (t->out > -1) 361 dup2(t->out, 1); 362 if (t->in > -1) 363 dup2(t->in, 0); 364 365 if (tool == TEEAS && Sflag) 366 dup2(devnullfd, 1); 367 if (!dflag && tool != CC1 && tool != LD) 368 dup2(devnullfd, 2); 369 370 if (dflag) { 371 fprintf(stderr, "%s", t->cmd); 372 for (ap = t->args.s+1; *ap; ap++) 373 fprintf(stderr, " %s", *ap); 374 putc('\n', stderr); 375 } 376 if (strcmp(t->cmd, "tee") == 0) 377 tee(t->outfile); 378 execvp(t->cmd, t->args.s); 379 if (dflag) { 380 fprintf(stderr, 381 "scc-cc: execvp %s: %s\n", 382 t->cmd, 383 strerror(errno)); 384 } 385 abort(); 386 default: 387 if (t->in > -1) 388 close(t->in); 389 if (t->out > -1) 390 close(t->out); 391 392 for (i = 0; i < t->args.n; i++) 393 free(t->args.s[i]); 394 t->args.n = 0; 395 396 break; 397 } 398 } 399 400 static int 401 toolfor(char *file) 402 { 403 char *dot = strrchr(file, '.'); 404 405 if (Eflag) 406 return CC1; 407 408 if (dot) { 409 if (!strcmp(dot, ".c")) 410 return CC1; 411 if (!strcmp(dot, ".ir")) 412 return CC2; 413 if (!strcmp(dot, ".qbe")) 414 return QBE; 415 if (!strcmp(dot, ".s")) 416 return AS; 417 if (!strcmp(dot, ".o")) 418 return LD; 419 if (!strcmp(dot, ".a")) 420 return LD; 421 } else if (!strcmp(file, "-")) { 422 return CC1; 423 } 424 425 die("scc-cc: unrecognized filetype of %s", file); 426 } 427 428 static int 429 valid(int tool, struct tool *t) 430 { 431 int st; 432 433 if (waitpid(t->pid, &st, 0) == -1 || WIFSIGNALED(st)) 434 goto internal; 435 if (WIFEXITED(st) && WEXITSTATUS(st) == 0) 436 return 1; 437 if (!failure && (tool == CC1 || tool == LD)) 438 goto fail; 439 440 internal: 441 if (!failure) 442 fprintf(stderr, "scc-cc:%s: internal error\n", t->bin); 443 fail: 444 failure = 1; 445 return 0; 446 } 447 448 static int 449 validatetools(void) 450 { 451 struct tool *t; 452 unsigned i; 453 int tool, st, failed = LAST_TOOL; 454 455 for (tool = 0; tool < LAST_TOOL; ++tool) { 456 t = &tools[tool]; 457 if (!t->pid) 458 continue; 459 if (!valid(tool, t)) 460 failed = tool; 461 if (tool >= failed && t->outfile) 462 unlink(t->outfile); 463 free(t->outfile); 464 t->pid = 0; 465 } 466 if (failed < LAST_TOOL) { 467 unlink(objfile); 468 return 0; 469 } 470 471 return 1; 472 } 473 474 static int 475 buildfile(char *file, int tool) 476 { 477 int nexttool; 478 479 for (; tool < LAST_TOOL; tool = nexttool) { 480 switch (tool) { 481 case CC1: 482 if (Eflag && outfile) 483 nexttool = TEEIR; 484 else if (Eflag || Mflag) 485 nexttool = LAST_TOOL; 486 else 487 nexttool = kflag ? TEEIR : CC2; 488 break; 489 case TEEIR: 490 nexttool = (Eflag) ? LAST_TOOL : CC2; 491 break; 492 case CC2: 493 if (Qflag) 494 nexttool = kflag ? TEEQBE : QBE; 495 else 496 nexttool = (Sflag || kflag) ? TEEAS : AS; 497 break; 498 case TEEQBE: 499 nexttool = QBE; 500 break; 501 case QBE: 502 nexttool = (Sflag || kflag) ? TEEAS : AS; 503 break; 504 case TEEAS: 505 nexttool = Sflag ? LAST_TOOL : AS; 506 break; 507 case AS: 508 nexttool = LAST_TOOL; 509 break; 510 default: 511 nexttool = LAST_TOOL; 512 continue; 513 } 514 515 spawn(settool(tool, file, nexttool)); 516 } 517 518 return validatetools(); 519 } 520 521 static void 522 build(struct items *chain, int link) 523 { 524 int i, tool; 525 526 for (i = 0; i < chain->n; ++i) { 527 if (!strcmp(chain->s[i], "-l")) { 528 newitem(&linkargs, chain->s[i++]); 529 newitem(&linkargs, chain->s[i]); 530 continue; 531 } 532 tool = toolfor(chain->s[i]); 533 if (tool == LD) { 534 newitem(&linkargs, chain->s[i]); 535 continue; 536 } 537 if (buildfile(chain->s[i], tool)) { 538 newitem(&linkargs, objfile); 539 newitem((!link || kflag) ? &objout : &objtmp, objfile); 540 } 541 } 542 } 543 544 static void 545 usage(void) 546 { 547 fputs("usage: cc [options] file...\n", stderr); 548 exit(1); 549 } 550 551 int 552 main(int argc, char *argv[]) 553 { 554 struct items linkchain = { .n = 0, }; 555 int link, n; 556 557 atexit(terminate); 558 signal(SIGHUP, sighandler); 559 signal(SIGINT, sighandler); 560 signal(SIGTERM, sighandler); 561 562 if (!(arch = getenv("ARCH"))) 563 arch = ARCH; 564 if (!(sys = getenv("SYS"))) 565 sys = SYS; 566 if (!(abi = getenv("ABI"))) 567 abi = ABI; 568 if (!(format = getenv("FORMAT"))) 569 format = FORMAT; 570 if (!(prefix = getenv("SCCPREFIX"))) 571 prefix = PREFIX; 572 if (!(libprefix = getenv("SCCLIBPREFIX"))) 573 libprefix = LIBPREFIX; 574 575 ARGBEGIN { 576 case 'D': 577 newitem(&cc1args, "-D"); 578 newitem(&cc1args, EARGF(usage())); 579 break; 580 case 'M': 581 Mflag = 1; 582 break; 583 case 'E': 584 Eflag = 1; 585 break; 586 case 'I': 587 newitem(&cc1args, "-I"); 588 newitem(&cc1args, EARGF(usage())); 589 break; 590 case 'L': 591 newitem(&linkargs, "-L"); 592 newitem(&linkargs, EARGF(usage())); 593 break; 594 case 'O': 595 EARGF(usage()); 596 break; 597 case 'S': 598 Sflag = 1; 599 break; 600 case 'U': 601 newitem(&cc1args, "-U"); 602 newitem(&cc1args, EARGF(usage())); 603 break; 604 case 'c': 605 cflag = 1; 606 break; 607 case 'd': 608 dflag = 1; 609 break; 610 case 'g': 611 gflag = 1; 612 break; 613 case 'k': 614 kflag = 1; 615 break; 616 case 'l': 617 newitem(&linkchain, "-l"); 618 newitem(&linkchain, EARGF(usage())); 619 break; 620 case 'm': 621 arch = EARGF(usage()); 622 break; 623 case 'o': 624 outfile = EARGF(usage()); 625 break; 626 case 's': 627 sflag = 1; 628 break; 629 case 't': 630 sys = EARGF(usage()); 631 break; 632 case 'w': 633 Wflag = 0; 634 break; 635 case 'W': 636 Wflag = 1; 637 break; 638 case 'q': 639 Qflag = 0; 640 break; 641 case 'Q': 642 Qflag = 1; 643 break; 644 case '-': 645 fprintf(stderr, 646 "scc-cc: ignored parameter --%s\n", EARGF(usage())); 647 break; 648 default: 649 usage(); 650 } ARGOPERAND { 651 operand: 652 newitem(&linkchain, ARGOP()); 653 } ARGEND 654 655 for (; *argv; --argc, ++argv) 656 goto operand; 657 658 if (Eflag && linkchain.n == 0) 659 newitem(&linkchain, "-"); 660 661 if (Eflag && Mflag || 662 (Eflag || Mflag) && (Sflag || kflag) || 663 linkchain.n == 0 || 664 linkchain.n > 1 && cflag && outfile) 665 usage(); 666 667 if (!dflag) { 668 if ((devnullfd = open("/dev/null", O_WRONLY)) < 0) 669 fputs("scc-cc: could not open /dev/null\n", stderr); 670 } 671 672 if (!(tmpdir = getenv("TMPDIR")) || !tmpdir[0]) 673 tmpdir = "."; 674 tmpdirln = strlen(tmpdir); 675 676 build(&linkchain, (link = !(Mflag || Eflag || Sflag || cflag))); 677 678 if (!(link || cflag)) 679 return failure; 680 681 if (link && !failure) { 682 spawn(settool(LD, NULL, LAST_TOOL)); 683 validatetools(); 684 } 685 686 return failure; 687 }