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 #include <unistd.h>
7
8 #define DEFAULT_DICT_PATH "/usr/share/dict/linux.words"
9 #define STARTING_SZ 8192
10 #define MAX_ARRAY_SZ 1073741824
11
12 struct dict {
13         int num_words;
14         int array_sz;
15         char *words[0];
16 };
17
18 static int alloc_dict(struct dict **d, int array_sz)
19 {
20         struct dict *nd;
21         int num_bytes;
22         if (array_sz > MAX_ARRAY_SZ) {
23                 fprintf(stderr, "can't allocate a words array bigger "
24                         "than %d\n", MAX_ARRAY_SZ);
25                 return EINVAL;
26         }
27
28         num_bytes = sizeof(struct dict) + (array_sz * sizeof(char*));
29         nd = realloc(*d, num_bytes);
30         if (!nd) {
31                 fprintf(stderr, "failed to realloc to size %d\n",
32                         num_bytes);
33                 return ENOSPC;
34         }
35         *d = nd;
36
37         nd->array_sz = array_sz;
38         return 0;
39 }
40
41 static struct dict* read_dict(FILE *fp)
42 {
43         char word[500];
44         struct dict *d =
45                 malloc(sizeof(struct dict) + (STARTING_SZ * sizeof(char*)));
46         if (! d) {
47                 fprintf(stderr, "failed to allocate dict\n");
48                 return NULL;
49         }
50         d->num_words = 0;
51         d->array_sz = STARTING_SZ;
52
53         while (1) {
54                 if (! fgets(word, sizeof(word), fp)) {
55                         if (ferror(fp)) {
56                                 int err = errno;
57                                 fprintf(stderr, "%s: error reading line %d: "
58                                         "%s (%d)\n",
59                                         __func__, d->num_words + 1,
60                                         strerror(err), err);
61                         }
62                         else {
63                                 return d;
64                         }
65                 }
66                 d->words[d->num_words] = strdup(word);
67                 if (!d->words[d->num_words]) {
68                         fprintf(stderr, "failed to allocate word %d\n",
69                                 d->num_words);
70                         free(d);
71                         return NULL;
72                 }
73                 d->num_words++;
74                 if (d->num_words >= d->array_sz) {
75                         if (alloc_dict(&d, d->array_sz * 2)) {
76                                 free(d);
77                                 return NULL;
78                         }
79                 }
80         }
81 }
82
83 static const char* choose_random_word(struct dict *dict)
84 {
85         int choice = random() % dict->num_words;
86
87         return dict->words[choice];
88 }
89
90 static void print_usage(char *program_name)
91 {
92         printf("%s: prints a random word from a linebreak-delimited file.\n",
93                program_name);
94         printf("options:\n");
95         printf("\t-d: the dictionary file to use.  Default: %s.\n",
96                DEFAULT_DICT_PATH);
97         printf("\t-h: this help message.\n");
98 }
99
100 static void parse_args(char **argv, int argc, char **dict_path)
101 {
102         char c;
103         *dict_path = DEFAULT_DICT_PATH;
104         opterr = 0;
105         while ((c = getopt(argc, argv, "d:h")) != -1) {
106                 switch (c) {
107                 case 'd':
108                         *dict_path = optarg;
109                         break;
110                 case 'h':
111                         print_usage(argv[0]);
112                         exit(0);
113                         break;
114                 default:
115                         fprintf (stderr, "Argument parsing error.\n");
116                         exit(1);
117                 }
118         }
119 }
120
121 int main(int argc, char **argv)
122 {
123         FILE *fp;
124         char *dict_path;
125         const char *word;
126         struct dict *dict;
127         struct timeval tv;
128
129         parse_args(argv, argc, &dict_path);
130         gettimeofday(&tv, NULL);
131         srandom(tv.tv_usec * tv.tv_sec);
132
133         fp = fopen(dict_path, "r");
134         if (! fp) {
135                 int err = errno;
136                 fprintf(stderr, "failed to open %s: %s (%d).\n",
137                         dict_path, strerror(err), err);
138                 return 1;
139         }
140         dict = read_dict(fp);
141         if (! dict)
142                 return 1;
143         fclose(fp);
144
145         word = choose_random_word(dict);
146         fputs(word, stdout); 
147
148         return 0;
149 }