about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorDacav <dacav@tilde.institute>2020-08-28 11:47:33 +0200
committerDacav <dacav@tilde.institute>2020-08-28 11:47:33 +0200
commitb71bf6f6a95f49b1d2b53a2f673d5d93836b4fc1 (patch)
tree678cdb8ffcc855322744f10dc276b87378c09696
downloadforg-b71bf6f6a95f49b1d2b53a2f673d5d93836b4fc1.tar.gz
init
-rw-r--r--Makefile7
-rw-r--r--README.txt20
-rw-r--r--filehash.c53
-rw-r--r--filehash.h7
-rw-r--r--forg-import.c181
5 files changed, 268 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..50e1624
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,7 @@
+forg-import: \
+	forg-import.o \
+	filehash.o
+
+.PHONY : clean
+clean:
+	rm -f *.o forg-import
diff --git a/README.txt b/README.txt
new file mode 100644
index 0000000..8751397
--- /dev/null
+++ b/README.txt
@@ -0,0 +1,20 @@
+keep - Maintain a collection of files
+
+----------------------------------------------------------------------
+
+SYNOPSIS
+
+keep import -r repository [opts] file [file...]
+find path -print0 | keep import -r repository [opts] -
+
+DESCRIPTION
+
+
+
+----------------------------------------------------------------------
+
+SYNOPSIS
+
+keep organize -r repository [options]
+
+DESCRIPTION
diff --git a/filehash.c b/filehash.c
new file mode 100644
index 0000000..5616226
--- /dev/null
+++ b/filehash.c
@@ -0,0 +1,53 @@
+#include "filehash.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+struct filehash_t
+{
+    const char *hash_program;
+    const char **argv;
+    unsigned max_per_invocation;
+};
+
+filehash_t *filehash_new(const char *hash_program, unsigned max_per_invocation)
+{
+    filehash_t *fh;
+
+    fh = malloc(sizeof(filehash_t));
+    if (!fh)
+        return NULL;
+
+    *fh = (filehash_t){
+        .hash_program = strdup(hash_program),
+        .max_per_invocation = max_per_invocation,
+        .argv = reallocarray(
+            NULL,
+            1 + max_per_invocation,
+            sizeof(const char *)
+        ),
+    };
+
+    if (!fh->hash_program)
+        goto fail;
+    if (!fh->argv)
+        goto fail;
+
+    fh->argv[max_per_invocation] = NULL;
+
+    return fh;
+
+fail:
+    filehash_free(fh);
+    return NULL;
+}
+
+void filehash_free(filehash_t *fh)
+{
+    if (!fh)
+        return;
+
+    free(fh->argv);
+    free((void *)fh->hash_program);
+    free(fh);
+}
diff --git a/filehash.h b/filehash.h
new file mode 100644
index 0000000..4f99b0e
--- /dev/null
+++ b/filehash.h
@@ -0,0 +1,7 @@
+#pragma once
+
+typedef struct filehash_t filehash_t;
+
+filehash_t *filehash_new(const char *hash_program, unsigned max_per_invocation);
+
+void filehash_free(filehash_t *);
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);
+}