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