/* 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); }