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 }