1 /* worgen - generates word combinations out of one to three word lists.
2 * A helper utility for the eschalot tor names generator. */
3
4 /*
5 * Copyright (c) 2013 Unperson Hiro <blacksunhq56imku.onion>
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20 #include <ctype.h>
21 #include <getopt.h>
22 #include <inttypes.h>
23 #include <stdarg.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
28
29 #define VERSION "1.2.0"
30
31 /* constants */
32 #define MAX_WORDS 0xFFFFFFFFu
33 #define MAX_LEN 16
34 #define BASE32_ALPHABET "abcdefghijklmnopqrstuvwxyz234567"
35
36 extern char *__progname;
37
38 struct f {
39 unsigned int minlen;
40 unsigned int maxlen;
41 unsigned int wordcount;
42 char fn[FILENAME_MAX + 1];
43 char **words;
44 };
45
46 void readfile(struct f *);
47 void usage(void);
48 void error(char *, ...);
49 void verbose(char *, ...);
50 void normal(char *, ...);
51 void (*msg)(char *, ...) = verbose;
52 size_t mystrnlen(const char *, size_t);
53
54 int
main(int argc,char * argv[])55 main(int argc, char *argv[])
56 {
57 struct f w[4];
58 signed int lists = -1;
59 unsigned int i, j, k, len1, len2, len3;
60
61 /* Arguments processing */
62 if (argc != 4 && argc != 6 && argc != 8)
63 usage();
64
65 for (i = 0; i < (unsigned int)argc; i += 2) {
66 j = i / 2;
67 strncpy(w[j].fn, argv[i], FILENAME_MAX);
68 w[j].minlen = strtoul(argv[i + 1], NULL, 0);
69 w[j].maxlen = strtoul(strchr(argv[i + 1], '-') + 1, NULL, 0);
70 w[j].wordcount = 0;
71 w[j].words = NULL;
72 lists++;
73 if (i == 0)
74 msg("Will be producing %d-%d character long word combinations.\n",
75 w[j].minlen, w[j].maxlen, w[j].fn);
76 else
77 msg("Reading %d-%d characters words from %s.\n",
78 w[j].minlen, w[j].maxlen, w[j].fn);
79
80 /* Basic sanity checks */
81 if (!w[j].minlen || !w[j].maxlen || w[j].maxlen > MAX_LEN)
82 usage();
83 }
84
85 /* Read words from files */
86 for (i = 1; i <= (unsigned int)lists; i++) {
87 readfile(&w[i]);
88 }
89
90 /* Mix it up */
91 for (i = 0; i < w[1].wordcount; i++) {
92 fprintf(stderr, "\rWorking. %d%% complete, %d words (approximately %dMb) produced.",
93 (i + 1) * 100 / w[1].wordcount, w[0].wordcount,
94 (w[0].wordcount /1024 / 1024 * ((w[0].minlen + w[0].maxlen) / 2 + 1)));
95 fflush(stderr);
96
97 len1 = mystrnlen(w[1].words[i], MAX_LEN);
98 if (len1 < w[1].minlen || len1 > w[1].maxlen)
99 continue;
100
101 if (len1 >= w[0].minlen && len1 <= w[0].maxlen) {
102 printf("%s\n", w[1].words[i]);
103 w[0].wordcount++;
104 }
105
106 for (j = 0; j < w[2].wordcount && lists > 1; j++) {
107 len2 = mystrnlen(w[2].words[j], MAX_LEN);
108 if (len2 < w[2].minlen || len2 > w[2].maxlen)
109 continue;
110
111 if (len1 + len2 >= w[0].minlen && len1 + len2 <= w[0].maxlen) {
112 printf("%s%s\n", w[1].words[i], w[2].words[j]);
113 w[0].wordcount++;
114 }
115
116 for (k = 0; k < w[3].wordcount && lists > 2; k++) {
117 len3 = mystrnlen(w[3].words[k], MAX_LEN);
118 if (len3 < w[3].minlen || len3 > w[3].maxlen)
119 continue;
120
121 if (len1 + len2 + len3 >= w[0].minlen && len1 + len2 + len3 <= w[0].maxlen) {
122 printf("%s%s%s\n", w[1].words[i], w[2].words[j], w[3].words[k]);
123 w[0].wordcount++;
124 }
125 }
126 }
127 }
128 msg("\nFinal count: %d word combinations.\n", w[0].wordcount);
129 }
130
131 void
usage(void)132 usage(void)
133 {
134 fprintf(stderr,
135 "Version: %s\n"
136 "\n"
137 "usage: %s min-max filename1 min1-max1 [filename2 min2-max2 [filename3 min3-max3]]\n"
138 " min-max : length limits for the output strings\n"
139 " filename1 : name of the first word list file (required)\n"
140 " min1-max1 : length limits for the words from the first file\n"
141 " filename2 : name of the second word list file (optional)\n"
142 " min2-max2 : length limits for the words from the first file\n"
143 " filename3 : name of the third word list file (optional)\n"
144 " min3-max3 : length limits for the words from the first file\n\n"
145 " Example: %s 8-12 wordlist1.txt 5-10 wordlist2.txt 3-5 > results.txt\n\n"
146 " Generates word combinations from 8 to 12 characters long\n"
147 " using 5-10 character long words from 'wordlist1.txt'\n"
148 " followed by 3-5 character long words from 'wordlist2.txt'.\n"
149 " Saves the results to 'results.txt'.\n",
150 VERSION, __progname, __progname);
151 exit(1);
152 }
153
154 /* Read words from file */
155 void
readfile(struct f * list)156 readfile(struct f *list)
157 {
158 FILE *file;
159 signed int c;
160 char word[MAX_LEN + 1];
161 unsigned int len, i, j = 0;
162 _Bool inword;
163
164 if ((file = fopen(list->fn, "r")) == NULL)
165 error("Failed to open %s!\n", list->fn);
166
167 msg("Loading words from %s.\n", list->fn);
168
169 while ((c = fgetc(file)) != EOF) {
170 c = tolower(c);
171 inword = 0;
172 for (i = 0; i < 32; i++) {
173 if (c == BASE32_ALPHABET[i]) {
174 word[j++] = c;
175 inword = 1;
176 break;
177 }
178 }
179
180 if ((!inword && j > 0) || j > MAX_LEN) {
181 word[j] = '\0';
182 j = 0;
183 len = mystrnlen(word, MAX_LEN);
184 if (len >= list->minlen &&
185 len <= list->maxlen &&
186 list->wordcount < MAX_WORDS) {
187 if ((list->words =
188 (char **)realloc(list->words,
189 (list->wordcount + 1) * sizeof(char *))) == NULL)
190 error("realloc() failed!\n");
191
192 if ((list->words[list->wordcount] =
193 (char *)malloc(mystrnlen(word, MAX_LEN) + 1)) == NULL)
194 error("malloc() failed!\n");
195
196 strncpy(list->words[list->wordcount], word, mystrnlen(word, MAX_LEN) + 1);
197 list->wordcount++;
198 }
199 }
200 }
201 fclose(file);
202 if (list->wordcount == 0)
203 error("Could not find any valid words!\n");
204 else
205 msg("Loaded %d words from %s.\n", list->wordcount, list->fn);
206 }
207
208 /* Print error message and exit. Not using the err/errx,
209 * since not all Linuxes implement them properly. */
210 void
error(char * message,...)211 error(char *message, ...)
212 {
213 va_list ap;
214
215 va_start(ap, message);
216 fprintf(stderr, "ERROR: ");
217 vfprintf(stderr, message, ap);
218 va_end(ap);
219 fflush(stderr);
220 exit(1);
221 }
222
223 /* Spam STDERR with diagnostic messages... */
224 void
verbose(char * message,...)225 verbose(char *message, ...) {
226 va_list ap;
227 va_start(ap, message);
228 vfprintf(stderr, message, ap);
229 va_end(ap);
230 fflush(stderr);
231 }
232
233 /* ...or be a very quiet thinker. */
234 void
normal(char * notused,...)235 normal(__attribute__((unused)) char *notused, ...) {
236 }
237
238 /* Local version of strnlen function for older OSes. */
239 size_t
mystrnlen(const char * s,size_t ml)240 mystrnlen(const char *s, size_t ml)
241 {
242 unsigned int i;
243 for (i = 0; *(s + i) != '\0' && i < ml; i++)
244 ;
245 return i;
246
247 }
248
249