scc

A fork of https://git.simple-cc.org/scc/ for Qute

git clone https://git.8pit.net/scc.git

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