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