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 /forg-import.c | |
download | forg-b71bf6f6a95f49b1d2b53a2f673d5d93836b4fc1.tar.gz |
init
Diffstat (limited to 'forg-import.c')
-rw-r--r-- | forg-import.c | 181 |
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); +} |