scc

Simple C99 Compiler
Log | Files | Refs | README | LICENSE

scc.c (9267B)


      1 /* See LICENSE file for copyright and license details. */
      2 #define _POSIX_SOURCE
      3 #define _XOPEN_SOURCE 500
      4 #include <sys/types.h>
      5 #include <sys/wait.h>
      6 #include <unistd.h>
      7 
      8 #include <errno.h>
      9 #include <limits.h>
     10 #include <signal.h>
     11 #include <stdio.h>
     12 #include <stdlib.h>
     13 #include <string.h>
     14 
     15 #include "../../inc/arg.h"
     16 #include "../../inc/cc.h"
     17 
     18 enum {
     19 	CC1,
     20 	TEEIR,
     21 	CC2,
     22 	TEEQBE,
     23 	QBE,
     24 	TEEAS,
     25 	AS,
     26 	LD,
     27 	STRIP,
     28 	LAST_TOOL,
     29 };
     30 
     31 static struct tool {
     32 	char   cmd[PATH_MAX];
     33 	char   bin[16];
     34 	char  *outfile;
     35 	struct items args;
     36 	unsigned nparams;
     37 	int    in, out, init;
     38 	pid_t  pid;
     39 } tools[] = {
     40 	[CC1]    = { .bin = "cc1",   .cmd = PREFIX "/libexec/scc/", },
     41 	[TEEIR]  = { .bin = "tee",   .cmd = "tee", },
     42 	[CC2]    = { .bin = "cc2",   .cmd = PREFIX "/libexec/scc/", },
     43 	[TEEQBE] = { .bin = "tee",   .cmd = "tee", },
     44 	[QBE]    = { .bin = "qbe",   .cmd = "qbe", },
     45 	[TEEAS]  = { .bin = "tee",   .cmd = "tee", },
     46 	[AS]     = { .bin = "as",    .cmd = "as", },
     47 	[LD]     = { .bin = "gcc",   .cmd = "gcc", }, /* TODO use ld */
     48 	[STRIP]  = { .bin = "strip", .cmd = "strip", },
     49 };
     50 
     51 char *argv0;
     52 static char *arch, *objfile, *outfile;
     53 static char *tmpdir;
     54 static size_t tmpdirln;
     55 static struct items objtmp, objout;
     56 static int Eflag, Sflag, cflag, kflag, sflag;
     57 
     58 extern int failure;
     59 
     60 static void
     61 terminate(void)
     62 {
     63 	unsigned i;
     64 
     65 	if (!kflag) {
     66 		for (i = 0; i < objtmp.n; ++i)
     67 			unlink(objtmp.s[i]);
     68 	}
     69 }
     70 
     71 static void
     72 addarg(int tool, char *arg)
     73 {
     74 	struct tool *t = &tools[tool];
     75 
     76 	if (t->args.n < 1)
     77 		t->args.n = 1;
     78 
     79 	newitem(&t->args, arg);
     80 }
     81 
     82 static void
     83 setargv0(int tool, char *arg)
     84 {
     85 	struct tool *t = &tools[tool];
     86 
     87 	if (t->args.n > 0)
     88 		t->args.s[0] = arg;
     89 	else
     90 		newitem(&t->args, arg);
     91 }
     92 
     93 static int
     94 inittool(int tool)
     95 {
     96 	struct tool *t = &tools[tool];
     97 	size_t binln;
     98 	int n;
     99 
    100 	if (t->init)
    101 		return tool;
    102 
    103 	switch (tool) {
    104 	case CC1: /* FALLTHROUGH */
    105 	case CC2:
    106 		binln = strlen(t->bin);
    107 		if (arch) {
    108 			n = snprintf(t->bin + binln,
    109 				     sizeof(t->bin) - binln,
    110 				     "-%s", arch);
    111 			if (n < 0 || n >= sizeof(t->bin))
    112 				die("scc: target tool bin too long");
    113 			binln = strlen(t->bin);
    114 		}
    115 
    116 		if (strlen(t->cmd) + binln + 1 > sizeof(t->cmd))
    117 			die("scc: target tool path too long");
    118 		strcat(t->cmd, t->bin);
    119 		break;
    120 	case LD:
    121 		addarg(tool, "-o");
    122 		t->outfile = outfile ? outfile : xstrdup("a.out");
    123 		addarg(tool, t->outfile);
    124 		break;
    125 	case AS:
    126 		addarg(tool, "-o");
    127 		break;
    128 	default:
    129 		break;
    130 	}
    131 
    132 	setargv0(tool, t->bin);
    133 	t->nparams = t->args.n;
    134 	t->init = 1;
    135 
    136 	return tool;
    137 }
    138 
    139 static char *
    140 outfname(char *path, char *type)
    141 {
    142 	char *new, sep, *p;
    143 	size_t newsz, pathln;
    144 	int tmpfd, n;
    145 
    146 	if (path) {
    147 		sep = '.';
    148 		if (p = strrchr(path, '/'))
    149 			path = p + 1;
    150 		pathln = strlen(path);
    151 		if (p = strrchr(path, '.'))
    152 			pathln -= strlen(p);
    153 	} else {
    154 		sep = '/';
    155 		type = "scc-XXXXXX";
    156 		path = tmpdir;
    157 		pathln = tmpdirln;
    158 	}
    159 
    160 	newsz = pathln + 1 + strlen(type) + 1;
    161 	new = xmalloc(newsz);
    162 	n = snprintf(new, newsz, "%.*s%c%s", pathln, path, sep, type);
    163 	if (n < 0 || n >= newsz)
    164 		die("scc: wrong output filename");
    165 	if ((tmpfd = mkstemp(new)) < 0 && errno != EINVAL)
    166 		die("scc: could not create output file '%s': %s",
    167 		    new, strerror(errno));
    168 	close(tmpfd);
    169 
    170 	return new;
    171 }
    172 
    173 static int
    174 settool(int tool, char *infile, int nexttool)
    175 {
    176 	struct tool *t = &tools[tool];
    177 	unsigned i;
    178 	int fds[2];
    179 	static int fdin = -1;
    180 
    181 	switch (tool) {
    182 	case TEEIR:
    183 		t->outfile = outfname(infile, "ir");
    184 		addarg(tool, t->outfile);
    185 		break;
    186 	case TEEQBE:
    187 		t->outfile = outfname(infile, "qbe");
    188 		addarg(tool, t->outfile);
    189 		break;
    190 	case TEEAS:
    191 		t->outfile = outfname(infile, "as");
    192 		addarg(tool, t->outfile);
    193 		break;
    194 	case AS:
    195 		if (cflag && outfile) {
    196 			objfile = outfile;
    197 		} else {
    198 			objfile = (cflag || kflag) ? infile : NULL;
    199 			objfile = outfname(objfile, "o");
    200 		}
    201 		t->outfile = xstrdup(objfile);
    202 		addarg(tool, t->outfile);
    203 		break;
    204 	case STRIP:
    205 		if (cflag || kflag) {
    206 			for (i = 0; i < objout.n; ++i)
    207 				addarg(tool, xstrdup(objout.s[i]));
    208 		}
    209 		if (!cflag && tools[LD].outfile)
    210 			addarg(tool, tools[LD].outfile);
    211 		break;
    212 	default:
    213 		break;
    214 	}
    215 
    216 	if (fdin > -1) {
    217 		t->in = fdin;
    218 		fdin = -1;
    219 	} else {
    220 		t->in = -1;
    221 		if (infile)
    222 			addarg(tool, xstrdup(infile));
    223 	}
    224 
    225 	if (nexttool < LAST_TOOL) {
    226 		if (pipe(fds))
    227 			die("scc: pipe: %s", strerror(errno));
    228 		t->out = fds[1];
    229 		fdin = fds[0];
    230 	} else {
    231 		t->out = -1;
    232 	}
    233 
    234 	addarg(tool, NULL);
    235 
    236 	return tool;
    237 }
    238 
    239 static void
    240 spawn(int tool)
    241 {
    242 	struct tool *t = &tools[tool];
    243 
    244 	switch (t->pid = fork()) {
    245 	case -1:
    246 		die("scc: %s: %s", t->bin, strerror(errno));
    247 	case 0:
    248 		if (t->out > -1)
    249 			dup2(t->out, 1);
    250 		if (t->in > -1)
    251 			dup2(t->in, 0);
    252 		execvp(t->cmd, t->args.s);
    253 		fprintf(stderr, "scc: execvp %s: %s\n",
    254 		        t->cmd, strerror(errno));
    255 		_exit(1);
    256 	default:
    257 		if (t->in > -1)
    258 			close(t->in);
    259 		if (t->out > -1)
    260 			close(t->out);
    261 		break;
    262 	}
    263 }
    264 
    265 static int
    266 toolfor(char *file)
    267 {
    268 	char *dot = strrchr(file, '.');
    269 
    270 	if (dot) {
    271 		if (!strcmp(dot, ".c"))
    272 			return CC1;
    273 		if (!strcmp(dot, ".ir"))
    274 			return CC2;
    275 		if (!strcmp(dot, ".qbe"))
    276 			return QBE;
    277 		if (!strcmp(dot, ".as"))
    278 			return AS;
    279 		if (!strcmp(dot, ".o"))
    280 			return LD;
    281 	}
    282 
    283 	die("scc: do not recognize filetype of %s", file);
    284 }
    285 
    286 static int
    287 validatetools(void)
    288 {
    289 	struct tool *t;
    290 	unsigned i;
    291 	int tool, st, failed = LAST_TOOL;
    292 
    293 	for (tool = 0; tool < LAST_TOOL; ++tool) {
    294 		t = &tools[tool];
    295 		if (t->pid) {
    296 			if (waitpid(t->pid, &st, 0) < 0 ||
    297 			    !WIFEXITED(st) || WEXITSTATUS(st) != 0) {
    298 				failure = 1;
    299 				failed = tool;
    300 			}
    301 			if (tool >= failed && t->outfile)
    302 				unlink(t->outfile);
    303 			for (i = t->nparams; i < t->args.n; ++i)
    304 				free(t->args.s[i]);
    305 			t->args.n = t->nparams;
    306 			t->pid = 0;
    307 		}
    308 	}
    309 	if (failed < LAST_TOOL) {
    310 		unlink(objfile);
    311 		free(objfile);
    312 		objfile = NULL;
    313 		return 0;
    314 	}
    315 
    316 	return 1;
    317 }
    318 
    319 static int
    320 buildfile(char *file, int tool)
    321 {
    322 	int nexttool;
    323 
    324 	for (; tool < LAST_TOOL; tool = nexttool) {
    325 		switch (tool) {
    326 		case CC1:
    327 			if (Eflag)
    328 				nexttool = LAST_TOOL;
    329 			else
    330 				nexttool = kflag ? TEEIR : CC2;
    331 			break;
    332 		case TEEIR:
    333 			nexttool = CC2;
    334 			break;
    335 		case CC2:
    336 			if (!arch || strcmp(arch, "qbe"))
    337 				nexttool = (Sflag || kflag) ? TEEAS : AS;
    338 			else
    339 				nexttool = kflag ? TEEQBE : QBE;
    340 			break;
    341 		case TEEQBE:
    342 			nexttool = QBE;
    343 			break;
    344 		case QBE:
    345 			nexttool = (Sflag || kflag) ? TEEAS : AS;
    346 			break;
    347 		case TEEAS:
    348 			nexttool = Sflag ? LAST_TOOL : AS;
    349 			break;
    350 		case AS:
    351 			nexttool = LAST_TOOL;
    352 			break;
    353 		default:
    354 			nexttool = LAST_TOOL;
    355 			continue;
    356 		}
    357 
    358 		spawn(settool(inittool(tool), file, nexttool));
    359 	}
    360 
    361 	return validatetools();
    362 }
    363 
    364 static void
    365 build(struct items *chain, int link)
    366 {
    367 	int i, tool;
    368 
    369 	if (link)
    370 		inittool(LD);
    371 
    372 	for (i = 0; i < chain->n; ++i) {
    373 		if (!strcmp(chain->s[i], "-l")) {
    374 			if (link) {
    375 				addarg(LD, xstrdup(chain->s[i++]));
    376 				addarg(LD, xstrdup(chain->s[i]));
    377 			} else {
    378 				++i;
    379 			}
    380 			continue;
    381 		}
    382 		tool = toolfor(chain->s[i]);
    383 		if (tool == LD) {
    384 			if (link)
    385 				addarg(LD, xstrdup(chain->s[i]));
    386 			continue;
    387 		}
    388 		if (buildfile(chain->s[i], tool)) {
    389 			if (link)
    390 				addarg(LD, xstrdup(objfile));
    391 			newitem((!link || kflag) ? &objout : &objtmp, objfile);
    392 		}
    393 	}
    394 }
    395 
    396 static void
    397 usage(void)
    398 {
    399 	die("usage: scc [-D def[=val]]... [-U def]... [-I dir]... "
    400 	    "[-L dir]... [-l dir]...\n"
    401 	    "           [-gksw] [-m arch] [-E|-S] [-o outfile] file...\n"
    402 	    "       scc [-D def[=val]]... [-U def]... [-I dir]... "
    403 	    "[-L dir]... [-l dir]...\n"
    404 	    "           [-gksw] [-m arch] [-E|-S] -c file...\n"
    405 	    "       scc [-D def[=val]]... [-U def]... [-I dir]... "
    406 	    "[-L dir]... [-l dir]...\n"
    407 	    "           [-gksw] [-m arch] -c -o outfile file");
    408 }
    409 
    410 int
    411 main(int argc, char *argv[])
    412 {
    413 	struct items linkchain = { .n = 0, };
    414 	int link;
    415 
    416 	atexit(terminate);
    417 
    418 	arch = getenv("ARCH");
    419 
    420 	ARGBEGIN {
    421 	case 'D':
    422 		addarg(CC1, "-D");
    423 		addarg(CC1, EARGF(usage()));
    424 		break;
    425 	case 'E':
    426 		Eflag = 1;
    427 		addarg(CC1, "-E");
    428 		break;
    429 	case 'I':
    430 		addarg(CC1, "-I");
    431 		addarg(CC1, EARGF(usage()));
    432 		break;
    433 	case 'L':
    434 		addarg(LD, "-L");
    435 		addarg(LD, EARGF(usage()));
    436 		break;
    437 	case 'S':
    438 		Sflag = 1;
    439 		break;
    440 	case 'U':
    441 		addarg(CC1, "-U");
    442 		addarg(CC1, EARGF(usage()));
    443 		break;
    444 	case 'c':
    445 		cflag = 1;
    446 		break;
    447 	case 'g':
    448 		addarg(AS, "-g");
    449 		addarg(LD, "-g");
    450 		break;
    451 	case 'k':
    452 		kflag = 1;
    453 		break;
    454 	case 'l':
    455 		newitem(&linkchain, "-l");
    456 		newitem(&linkchain, EARGF(usage()));
    457 		break;
    458 	case 'm':
    459 		arch = EARGF(usage());
    460 		break;
    461 	case 'o':
    462 		outfile = xstrdup(EARGF(usage()));
    463 		break;
    464 	case 's':
    465 		sflag = 1;
    466 		break;
    467 	case 'w':
    468 		addarg(CC1, "-w");
    469 		break;
    470 	case '-':
    471 		fprintf(stderr,
    472 		        "scc: ignored parameter --%s\n", EARGF(usage()));
    473 		break;
    474 	default:
    475 		usage();
    476 	} ARGOPERAND {
    477 operand:
    478 		newitem(&linkchain, ARGOP());
    479 	} ARGEND
    480 
    481 	for (; *argv; --argc, ++argv)
    482 		goto operand;
    483 
    484 	if (Eflag && (Sflag || kflag) || linkchain.n == 0 ||
    485 	    linkchain.n > 1 && cflag && outfile)
    486 		usage();
    487 
    488 	if (!(tmpdir = getenv("TMPDIR")) || !tmpdir[0])
    489 		tmpdir = ".";
    490 	tmpdirln = strlen(tmpdir);
    491 
    492 	build(&linkchain, (link = !(Eflag || Sflag || cflag)));
    493 
    494 	if (!(link || cflag))
    495 		return failure;
    496 
    497 	if (link && !failure) {
    498 		spawn(settool(LD, NULL, LAST_TOOL));
    499 		validatetools();
    500 	}
    501 
    502 	if (sflag) {
    503 		spawn(settool(inittool(STRIP), NULL, LAST_TOOL));
    504 		validatetools();
    505 	}
    506 
    507 	return failure;
    508 }