about summary refs log tree commit diff stats
path: root/forg-import.c
diff options
context:
space:
mode:
Diffstat (limited to 'forg-import.c')
-rw-r--r--forg-import.c181
1 files changed, 181 insertions, 0 deletions
diff --git a/forg-import.c b/forg-import.c
new file mode 100644
index 0000000..241e3f3
--- /dev/null
+++ b/forg-import.c
@@ -0,0 +1,181 @@
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#define optstr "0h:L:r:"
+
+typedef struct {
+    const char *hash;
+    const char *repository;
+    unsigned hash_max_lines;
+    char **files;
+    unsigned n_files;
+    char input_sep;
+} options_t;
+
+static void usage(const char *prgname)
+{
+    errx(EX_USAGE, "usage %s ...", prgname);
+}
+
+static int str_to_uint(const char *str, unsigned *out)
+{
+    unsigned long result;
+    char *end;
+
+    errno = 0;
+    result = strtoul(str, &end, 10);
+
+    if (errno)
+        goto fail;
+
+    if (*end != '\0') {
+        errno = EINVAL;
+        goto fail;
+    }
+
+    if (result > UINT_MAX) {
+        errno = ERANGE;
+        goto fail;
+    }
+
+    *out = result;
+    return 0;
+
+fail:
+    warn("str_to_uint \"%s\"", str);
+    return -1;
+}
+
+static options_t read_opts(int argc, char **argv)
+{
+    int o;
+    const char *prgname;
+    options_t options = {
+        .hash = "md5sum",
+        .repository = ".",
+        .hash_max_lines = 4096,
+        .input_sep = '\n',
+    };
+
+    prgname = argc < 1 ? "keep" : argv[0];
+
+    while (o = getopt(argc, argv, optstr), o != -1) {
+        switch (o) {
+            case '0':
+                options.input_sep = '\0';
+                break;
+            case 'h':
+                options.hash = optarg;
+                break;
+            case 'L':
+                if (str_to_uint(optarg, &options.hash_max_lines))
+                    goto fail;
+                break;
+            case 'r':
+                options.repository = optarg;
+                break;
+            case '?':
+            default:
+                usage(prgname);
+        }
+    }
+
+    options.files = argv + optind;
+    options.n_files = argc - optind;
+
+    return options;
+
+fail:
+    err(EX_CONFIG, "bad options");
+}
+
+static const char *iter_files(const options_t *opts, void **aux)
+{
+    struct ctx {
+        enum {
+            from_files_list,
+            from_stdin,
+        } from;
+
+        union {
+            struct {
+                int next;
+            } files_list;
+            struct {
+                char *line;
+                size_t len;
+            } stdin;
+        };
+    };
+
+    struct ctx *ctx = *aux;
+    ssize_t nread;
+
+    if (!ctx) { /* initialization */
+
+        *aux = ctx = malloc(sizeof(struct ctx));
+        if (!ctx)
+            err(EX_OSERR, "malloc");
+
+        *ctx = (struct ctx){
+            opts->n_files ? from_files_list : from_stdin
+        };
+    }
+
+    switch (ctx->from) {
+        case from_stdin:
+            errno = 0;
+            nread = getdelim(
+                &ctx->stdin.line,
+                &ctx->stdin.len,
+                opts->input_sep,
+                stdin
+            );
+            if (nread == -1) {
+                if (!errno) {
+                    free(ctx->stdin.line);
+                    goto done;
+                }
+                err(EX_OSERR, "getdelim(..., stdin)");
+            }
+            if (opts->input_sep && ctx->stdin.line[nread - 1] == opts->input_sep)
+                ctx->stdin.line[nread - 1] = '\0';
+            return ctx->stdin.line;
+
+        case from_files_list:
+            if (ctx->files_list.next >= opts->n_files)
+                goto done;
+            return opts->files[ctx->files_list.next++];
+    }
+    err(EX_SOFTWARE, __FILE__":%d - buggy", __LINE__);
+
+done:
+    free(*aux);
+    *aux = NULL;
+    return NULL;
+}
+
+static int run(const options_t *opts)
+{
+    void *aux = NULL;
+    const char *file;
+
+    while (file = iter_files(opts, &aux), file)
+        warnx("[%s]", file);
+
+    return 0;
+}
+
+int main(int argc, char **argv)
+{
+    options_t opts;
+
+    opts = read_opts(argc, argv);
+    return run(&opts);
+}