scc

simple c99 compiler
git clone git://git.simple-cc.org/scc
Log | Files | Refs | Submodules | README | LICENSE

cc.c (13458B)


      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/config.h>
     29 #include <scc/scc.h>
     30 #include <scc/sys.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 	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 = "qbe"},
     57 	[TEEAS]  = {.bin = "tee"},
     58 	[AS]     = {.bin = ASBIN},
     59 	[LD]     = {.bin = LDBIN},
     60 };
     61 
     62 static char *arch, *sys, *abi, *format;
     63 static char *objfile, *outfile;
     64 static char *prefix, *libprefix;
     65 static char *tmpdir;
     66 static size_t tmpdirln;
     67 static struct items objtmp, objout;
     68 static struct items linkargs, cc1args;
     69 
     70 static int Mflag, Eflag, Sflag, Wflag,
     71            cflag, dflag, kflag, sflag, Qflag = 1, /* TODO: Remove Qflag */
     72            gflag;
     73 
     74 static int devnullfd = -1;
     75 
     76 extern int failure;
     77 
     78 static void
     79 terminate(void)
     80 {
     81 	unsigned i;
     82 
     83 	if (!kflag) {
     84 		for (i = 0; i < objtmp.n; ++i)
     85 			unlink(objtmp.s[i]);
     86 	}
     87 }
     88 
     89 static void
     90 sighandler(int sig)
     91 {
     92 	terminate();
     93 	_exit(1);
     94 }
     95 
     96 static void
     97 addarg(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;
    103 
    104 	if (!arg) {
    105 		newitem(&t->args, NULL);
    106 		return;
    107 	}
    108 
    109 	if (arg[0] == '-') {
    110 		newitem(&t->args, xstrdup(arg));
    111 		return;
    112 	}
    113 
    114 	if (!strcmp(arg, "%c")) {
    115 		for (n = 0; n < linkargs.n; ++n)
    116 			newitem(&t->args, xstrdup(linkargs.s[n]));
    117 		return;
    118 	}
    119 
    120 	for (cnt = 0 ; *arg && cnt < FILENAME_MAX; ++arg) {
    121 		if (*arg != '%') {
    122 			buff[cnt++] = *arg;
    123 			continue;
    124 		}
    125 
    126 		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 		}
    146 
    147 		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 	}
    153 
    154 	if (cnt >= FILENAME_MAX)
    155 		abort();
    156 
    157 	buff[cnt] = '\0';
    158 	newitem(&t->args, xstrdup(buff));
    159 }
    160 
    161 static char *
    162 cc2fmt(int tool)
    163 {
    164 	if (Qflag)
    165 		return "%s/libexec/scc/%s-qbe_%s-%s";
    166 	return "%s/libexec/scc/%s-%s-%s";
    167 }
    168 
    169 static char *
    170 outfname(char *path, char *type)
    171 {
    172 	char *new, sep, *p;
    173 	size_t newsz, pathln;
    174 	int tmpfd, n;
    175 
    176 	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 	}
    189 
    190 	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 	}
    201 
    202 	return new;
    203 }
    204 
    205 static int
    206 settool(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;
    212 
    213 	strcpy(t->cmd, t->bin);
    214 	assert(t->args.n == 0);
    215 	newitem(&t->args, xstrdup(t->bin));
    216 
    217 	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 		for (n = 0; n < cc1args.n; ++n)
    226 			addarg(tool, cc1args.s[n]);
    227 		for (n = 0; sysincludes[n]; ++n) {
    228 			addarg(tool, "-I");
    229 			addarg(tool, sysincludes[n]);
    230 		}
    231 		fmt = "%s/libexec/scc/%s";
    232 		goto local_tool;
    233 	case CC2:
    234 		fmt = cc2fmt(tool);
    235 	local_tool:
    236 		n = snprintf(t->cmd, sizeof(t->cmd),
    237 		             fmt, prefix, t->bin, arch, abi);
    238 		if (n < 0 || n >= sizeof(t->cmd))
    239 			die("scc-cc: target tool path is too long");
    240 		break;
    241 	case QBE:
    242 		strcpy(t->cmd, "qbe");
    243 
    244 		if (!strcmp(arch, "amd64") && !strcmp(abi, "sysv"))
    245 			fmt = "amd64_sysv";
    246 		else if (!strcmp(arch, "arm64") && !strcmp(abi, "sysv"))
    247 			fmt = "arm64";
    248 		else if (!strcmp(arch, "riscv64") && !strcmp(abi, "sysv"))
    249 			fmt = "rv64";
    250 		else
    251 			die("not supported arch-abi (%s-%s) for qbe", arch, abi);
    252 
    253 		addarg(tool, "-t");
    254 		addarg(tool, fmt);
    255 		break;
    256 	case LD:
    257 		if (!outfile)
    258 			outfile = "a.out";
    259 		t->outfile = xstrdup(outfile);
    260 		if (gflag)
    261 			addarg(tool, "-g");
    262 		if (sflag)
    263 			addarg(tool, "-s");
    264 		for (n = 0; ldcmd[n]; n++)
    265 			addarg(tool, ldcmd[n]);
    266                 break;
    267 	case TEEIR:
    268 		if (Eflag && outfile)
    269 			t->outfile = xstrdup(outfile);
    270 		else
    271 			t->outfile = outfname(infile, "ir");
    272 		addarg(tool, t->outfile);
    273 		break;
    274 	case TEEQBE:
    275 		t->outfile = outfname(infile, "qbe");
    276 		addarg(tool, t->outfile);
    277 		break;
    278 	case TEEAS:
    279 		t->outfile = outfname(infile, "s");
    280 		addarg(tool, t->outfile);
    281 		break;
    282 	case AS:
    283 		if (cflag && outfile) {
    284 			objfile = xstrdup(outfile);
    285 		} else {
    286 			objfile = (cflag || kflag) ? infile : NULL;
    287 			objfile = outfname(objfile, "o");
    288 		}
    289 		t->outfile = xstrdup(objfile);
    290 		if (gflag)
    291 			addarg(tool, "-g");
    292 		for (n = 0; ascmd[n]; n++)
    293 			addarg(tool, ascmd[n]);
    294                 break;
    295 		break;
    296 	default:
    297 		break;
    298 	}
    299 
    300 	if (fdin > -1) {
    301 		t->in = fdin;
    302 		fdin = -1;
    303 	} else {
    304 		t->in = -1;
    305 		if (infile)
    306 			addarg(tool, infile);
    307 	}
    308 
    309 	if (nexttool < LAST_TOOL) {
    310 		if (pipe(fds))
    311 			die("scc-cc: pipe: %s", strerror(errno));
    312 		t->out = fds[1];
    313 		fdin = fds[0];
    314 	} else {
    315 		t->out = -1;
    316 	}
    317 
    318 	addarg(tool, NULL);
    319 
    320 	return tool;
    321 }
    322 
    323 /*
    324  * We cannot call exit() because it would call the atexit()
    325  * handlers. If it finishes correctly we already called
    326  * fflush() in the output buffers so it is not a problem
    327  * not following the normal exit path.
    328  */
    329 static void
    330 tee(char *fname)
    331 {
    332 	FILE *fp;
    333 	int ch;
    334 
    335 	if ((fp = fopen(fname, "w")) == NULL)
    336 		goto err;
    337 
    338 	while ((ch = getchar()) != EOF) {
    339 		putc(ch, stdout);
    340 		putc(ch, fp);
    341 	}
    342 
    343 	fflush(stdout);
    344 	fflush(fp);
    345 
    346 	if (ferror(stdin) || ferror(stdout) || ferror(fp))
    347 		goto err;
    348 	_exit(0);
    349 
    350 err:
    351 	fprintf(stderr,
    352 		"tee: teeing %s: %s\n",
    353 		fname,
    354 		strerror(errno));
    355 
    356 	_exit(1);
    357 }
    358 
    359 static void
    360 spawn(int tool)
    361 {
    362 	int i;
    363 	char **ap;
    364 	struct tool *t = &tools[tool];
    365 
    366 	switch (t->pid = fork()) {
    367 	case -1:
    368 		die("scc-cc: %s: %s", t->bin, strerror(errno));
    369 	case 0:
    370 		if (t->out > -1)
    371 			dup2(t->out, 1);
    372 		if (t->in > -1)
    373 			dup2(t->in, 0);
    374 
    375 		if (tool == TEEAS && Sflag)
    376 			dup2(devnullfd, 1);
    377 		if (!dflag && tool != CC1 && tool != LD)
    378 			dup2(devnullfd, 2);
    379 
    380 		if (dflag) {
    381 			fprintf(stderr, "%s", t->cmd);
    382 			for (ap = t->args.s+1; *ap; ap++)
    383 				fprintf(stderr, " %s", *ap);
    384 			putc('\n', stderr);
    385 		}
    386 		if (strcmp(t->cmd, "tee") == 0)
    387 			tee(t->outfile);
    388 		execvp(t->cmd, t->args.s);
    389 		if (dflag) {
    390 			fprintf(stderr,
    391 			        "scc-cc: execvp %s: %s\n",
    392 				t->cmd,
    393 			        strerror(errno));
    394 		}
    395 		abort();
    396 	default:
    397 		if (t->in > -1)
    398 			close(t->in);
    399 		if (t->out > -1)
    400 			close(t->out);
    401 
    402 		for (i = 0; i < t->args.n; i++)
    403 			free(t->args.s[i]);
    404 		t->args.n = 0;
    405 
    406 		break;
    407 	}
    408 }
    409 
    410 static int
    411 toolfor(char *file)
    412 {
    413 	char *dot = strrchr(file, '.');
    414 
    415 	if (Eflag)
    416 		return CC1;
    417 
    418 	if (dot) {
    419 		if (!strcmp(dot, ".c"))
    420 			return CC1;
    421 		if (!strcmp(dot, ".ir"))
    422 			return CC2;
    423 		if (!strcmp(dot, ".qbe"))
    424 			return QBE;
    425 		if (!strcmp(dot, ".s"))
    426 			return AS;
    427 		if (!strcmp(dot, ".o"))
    428 			return LD;
    429 		if (!strcmp(dot, ".a"))
    430 			return LD;
    431 	} else if (!strcmp(file, "-")) {
    432 		return CC1;
    433 	}
    434 
    435 	die("scc-cc: unrecognized filetype of %s", file);
    436 }
    437 
    438 static int
    439 valid(int tool, struct tool *t)
    440 {
    441 	int st;
    442 
    443 	if (waitpid(t->pid, &st, 0) == -1 || WIFSIGNALED(st))
    444 		goto internal;
    445 	if (WIFEXITED(st) && WEXITSTATUS(st) == 0)
    446 		return 1;
    447 	if (!failure && (tool == CC1 || tool == LD))
    448 		goto fail;
    449 
    450 internal:
    451 	if (!failure)
    452 		fprintf(stderr, "scc-cc:%s: internal error\n", t->bin);
    453 fail:
    454 	failure = 1;
    455 	return 0;
    456 }
    457 
    458 static int
    459 validatetools(void)
    460 {
    461 	struct tool *t;
    462 	unsigned i;
    463 	int tool, st, failed = LAST_TOOL;
    464 
    465 	for (tool = 0; tool < LAST_TOOL; ++tool) {
    466 		t = &tools[tool];
    467 		if (!t->pid)
    468 			continue;
    469 		if (!valid(tool, t))
    470 			failed = tool;
    471 		if (tool >= failed && t->outfile)
    472 			unlink(t->outfile);
    473 		free(t->outfile);
    474 		t->pid = 0;
    475 	}
    476 	if (failed < LAST_TOOL) {
    477 		unlink(objfile);
    478 		return 0;
    479 	}
    480 
    481 	return 1;
    482 }
    483 
    484 static int
    485 buildfile(char *file, int tool)
    486 {
    487 	int nexttool;
    488 
    489 	for (; tool < LAST_TOOL; tool = nexttool) {
    490 		switch (tool) {
    491 		case CC1:
    492 			if (Eflag && outfile)
    493 				nexttool = TEEIR;
    494 			else if (Eflag || Mflag)
    495 				nexttool = LAST_TOOL;
    496 			else
    497 				nexttool = kflag ? TEEIR : CC2;
    498 			break;
    499 		case TEEIR:
    500 			nexttool = (Eflag) ? LAST_TOOL : CC2;
    501 			break;
    502 		case CC2:
    503 			if (Qflag)
    504 				nexttool = kflag ? TEEQBE : QBE;
    505 			else
    506 				nexttool = (Sflag || kflag) ? TEEAS : AS;
    507 			break;
    508 		case TEEQBE:
    509 			nexttool = QBE;
    510 			break;
    511 		case QBE:
    512 			nexttool = (Sflag || kflag) ? TEEAS : AS;
    513 			break;
    514 		case TEEAS:
    515 			nexttool = Sflag ? LAST_TOOL : AS;
    516 			break;
    517 		case AS:
    518 			nexttool = LAST_TOOL;
    519 			break;
    520 		default:
    521 			nexttool = LAST_TOOL;
    522 			continue;
    523 		}
    524 
    525 		spawn(settool(tool, file, nexttool));
    526 	}
    527 
    528 	return validatetools();
    529 }
    530 
    531 static void
    532 build(struct items *chain, int link)
    533 {
    534 	int i, tool;
    535 
    536 	for (i = 0; i < chain->n; ++i) {
    537 		if (!strcmp(chain->s[i], "-l")) {
    538 			newitem(&linkargs, chain->s[i++]);
    539 			newitem(&linkargs, chain->s[i]);
    540 			continue;
    541 		}
    542 		tool = toolfor(chain->s[i]);
    543 		if (tool == LD) {
    544 			newitem(&linkargs, chain->s[i]);
    545 			continue;
    546 		}
    547 		if (buildfile(chain->s[i], tool)) {
    548 			newitem(&linkargs, objfile);
    549 			newitem((!link || kflag) ? &objout : &objtmp, objfile);
    550 		}
    551 	}
    552 }
    553 
    554 static void
    555 usage(void)
    556 {
    557 	fputs("usage: scc-cc [options] file...\n", stderr);
    558 	exit(1);
    559 }
    560 
    561 static char *
    562 param(char **argp, char ***argv)
    563 {
    564 	char *s;
    565 
    566 	if ((*argp)[1] != '\0') {
    567 		s = (*argp) + 1;
    568 		*argp += strlen(*argp) - 1;
    569 	} else if ((*argv)[1] != NULL) {
    570 		s = (*argv)[1];
    571 		(*argv)++;
    572 	} else {
    573 		usage();
    574 	}
    575 
    576 	return s;
    577 }
    578 
    579 int
    580 main(int argc, char *argv[])
    581 {
    582 	struct items linkchain = { .n = 0, };
    583 	int link, n;
    584 
    585 	atexit(terminate);
    586 	signal(SIGHUP, sighandler);
    587 	signal(SIGINT, sighandler);
    588 	signal(SIGTERM, sighandler);
    589 
    590 	if (!(arch = getenv("ARCH")))
    591 		arch = ARCH;
    592 	if (!(sys = getenv("SYS")))
    593 		sys = SYS;
    594 	if (!(abi = getenv("ABI")))
    595 		abi = ABI;
    596 	if (!(format = getenv("FORMAT")))
    597 		format = FORMAT;
    598 	if (!(prefix = getenv("SCCPREFIX")))
    599 		prefix = PREFIX;
    600 	if (!(libprefix = getenv("SCCLIBPREFIX")))
    601 		libprefix = LIBPREFIX;
    602 
    603 	for (++argv; *argv; ++argv) {
    604 		char *arg = *argv;
    605 		if (arg[0] == '-' && arg[1] == '-')
    606 			break;
    607 		if (arg[0] != '-') {
    608 			newitem(&linkchain, arg);
    609 			continue;
    610 		}
    611 
    612 		if (!strcmp(arg, "-static") ||
    613 		    !strcmp(arg, "-dynamic") ||
    614 		    !strcmp(arg, "-pedantic") ||
    615 		    !strcmp(arg, "-ansi") ||
    616 		    !strncmp(arg, "-std=", 5)) {
    617 			continue;
    618 		}
    619 
    620 		for (++arg; *arg; ++arg) {
    621 			switch (*arg) {
    622 			case 'D':
    623 				newitem(&cc1args, "-D");
    624 				newitem(&cc1args, param(&arg, &argv));
    625 				break;
    626 			case 'M':
    627 				Mflag = 1;
    628 				break;
    629 			case 'E':
    630 				Eflag = 1;
    631 				break;
    632 			case 'I':
    633 				newitem(&cc1args, "-I");
    634 				newitem(&cc1args, param(&arg, &argv));
    635 				break;
    636 			case 'L':
    637 				newitem(&linkargs, "-L");
    638 				newitem(&linkargs, param(&arg, &argv));
    639 				break;
    640 			case 'O':
    641 				param(&arg, &argv);
    642 				break;
    643 			case 'S':
    644 				Sflag = 1;
    645 				break;
    646 			case 'U':
    647 				newitem(&cc1args, "-U");
    648 				newitem(&cc1args, param(&arg, &argv));
    649 				break;
    650 			case 'c':
    651 				cflag = 1;
    652 				break;
    653 			case 'd':
    654 				dflag = 1;
    655 				break;
    656 			case 'g':
    657 				gflag = 1;
    658 				break;
    659 			case 'k':
    660 				kflag = 1;
    661 				break;
    662 			case 'l':
    663 				newitem(&linkchain, "-l");
    664 				newitem(&linkchain, param(&arg, &argv));
    665 				break;
    666 			case 'a':
    667 				arch = param(&arg, &argv);
    668 				break;
    669 			case 't':
    670 				sys = param(&arg, &argv);
    671 				break;
    672 			case 'o':
    673 				outfile = param(&arg, &argv);
    674 				break;
    675 			case 's':
    676 				sflag = 1;
    677 				break;
    678 			case 'w':
    679 				Wflag = 1;
    680 				break;
    681 			case 'W':
    682 				param(&arg, &argv);
    683 				Wflag = 1;
    684 				break;
    685 			case 'q':
    686 				Qflag = 0;
    687 				break;
    688 			case 'Q':
    689 				Qflag = 1;
    690 				break;
    691 			case 'm':
    692 			case 'f':
    693 				/* ignore any -f or -m option */
    694 				param(&arg, &argv);
    695 				break;
    696 			default:
    697 				usage();
    698 			}
    699 		}
    700 	}
    701 
    702 	for (; *argv; ++argv)
    703 		newitem(&linkchain, *argv);
    704 
    705 	if (Eflag && linkchain.n == 0)
    706 		newitem(&linkchain, "-");
    707 
    708 	if (Eflag && Mflag ||
    709             (Eflag || Mflag) && (Sflag || kflag) ||
    710 	    linkchain.n == 0 ||
    711 	    linkchain.n > 1 && cflag && outfile)
    712 		usage();
    713 
    714 	if (!dflag) {
    715 		if ((devnullfd = open("/dev/null", O_WRONLY)) < 0)
    716 			fputs("scc-cc: could not open /dev/null\n", stderr);
    717 	}
    718 
    719 	if (!(tmpdir = getenv("TMPDIR")) || !tmpdir[0])
    720 		tmpdir = ".";
    721 	tmpdirln = strlen(tmpdir);
    722 
    723 	build(&linkchain, (link = !(Mflag || Eflag || Sflag || cflag)));
    724 
    725 	if (!(link || cflag))
    726 		return failure;
    727 
    728 	if (link && !failure) {
    729 		spawn(settool(LD, NULL, LAST_TOOL));
    730 		validatetools();
    731 	}
    732 
    733 	return failure;
    734 }