1 /* an - Anagram generator
2 Copyright (C) 2012 Paul Martin <pm@debian.org>
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License along
15 with this program; if not, write to the Free Software Foundation, Inc.,
16 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18
19 #include <unicode/ustring.h>
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include <stdbool.h>
23 #include <string.h>
24 #include <limits.h>
25 #include <getopt.h>
26 #include "bitfield.h"
27 #include "unicode.h"
28 #include "words.h"
29 #include "malloc.h"
30 #include "an.h"
31
32 static void
find_anagrams(struct wordlist * words,struct wordlist * stack,struct bitfield * bits,int maxwords,int length,int * maxtotal)33 find_anagrams(struct wordlist *words, struct wordlist *stack,
34 struct bitfield *bits, int maxwords, int length, int *maxtotal)
35 {
36 struct bitfield *remaining_bits = NULL;
37 struct wordlist *w = words;
38
39 if (maxwords <= 0)
40 return;
41
42 if (*maxtotal <= 0)
43 return;
44
45 /* printf("word depth = %d, length = %d\n", INT_MAX-maxwords, length); */
46
47 while (w != NULL) {
48 if (length >= w->word->length) {
49 if (bf_contains(bits, w->word->bits)) {
50 int newlength = length - w->word->length;
51
52 /* printf(" considering %s\n", w->word->utf8_form); */
53 stack = push_wordstack(stack, w->word);
54 if (newlength > 0) {
55 remaining_bits = bf_subtract(bits, w->word->bits);
56 find_anagrams(w, stack, remaining_bits,
57 maxwords - 1, newlength, maxtotal);
58 free_bitfield(remaining_bits);
59 } else {
60 if (*maxtotal > 0) {
61 print_wordstack(stack);
62 fputs("\n", stdout);
63 (*maxtotal)--;
64 }
65 }
66 stack = pop_wordstack(stack);
67 }
68 }
69 w = w->next;
70 }
71 }
72
73
74 static void
print_help(const char * progname)75 print_help(const char *progname)
76 {
77 const char *helptext[] = {
78 "-c, --contain PHRASE print anagrams containing PHRASE",
79 "-d, --dict DICTIONARY search DICTIONARY for words",
80 "-l, --length WORDS find anagrams with up to WORDS number of words",
81 "-m, --minimum WORDLEN only use words at least WORDLEN long",
82 "-n, --number NUM print a maximum of NUM anagrams",
83 "-w, --words print words that PHRASE letters make",
84 "-t, --test ANAG test if ANAG can be made with PHRASE",
85 "-u, --used PHRASE flag PHRASE letters as already used",
86 "-h, --help display this help and exit",
87 "-v, --version output version information and exit",
88 NULL
89 };
90 const char **h = helptext;
91
92 printf("Usage: %s [options] PHRASE\n\n", progname);
93 while (*h) {
94 printf(" %s\n", *h);
95 h++;
96 }
97 }
98
99 int
main(int argc,char ** argv)100 main(int argc, char **argv)
101 {
102 char *dictionary = NULL;
103 char *contains = NULL;
104 struct word phrase, contain;
105 struct bitfield *temp_bf;
106 UChar *internal, *alphabet;
107 struct wordlist *words = NULL, *stack = NULL, *w;
108 int maxlen, minlen = 0;
109 int maxwords = INT_MAX, maxtotal = INT_MAX;
110 int opt, longindex;
111 bool just_test = false, include_contains = false, just_words = false;
112 const struct option long_opts[] = {
113 {"contain", required_argument, 0, 'c'},
114 {"dict", required_argument, 0, 'd'},
115 {"length", required_argument, 0, 'l'},
116 {"minimum", required_argument, 0, 'm'},
117 {"number", required_argument, 0, 'n'},
118 {"words", no_argument, 0, 'w'},
119 {"help", no_argument, 0, 'h'},
120 {"used", required_argument, 0, 'u'},
121 {"test", required_argument, 0, 't'},
122 {"version", no_argument, 0, 'v'},
123 {0, 0, 0, 0}
124 };
125
126 memset(&phrase, 0, sizeof(phrase));
127 memset(&contain, 0, sizeof(contain));
128
129 /* parse arguments */
130
131 while ((opt = getopt_long (argc, argv, "c:d:hl:m:n:t:u:vw",
132 long_opts, &longindex)) != -1) {
133 switch (opt) {
134 case 'c':
135 contains = safe_strdup(optarg);
136 just_test = false;
137 include_contains = true;
138 break;
139 case 'd':
140 dictionary = safe_strdup(optarg);
141 break;
142 case 'h':
143 case '?':
144 print_help(argv[0]);
145 exit(0);
146 case 'l':
147 maxwords = atoi(optarg);
148 break;
149 case 'm':
150 minlen = atoi(optarg);
151 break;
152 case 'n':
153 maxtotal = atoi(optarg);
154 break;
155 case 't':
156 contains = safe_strdup(optarg);
157 just_test = true;
158 break;
159 case 'u':
160 contains = safe_strdup(optarg);
161 include_contains = false;
162 break;
163 case 'v':
164 printf("%s\n", VERSION);
165 exit(0);
166 case 'w':
167 just_words = true;
168 break;
169 default:
170 fprintf(stderr, "Unexpected option %c\n", optopt);
171 exit(99);
172 }
173 }
174
175 if (argc - optind != 1) {
176 fprintf(stderr, "%s: incorrect number of arguments\n", argv[0]);
177 print_help(argv[0]);
178 exit(1);
179 }
180
181 phrase.utf8_form = safe_strdup(argv[optind]);
182
183 internal = utf8tointernal(phrase.utf8_form);
184
185 maxlen = phrase.length = u_strlen(internal);
186
187 alphabet = make_alphabet(internal);
188 if (u_strlen(alphabet) > BITFIELD_MAX_LENGTH) {
189 fprintf(stderr, "The phrase contains too many unique letters.\n");
190 exit(1);
191 }
192
193 phrase.bits = make_bitfield(internal, alphabet);
194 free(internal);
195
196 if (contains) {
197 contain.utf8_form = contains;
198 internal = utf8tointernal(contains);
199 contain.length = u_strlen(internal);
200 contain.bits = make_bitfield(internal, alphabet);
201 if ((contain.bits == NULL) || !bf_contains(phrase.bits, contain.bits)) {
202 printf("Phrase '%s' does not contain '%s'\n",
203 phrase.utf8_form, contain.utf8_form);
204 exit(1);
205 }
206 if (include_contains)
207 stack = push_wordstack(stack, &contain);
208 temp_bf = bf_subtract(phrase.bits, contain.bits);
209 free_bitfield(phrase.bits);
210 phrase.bits = temp_bf;
211 maxlen -= contain.length;
212 if (just_test) {
213 if (maxlen == 0) {
214 printf("%s can be made from %s\n",
215 contain.utf8_form,
216 phrase.utf8_form);
217 exit(0);
218 } else {
219 printf("%s can not be made from %s\n",
220 contain.utf8_form,
221 phrase.utf8_form);
222 exit(1);
223 }
224 }
225 }
226
227 if (dictionary == NULL)
228 dictionary = safe_strdup(DEFAULT_DICT);
229
230 load_words(&words, alphabet, phrase.bits, maxlen, minlen, dictionary);
231
232 free(alphabet);
233
234 if (just_words) {
235 for (w = words; w != NULL; w = w->next) {
236 /* printf(" %3d %s\n", w->word->length, w->word->utf8_form); */
237 puts(w->word->utf8_form);
238 }
239 } else
240 find_anagrams(words, stack, phrase.bits, maxwords, maxlen, &maxtotal);
241
242 return 0;
243 }
244