about summary refs log blame commit diff stats
path: root/forg-import.c
blob: 241e3f3ffcbc923318c7ae1a0d08b258451cb7f2 (plain) (tree)




















































































































































































                                                                                 
#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);
}