/* Random selector
*
* Dylan Simon
*/
#include
#include
#include
#include
#include
#include
static char *Progname = NULL;
static unsigned Count = 1;
static FILE *In = NULL;
static char **Args = NULL;
static int Argc = 0;
static const struct argp_option options[] = {
{NULL, 'n', "NUM", 0, "Pick NUM items [1]", 0},
{}
};
error_t parse(int, char *, struct argp_state *);
static const struct argp parser = {
.options = options,
.parser = &parse,
.args_doc = "[FILE]\nITEM...",
.doc = "Pick items randomly from a list.\vWith more than one argument, choose from ITEMs. Otherwise, choose lines from FILE (or stdin). A NUM of 0 means choose as many as there are (i.e., shuffle only)."
};
static void *check(void *p)
{
if (!p)
{
fprintf(stderr, "%s: %m\n", Progname);
exit(3);
}
return p;
}
error_t parse(int key, char *optarg, struct argp_state *state)
{
char *e;
switch (key) {
case 'n':
Count = strtoul(optarg, &e, 0);
if (*e || !*optarg)
argp_error(state, "invalid item count: %s", optarg);
break;
case 's':
srand(atoi(optarg));
break;
case ARGP_KEY_ARGS:
if (state->argc - state->next > 1)
{
Args = &state->argv[state->next];
Argc = state->argc - state->next;
}
else
{
In = fopen(state->argv[state->next], "r");
if (!In)
argp_failure(state, 1, errno, "%s", state->argv[state->next]);
}
break;
case ARGP_KEY_NO_ARGS:
In = stdin;
break;
default:
return ARGP_ERR_UNKNOWN;
}
return 0;
}
static char *next(size_t *len)
{
if (Argc)
{
Argc --;
*len = strlen(Args[0]);
Args[0][(*len)++] = '\n';
return *Args++;
}
if (In)
{
static char *buf = NULL;
static size_t buflen = 0;
ssize_t r = getline(&buf, &buflen, In);
if (r != -1)
{
*len = r;
return buf;
}
if (ferror(In))
fprintf(stderr, "%s: %m\n", Progname);
In = NULL;
}
return NULL;
}
static struct item {
size_t len;
char buf[];
} **Item = NULL;
static unsigned Total = 0;
static void keep(unsigned n, char *buf, size_t len)
{
unsigned l = Count;
if (!l)
{
static unsigned items = 0;
if (items < Total)
{
items = 2*Total;
Item = check(realloc(Item, items*sizeof(*Item)));
}
l = Total;
}
else if (n >= l)
return;
else if (Item[l-1])
free(Item[l-1]);
memmove(&Item[n+1], &Item[n], (l-1-n)*sizeof(*Item));
struct item *i = check(malloc(sizeof(struct item) + len));
i->len = len;
memcpy(i->buf, buf, len);
Item[n] = i;
}
static unsigned outof(unsigned n)
{
return rand() / (RAND_MAX/n);
}
int main(int argc, char **argv)
{
Progname = argv[0];
srand(getpid() ^ time(NULL));
if ((errno = argp_parse(&parser, argc, argv, 0, 0, 0)))
{
fprintf(stderr, "%s: argp: %m\n", Progname);
exit(1);
}
Item = check(calloc(Count, sizeof(*Item)));
char *buf;
size_t len;
while ((buf = next(&len)))
keep(outof(++Total), buf, len);
unsigned n;
for (n = 0; n < (Count ?: Total); n++)
if (Item[n])
fwrite(Item[n]->buf, 1, Item[n]->len, stdout);
exit(0);
}