diff options
author | Dacav <dacav@tilde.institute> | 2020-08-28 11:47:33 +0200 |
---|---|---|
committer | Dacav <dacav@tilde.institute> | 2020-08-28 11:47:33 +0200 |
commit | b71bf6f6a95f49b1d2b53a2f673d5d93836b4fc1 (patch) | |
tree | 678cdb8ffcc855322744f10dc276b87378c09696 | |
download | forg-b71bf6f6a95f49b1d2b53a2f673d5d93836b4fc1.tar.gz |
init
-rw-r--r-- | Makefile | 7 | ||||
-rw-r--r-- | README.txt | 20 | ||||
-rw-r--r-- | filehash.c | 53 | ||||
-rw-r--r-- | filehash.h | 7 | ||||
-rw-r--r-- | forg-import.c | 181 |
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); +} |