scc

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

cc.c (11504B)


      1 #define _POSIX_SOURCE
      2 #define _XOPEN_SOURCE 500
      3 #include <sys/types.h>
      4 #include <sys/wait.h>
      5 #include <unistd.h>
      6 
      7 #include <errno.h>
      8 #include <fcntl.h>
      9 #include <limits.h>
     10 #include <signal.h>
     11 #include <stdio.h>
     12 #include <stdlib.h>
     13 #include <string.h>
     14 
     15 #include "config.h"
     16 #include <scc/arg.h>
     17 #include <scc/scc.h>
     18 #include <scc/syscrts.h>
     19 #include <scc/sysincludes.h>
     20 #include <scc/syslibs.h>
     21 #include <scc/ldflags.h>
     22 
     23 enum {
     24 	CC1,
     25 	TEEIR,
     26 	CC2,
     27 	TEEQBE,
     28 	QBE,
     29 	TEEAS,
     30 	AS,
     31 	LD,
     32 	STRIP,
     33 	LAST_TOOL,
     34 };
     35 
     36 static struct tool {
     37 	char   cmd[PATH_MAX];
     38 	char   bin[32];
     39 	char  *outfile;
     40 	struct items args;
     41 	unsigned nparams;
     42 	int    in, out, init;
     43 	pid_t  pid;
     44 } tools[] = {
     45 	[CC1]    = { .cmd = "cc1" },
     46 	[TEEIR]  = { .bin = "tee",   .cmd = "tee", },
     47 	[CC2]    = { .cmd = "cc2" },
     48 	[TEEQBE] = { .bin = "tee",   .cmd = "tee", },
     49 	[QBE]    = { .bin = "qbe",   .cmd = "qbe", },
     50 	[TEEAS]  = { .bin = "tee",   .cmd = "tee", },
     51 	[AS]     = { .bin = "as",    .cmd = "as", },
     52 	[LD]     = { .bin = "ld",    .cmd = "ld", },
     53 	[STRIP]  = { .bin = "strip", .cmd = "strip", },
     54 };
     55 
     56 char *argv0;
     57 static char *arch, *sys, *abi, *format;
     58 static char *prefix, *objfile, *outfile;
     59 static char *tmpdir;
     60 static size_t tmpdirln;
     61 static struct items objtmp, objout;
     62 static int Mflag, Eflag, Sflag, Wflag,
     63            cflag, dflag, kflag, sflag, Qflag = 1; /* TODO: Remove Qflag */
     64 static int devnullfd = -1;
     65 
     66 extern int failure;
     67 
     68 static void
     69 terminate(void)
     70 {
     71 	unsigned i;
     72 
     73 	if (!kflag) {
     74 		for (i = 0; i < objtmp.n; ++i)
     75 			unlink(objtmp.s[i]);
     76 	}
     77 }
     78 
     79 static char *
     80 path(char *s)
     81 {
     82 	char *arg, buff[FILENAME_MAX];
     83 	size_t len, cnt;
     84 
     85 	for (cnt = 0 ; *s && cnt < FILENAME_MAX; ++s) {
     86 		if (*s != '%') {
     87 			buff[cnt++] = *s;
     88 			continue;
     89 		}
     90 
     91 		switch (*++s) {
     92 		case 'a':
     93 			arg = arch;
     94 			break;
     95 		case 's':
     96 			arg = sys;
     97 			break;
     98 		case 'p':
     99 			arg = prefix;
    100 			break;
    101 		case 'b':
    102 			arg = abi;
    103 			break;
    104 		default:
    105 			buff[cnt++] = *s;
    106 			continue;
    107 		}
    108 
    109 		len = strlen(arg);
    110 		if (len + cnt >= FILENAME_MAX)
    111 			goto too_long;
    112 		memcpy(buff+cnt, arg, len);
    113 		cnt += len;
    114 	}
    115 
    116 	if (cnt < FILENAME_MAX) {
    117 		buff[cnt] = '\0';
    118 		return xstrdup(buff);
    119 	}
    120 
    121 too_long:
    122 	die("cc: pathname too long");
    123 }
    124 
    125 static void
    126 addarg(int tool, char *arg)
    127 {
    128 	struct tool *t = &tools[tool];
    129 
    130 	if (t->args.n < 1)
    131 		t->args.n = 1;
    132 
    133 	newitem(&t->args, arg);
    134 }
    135 
    136 static void
    137 setargv0(int tool, char *arg)
    138 {
    139 	struct tool *t = &tools[tool];
    140 
    141 	if (t->args.n > 0)
    142 		t->args.s[0] = arg;
    143 	else
    144 		newitem(&t->args, arg);
    145 }
    146 
    147 static int
    148 qbe(int tool)
    149 {
    150 	if (tool != CC2 || !Qflag)
    151 		return 0;
    152 	if (!strcmp(arch, "amd64") && !strcmp(abi, "sysv"))
    153 		return 1;
    154 	return 0;
    155 }
    156 
    157 static int
    158 inittool(int tool)
    159 {
    160 	struct tool *t = &tools[tool];
    161 	char *crt, *fmt;
    162 	int n;
    163 
    164 	if (t->init)
    165 		return tool;
    166 
    167 	switch (tool) {
    168 	case CC1:
    169 		if (Wflag)
    170 			addarg(tool, "-w");
    171 		for (n = 0; sysincludes[n]; ++n) {
    172 			addarg(tool, "-I");
    173 			addarg(tool, path(sysincludes[n]));
    174 		}
    175 	case CC2:
    176 		fmt = (qbe(tool)) ? "%s-qbe_%s-%s" : "%s-%s-%s";
    177 		n = snprintf(t->bin, sizeof(t->bin), fmt, t->cmd, arch, abi);
    178 		if (n < 0 || n >= sizeof(t->bin))
    179 			die("cc: target tool name is too long");
    180 
    181 		n = snprintf(t->cmd, sizeof(t->cmd),
    182 		             "%s/libexec/scc/%s", prefix, t->bin);
    183 		if (n < 0 || n >= sizeof(t->cmd))
    184 			die("cc: target tool path is too long");
    185 		break;
    186 	case LD:
    187 		for (n = 0; ldflags[n]; ++n)
    188 			addarg(tool, ldflags[n]);
    189 		addarg(tool, "-o");
    190 		t->outfile = outfile ? outfile : xstrdup("a.out");
    191 		addarg(tool, t->outfile);
    192 		for (n = 0; syslibs[n]; ++n) {
    193 			addarg(tool, "-L");
    194 			addarg(tool, path(syslibs[n]));
    195 		}
    196 		for (n = 0; syscrts[n]; ++n)
    197 			addarg(tool, path(syscrts[n]));
    198 		break;
    199 	case AS:
    200 		addarg(tool, "-o");
    201 		break;
    202 	default:
    203 		break;
    204 	}
    205 
    206 	setargv0(tool, t->bin);
    207 	t->nparams = t->args.n;
    208 	t->init = 1;
    209 
    210 	return tool;
    211 }
    212 
    213 static char *
    214 outfname(char *path, char *type)
    215 {
    216 	char *new, sep, *p;
    217 	size_t newsz, pathln;
    218 	int tmpfd, n;
    219 
    220 	if (path) {
    221 		sep = '.';
    222 		if (p = strrchr(path, '/'))
    223 			path = p + 1;
    224 		pathln = strlen(path);
    225 		if (p = strrchr(path, '.'))
    226 			pathln -= strlen(p);
    227 	} else {
    228 		sep = '/';
    229 		type = "scc-XXXXXX";
    230 		path = tmpdir;
    231 		pathln = tmpdirln;
    232 	}
    233 
    234 	newsz = pathln + 1 + strlen(type) + 1;
    235 	new = xmalloc(newsz);
    236 	n = snprintf(new, newsz, "%.*s%c%s", (int)pathln, path, sep, type);
    237 	if (n < 0 || n >= newsz)
    238 		die("cc: wrong output filename");
    239 	if (sep == '/') {
    240 		if ((tmpfd = mkstemp(new)) < 0)
    241 			die("cc: could not create output file '%s': %s",
    242 			    new, strerror(errno));
    243 		close(tmpfd);
    244 	}
    245 
    246 	return new;
    247 }
    248 
    249 static int
    250 settool(int tool, char *infile, int nexttool)
    251 {
    252 	struct tool *t = &tools[tool];
    253 	unsigned i;
    254 	int fds[2];
    255 	static int fdin = -1;
    256 
    257 	switch (tool) {
    258 	case TEEIR:
    259 		t->outfile = outfname(infile, "ir");
    260 		addarg(tool, t->outfile);
    261 		break;
    262 	case TEEQBE:
    263 		t->outfile = outfname(infile, "qbe");
    264 		addarg(tool, t->outfile);
    265 		break;
    266 	case TEEAS:
    267 		t->outfile = outfname(infile, "s");
    268 		addarg(tool, t->outfile);
    269 		break;
    270 	case AS:
    271 		if (cflag && outfile) {
    272 			objfile = outfile;
    273 		} else {
    274 			objfile = (cflag || kflag) ? infile : NULL;
    275 			objfile = outfname(objfile, "o");
    276 		}
    277 		t->outfile = xstrdup(objfile);
    278 		addarg(tool, t->outfile);
    279 		break;
    280 	case STRIP:
    281 		if (cflag || kflag) {
    282 			for (i = 0; i < objout.n; ++i)
    283 				addarg(tool, xstrdup(objout.s[i]));
    284 		}
    285 		if (!cflag && tools[LD].outfile)
    286 			addarg(tool, tools[LD].outfile);
    287 		break;
    288 	default:
    289 		break;
    290 	}
    291 
    292 	if (fdin > -1) {
    293 		t->in = fdin;
    294 		fdin = -1;
    295 	} else {
    296 		t->in = -1;
    297 		if (infile)
    298 			addarg(tool, xstrdup(infile));
    299 	}
    300 
    301 	if (nexttool < LAST_TOOL) {
    302 		if (pipe(fds))
    303 			die("cc: pipe: %s", strerror(errno));
    304 		t->out = fds[1];
    305 		fdin = fds[0];
    306 	} else {
    307 		t->out = -1;
    308 	}
    309 
    310 	addarg(tool, NULL);
    311 
    312 	return tool;
    313 }
    314 
    315 static void
    316 spawn(int tool)
    317 {
    318 	char **ap;
    319 	struct tool *t = &tools[tool];
    320 
    321 	switch (t->pid = fork()) {
    322 	case -1:
    323 		die("cc: %s: %s", t->bin, strerror(errno));
    324 	case 0:
    325 		if (t->out > -1)
    326 			dup2(t->out, 1);
    327 		if (t->in > -1)
    328 			dup2(t->in, 0);
    329 		if (!dflag && tool != CC1 && tool != LD)
    330 			dup2(devnullfd, 2);
    331 		if (dflag) {
    332 			for (ap = t->args.s; *ap; ap++)
    333 				fprintf(stderr, " %s", *ap);
    334 			putc('\n', stderr);
    335 		}
    336 		execvp(t->cmd, t->args.s);
    337 		if (dflag) {
    338 			fprintf(stderr,
    339 			        "cc: execvp %s: %s\n",
    340 				t->cmd,
    341 			        strerror(errno));
    342 		}
    343 		abort();
    344 	default:
    345 		if (t->in > -1)
    346 			close(t->in);
    347 		if (t->out > -1)
    348 			close(t->out);
    349 		break;
    350 	}
    351 }
    352 
    353 static int
    354 toolfor(char *file)
    355 {
    356 	char *dot = strrchr(file, '.');
    357 
    358 	if (Eflag)
    359 		return CC1;
    360 
    361 	if (dot) {
    362 		if (!strcmp(dot, ".c"))
    363 			return CC1;
    364 		if (!strcmp(dot, ".ir"))
    365 			return CC2;
    366 		if (!strcmp(dot, ".qbe"))
    367 			return QBE;
    368 		if (!strcmp(dot, ".s"))
    369 			return AS;
    370 		if (!strcmp(dot, ".o"))
    371 			return LD;
    372 		if (!strcmp(dot, ".a"))
    373 			return LD;
    374 	} else if (!strcmp(file, "-")) {
    375 		return CC1;
    376 	}
    377 
    378 	die("cc: unrecognized filetype of %s", file);
    379 }
    380 
    381 static int
    382 valid(int tool, struct tool *t)
    383 {
    384 	int st;
    385 
    386 	if (waitpid(t->pid, &st, 0) == -1 || WIFSIGNALED(st))
    387 		goto internal;
    388 	if (WIFEXITED(st) && WEXITSTATUS(st) == 0)
    389 		return 1;
    390 	if (!failure && (tool == CC1 || tool == LD))
    391 		goto fail;
    392 
    393 internal:
    394 	if (!failure)
    395 		fprintf(stderr, "cc:%s: internal error\n", t->bin);
    396 fail:
    397 	failure = 1;
    398 	return 0;
    399 }
    400 
    401 static int
    402 validatetools(void)
    403 {
    404 	struct tool *t;
    405 	unsigned i;
    406 	int tool, st, failed = LAST_TOOL;
    407 
    408 	for (tool = 0; tool < LAST_TOOL; ++tool) {
    409 		t = &tools[tool];
    410 		if (!t->pid)
    411 			continue;
    412 		if (!valid(tool, t))
    413 			failed = tool;
    414 		if (tool >= failed && t->outfile)
    415 			unlink(t->outfile);
    416 		for (i = t->nparams; i < t->args.n; ++i)
    417 			free(t->args.s[i]);
    418 		t->args.n = t->nparams;
    419 		t->pid = 0;
    420 	}
    421 	if (failed < LAST_TOOL) {
    422 		unlink(objfile);
    423 		free(objfile);
    424 		objfile = NULL;
    425 		return 0;
    426 	}
    427 
    428 	return 1;
    429 }
    430 
    431 static int
    432 buildfile(char *file, int tool)
    433 {
    434 	int nexttool;
    435 
    436 	for (; tool < LAST_TOOL; tool = nexttool) {
    437 		switch (tool) {
    438 		case CC1:
    439 			if (Eflag || Mflag)
    440 				nexttool = LAST_TOOL;
    441 			else
    442 				nexttool = kflag ? TEEIR : CC2;
    443 			break;
    444 		case TEEIR:
    445 			nexttool = CC2;
    446 			break;
    447 		case CC2:
    448 			if (Qflag)
    449 				nexttool = kflag ? TEEQBE : QBE;
    450 			else
    451 				nexttool = (Sflag || kflag) ? TEEAS : AS;
    452 			break;
    453 		case TEEQBE:
    454 			nexttool = QBE;
    455 			break;
    456 		case QBE:
    457 			nexttool = (Sflag || kflag) ? TEEAS : AS;
    458 			break;
    459 		case TEEAS:
    460 			nexttool = Sflag ? LAST_TOOL : AS;
    461 			break;
    462 		case AS:
    463 			nexttool = LAST_TOOL;
    464 			break;
    465 		default:
    466 			nexttool = LAST_TOOL;
    467 			continue;
    468 		}
    469 
    470 		spawn(settool(inittool(tool), file, nexttool));
    471 	}
    472 
    473 	return validatetools();
    474 }
    475 
    476 static void
    477 build(struct items *chain, int link)
    478 {
    479 	int i, tool;
    480 
    481 	if (link)
    482 		inittool(LD);
    483 
    484 	for (i = 0; i < chain->n; ++i) {
    485 		if (!strcmp(chain->s[i], "-l")) {
    486 			if (link) {
    487 				addarg(LD, xstrdup(chain->s[i++]));
    488 				addarg(LD, xstrdup(chain->s[i]));
    489 			} else {
    490 				++i;
    491 			}
    492 			continue;
    493 		}
    494 		tool = toolfor(chain->s[i]);
    495 		if (tool == LD) {
    496 			if (link)
    497 				addarg(LD, xstrdup(chain->s[i]));
    498 			continue;
    499 		}
    500 		if (buildfile(chain->s[i], tool)) {
    501 			if (link)
    502 				addarg(LD, xstrdup(objfile));
    503 			newitem((!link || kflag) ? &objout : &objtmp, objfile);
    504 		}
    505 	}
    506 }
    507 
    508 static void
    509 usage(void)
    510 {
    511 	fputs("usage: cc [options] file...\n", stderr);
    512 	exit(1);
    513 }
    514 
    515 int
    516 main(int argc, char *argv[])
    517 {
    518 	struct items linkchain = { .n = 0, };
    519 	int link;
    520 
    521 	atexit(terminate);
    522 
    523 	if (!(arch = getenv("ARCH")))
    524 		arch = ARCH;
    525 	if (!(sys = getenv("SYS")))
    526 		sys = SYS;
    527 	if (!(abi = getenv("ABI")))
    528 		abi = ABI;
    529 	if (!(format = getenv("FORMAT")))
    530 		format = FORMAT;
    531 	if (!(prefix = getenv("SCCPREFIX")))
    532 		prefix = PREFIX;
    533 
    534 	ARGBEGIN {
    535 	case 'D':
    536 		addarg(CC1, "-D");
    537 		addarg(CC1, EARGF(usage()));
    538 		break;
    539 	case 'M':
    540 		Mflag = 1;
    541 		addarg(CC1, "-M");
    542 		break;
    543 	case 'E':
    544 		Eflag = 1;
    545 		addarg(CC1, "-E");
    546 		break;
    547 	case 'I':
    548 		addarg(CC1, "-I");
    549 		addarg(CC1, EARGF(usage()));
    550 		break;
    551 	case 'L':
    552 		addarg(LD, "-L");
    553 		addarg(LD, EARGF(usage()));
    554 		break;
    555 	case 'O':
    556 		EARGF(usage());
    557 		break;
    558 	case 'S':
    559 		Sflag = 1;
    560 		break;
    561 	case 'U':
    562 		addarg(CC1, "-U");
    563 		addarg(CC1, EARGF(usage()));
    564 		break;
    565 	case 'c':
    566 		cflag = 1;
    567 		break;
    568 	case 'd':
    569 		dflag = 1;
    570 		break;
    571 	case 'g':
    572 		addarg(AS, "-g");
    573 		addarg(LD, "-g");
    574 		break;
    575 	case 'k':
    576 		kflag = 1;
    577 		break;
    578 	case 'l':
    579 		newitem(&linkchain, "-l");
    580 		newitem(&linkchain, EARGF(usage()));
    581 		break;
    582 	case 'm':
    583 		arch = EARGF(usage());
    584 		break;
    585 	case 'o':
    586 		outfile = xstrdup(EARGF(usage()));
    587 		break;
    588 	case 's':
    589 		sflag = 1;
    590 		break;
    591 	case 't':
    592 		sys = EARGF(usage());
    593 		break;
    594 	case 'w':
    595 		Wflag = 0;
    596 		break;
    597 	case 'W':
    598 		Wflag = 1;
    599 		break;
    600 	case 'q':
    601 		Qflag = 0;
    602 		break;
    603 	case 'Q':
    604 		Qflag = 1;
    605 		break;
    606 	case '-':
    607 		fprintf(stderr,
    608 		        "cc: ignored parameter --%s\n", EARGF(usage()));
    609 		break;
    610 	default:
    611 		usage();
    612 	} ARGOPERAND {
    613 operand:
    614 		newitem(&linkchain, ARGOP());
    615 	} ARGEND
    616 
    617 	for (; *argv; --argc, ++argv)
    618 		goto operand;
    619 
    620 	if (Eflag && linkchain.n == 0)
    621 		newitem(&linkchain, "-");
    622 
    623 	if (Eflag && Mflag ||
    624             (Eflag || Mflag) && (Sflag || kflag) ||
    625 	    linkchain.n == 0 ||
    626 	    linkchain.n > 1 && cflag && outfile)
    627 		usage();
    628 
    629 	if (!dflag) {
    630 		if ((devnullfd = open("/dev/null", O_WRONLY)) < 0)
    631 			fputs("cc: could not open /dev/null\n", stderr);
    632 	}
    633 
    634 	if (!(tmpdir = getenv("TMPDIR")) || !tmpdir[0])
    635 		tmpdir = ".";
    636 	tmpdirln = strlen(tmpdir);
    637 
    638 	build(&linkchain, (link = !(Mflag || Eflag || Sflag || cflag)));
    639 
    640 	if (!(link || cflag))
    641 		return failure;
    642 
    643 	if (link && !failure) {
    644 		addarg(LD, xstrdup("-lc"));
    645 		addarg(LD, xstrdup("-lcrt"));
    646 		spawn(settool(LD, NULL, LAST_TOOL));
    647 		validatetools();
    648 	}
    649 
    650 	if (sflag) {
    651 		spawn(settool(inittool(STRIP), NULL, LAST_TOOL));
    652 		validatetools();
    653 	}
    654 
    655 	return failure;
    656 }