--- /dev/null
+/*
+ * It's easiest to think of what cc does as a sequence of four
+ * conversions. Each conversion produces the inputs to the next step
+ * and the number of types is reduced. If the step is the final
+ * step for the conversion then the file is generated with the expected
+ * name but if it will be consumed by a later stage it is a temporary
+ * scratch file.
+ *
+ * Stage 1: (-c -o overrides object name)
+ *
+ * Ending Action
+ * $1.S preprocessor - may make $1.s
+ * $1.s nothing
+ * $1.c preprocessor, may make $1.% or /dev/tty
+ * $1.o nothing
+ * $1.a nothing (library)
+ *
+ * Stage 2: (not -E)
+ *
+ * Ending Action
+ * $1.s nothing
+ * $1.% cc, opt - make $1.s
+ * $1.o nothing
+ * $1.a nothing (library)
+ *
+ * Stage 3: (not -E or -S)
+ *
+ * Ending Action
+ * $1.s assembler - makes $1.o
+ * $1.o nothing
+ * $1.a nothing (library)
+ *
+ * Stage 4: (run if no -c -E -S)
+ *
+ * ld [each .o|.a in order] [each -l lib in order] -lc
+ * (with -b -o $1 etc)
+ *
+ * TODO:
+ *
+ * Platform specifics
+ * Search library paths for libraries (or pass to ld and make ld do it)
+ * Turn on temp removal once confident
+ * Split I/D
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#define CMD_AS "/bin/as"
+#define CMD_CC "/usr/lib/cc"
+#define CMD_COPT "/usr/lib/copt"
+#define COPT_FILE "/usr/lib/cc-copt"
+#define CMD_LD "/bin/ld"
+#define CMD_CPP "/usr/lib/cpp"
+#define CRT0 "/usr/lib/crt0.o"
+
+struct obj {
+ struct obj *next;
+ char *name;
+ uint8_t type;
+#define TYPE_S 1
+#define TYPE_C 2
+#define TYPE_s 3
+#define TYPE_C_pp 4
+#define TYPE_O 5
+#define TYPE_A 6
+ uint8_t used;
+};
+
+struct objhead {
+ struct obj *head;
+ struct obj *tail;
+};
+
+struct objhead objlist;
+struct objhead liblist;
+struct objhead inclist;
+struct objhead deflist;
+struct objhead libpathlist;
+
+int keep_temp;
+int last_phase = 4;
+int only_one_input;
+char *target;
+int strip;
+int c_files;
+int standalone;
+
+#define MAXARG 64
+
+int arginfd, argoutfd;
+char *arglist[MAXARG];
+char **argptr;
+char *rmlist[MAXARG];
+char **rmptr = rmlist;
+
+static void remove_temporaries(void)
+{
+ char **p = rmlist;
+ while (p < rmptr) {
+ if (keep_temp == 0)
+ printf("remove %s\n", *p);
+ free(*p++);
+ }
+ rmptr = rmlist;
+}
+
+static void fatal(void)
+{
+ remove_temporaries();
+ exit(1);
+}
+
+static void memory(void)
+{
+ fprintf(stderr, "cc: out of memory.\n");
+ fatal();
+}
+
+static void append_obj(struct objhead *h, char *p, uint8_t type)
+{
+ struct obj *o = malloc(sizeof(struct obj));
+ if (o == NULL)
+ memory();
+ o->name = p;
+ o->next = NULL;
+ o->used = 0;
+ o->type = type;
+ if (h->tail)
+ h->tail->next = o;
+ else {
+ h->tail = o;
+ h->head = o;
+ }
+}
+
+static char *pathmod(char *p, char *f, char *t, int rmif)
+{
+ char *x = strrchr(p, '.');
+ if (x == NULL) {
+ fprintf(stderr, "cc: no extension on '%s'.\n", p);
+ fatal();
+ }
+ if (strcmp(x, f)) {
+ fprintf(stderr, "cc: internal got '%s' expected '%s'.\n",
+ p, t);
+ fatal();
+ }
+ strcpy(x, t);
+ if (rmif != last_phase) {
+ *rmptr = strdup(p);
+ if (*rmptr++ == NULL)
+ memory();
+ }
+ return p;
+}
+
+static void add_argument(char *p)
+{
+ if (argptr == &arglist[MAXARG]) {
+ fprintf(stderr, "cc: too many arguments to command.\n");
+ fatal();
+ }
+ *argptr++ = p;
+}
+
+static void add_argument_list(char *header, struct objhead *h)
+{
+ struct obj *i = h->head;
+ while (i) {
+ if (header)
+ add_argument(header);
+ add_argument(i->name);
+ i->used = 1;
+ i = i->next;
+ }
+}
+
+static void run_command(void)
+{
+ pid_t pid, p;
+ int status;
+
+ fflush(stdout);
+
+ pid = fork();
+ if (pid == -1) {
+ perror("fork");
+ fatal();
+ }
+ if (pid == 0) {
+ if (arginfd != -1) {
+ dup2(arginfd, 0);
+ close(arginfd);
+ }
+ if (argoutfd != -1) {
+ dup2(argoutfd, 1);
+ close(argoutfd);
+ }
+ execv(arglist[0], arglist);
+ perror("execv");
+ exit(255);
+ }
+ if (arginfd)
+ close(arginfd);
+ if (argoutfd)
+ close(argoutfd);
+ while ((p = waitpid(pid, &status, 0)) != pid) {
+ if (p == -1) {
+ perror("waitpid");
+ fatal();
+ }
+ }
+ if (WIFSIGNALED(status) || WEXITSTATUS(status))
+ fatal();
+}
+
+static void redirect_in(const char *p)
+{
+ arginfd = open(p, O_RDONLY);
+ if (arginfd == -1) {
+ perror(p);
+ fatal();
+ }
+}
+
+static void redirect_out(const char *p)
+{
+ argoutfd = open(p, O_WRONLY | O_CREAT, 0666);
+ if (argoutfd == -1) {
+ perror(p);
+ fatal();
+ }
+}
+
+static void build_arglist(char *p)
+{
+ arginfd = -1;
+ argoutfd = -1;
+ argptr = arglist;
+ add_argument(p);
+}
+
+void convert_s_to_o(char *path)
+{
+ build_arglist(CMD_AS);
+ add_argument(path);
+ run_command();
+ pathmod(path, ".s", ".o", 3); /* 3 ?? check */
+}
+
+void convert_c_to_s(char *path)
+{
+ char *tmp;
+ build_arglist(CMD_CC);
+ redirect_in(path);
+ tmp = strdup(pathmod(path, ".%", ".@", 0));
+ if (tmp == NULL)
+ memory();
+ redirect_out(tmp);
+ run_command();
+ build_arglist(CMD_COPT);
+ add_argument(COPT_FILE);
+ redirect_in(tmp);
+ redirect_out(pathmod(path, ".%", ".s", 2));
+ free(tmp);
+}
+
+void convert_S_to_s(char *path)
+{
+ build_arglist(CMD_CPP);
+ redirect_in(path);
+ redirect_out(pathmod(path, ".S", ".s", 1));
+ run_command();
+}
+
+void preprocess_c(char *path)
+{
+ build_arglist(CMD_CPP);
+
+ add_argument_list("-I", &inclist);
+ add_argument_list("-D", &deflist);
+ add_argument(path);
+ /* Weird one .. -E goes to stdout */
+ if (last_phase != 1)
+ redirect_out(pathmod(path, ".c", ".%", -1));
+ run_command();
+}
+
+void link_phase(void)
+{
+ build_arglist(CMD_LD);
+ add_argument("-b");
+ if (strip)
+ add_argument("-s");
+ add_argument("-o");
+ add_argument(target);
+ if (!standalone)
+ add_argument(CRT0);
+ add_argument_list(NULL, &objlist);
+ add_argument_list(NULL, &liblist);
+}
+
+void sequence(struct obj *i)
+{
+ printf("Processing %s %d\n", i->name, i->type);
+ if (i->type == TYPE_S) {
+ convert_S_to_s(i->name);
+ i->type = TYPE_s;
+ i->used = 1;
+ }
+ if (i->type == TYPE_C) {
+ preprocess_c(i->name);
+ i->type = TYPE_C_pp;
+ i->used = 1;
+ }
+ if (last_phase == 1)
+ return;
+ printf("Processing %s %d\n", i->name, i->type);
+ if (i->type == TYPE_C_pp) {
+ convert_c_to_s(i->name);
+ i->type = TYPE_s;
+ i->used = 1;
+ }
+ if (last_phase == 2)
+ return;
+ printf("Processing %s %d\n", i->name, i->type);
+ if (i->type == TYPE_s) {
+ convert_s_to_o(i->name);
+ i->type = TYPE_O;
+ i->used = 1;
+ }
+}
+
+void processing_loop(void)
+{
+ struct obj *i = objlist.head;
+ while (i) {
+ sequence(i);
+ remove_temporaries();
+ i = i->next;
+ }
+ if (last_phase == 3)
+ return;
+ link_phase();
+}
+
+void unused_files(void)
+{
+ struct obj *i = objlist.head;
+ while (i) {
+ if (!i->used)
+ fprintf(stderr, "cc: warning file %s unused.\n",
+ i->name);
+ i = i->next;
+ }
+}
+
+void usage(void)
+{
+ fprintf(stderr, "usage...\n");
+ fatal();
+}
+
+char **add_macro(char **p)
+{
+ if ((*p)[2])
+ append_obj(&deflist, *p + 2, 0);
+ else
+ append_obj(&deflist, *++p, 0);
+ return p;
+}
+
+char **add_library(char **p)
+{
+ if ((*p)[2])
+ append_obj(&liblist, *p + 2, TYPE_A);
+ else
+ append_obj(&liblist, *++p, TYPE_A);
+ return p;
+}
+
+char **add_library_path(char **p)
+{
+ if ((*p)[2])
+ append_obj(&libpathlist, *p + 2, 0);
+ else
+ append_obj(&libpathlist, *++p, 0);
+ return p;
+}
+
+
+char **add_includes(char **p)
+{
+ if ((*p)[2])
+ append_obj(&inclist, *p + 2, 0);
+ else
+ append_obj(&inclist, *++p, 0);
+ return p;
+}
+
+
+void dunno(const char *p)
+{
+ fprintf(stderr, "cc: don't know what to do with '%s'.\n", p);
+ fatal();
+}
+
+void add_file(char *p)
+{
+ char *x = strrchr(p, '.');
+ if (x == NULL)
+ dunno(p);
+ switch (x[1]) {
+ case 'a':
+ append_obj(&objlist, p, TYPE_A);
+ break;
+ case 's':
+ append_obj(&objlist, p, TYPE_s);
+ break;
+ case 'S':
+ append_obj(&objlist, p, TYPE_S);
+ break;
+ case 'c':
+ append_obj(&objlist, p, TYPE_C);
+ c_files++;
+ break;
+ case 'o':
+ append_obj(&objlist, p, TYPE_O);
+ break;
+ default:
+ dunno(p);
+ }
+}
+
+void one_input(void)
+{
+ fprintf(stderr, "cc: too many files for -E\n");
+ fatal();
+}
+
+void uniopt(char *p)
+{
+ if (p[2])
+ usage();
+}
+
+int main(int argc, char *argv[])
+{
+ char **p = argv;
+ signal(SIGCHLD, SIG_DFL);
+
+ while (*++p) {
+ /* filename or option ? */
+ if (**p != '-') {
+ add_file(*p);
+ continue;
+ }
+ switch ((*p)[1]) {
+ /* Don't link */
+ case 'c':
+ uniopt(*p);
+ last_phase = 3;
+ break;
+ /* Don't assemble */
+ case 'S':
+ uniopt(*p);
+ last_phase = 2;
+ break;
+ /* Only pre-process */
+ case 'E':
+ uniopt(*p);
+ last_phase = 1;
+ only_one_input = 1;
+ break;
+ case 'l':
+ p = add_library(p);
+ break;
+ case 'I':
+ p = add_includes(p);
+ break;
+ case 'L':
+ p = add_library_path(p);
+ break;
+ case 'D':
+ p = add_macro(p);
+ break;
+ case 'i':
+/* split_id();*/
+ uniopt(*p);
+ break;
+ case 'o':
+ if (target != NULL) {
+ fprintf(stderr,
+ "-o can only be used once.\n");
+ fatal();
+ }
+ if ((*p)[2])
+ target = *p + 2;
+ else
+ target = *++p;
+ break;
+ case 'X':
+ uniopt(*p);
+ keep_temp = 1;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ if (target == NULL)
+ target = "a.out";
+ if (only_one_input && c_files > 1)
+ one_input();
+ processing_loop();
+ unused_files();
+ return 0;
+}