Makefile: add pickrand
[cmccabe-bin] / random-word.c
1 #include <errno.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <sys/time.h>
6
7 #define DICT "/usr/share/dict/linux.words"
8 #define STARTING_SZ 8192
9 #define MAX_ARRAY_SZ 1073741824
10
11 struct dict {
12         int num_words;
13         int array_sz;
14         char *words[0];
15 };
16
17 static int alloc_dict(struct dict **d, int array_sz)
18 {
19         struct dict *nd;
20         int num_bytes;
21         if (array_sz > MAX_ARRAY_SZ) {
22                 fprintf(stderr, "can't allocate a words array bigger "
23                         "than %d\n", MAX_ARRAY_SZ);
24                 return EINVAL;
25         }
26
27         num_bytes = sizeof(struct dict) + (array_sz * sizeof(char*));
28         nd = realloc(*d, num_bytes);
29         if (!nd) {
30                 fprintf(stderr, "failed to realloc to size %d\n",
31                         num_bytes);
32                 return ENOSPC;
33         }
34         *d = nd;
35
36         nd->array_sz = array_sz;
37         return 0;
38 }
39
40 static struct dict* read_dict(FILE *fp)
41 {
42         char word[500];
43         struct dict *d =
44                 malloc(sizeof(struct dict) + (STARTING_SZ * sizeof(char*)));
45         if (! d) {
46                 fprintf(stderr, "failed to allocate dict\n");
47                 return NULL;
48         }
49         d->num_words = 0;
50         d->array_sz = STARTING_SZ;
51
52         while (1) {
53                 if (! fgets(word, sizeof(word), fp)) {
54                         if (ferror(fp)) {
55                                 int err = errno;
56                                 fprintf(stderr, "%s: error reading line %d: "
57                                         "%s (%d)\n",
58                                         __func__, d->num_words + 1,
59                                         strerror(err), err);
60                         }
61                         else {
62                                 return d;
63                         }
64                 }
65                 d->words[d->num_words] = strdup(word);
66                 if (!d->words[d->num_words]) {
67                         fprintf(stderr, "failed to allocate word %d\n",
68                                 d->num_words);
69                         free(d);
70                         return NULL;
71                 }
72                 d->num_words++;
73                 if (d->num_words >= d->array_sz) {
74                         if (alloc_dict(&d, d->array_sz * 2)) {
75                                 free(d);
76                                 return NULL;
77                         }
78                 }
79         }
80 }
81
82 static const char* choose_random_word(struct dict *dict)
83 {
84         int choice = random() % dict->num_words;
85
86         return dict->words[choice];
87 }
88
89 int main(void)
90 {
91         FILE *fp;
92         const char *word;
93         struct dict *dict;
94         struct timeval tv;
95
96         gettimeofday(&tv, NULL);
97         srandom(tv.tv_usec * tv.tv_sec);
98
99         fp = fopen(DICT, "r");
100         if (! fp) {
101                 int err = errno;
102                 fprintf(stderr, "failed to open %s: %s (%d).\n",
103                         DICT, strerror(err), err);
104                 return 1;
105         }
106         dict = read_dict(fp);
107         if (! dict)
108                 return 1;
109         fclose(fp);
110
111         word = choose_random_word(dict);
112         fputs(word, stdout); 
113
114         return 0;
115 }