ref: ffba7ee67f3de764e7820dfbb47cd00e87fcea8d
dir: /driver/posix/scc.c/
/* See LICENSE file for copyright and license details. */
#define _POSIX_SOURCE
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <errno.h>
#include <limits.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../../inc/arg.h"
#include "../../inc/cc.h"
#define ADDARG(t, p) ((tools[t].args[++tools[t].nargs]) = (p))
#define NARGS 64
enum {
CC1,
CC2,
QBE,
NR_TOOLS,
};
static struct tool {
char cmd[PATH_MAX];
char *args[NARGS];
char bin[16];
int nargs, in, out;
pid_t pid;
} tools[NR_TOOLS] = {
[CC1] = { .bin = "cc1", .cmd = PREFIX "/libexec/scc/", },
[CC2] = { .bin = "cc2", .cmd = PREFIX "/libexec/scc/", },
[QBE] = { .bin = "qbe", .bin = "qbe", .cmd = "qbe", },
};
char *argv0;
static char *arch;
static int Eflag;
static void
terminate(void)
{
int i;
for (i = 0; i < NR_TOOLS; ++i) {
if (tools[i].pid)
kill(tools[i].pid, SIGTERM);
}
}
int
inittool(int tool)
{
struct tool *t = &tools[tool];
size_t binln;
int n;
if (!t->args[0]) {
switch (tool) {
case CC1:
case CC2:
binln = strlen(t->bin);
if (arch) {
n = snprintf(t->bin + binln,
sizeof(t->bin) - binln,
"-%s", arch);
if (n < 0 || n >= sizeof(t->bin))
die("scc: target tool bin too long");
binln = strlen(t->bin);
}
if (strlen(t->cmd) + binln + 1 > sizeof(t->cmd))
die("scc: target tool path too long");
strcat(t->cmd, t->bin);
break;
default:
break;
}
t->args[0] = t->bin;
}
return tool;
}
int
settool(int t, int output)
{
struct tool *tool = &tools[t];
int fds[2], n;
static int fdin;
if (fdin) {
tool->in = fdin;
fdin = 0;
}
if (output < NR_TOOLS) {
if (pipe(fds))
die("scc: pipe: %s", strerror(errno));
tool->out = fds[1];
fdin = fds[0];
}
return t;
}
void
spawn(int t)
{
struct tool *tool = &tools[t];
switch (tool->pid = fork()) {
case -1:
die("scc: %s: %s", tool->bin, strerror(errno));
case 0:
if (tool->out)
dup2(tool->out, 1);
if (tool->in)
dup2(tool->in, 0);
execvp(tool->cmd, tool->args);
fprintf(stderr, "scc: execv %s: %s\n",
tool->cmd, strerror(errno));
_exit(1);
default:
if (tool->in)
close(tool->in);
if (tool->out)
close(tool->out);
break;
}
}
void
build(char *file)
{
int tool, out;
for (tool = CC1; tool < NR_TOOLS; tool = out) {
switch (tool) {
case CC1:
out = CC2;
ADDARG(tool, file);
break;
case CC2:
out = (!arch || strcmp(arch, "qbe")) ? NR_TOOLS : QBE;
break;
case QBE:
out = NR_TOOLS;
break;
default:
break;
}
spawn(settool(inittool(tool), out));
}
}
static void
usage(void)
{
die("usage: %s [-m arch] input ...");
}
int
main(int argc, char *argv[])
{
int st, i;
pid_t pid;
atexit(terminate);
arch = getenv("ARCH");
ARGBEGIN {
case 'E':
Eflag = 1;
ADDARG(CC1, "-E");
break;
case 'm':
arch = EARGF(usage());
break;
case '-':
fprintf(stderr, "scc: ignored parameter --%s\n", EARGF(usage()));
break;
default:
usage();
} ARGEND
if (!argc)
die("scc: fatal error: no input files");
build(*argv);
for (i = 0; i < NR_TOOLS; ++i) {
if ((pid = wait(&st)) < 0)
break;
if (pid == tools[i].pid)
tools[i].pid = 0;
if (!WIFEXITED(st) || WEXITSTATUS(st) != 0)
exit(-1);
}
return 0;
}