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