diff options
| -rw-r--r-- | src/prc/CMakeLists.txt | 14 | ||||
| -rw-r--r-- | src/prc/posix.c | 106 | ||||
| -rw-r--r-- | src/prc/prc.c | 695 | ||||
| -rw-r--r-- | src/prc/prc.h | 12 | ||||
| -rw-r--r-- | src/prc1/CMakeLists.txt | 3 | ||||
| -rw-r--r-- | src/prc1/kw.c | 2 | ||||
| -rw-r--r-- | src/prc1/prc1.c | 9 |
7 files changed, 836 insertions, 5 deletions
diff --git a/src/prc/CMakeLists.txt b/src/prc/CMakeLists.txt new file mode 100644 index 0000000..631417d --- /dev/null +++ b/src/prc/CMakeLists.txt @@ -0,0 +1,14 @@ +add_executable(${PROJECT_NAME} ${PROJECT_NAME}.c) +include(GNUInstallDirs) +target_compile_definitions(${PROJECT_NAME} PRIVATE + PREFIX="${CMAKE_INSTALL_PREFIX}" + LIBEXECDIR="${CMAKE_INSTALL_LIBEXECDIR}/${PROJECT_NAME}") +target_compile_options(${PROJECT_NAME} PRIVATE -g -Wall -pedantic) +set_target_properties(${PROJECT_NAME} PROPERTIES C_STANDARD 99 C_EXTENSIONS OFF) +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + target_compile_options(${PROJECT_NAME} PRIVATE -Og) +endif() +if(UNIX) + target_sources(${PROJECT_NAME} PRIVATE posix.c) +endif() +install(TARGETS ${PROJECT_NAME}) diff --git a/src/prc/posix.c b/src/prc/posix.c new file mode 100644 index 0000000..5510130 --- /dev/null +++ b/src/prc/posix.c @@ -0,0 +1,106 @@ +#define _POSIX_C_SOURCE 200809L + +#include "prc.h" +#include <errno.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/wait.h> +#include <unistd.h> + +struct spawn +{ + pid_t pid; +}; + +void spawn_free(struct spawn *s) +{ + free(s); +} + +int spawn_wait(struct spawn *s) +{ + int stat; + pid_t pid = waitpid(s->pid, &stat, 0); + + if (pid < 0) + { + perror("waitpid(2)"); + return -1; + } + else if (WIFEXITED(stat) && WEXITSTATUS(stat) != EXIT_SUCCESS) + return -1; + else if (WIFSTOPPED(stat)) + return -1; + + return 0; +} + +static int setupfd(FILE *f, int dst) +{ + int fd; + + if (!f) + return 0; + else if ((fd = fileno(f)) < 0) + { + perror("fileno(3)"); + return -1; + } + else if (dup2(fd, dst) < 0) + { + perror("dup(2)"); + return -1; + } + + return 0; +} + +struct spawn *spawnvi(const char *path, char *const argv[], FILE *in, FILE *out) +{ + struct spawn *ret = malloc(sizeof *ret); + + if (!ret) + { + perror("malloc(3)"); + goto failure; + } + + switch ((ret->pid = fork())) + { + case -1: + fprintf(stderr, "fork(2): %s: %s\n", path, strerror(errno)); + goto failure; + case 0: + if (setupfd(in, STDIN_FILENO)) + goto failure; + else if (setupfd(out, STDOUT_FILENO)) + goto failure; + else if (execvp(path, argv)) + { + fprintf(stderr, "execv(2): %s: %s\n", path, strerror(errno)); + goto failure; + } + default: + break; + } + + return ret; + +failure: + free(ret); + return NULL; +} + +struct spawn *spawnv(const char *path, char *const argv[], FILE *out) +{ + return spawnvi(path, argv, NULL, out); +} + +struct spawn *spawni(const char *path, FILE *in, FILE *out) +{ + char *v[] = {(char *)path, NULL}; + + return spawnvi(path, v, in, out); +} diff --git a/src/prc/prc.c b/src/prc/prc.c new file mode 100644 index 0000000..fa2e751 --- /dev/null +++ b/src/prc/prc.c @@ -0,0 +1,695 @@ +#include "prc.h" +#include <errno.h> +#include <stdarg.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +struct input +{ + const char **v; + size_t n; +}; + +struct rwinput +{ + char **v; + size_t n; +}; + +struct dstr +{ + char *s; +}; + +struct proc +{ + struct spawn *s; + char *path; + FILE *out; +}; + +struct procs +{ + struct proc *p; + size_t n; +}; + +struct ldflags +{ + const char **v; + size_t n; +}; + +struct state +{ + const char *as, *qbe, *ld, *prefix, *libexecdir, *name, *out; + const struct arg *arg; + struct input sources, incs, libs, ldirs; + struct procs srcprocs, qbeprocs, asprocs; + struct ldflags ldflags; + int componly, debug, verbose; +}; + +struct arg +{ + const char *s; + int (*f)(struct state *, const char *); + + enum + { + NOVAL, + SPACEVAL = 1, + EQVAL = 1 << 1, + JOINVAL = 1 << 2 + } v; +}; + +static int comp(struct state *s, const char *val) +{ + s->componly = 1; + return 0; +} + +static int debug(struct state *s, const char *val) +{ + s->debug = 1; + return 0; +} + +static int verbose(struct state *s, const char *val) +{ + s->verbose = 1; + return 0; +} + +static int ldflag(struct state *s, const char *val) +{ + struct ldflags *lf = &s->ldflags; + size_t n = lf->n + 1; + const char **v = realloc(lf->v, n * sizeof *v); + + if (!v) + { + perror("realloc(3)"); + return -1; + } + + v[lf->n++] = val; + lf->v = v; + return 0; +} + +static int optim(struct state *s, const char *val) +{ + return 0; +} + +static int out(struct state *s, const char *val) +{ + s->out = val; + return 0; +} + +static int addinput(struct state *s, struct input *i, const char *val) +{ + size_t n = i->n + 1; + const char **v = realloc(i->v, n * sizeof *v); + + if (!v) + { + perror("realloc(3)"); + return -1; + } + + v[i->n++] = val; + i->v = v; + s->arg = NULL; + return 0; +} + +static int inc(struct state *s, const char *val) +{ + return addinput(s, &s->incs, val); +} + +static int lib(struct state *s, const char *val) +{ + return addinput(s, &s->libs, val); +} + +static int ldir(struct state *s, const char *val) +{ + return addinput(s, &s->ldirs, val); +} + +static int opt(struct state *s, const struct arg *a, const char *arg) +{ + const struct arg *prev = s->arg; + const char *v = arg + strlen(a->s); + + if (prev && prev->v) + { + fprintf(stderr, "%s: option '%s' requires a value\n", s->name, prev->s); + return -1; + } + else if (*v) + { + if (!(a->v & JOINVAL)) + { + fprintf(stderr, "%s: unrecognized command-line option '%s'\n", + s->name, arg); + return -1; + } + } + else if (a->v & SPACEVAL) + { + s->arg = a; + return 0; + } + else + v = NULL; + + return a->f(s, v); +} + +static int addfile(struct state *s, const char *arg) +{ + return addinput(s, &s->sources, arg); +} + +static int arg(struct state *s, const char *arg) +{ + static const struct arg args[] = + { + {"-c", comp}, + {"-d", debug}, + {"-v", verbose}, + {"-O", optim, JOINVAL}, + {"-Wl,", ldflag, JOINVAL}, + {"-o", out, SPACEVAL | JOINVAL}, + {"-I", inc, SPACEVAL | JOINVAL}, + {"-l", lib, SPACEVAL | JOINVAL}, + {"-L", ldir, SPACEVAL | JOINVAL} + }; + + const struct arg *prev = s->arg; + + for (size_t i = 0; i < sizeof args / sizeof *args; i++) + { + const struct arg *a = &args[i]; + + if (!strncmp(arg, a->s, strlen(a->s))) + return opt(s, a, arg); + } + + if (prev) + { + if (*arg != '-' && prev->f(s, arg)) + return -1; + + s->arg = NULL; + } + else + return addfile(s, arg); + + return 0; +} + +static int append(struct dstr *d, const char *fmt, ...) +{ + int ret = 1, n; + char *s; + va_list ap; + size_t len = d->s ? strlen(d->s) : 0; + + va_start(ap, fmt); + + if ((n = vsnprintf(NULL, 0, fmt, ap)) < 0) + { + fprintf(stderr, "vsnprintf(3) failed\n"); + goto end; + } + else if (!(s = realloc(d->s, len + n + 1))) + { + perror("realloc(3)"); + goto end; + } + + va_end(ap); + va_start(ap, fmt); + vsprintf(s + len, fmt, ap); + d->s = s; + ret = 0; + +end: + va_end(ap); + return ret; +} + +static void free_dstr(struct dstr *d) +{ + free(d->s); +} + +static int as(struct state *s, const char *src) +{ + fprintf(stderr, "%s: TODO\n", __func__); + return -1; +} + +static void printcmd(const struct dstr *path, const struct dstr *args) +{ + fprintf(stderr, "%s ", path->s); + + while (args->s) + fprintf(stderr, "%s ", (args++)->s); + + fputc('\n', stderr); +} + +static void printargv(char *const argv[]) +{ + while (*argv) + fprintf(stderr, "%s ", *argv++); + + fputc('\n', stderr); +} + +static int prc1(struct state *s, const char *src) +{ + int ret = -1; + const struct input *incs = &s->incs; + struct procs *srcprocs = &s->srcprocs; + FILE *out = NULL; + struct dstr path = {0}, *args = NULL; + /* mandatory arguments: + * 1: argv[0] + * 2: src + * [-d] + * <includes> + * 3: argv[n] + */ + size_t n = incs->n + 3, npr = srcprocs->n + 1, argi = 0; + struct spawn *proc; + struct proc *sp; + + if (s->debug) + n++; + + if (!(args = malloc(n * sizeof *args))) + { + perror("malloc(3)"); + goto end; + } + + for (size_t i = 0; i < n; i++) + args[i] = (struct dstr){0}; + + if (append(&args[argi++], "prc1") + || append(&args[argi++], src) + || (s->debug && append(&args[argi++], "-d"))) + goto end; + else if (!(sp = realloc(srcprocs->p, npr * sizeof *sp))) + { + perror("realloc(3)"); + goto end; + } + + srcprocs->p = sp; + + if (append(&path, "%s/%s/prc1", s->prefix, s->libexecdir)) + goto end; + + for (size_t i = 0; i < incs->n; i++) + if (append(&args[argi++], "-I%s", incs->v[i])) + goto end; + + if (!(out = tmpfile())) + { + perror("tmpfile(3)"); + goto end; + } + else if (s->verbose) + printcmd(&path, args); + + /* HACK: struct dstr is ultimately syntax sugar for char *. */ + if (!(proc = spawnv(path.s, (char **)args, out))) + goto end; + + sp[srcprocs->n++] = (struct proc){.out = out, .s = proc}; + ret = 0; + +end: + if (args) + for (size_t i = 0; i < n; i++) + free_dstr(&args[i]); + + if (ret && out && fclose(out)) + perror("fclose(3)"); + + free(args); + free_dstr(&path); + return ret; +} + +static int qbe(struct state *s, const char *src) +{ + fprintf(stderr, "%s: TODO\n", __func__); + return -1; +} + +static int runsuffix(struct state *s, const char *src, const char *suffix, + int (*f)(struct state *, const char *)) +{ + const char *sep = strchr(src, '.'); + + if (sep) + return strcmp(++sep, suffix) ? 0 : f(s, src); + + return f(s, src); +} + +static int waitall(const struct procs *pr) +{ + for (size_t i = 0; i < pr->n; i++) + { + const struct proc *p = &pr->p[i]; + + if (spawn_wait(p->s)) + return -1; + /* prepare stream for read. */ + else if (p->out && fseek(p->out, 0, SEEK_SET)) + { + perror("fseek(3)"); + return -1; + } + } + + return 0; +} + +static int runprc1(struct state *s) +{ + const struct input *srcs = &s->sources; + + for (size_t i = 0; i < srcs->n; i++) + if (runsuffix(s, srcs->v[i], "prose", prc1)) + return -1; + + return waitall(&s->srcprocs); +} + +static int redirect(const char *path, FILE *in, struct procs *prs) +{ + FILE *out = NULL; + size_t n = prs->n + 1; + struct spawn *sp = NULL; + struct proc *procs = realloc(prs->p, n * sizeof *procs), *p; + + if (!procs) + { + perror("realloc(3)"); + goto failure; + } + + p = &procs[prs->n++]; + *p = (struct proc){0}; + prs->p = procs; + + if (!(out = tmpfile())) + { + perror("tmpfile(3)"); + goto failure; + } + else if (!(sp = spawni(path, in, out))) + goto failure; + + *p = (struct proc){.s = sp, .out = out}; + return 0; + +failure: + + if (out && fclose(out)) + perror("fclose(3)"); + + return -1; +} + +static int runqbe(struct state *s) +{ + const struct input *srcs = &s->sources; + const struct procs *srcpr = &s->srcprocs; + struct procs *qbepr = &s->qbeprocs; + + for (size_t i = 0; i < srcs->n; i++) + if (runsuffix(s, srcs->v[i], "qbe", qbe)) + return -1; + + for (size_t i = 0; i < srcpr->n; i++) + if (redirect(s->qbe, srcpr->p[i].out, qbepr)) + return -1; + + return waitall(qbepr); +} + +static int dump(const char *path, FILE *in, struct procs *prs) +{ + struct spawn *s = NULL; + struct proc *p, *np; + size_t n = prs->n + 1; + char *argv[4] = {(char *)path, "-o"}, *tmp = malloc(L_tmpnam); + + if (!tmp) + { + perror("malloc(3)"); + goto failure; + } + else if (!(argv[2] = tmpnam(tmp))) + { + fprintf(stderr, "%s: tmpnam(3) failed\n", __func__); + goto failure; + } + else if (!(p = realloc(prs->p, n * sizeof *p))) + { + perror("realloc(3)"); + goto failure; + } + + np = &p[prs->n++]; + *np = (struct proc){0}; + prs->p = p; + + if (!(s = spawnvi(path, argv, in, NULL))) + goto failure; + + *np = (struct proc){.s = s, .path = tmp}; + return 0; + +failure: + free(tmp); + spawn_free(s); + return -1; +} + +static int runas(struct state *s) +{ + const struct input *srcs = &s->sources; + const struct procs *qbepr = &s->qbeprocs; + struct procs *aspr = &s->asprocs; + + for (size_t i = 0; i < srcs->n; i++) + if (runsuffix(s, srcs->v[i], "s", as)) + return -1; + + for (size_t i = 0; i < qbepr->n; i++) + if (dump(s->as, qbepr->p[i].out, aspr)) + return -1; + + return waitall(aspr); +} + +static int isobj(const char *path) +{ + static const char ext[] = ".o"; + const char *src = path, *r = strstr(src, ext); + + return r && strlen(r) == strlen(ext); +} + +static int ld(struct state *s) +{ + int ret = -1; + const struct input *srcs = &s->sources; + const struct procs *asp = &s->asprocs; + const struct ldflags *lf = &s->ldflags; + /* + * argv[0] + * -o + * <out> + * ... + * <ldflags> + * -lc + * argv[n] + */ + size_t n = asp->n + lf->n + 5, i = 0; + struct spawn *sp = NULL; + char **argv = NULL; + + for (size_t i = 0; i < srcs->n; i++) + if (isobj(srcs->v[i])) + n++; + + if (!(argv = malloc(n * sizeof *argv))) + { + perror("malloc(3)"); + goto end; + } + + argv[i++] = (char *)s->ld; + argv[i++] = "-o"; + argv[i++] = (char *)s->out; + + for (size_t j = 0; j < asp->n; j++) + argv[i++] = asp->p[j].path; + + for (size_t j = 0; j < srcs->n; j++) + { + const char *path = srcs->v[j]; + + if (isobj(path)) + argv[i++] = (char *)path; + } + + for (size_t j = 0; j < lf->n; j++) + argv[i++] = (char *)lf->v[j]; + + argv[i++] = "-lc"; + argv[i++] = NULL; + + if (s->verbose) + printargv(argv); + + if (!(sp = spawnv(s->ld, argv, NULL)) || spawn_wait(sp)) + goto end; + + ret = 0; +end: + free(argv); + spawn_free(sp); + return ret; +} + +static int reconcile(struct state *s) +{ + if (runprc1(s) || runqbe(s) || runas(s)) + return -1; + + return s->componly ? 0 : ld(s); +} + +static void free_input(struct input *i) +{ + free(i->v); +} + +static void free_procs(struct procs *v) +{ + for (size_t i = 0; i < v->n; i++) + { + struct proc *p = &v->p[i]; + FILE *out = p->out; + char *path = p->path; + + if (out && fclose(out)) + perror("fclose(3)"); + + if (path && remove(path)) + fprintf(stderr, "%s: remove(3) %s: %s\n", __func__, path, + strerror(errno)); + + free(path); + spawn_free(p->s); + } + + free(v->p); +} + +static void free_state(struct state *s) +{ + free_input(&s->sources); + free_input(&s->incs); + free_input(&s->libs); + free_input(&s->ldirs); + free_procs(&s->srcprocs); + free_procs(&s->qbeprocs); + free_procs(&s->asprocs); + free(s->ldflags.v); +} + +static void setdefaults(struct state *s) +{ + if (!s->ld) + s->ld = "ld"; + + if (!s->as) + s->as = "as"; + + if (!s->qbe) + s->qbe = "qbe"; + + if (!s->prefix) + s->prefix = PREFIX; + + if (!s->libexecdir) + s->libexecdir = LIBEXECDIR; + + s->out = "a.out"; +} + +int main(int argc, char *argv[]) +{ + int ret = EXIT_FAILURE; + struct state s = + { + .name = *argv, + .out = "a.out", + .ld = getenv("LD"), + .as = getenv("AS"), + .qbe = getenv("PRCQBE"), + .prefix = getenv("PRCPREFIX"), + .libexecdir = getenv("PRCLIBEXECDIR") + }; + + if (argc == 1) + { + fprintf(stderr, "%s [options ...] file...\n", *argv); + goto end; + } + + setdefaults(&s); + + for (int i = 1; i < argc; i++) + if (arg(&s, argv[i])) + goto end; + + if (s.arg) + { + fprintf(stderr, "%s: command-line argument '%s' requires an argument\n", + s.name, s.arg->s); + goto end; + } + else if (!s.sources.n) + { + fprintf(stderr, "%s: missing input files\n", __func__); + goto end; + } + else if (reconcile(&s)) + goto end; + + ret = EXIT_SUCCESS; +end: + free_state(&s); + return ret; +} diff --git a/src/prc/prc.h b/src/prc/prc.h new file mode 100644 index 0000000..56f7807 --- /dev/null +++ b/src/prc/prc.h @@ -0,0 +1,12 @@ +#ifndef PRC_H +#define PRC_H + +#include <stdio.h> + +struct spawn *spawni(const char *s, FILE *in, FILE *out); +struct spawn *spawnv(const char *s, char *const argv[], FILE *out); +struct spawn *spawnvi(const char *s, char *const argv[], FILE *in, FILE *out); +int spawn_wait(struct spawn *s); +void spawn_free(struct spawn *s); + +#endif diff --git a/src/prc1/CMakeLists.txt b/src/prc1/CMakeLists.txt index 3579ca6..e2b9ad4 100644 --- a/src/prc1/CMakeLists.txt +++ b/src/prc1/CMakeLists.txt @@ -36,4 +36,5 @@ set_target_properties(prc1 PROPERTIES C_STANDARD 99 C_EXTENSIONS OFF) if(CMAKE_BUILD_TYPE STREQUAL "Debug") target_compile_options(prc1 PRIVATE -Og) endif() -# install(TARGETS prc1) +include(GNUInstallDirs) +install(TARGETS prc1 DESTINATION ${CMAKE_INSTALL_LIBEXECDIR}/${PROJECT_NAME}) diff --git a/src/prc1/kw.c b/src/prc1/kw.c index d5e22ca..c72ac97 100644 --- a/src/prc1/kw.c +++ b/src/prc1/kw.c @@ -18,7 +18,7 @@ static const char *kws[] = /* built-in functions */ "display", "warn", /* flow control */ - "while", "if", "else", "end", "return", "go", "label", "default", + "while", "if", "else", "end", "return", "go", "label", /*"default",*/ /* prepositions */ "by", "from", "to", "than", "of", "etc", /* comparators */ diff --git a/src/prc1/prc1.c b/src/prc1/prc1.c index 69165ba..6378907 100644 --- a/src/prc1/prc1.c +++ b/src/prc1/prc1.c @@ -14,16 +14,19 @@ int main(int argc, char *argv[]) FILE *f = stdin; int opened = 0, ret = EXIT_FAILURE; - if (argc != 2) + for (int i = 1; i < argc; i++) + fprintf(stderr, "[%d]=%s\n", i, argv[i]); + + if (argc < 2) { - fprintf(stderr, "%s: error: missing input file\n", *argv); + fprintf(stderr, "%s [options ...] file\n", *argv); goto end; } else if (strcmp(argv[1], "-")) { if (!(f = fopen(argv[1], "rb"))) { - fprintf(stderr, "failed to open %s: %s\n", argv[1], + fprintf(stderr, "%s: failed to open %s: %s\n", *argv, argv[1], strerror(errno)); goto end; } |
