cc.c - scc - simple c99 compiler
 (HTM) git clone git://git.simple-cc.org/scc
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) Submodules
 (DIR) README
 (DIR) LICENSE
       ---
       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 }