scc

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

cc.c (11451B)


      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/sysincludes.h>
     30 #include <scc/sysld.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 	unsigned nparams;
     50 	int    in, out, init;
     51 	pid_t  pid;
     52 } tools[] = {
     53 	[CC1]    = {.cmd = "cc1"},
     54 	[TEEIR]  = {.bin = "tee",   .cmd = "tee"},
     55 	[CC2]    = {.cmd = "cc2"},
     56 	[TEEQBE] = {.bin = "tee",   .cmd = "tee"},
     57 	[QBE]    = {.bin = "qbe"},
     58 	[TEEAS]  = {.bin = "tee",   .cmd = "tee"},
     59 	[AS]     = {.bin = "as",    .cmd = "as"},
     60 	[LD]     = {.bin = "ld",    .cmd = "ld"},
     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, linkargs;
     70 static int Mflag, Eflag, Sflag, Wflag,
     71            cflag, dflag, kflag, sflag, Qflag = 1; /* TODO: Remove Qflag */
     72 static int devnullfd = -1;
     73 
     74 extern int failure;
     75 
     76 static void
     77 terminate(void)
     78 {
     79 	unsigned i;
     80 
     81 	if (!kflag) {
     82 		for (i = 0; i < objtmp.n; ++i)
     83 			unlink(objtmp.s[i]);
     84 	}
     85 }
     86 
     87 static void
     88 addarg(int tool, char *arg)
     89 {
     90 	struct tool *t = &tools[tool];
     91 	char *p, buff[FILENAME_MAX];
     92 	size_t len, cnt;
     93 	int n;
     94 
     95 	if (t->args.n < 1)
     96 		t->args.n = 1;
     97 
     98 	if (!arg || arg[0] == '-') {
     99 		newitem(&t->args, arg);
    100 		return;
    101 	}
    102 
    103 	if (!strcmp(arg, "%c")) {
    104 		for (n = 0; n < linkargs.n; ++n)
    105 			newitem(&t->args, linkargs.s[n]);
    106 		return;
    107 	}
    108 
    109 	for (cnt = 0 ; *arg && cnt < FILENAME_MAX; ++arg) {
    110 		if (*arg != '%') {
    111 			buff[cnt++] = *arg;
    112 			continue;
    113 		}
    114 
    115 		switch (*++arg) {
    116 		case 'a':
    117 			p = arch;
    118 			break;
    119 		case 's':
    120 			p = sys;
    121 			break;
    122 		case 'p':
    123 			p = libprefix;
    124 			break;
    125 		case 'b':
    126 			p = abi;
    127 			break;
    128 		case 'o':
    129 			p = outfile;
    130 			break;
    131 		default:
    132 			buff[cnt++] = *arg;
    133 			continue;
    134 		}
    135 
    136 		len = strlen(p);
    137 		if (len + cnt >= FILENAME_MAX)
    138 			die("cc: pathname too long");
    139 		memcpy(buff+cnt, p, len);
    140 		cnt += len;
    141 	}
    142 
    143 	if (cnt >= FILENAME_MAX)
    144 		abort();
    145 
    146 	buff[cnt] = '\0';
    147 	newitem(&t->args, xstrdup(buff));
    148 }
    149 
    150 static void
    151 setargv0(int tool, char *arg)
    152 {
    153 	struct tool *t = &tools[tool];
    154 
    155 	if (t->args.n > 0)
    156 		t->args.s[0] = arg;
    157 	else
    158 		newitem(&t->args, arg);
    159 }
    160 
    161 static char *
    162 cc12fmt(int tool)
    163 {
    164 	if (tool == CC1)
    165 		return "%s";
    166 	if (Qflag && !strcmp(arch, "amd64") && !strcmp(abi, "sysv"))
    167 		return "%s-qbe_%s-%s";
    168 	return "%s-%s-%s";
    169 }
    170 
    171 static int
    172 inittool(int tool)
    173 {
    174 	struct tool *t = &tools[tool];
    175 	char *crt, *fmt;
    176 	int n;
    177 
    178 	if (t->init)
    179 		return tool;
    180 
    181 	switch (tool) {
    182 	case CC1:
    183 		if (Wflag)
    184 			addarg(tool, "-w");
    185 		for (n = 0; sysincludes[n]; ++n) {
    186 			addarg(tool, "-I");
    187 			addarg(tool, sysincludes[n]);
    188 		}
    189 	case CC2:
    190 		fmt = cc12fmt(tool);
    191 		n = snprintf(t->bin, sizeof(t->bin), fmt, t->cmd, arch, abi);
    192 		if (n < 0 || n >= sizeof(t->bin))
    193 			die("cc: target tool name is too long");
    194 	case QBE:
    195 		n = snprintf(t->cmd, sizeof(t->cmd),
    196 		             "%s/libexec/scc/%s", prefix, t->bin);
    197 		if (n < 0 || n >= sizeof(t->cmd))
    198 			die("cc: target tool path is too long");
    199 		break;
    200 	case LD:
    201 		t->outfile = outfile;
    202 		if (sflag)
    203 			addarg(tool, "-s");
    204 		for (n = 0; ldcmd[n]; n++)
    205 			addarg(tool, ldcmd[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 = objfile;
    286 		addarg(tool, t->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, 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 			fprintf(stderr, "%s", t->cmd);
    333 			for (ap = t->args.s+1; *ap; ap++)
    334 				fprintf(stderr, " %s", *ap);
    335 			putc('\n', stderr);
    336 		}
    337 		execvp(t->cmd, t->args.s);
    338 		if (dflag) {
    339 			fprintf(stderr,
    340 			        "cc: execvp %s: %s\n",
    341 				t->cmd,
    342 			        strerror(errno));
    343 		}
    344 		abort();
    345 	default:
    346 		if (t->in > -1)
    347 			close(t->in);
    348 		if (t->out > -1)
    349 			close(t->out);
    350 		break;
    351 	}
    352 }
    353 
    354 static int
    355 toolfor(char *file)
    356 {
    357 	char *dot = strrchr(file, '.');
    358 
    359 	if (Eflag)
    360 		return CC1;
    361 
    362 	if (dot) {
    363 		if (!strcmp(dot, ".c"))
    364 			return CC1;
    365 		if (!strcmp(dot, ".ir"))
    366 			return CC2;
    367 		if (!strcmp(dot, ".qbe"))
    368 			return QBE;
    369 		if (!strcmp(dot, ".s"))
    370 			return AS;
    371 		if (!strcmp(dot, ".o"))
    372 			return LD;
    373 		if (!strcmp(dot, ".a"))
    374 			return LD;
    375 	} else if (!strcmp(file, "-")) {
    376 		return CC1;
    377 	}
    378 
    379 	die("cc: unrecognized filetype of %s", file);
    380 }
    381 
    382 static int
    383 valid(int tool, struct tool *t)
    384 {
    385 	int st;
    386 
    387 	if (waitpid(t->pid, &st, 0) == -1 || WIFSIGNALED(st))
    388 		goto internal;
    389 	if (WIFEXITED(st) && WEXITSTATUS(st) == 0)
    390 		return 1;
    391 	if (!failure && (tool == CC1 || tool == LD))
    392 		goto fail;
    393 
    394 internal:
    395 	if (!failure)
    396 		fprintf(stderr, "cc:%s: internal error\n", t->bin);
    397 fail:
    398 	failure = 1;
    399 	return 0;
    400 }
    401 
    402 static int
    403 validatetools(void)
    404 {
    405 	struct tool *t;
    406 	unsigned i;
    407 	int tool, st, failed = LAST_TOOL;
    408 
    409 	for (tool = 0; tool < LAST_TOOL; ++tool) {
    410 		t = &tools[tool];
    411 		if (!t->pid)
    412 			continue;
    413 		if (!valid(tool, t))
    414 			failed = tool;
    415 		if (tool >= failed && t->outfile)
    416 			unlink(t->outfile);
    417 		t->args.n = t->nparams;
    418 		t->pid = 0;
    419 	}
    420 	if (failed < LAST_TOOL) {
    421 		unlink(objfile);
    422 		return 0;
    423 	}
    424 
    425 	return 1;
    426 }
    427 
    428 static int
    429 buildfile(char *file, int tool)
    430 {
    431 	int nexttool;
    432 
    433 	for (; tool < LAST_TOOL; tool = nexttool) {
    434 		switch (tool) {
    435 		case CC1:
    436 			if (Eflag || Mflag)
    437 				nexttool = LAST_TOOL;
    438 			else
    439 				nexttool = kflag ? TEEIR : CC2;
    440 			break;
    441 		case TEEIR:
    442 			nexttool = CC2;
    443 			break;
    444 		case CC2:
    445 			if (Qflag)
    446 				nexttool = kflag ? TEEQBE : QBE;
    447 			else
    448 				nexttool = (Sflag || kflag) ? TEEAS : AS;
    449 			break;
    450 		case TEEQBE:
    451 			nexttool = QBE;
    452 			break;
    453 		case QBE:
    454 			nexttool = (Sflag || kflag) ? TEEAS : AS;
    455 			break;
    456 		case TEEAS:
    457 			nexttool = Sflag ? LAST_TOOL : AS;
    458 			break;
    459 		case AS:
    460 			nexttool = LAST_TOOL;
    461 			break;
    462 		default:
    463 			nexttool = LAST_TOOL;
    464 			continue;
    465 		}
    466 
    467 		spawn(settool(inittool(tool), file, nexttool));
    468 	}
    469 
    470 	return validatetools();
    471 }
    472 
    473 static void
    474 build(struct items *chain, int link)
    475 {
    476 	int i, tool;
    477 
    478 	for (i = 0; i < chain->n; ++i) {
    479 		if (!strcmp(chain->s[i], "-l")) {
    480 			newitem(&linkargs, chain->s[i++]);
    481 			newitem(&linkargs, chain->s[i]);
    482 			continue;
    483 		}
    484 		tool = toolfor(chain->s[i]);
    485 		if (tool == LD) {
    486 			newitem(&linkargs, chain->s[i]);
    487 			continue;
    488 		}
    489 		if (buildfile(chain->s[i], tool)) {
    490 			newitem(&linkargs, objfile);
    491 			newitem((!link || kflag) ? &objout : &objtmp, objfile);
    492 		}
    493 	}
    494 }
    495 
    496 static void
    497 usage(void)
    498 {
    499 	fputs("usage: cc [options] file...\n", stderr);
    500 	exit(1);
    501 }
    502 
    503 int
    504 main(int argc, char *argv[])
    505 {
    506 	struct items linkchain = { .n = 0, };
    507 	int link, n;
    508 
    509 	atexit(terminate);
    510 
    511 	if (!(arch = getenv("ARCH")))
    512 		arch = ARCH;
    513 	if (!(sys = getenv("SYS")))
    514 		sys = SYS;
    515 	if (!(abi = getenv("ABI")))
    516 		abi = ABI;
    517 	if (!(format = getenv("FORMAT")))
    518 		format = FORMAT;
    519 	if (!(prefix = getenv("SCCPREFIX")))
    520 		prefix = PREFIX;
    521 	if (!(libprefix = getenv("SCCLIBPREFIX")))
    522 		libprefix = LIBPREFIX;
    523 
    524 	ARGBEGIN {
    525 	case 'D':
    526 		addarg(CC1, "-D");
    527 		addarg(CC1, EARGF(usage()));
    528 		break;
    529 	case 'M':
    530 		Mflag = 1;
    531 		addarg(CC1, "-M");
    532 		break;
    533 	case 'E':
    534 		Eflag = 1;
    535 		addarg(CC1, "-E");
    536 		break;
    537 	case 'I':
    538 		addarg(CC1, "-I");
    539 		addarg(CC1, EARGF(usage()));
    540 		break;
    541 	case 'L':
    542 		addarg(LD, "-L");
    543 		addarg(LD, EARGF(usage()));
    544 		break;
    545 	case 'O':
    546 		EARGF(usage());
    547 		break;
    548 	case 'S':
    549 		Sflag = 1;
    550 		break;
    551 	case 'U':
    552 		addarg(CC1, "-U");
    553 		addarg(CC1, EARGF(usage()));
    554 		break;
    555 	case 'c':
    556 		cflag = 1;
    557 		break;
    558 	case 'd':
    559 		dflag = 1;
    560 		break;
    561 	case 'g':
    562 		addarg(AS, "-g");
    563 		addarg(LD, "-g");
    564 		break;
    565 	case 'k':
    566 		kflag = 1;
    567 		break;
    568 	case 'l':
    569 		newitem(&linkchain, "-l");
    570 		newitem(&linkchain, EARGF(usage()));
    571 		break;
    572 	case 'm':
    573 		arch = EARGF(usage());
    574 		break;
    575 	case 'o':
    576 		outfile = EARGF(usage());
    577 		break;
    578 	case 's':
    579 		sflag = 1;
    580 		break;
    581 	case 't':
    582 		sys = EARGF(usage());
    583 		break;
    584 	case 'w':
    585 		Wflag = 0;
    586 		break;
    587 	case 'W':
    588 		Wflag = 1;
    589 		break;
    590 	case 'q':
    591 		Qflag = 0;
    592 		break;
    593 	case 'Q':
    594 		Qflag = 1;
    595 		break;
    596 	case '-':
    597 		fprintf(stderr,
    598 		        "cc: ignored parameter --%s\n", EARGF(usage()));
    599 		break;
    600 	default:
    601 		usage();
    602 	} ARGOPERAND {
    603 operand:
    604 		newitem(&linkchain, ARGOP());
    605 	} ARGEND
    606 
    607 	for (; *argv; --argc, ++argv)
    608 		goto operand;
    609 
    610 	if (Eflag && linkchain.n == 0)
    611 		newitem(&linkchain, "-");
    612 
    613 	if (Eflag && Mflag ||
    614             (Eflag || Mflag) && (Sflag || kflag) ||
    615 	    linkchain.n == 0 ||
    616 	    linkchain.n > 1 && cflag && outfile)
    617 		usage();
    618 
    619 	if (!dflag) {
    620 		if ((devnullfd = open("/dev/null", O_WRONLY)) < 0)
    621 			fputs("cc: could not open /dev/null\n", stderr);
    622 	}
    623 
    624 	if (!outfile)
    625 		outfile = "a.out";
    626 
    627 	if (!(tmpdir = getenv("TMPDIR")) || !tmpdir[0])
    628 		tmpdir = ".";
    629 	tmpdirln = strlen(tmpdir);
    630 
    631 	build(&linkchain, (link = !(Mflag || Eflag || Sflag || cflag)));
    632 
    633 	if (!(link || cflag))
    634 		return failure;
    635 
    636 	if (link && !failure) {
    637 		inittool(LD);
    638 		spawn(settool(LD, NULL, LAST_TOOL));
    639 		validatetools();
    640 	}
    641 
    642 	return failure;
    643 }