1/*2 * Scc is a pure C99 source code, except this file that is3 * intended for a POSIX.2008 environment. This situation creates4 * a problem where some systems require the macro _ANSI_SOURCE to5 * limit the namespace to a pure ISO namespace. NetBSD had a bug6 * and using POSIX_C_SOURCE and _ANSI_SOURCE at the same time7 * generates a syntax error in the system headers. The bug was8 * already fixed in NetBSD but keeping the workaround here does9 * not hurt and it helps to keep back compatibility.10 */11#undef _POSIX_C_SOURCE12#undef _ANSI_SOURCE13#define _POSIX_C_SOURCE 200809L1415#include <fcntl.h>16#include <sys/types.h>17#include <sys/wait.h>18#include <unistd.h>1920#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>2728#include <scc/config.h>29#include <scc/scc.h>30#include <scc/sys.h>3132enum {33 CC1,34 TEEIR,35 CC2,36 TEEQBE,37 QBE,38 TEEAS,39 AS,40 LD,41 LAST_TOOL,42};4344static struct tool {45 char cmd[PATH_MAX];46 char bin[32];47 char *outfile;48 struct items args;49 int in, out;50 pid_t pid;51} tools[] = {52 [CC1] = {.bin = "cc1"},53 [TEEIR] = {.bin = "tee"},54 [CC2] = {.bin = "cc2"},55 [TEEQBE] = {.bin = "tee"},56 [QBE] = {.bin = QBEBIN},57 [TEEAS] = {.bin = "tee"},58 [AS] = {.bin = ASBIN},59 [LD] = {.bin = LDBIN},60};6162static char *arch, *sys, *abi, *format;63static char *objfile, *outfile;64static char *prefix, *libprefix;65static char *tmpdir;66static size_t tmpdirln;67static struct items objtmp, objout;68static struct items linkargs, cc1args;6970static int Mflag, Eflag, Rflag, Sflag, Wflag,71 cflag, dflag, kflag, sflag, Qflag = 1,72 gflag;7374static int devnullfd = -1;7576extern int failure;7778static void79terminate(void)80{81 unsigned i;8283 if (!kflag) {84 for (i = 0; i < objtmp.n; ++i)85 unlink(objtmp.s[i]);86 }87}8889static void90sighandler(int sig)91{92 terminate();93 _exit(1);94}9596static void97addarg(int tool, char *arg)98{99 struct tool *t = &tools[tool];100 char *p, buff[FILENAME_MAX];101 size_t len, cnt;102 int n;103104 if (!arg) {105 newitem(&t->args, NULL);106 return;107 }108109 if (arg[0] == '-') {110 newitem(&t->args, xstrdup(arg));111 return;112 }113114 if (!strcmp(arg, "%c")) {115 for (n = 0; n < linkargs.n; ++n)116 newitem(&t->args, xstrdup(linkargs.s[n]));117 return;118 }119120 for (cnt = 0 ; *arg && cnt < FILENAME_MAX; ++arg) {121 if (*arg != '%') {122 buff[cnt++] = *arg;123 continue;124 }125126 switch (*++arg) {127 case 'a':128 p = arch;129 break;130 case 's':131 p = sys;132 break;133 case 'p':134 p = libprefix;135 break;136 case 'b':137 p = abi;138 break;139 case 'o':140 p = t->outfile;141 break;142 default:143 buff[cnt++] = *arg;144 continue;145 }146147 len = strlen(p);148 if (len + cnt >= FILENAME_MAX)149 die("scc-cc: pathname too long");150 memcpy(buff+cnt, p, len);151 cnt += len;152 }153154 if (cnt >= FILENAME_MAX)155 abort();156157 buff[cnt] = '\0';158 newitem(&t->args, xstrdup(buff));159}160161static char *162cc2fmt(int tool)163{164 if (Qflag)165 return "%s/libexec/scc/%s-qbe_%s-%s";166 return "%s/libexec/scc/%s-%s-%s";167}168169static char *170outfname(char *path, char *type)171{172 char *new, sep, *p;173 size_t newsz, pathln;174 int tmpfd, n;175176 if (path) {177 sep = '.';178 if (p = strrchr(path, '/'))179 path = p + 1;180 pathln = strlen(path);181 if (p = strrchr(path, '.'))182 pathln -= strlen(p);183 } else {184 sep = '/';185 type = "scc-XXXXXX";186 path = tmpdir;187 pathln = tmpdirln;188 }189190 newsz = pathln + 1 + strlen(type) + 1;191 new = xmalloc(newsz);192 n = snprintf(new, newsz, "%.*s%c%s", (int)pathln, path, sep, type);193 if (n < 0 || n >= newsz)194 die("scc-cc: wrong output filename");195 if (sep == '/') {196 if ((tmpfd = mkstemp(new)) < 0)197 die("scc-cc: could not create output file '%s': %s",198 new, strerror(errno));199 close(tmpfd);200 }201202 return new;203}204205static int206settool(int tool, char *infile, int nexttool)207{208 struct tool *t = &tools[tool];209 char *fmt;210 int n, fds[2];211 static int fdin = -1;212213 strcpy(t->cmd, t->bin);214 assert(t->args.n == 0);215 newitem(&t->args, xstrdup(t->bin));216217 switch (tool) {218 case CC1:219 if (Eflag)220 addarg(tool, "-E");221 if (Mflag)222 addarg(tool, "-M");223 if (Wflag)224 addarg(tool, "-w");225 if (Qflag)226 addarg(tool, "-D__QBE__");227228 for (n = 0; n < cc1args.n; ++n)229 addarg(tool, cc1args.s[n]);230 for (n = 0; sysincludes[n]; ++n) {231 addarg(tool, "-I");232 addarg(tool, sysincludes[n]);233 }234 fmt = "%s/libexec/scc/%s";235 goto local_tool;236 case CC2:237 fmt = cc2fmt(tool);238 local_tool:239 n = snprintf(t->cmd, sizeof(t->cmd),240 fmt, prefix, t->bin, arch, abi);241 if (n < 0 || n >= sizeof(t->cmd))242 die("scc-cc: target tool path is too long");243 break;244 case QBE:245 strcpy(t->cmd, QBEBIN);246247 if (!strcmp(arch, "amd64") && !strcmp(abi, "sysv"))248 fmt = "amd64_sysv";249 else if (!strcmp(arch, "arm64") && !strcmp(abi, "sysv"))250 fmt = "arm64";251 else if (!strcmp(arch, "riscv64") && !strcmp(abi, "sysv"))252 fmt = "rv64";253 else254 die("not supported arch-abi (%s-%s) for qbe", arch, abi);255256 addarg(tool, "-t");257 addarg(tool, fmt);258 break;259 case LD:260 if (!outfile)261 outfile = "a.out";262 t->outfile = xstrdup(outfile);263 if (gflag)264 addarg(tool, "-g");265 if (sflag)266 addarg(tool, "-s");267 for (n = 0; ldcmd[n]; n++)268 addarg(tool, ldcmd[n]);269 break;270 case TEEIR:271 if (Eflag && outfile)272 t->outfile = xstrdup(outfile);273 else274 t->outfile = outfname(infile, "ir");275 addarg(tool, t->outfile);276 break;277 case TEEQBE:278 if (Rflag && outfile)279 t->outfile = xstrdup(outfile);280 else281 t->outfile = outfname(infile, "qbe");282 addarg(tool, t->outfile);283 break;284 case TEEAS:285 if (Sflag && outfile)286 t->outfile = xstrdup(outfile);287 else288 t->outfile = outfname(infile, "s");289 addarg(tool, t->outfile);290 break;291 case AS:292 if (cflag && outfile) {293 objfile = xstrdup(outfile);294 } else {295 objfile = (cflag || kflag) ? infile : NULL;296 objfile = outfname(objfile, "o");297 }298 t->outfile = xstrdup(objfile);299 if (gflag)300 addarg(tool, "-g");301 for (n = 0; ascmd[n]; n++)302 addarg(tool, ascmd[n]);303 break;304 break;305 default:306 break;307 }308309 if (fdin > -1) {310 t->in = fdin;311 fdin = -1;312 } else {313 t->in = -1;314 if (infile)315 addarg(tool, infile);316 }317318 if (nexttool < LAST_TOOL) {319 if (pipe(fds))320 die("scc-cc: pipe: %s", strerror(errno));321 t->out = fds[1];322 fdin = fds[0];323 } else {324 t->out = -1;325 }326327 addarg(tool, NULL);328329 return tool;330}331332/*333 * We cannot call exit() because it would call the atexit()334 * handlers. If it finishes correctly we already called335 * fflush() in the output buffers so it is not a problem336 * not following the normal exit path.337 */338static void339tee(char *fname)340{341 FILE *fp;342 int ch;343344 if ((fp = fopen(fname, "w")) == NULL)345 goto err;346347 while ((ch = getchar()) != EOF) {348 putc(ch, stdout);349 putc(ch, fp);350 }351352 fflush(stdout);353 fflush(fp);354355 if (ferror(stdin) || ferror(stdout) || ferror(fp))356 goto err;357 _exit(0);358359err:360 fprintf(stderr,361 "tee: teeing %s: %s\n",362 fname,363 strerror(errno));364365 _exit(1);366}367368static void369spawn(int tool)370{371 int i;372 char **ap;373 struct tool *t = &tools[tool];374375 switch (t->pid = fork()) {376 case -1:377 die("scc-cc: %s: %s", t->bin, strerror(errno));378 case 0:379 if (t->out > -1)380 dup2(t->out, 1);381 if (t->in > -1)382 dup2(t->in, 0);383384 if (tool == TEEAS && Sflag)385 dup2(devnullfd, 1);386 if (tool == TEEQBE && Rflag)387 dup2(devnullfd, 1);388 if (!dflag && tool != CC1 && tool != LD)389 dup2(devnullfd, 2);390391 if (dflag) {392 fprintf(stderr, "%s", t->cmd);393 for (ap = t->args.s+1; *ap; ap++)394 fprintf(stderr, " %s", *ap);395 putc('\n', stderr);396 }397 if (strcmp(t->cmd, "tee") == 0)398 tee(t->outfile);399 execvp(t->cmd, t->args.s);400 if (dflag) {401 fprintf(stderr,402 "scc-cc: execvp %s: %s\n",403 t->cmd,404 strerror(errno));405 }406 abort();407 default:408 if (t->in > -1)409 close(t->in);410 if (t->out > -1)411 close(t->out);412413 for (i = 0; i < t->args.n; i++)414 free(t->args.s[i]);415 t->args.n = 0;416417 break;418 }419}420421static int422toolfor(char *file)423{424 char *dot = strrchr(file, '.');425426 if (Eflag)427 return CC1;428429 if (dot) {430 if (!strcmp(dot, ".c"))431 return CC1;432 if (!strcmp(dot, ".ir"))433 return CC2;434 if (!strcmp(dot, ".qbe"))435 return QBE;436 if (!strcmp(dot, ".s"))437 return AS;438 if (!strcmp(dot, ".o"))439 return LD;440 if (!strcmp(dot, ".a"))441 return LD;442 } else if (!strcmp(file, "-")) {443 return CC1;444 }445446 die("scc-cc: unrecognized filetype of %s", file);447}448449static int450valid(int tool, struct tool *t)451{452 int st;453454 if (waitpid(t->pid, &st, 0) == -1 || WIFSIGNALED(st))455 goto internal;456 if (WIFEXITED(st) && WEXITSTATUS(st) == 0)457 return 1;458 if (!failure && (tool == CC1 || tool == LD))459 goto fail;460461internal:462 if (!failure)463 fprintf(stderr, "scc-cc:%s: internal error\n", t->bin);464fail:465 failure = 1;466 return 0;467}468469static int470validatetools(void)471{472 struct tool *t;473 unsigned i;474 int tool, st, failed = LAST_TOOL;475476 for (tool = 0; tool < LAST_TOOL; ++tool) {477 t = &tools[tool];478 if (!t->pid)479 continue;480 if (!valid(tool, t))481 failed = tool;482 if (tool >= failed && t->outfile)483 unlink(t->outfile);484 free(t->outfile);485 t->pid = 0;486 }487 if (failed < LAST_TOOL) {488 unlink(objfile);489 return 0;490 }491492 return 1;493}494495static int496buildfile(char *file, int tool)497{498 int nexttool;499500 for (; tool < LAST_TOOL; tool = nexttool) {501 switch (tool) {502 case CC1:503 if (Eflag && outfile)504 nexttool = TEEIR;505 else if (Eflag || Mflag)506 nexttool = LAST_TOOL;507 else508 nexttool = kflag ? TEEIR : CC2;509 break;510 case TEEIR:511 nexttool = (Eflag) ? LAST_TOOL : CC2;512 break;513 case CC2:514 if (Qflag)515 nexttool = (Rflag || kflag) ? TEEQBE : QBE;516 else517 nexttool = (Sflag || kflag) ? TEEAS : AS;518 break;519 case TEEQBE:520 nexttool = Rflag ? LAST_TOOL : QBE;521 break;522 case QBE:523 nexttool = (Sflag || kflag) ? TEEAS : AS;524 break;525 case TEEAS:526 nexttool = Sflag ? LAST_TOOL : AS;527 break;528 case AS:529 nexttool = LAST_TOOL;530 break;531 default:532 nexttool = LAST_TOOL;533 continue;534 }535536 spawn(settool(tool, file, nexttool));537 }538539 return validatetools();540}541542static void543build(struct items *chain, int link)544{545 int i, tool;546547 for (i = 0; i < chain->n; ++i) {548 if (!strcmp(chain->s[i], "-l")) {549 newitem(&linkargs, chain->s[i++]);550 newitem(&linkargs, chain->s[i]);551 continue;552 }553 tool = toolfor(chain->s[i]);554 if (tool == LD) {555 newitem(&linkargs, chain->s[i]);556 continue;557 }558 if (buildfile(chain->s[i], tool)) {559 newitem(&linkargs, objfile);560 newitem((!link || kflag) ? &objout : &objtmp, objfile);561 }562 }563}564565static void566usage(void)567{568 fputs("usage: scc-cc [options] file...\n", stderr);569 exit(1);570}571572static char *573param(char **argp, char ***argv)574{575 char *s;576577 if ((*argp)[1] != '\0') {578 s = (*argp) + 1;579 *argp += strlen(*argp) - 1;580 } else if ((*argv)[1] != NULL) {581 s = (*argv)[1];582 (*argv)++;583 } else {584 usage();585 }586587 return s;588}589590int591main(int argc, char *argv[])592{593 struct items linkchain = { .n = 0, };594 int link, n;595596 atexit(terminate);597 signal(SIGHUP, sighandler);598 signal(SIGINT, sighandler);599 signal(SIGTERM, sighandler);600601 if (!(arch = getenv("ARCH")))602 arch = ARCH;603 if (!(sys = getenv("SYS")))604 sys = SYS;605 if (!(abi = getenv("ABI")))606 abi = ABI;607 if (!(format = getenv("FORMAT")))608 format = FORMAT;609 if (!(prefix = getenv("SCCPREFIX")))610 prefix = PREFIX;611 if (!(libprefix = getenv("SCCLIBPREFIX")))612 libprefix = LIBPREFIX;613614 for (++argv; *argv; ++argv) {615 char *arg = *argv;616 if (arg[0] == '-' && arg[1] == '-')617 break;618 if (arg[0] != '-') {619 newitem(&linkchain, arg);620 continue;621 }622623 if (!strcmp(arg, "-static") ||624 !strcmp(arg, "-dynamic") ||625 !strcmp(arg, "-pedantic") ||626 !strcmp(arg, "-ansi") ||627 !strcmp(arg, "-pipe") ||628 !strncmp(arg, "-std=", 5)) {629 continue;630 }631632 for (++arg; *arg; ++arg) {633 switch (*arg) {634 case 'D':635 newitem(&cc1args, "-D");636 newitem(&cc1args, param(&arg, &argv));637 break;638 case 'M':639 Mflag = 1;640 break;641 case 'E':642 Eflag = 1;643 break;644 case 'I':645 newitem(&cc1args, "-I");646 newitem(&cc1args, param(&arg, &argv));647 break;648 case 'L':649 newitem(&linkargs, "-L");650 newitem(&linkargs, param(&arg, &argv));651 break;652 case 'O':653 param(&arg, &argv);654 break;655 case 'S':656 Sflag = 1;657 break;658 case 'R':659 Rflag = 1;660 break;661 case 'U':662 newitem(&cc1args, "-U");663 newitem(&cc1args, param(&arg, &argv));664 break;665 case 'c':666 cflag = 1;667 break;668 case 'd':669 dflag = 1;670 break;671 case 'g':672 gflag = 1;673 break;674 case 'k':675 kflag = 1;676 break;677 case 'l':678 newitem(&linkchain, "-l");679 newitem(&linkchain, param(&arg, &argv));680 break;681 case 'a':682 arch = param(&arg, &argv);683 break;684 case 't':685 sys = param(&arg, &argv);686 break;687 case 'o':688 outfile = param(&arg, &argv);689 break;690 case 's':691 sflag = 1;692 break;693 case 'w':694 Wflag = 1;695 break;696 case 'W':697 param(&arg, &argv);698 Wflag = 1;699 break;700 case 'q':701 Qflag = 0;702 break;703 case 'Q':704 Qflag = 1;705 break;706 case 'm':707 case 'f':708 /* ignore any -f or -m option */709 param(&arg, &argv);710 break;711 default:712 usage();713 }714 }715 }716717 for (; *argv; ++argv)718 newitem(&linkchain, *argv);719720 if (Eflag && linkchain.n == 0)721 newitem(&linkchain, "-");722723 if (Eflag && Mflag ||724 (Eflag || Mflag) && (Sflag || Rflag || kflag) ||725 linkchain.n == 0 ||726 linkchain.n > 1 && cflag && outfile)727 usage();728729 if (!dflag) {730 if ((devnullfd = open("/dev/null", O_WRONLY)) < 0)731 fputs("scc-cc: could not open /dev/null\n", stderr);732 }733734 if (!(tmpdir = getenv("TMPDIR")) || !tmpdir[0])735 tmpdir = ".";736 tmpdirln = strlen(tmpdir);737738 build(&linkchain, (link = !(Mflag || Eflag || Rflag || Sflag || cflag)));739740 if (!(link || cflag))741 return failure;742743 if (link && !failure) {744 spawn(settool(LD, NULL, LAST_TOOL));745 validatetools();746 }747748 return failure;749}