--- /dev/null
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+#define DEFAULT_DICT_PATH "/usr/share/dict/linux.words"
+#define STARTING_SZ 8192
+#define MAX_ARRAY_SZ 1073741824
+
+struct dict {
+ int num_words;
+ int array_sz;
+ char *words[0];
+};
+
+static int alloc_dict(struct dict **d, int array_sz)
+{
+ struct dict *nd;
+ int num_bytes;
+ if (array_sz > MAX_ARRAY_SZ) {
+ fprintf(stderr, "can't allocate a words array bigger "
+ "than %d\n", MAX_ARRAY_SZ);
+ return EINVAL;
+ }
+
+ num_bytes = sizeof(struct dict) + (array_sz * sizeof(char*));
+ nd = realloc(*d, num_bytes);
+ if (!nd) {
+ fprintf(stderr, "failed to realloc to size %d\n",
+ num_bytes);
+ return ENOSPC;
+ }
+ *d = nd;
+
+ nd->array_sz = array_sz;
+ return 0;
+}
+
+static struct dict* read_dict(FILE *fp)
+{
+ char word[500];
+ struct dict *d =
+ malloc(sizeof(struct dict) + (STARTING_SZ * sizeof(char*)));
+ if (! d) {
+ fprintf(stderr, "failed to allocate dict\n");
+ return NULL;
+ }
+ d->num_words = 0;
+ d->array_sz = STARTING_SZ;
+
+ while (1) {
+ if (! fgets(word, sizeof(word), fp)) {
+ if (ferror(fp)) {
+ int err = errno;
+ fprintf(stderr, "%s: error reading line %d: "
+ "%s (%d)\n",
+ __func__, d->num_words + 1,
+ strerror(err), err);
+ }
+ else {
+ return d;
+ }
+ }
+ d->words[d->num_words] = strdup(word);
+ if (!d->words[d->num_words]) {
+ fprintf(stderr, "failed to allocate word %d\n",
+ d->num_words);
+ free(d);
+ return NULL;
+ }
+ d->num_words++;
+ if (d->num_words >= d->array_sz) {
+ if (alloc_dict(&d, d->array_sz * 2)) {
+ free(d);
+ return NULL;
+ }
+ }
+ }
+}
+
+static const char* choose_random_word(struct dict *dict)
+{
+ int choice = random() % dict->num_words;
+
+ return dict->words[choice];
+}
+
+static void print_usage(char *program_name)
+{
+ printf("%s: prints a random word from a linebreak-delimited file.\n",
+ program_name);
+ printf("options:\n");
+ printf("\t-d: the dictionary file to use. Default: %s.\n",
+ DEFAULT_DICT_PATH);
+ printf("\t-h: this help message.\n");
+}
+
+static void parse_args(char **argv, int argc, char **dict_path)
+{
+ char c;
+ *dict_path = DEFAULT_DICT_PATH;
+ opterr = 0;
+ while ((c = getopt(argc, argv, "d:h")) != -1) {
+ switch (c) {
+ case 'd':
+ *dict_path = optarg;
+ break;
+ case 'h':
+ print_usage(argv[0]);
+ exit(0);
+ break;
+ default:
+ fprintf (stderr, "Argument parsing error.\n");
+ exit(1);
+ }
+ }
+}
+
+int main(int argc, char **argv)
+{
+ FILE *fp;
+ char *dict_path;
+ const char *word;
+ struct dict *dict;
+ struct timeval tv;
+
+ parse_args(argv, argc, &dict_path);
+ gettimeofday(&tv, NULL);
+ srandom(tv.tv_usec * tv.tv_sec);
+
+ fp = fopen(dict_path, "r");
+ if (! fp) {
+ int err = errno;
+ fprintf(stderr, "failed to open %s: %s (%d).\n",
+ dict_path, strerror(err), err);
+ return 1;
+ }
+ dict = read_dict(fp);
+ if (! dict)
+ return 1;
+ fclose(fp);
+
+ word = choose_random_word(dict);
+ fputs(word, stdout);
+
+ return 0;
+}