diff options
Diffstat (limited to 'gpp.c')
-rw-r--r-- | gpp.c | 245 |
1 files changed, 245 insertions, 0 deletions
@@ -0,0 +1,245 @@ +/* See LICENSE file for copyright and license details. */ +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "arg.h" + + +char *argv0; + + +static void +usage(void) +{ + fprintf(stderr, "usage: %s [-D name[=value]] [-f file | [-i input-file] [-o output-file]] " + "[-n count] [-s symbol] [-u [-u]] [shell [argument] ...]\n", argv0); + exit(1); +} + + +static int +get_fd(const char *path) +{ + const char *p; + char *s; + long int tmp; + + if (!strcmp(path, "/dev/stdin")) + return STDIN_FILENO; + if (!strcmp(path, "/dev/stdout")) + return STDOUT_FILENO; + if (!strcmp(path, "/dev/stderr")) + return STDERR_FILENO; + + if (!strncmp(path, "/dev/fd/", sizeof("/dev/fd/") - 1)) + p = &path[sizeof("/dev/fd/") - 1]; + else if (!strncmp(path, "/proc/self/fd/", sizeof("/proc/self/fd/") - 1)) + p = &path[sizeof("/proc/self/fd/") - 1]; + else + return -1; + + if (!isdigit(*p)) + return -1; + errno = 0; + tmp = strtol(p, &s, 10); + if (errno || tmp > INT_MAX || *s) + return -1; + return (int)tmp; +} + + +static int +xopen(const char *path, int *do_close) +{ + int fd = get_fd(path); + if (fd >= 0) { + *do_close = 0; + return fd; + } + fd = open(path, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "%s: open %s O_RDONLY: %s\n", argv0, path, strerror(errno)); + exit(1); + } + *do_close = 1; + return fd; +} + + +static int +xcreate(const char *path) +{ + int fd = get_fd(path); + if (fd >= 0) + return fd; + fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (fd < 0) { + fprintf(stderr, "%s: open %s O_WRONLY|O_CREAT|O_TRUNC 0666: %s\n", argv0, path, strerror(errno)); + exit(1); + } + return fd; +} + + +int +main(int argc, char *argv[]) +{ + const char *shell_[] = {"bash", NULL}, **shell = shell_; + const char *input_file = NULL; + const char *output_file = NULL; + const char *symbol = NULL; + int iterations = -1; + int unshebang = 0; + long int tmp; + char *arg, *p, *q; + char *in_data = NULL, *out_data = NULL; + size_t in_size = 0, in_len = 0, in_off = 0; + size_t out_size = 0, out_len = 0, out_off = 0; + int in_fd, out_fd, do_close; + ssize_t r; + + ARGBEGIN { + case 'D': + arg = EARGF(usage()); + if (!*arg || *arg == '=') + usage(); + p = strchr(arg, '='); + if (p) + *p++ = '\0'; + if (setenv(arg, p ? p : "1", 1)) { + fprintf(stderr, "%s: setenv %s %s 1: %s\n", argv0, arg, p ? p : "1", strerror(errno)); + return 1; + } + break; + case 'f': + if (input_file || output_file) + usage(); + input_file = output_file = EARGF(usage()); + if (!*input_file) + usage(); + break; + case 'i': + if (input_file) + usage(); + input_file = EARGF(usage()); + if (!*input_file) + usage(); + break; + case 'n': + if (iterations >= 0) + usage(); + arg = EARGF(usage()); + if (!isdigit(*arg)) + usage(); + errno = 0; + tmp = strtol(arg, &arg, 10); + if (errno || tmp > INT_MAX || *arg) + usage(); + iterations = (int)tmp; + break; + case 'o': + if (output_file) + usage(); + output_file = EARGF(usage()); + if (!*output_file) + usage(); + break; + case 's': + if (symbol) + usage(); + symbol = EARGF(usage()); + break; + case 'u': + if (unshebang == 2) + usage(); + unshebang += 1; + break; + default: + usage(); + } ARGEND; + + if (argc) + shell = (void *)argv; + + if (setenv("_GPP", argv0, 1)) + fprintf(stderr, "%s: setenv _GPP %s 1: %s\n", argv0, argv0, strerror(errno)); + + if (iterations < 0) + iterations = 1; + if (!symbol) + symbol = "@"; + if (!input_file || (input_file[0] == '-' && !input_file[1])) + input_file = "/dev/stdin"; + if (!output_file || (output_file[0] == '-' && !output_file[1])) + output_file = "/dev/stdout"; + + in_fd = xopen(input_file, &do_close); + for (;;) { + if (in_len == in_size) { + in_data = realloc(in_data, in_size += 8096); + if (!in_data) { + fprintf(stderr, "%s: realloc: %s\n", argv0, strerror(errno)); + return 1; + } + } + r = read(in_fd, &in_data[in_len], in_size - in_len); + if (r <= 0) { + if (!r) + break; + fprintf(stderr, "%s: read %s: %s\n", argv0, input_file, strerror(errno)); + } + in_len += (size_t)r; + } + if (do_close) + close(in_fd); + + if (unshebang && in_len >= 2 && in_data[0] == '#' && in_data[1] == '!') { + p = memchr(in_data, '\n', in_len); + if (!p) + goto after_unshebang; + in_off = (size_t)(++p - in_data); + if (in_off == in_len) + goto after_unshebang; + if (unshebang >= 2) { + q = memchr(p--, '\n', in_len - in_off); + if (!q) + goto after_unshebang; + memmove(p, &p[1], (size_t)(q - in_data) - in_off--); + *--q = '\n'; + } + } +after_unshebang: + + while (iterations--) { + /* TODO parse: in -> out */ + + in_len = 0; + in_off = 0; + + /* TODO shell: out -> in */ + + out_len = 0; + out_off = 0; + } + + free(out_data); + out_fd = xcreate(output_file); + while (in_off < in_len) { + r = write(out_fd, &in_data[in_off], in_len - in_off); + if (r <= 0) { + fprintf(stderr, "%s: write %s: %s\n", argv0, output_file, strerror(errno)); + return 1; + } + in_off += (size_t)r; + } + if (close(out_fd)) + fprintf(stderr, "%s: write %s: %s\n", argv0, output_file, strerror(errno)); + free(in_data); + return 0; +} |