scc

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

cc.c (12000B)


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