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