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