xref: /openbsd-src/usr.bin/spell/spellprog.c (revision 47911bd667ac77dc523b8a13ef40b012dbffa741)
1 /*	$OpenBSD: spellprog.c,v 1.3 2002/06/03 17:53:32 kjell Exp $	*/
2 
3 /*
4  * Copyright (c) 1991, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *	This product includes software developed by the University of
18  *	California, Berkeley and its contributors.
19  * 4. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  *
35  *	@(#)spell.h	8.1 (Berkeley) 6/6/93
36  */
37 /*
38  * Copyright (C) Caldera International Inc.  2001-2002.
39  * All rights reserved.
40  *
41  * Redistribution and use in source and binary forms, with or without
42  * modification, are permitted provided that the following conditions
43  * are met:
44  * 1. Redistributions of source code and documentation must retain the above
45  *    copyright notice, this list of conditions and the following disclaimer.
46  * 2. Redistributions in binary form must reproduce the above copyright
47  *    notice, this list of conditions and the following disclaimer in the
48  *    documentation and/or other materials provided with the distribution.
49  * 3. All advertising materials mentioning features or use of this software
50  *    must display the following acknowledgement:
51  *	This product includes software developed or owned by Caldera
52  *	International, Inc.
53  * 4. Neither the name of Caldera International, Inc. nor the names of other
54  *    contributors may be used to endorse or promote products derived from
55  *    this software without specific prior written permission.
56  *
57  * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
58  * INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
59  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
60  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
61  * IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE FOR ANY DIRECT,
62  * INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
63  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
64  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
65  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
66  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
67  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
68  * POSSIBILITY OF SUCH DAMAGE.
69  */
70 
71 #ifndef lint
72 static const char copyright[] =
73 "@(#) Copyright (c) 1991, 1993\n\
74 	The Regents of the University of California.  All rights reserved.\n";
75 #endif /* not lint */
76 
77 #ifndef lint
78 #if 0
79 static const char sccsid[] = "@(#)spell.c	8.1 (Berkeley) 6/6/93";
80 #else
81 #endif
82 static const char rcsid[] = "$OpenBSD: spellprog.c,v 1.3 2002/06/03 17:53:32 kjell Exp $";
83 #endif /* not lint */
84 
85 #include <sys/param.h>
86 #include <sys/mman.h>
87 #include <sys/stat.h>
88 
89 #include <ctype.h>
90 #include <err.h>
91 #include <errno.h>
92 #include <fcntl.h>
93 #include <limits.h>
94 #include <locale.h>
95 #include <stdio.h>
96 #include <stdlib.h>
97 #include <string.h>
98 #include <unistd.h>
99 
100 #define DLEV 2
101 
102 int	 an(char *, char *, char *, int);
103 int	 bility(char *, char *, char *, int);
104 int	 es(char *, char *, char *, int);
105 int	 dict(char *, char *);
106 int	 i_to_y(char *, char *, char *, int);
107 int	 ily(char *, char *, char *, int);
108 int	 ize(char *, char *, char *, int);
109 int	 metry(char *, char *, char *, int);
110 int	 monosyl(char *, char *);
111 int	 ncy(char *, char *, char *, int);
112 int	 nop(void);
113 int	 trypref(char *, char *, int);
114 int	 tryword(char *, char *, int);
115 int	 s(char *, char *, char *, int);
116 int	 strip(char *, char *, char *, int);
117 int	 suffix(char *, int);
118 int	 tion(char *, char *, char *, int);
119 int	 vowel(int);
120 int	 y_to_e(char *, char *, char *, int);
121 int	 CCe(char *, char *, char *, int);
122 int	 VCe(char *, char *, char *, int);
123 char	*lookuppref(char **, char *);
124 char	*skipv(char *);
125 char	*estrdup(const char *);
126 void	 ise(void);
127 void	 print_word(FILE *);
128 void	 ztos(char *);
129 __dead void usage(void);
130 
131 /* from look.c */
132 int	 look(unsigned char *, unsigned char *, unsigned char *);
133 
134 struct suftab {
135 	char *suf;
136 	int (*p1)();	/* XXX - variable args */
137 	int n1;
138 	char *d1;
139 	char *a1;
140 	int (*p2)();	/* XXX - variable args */
141 	int n2;
142 	char *d2;
143 	char *a2;
144 } suftab[] = {
145 	{"ssen", ily, 4, "-y+iness", "+ness" },
146 	{"ssel", ily, 4, "-y+i+less", "+less" },
147 	{"se", s, 1, "", "+s", es, 2, "-y+ies", "+es" },
148 	{"s'", s, 2, "", "+'s"},
149 	{"s", s, 1, "", "+s"},
150 	{"ecn", ncy, 1, "", "-t+ce"},
151 	{"ycn", ncy, 1, "", "-cy+t"},
152 	{"ytilb", nop, 0, "", ""},
153 	{"ytilib", bility, 5, "-le+ility", ""},
154 	{"elbaif", i_to_y, 4, "-y+iable", ""},
155 	{"elba", CCe, 4, "-e+able", "+able"},
156 	{"yti", CCe, 3, "-e+ity", "+ity"},
157 	{"ylb", y_to_e, 1, "-e+y", ""},
158 	{"yl", ily, 2, "-y+ily", "+ly"},
159 	{"laci", strip, 2, "", "+al"},
160 	{"latnem", strip, 2, "", "+al"},
161 	{"lanoi", strip, 2, "", "+al"},
162 	{"tnem", strip, 4, "", "+ment"},
163 	{"gni", CCe, 3, "-e+ing", "+ing"},
164 	{"reta", nop, 0, "", ""},
165 	{"re", strip, 1, "", "+r", i_to_y, 2, "-y+ier", "+er"},
166 	{"de", strip, 1, "", "+d", i_to_y, 2, "-y+ied", "+ed"},
167 	{"citsi", strip, 2, "", "+ic"},
168 	{"cihparg", i_to_y, 1, "-y+ic", ""},
169 	{"tse", strip, 2, "", "+st", i_to_y, 3, "-y+iest", "+est"},
170 	{"cirtem", i_to_y, 1, "-y+ic", ""},
171 	{"yrtem", metry, 0, "-ry+er", ""},
172 	{"cigol", i_to_y, 1, "-y+ic", ""},
173 	{"tsigol", i_to_y, 2, "-y+ist", ""},
174 	{"tsi", VCe, 3, "-e+ist", "+ist"},
175 	{"msi", VCe, 3, "-e+ism", "+ist"},
176 	{"noitacif", i_to_y, 6, "-y+ication", ""},
177 	{"noitazi", ize, 5, "-e+ation", ""},
178 	{"rota", tion, 2, "-e+or", ""},
179 	{"noit", tion, 3, "-e+ion", "+ion"},
180 	{"naino", an, 3, "", "+ian"},
181 	{"na", an, 1, "", "+n"},
182 	{"evit", tion, 3, "-e+ive", "+ive"},
183 	{"ezi", CCe, 3, "-e+ize", "+ize"},
184 	{"pihs", strip, 4, "", "+ship"},
185 	{"dooh", ily, 4, "-y+hood", "+hood"},
186 	{"ekil", strip, 4, "", "+like"},
187 	{ NULL }
188 };
189 
190 char *preftab[] = {
191 	"anti",
192 	"bio",
193 	"dis",
194 	"electro",
195 	"en",
196 	"fore",
197 	"hyper",
198 	"intra",
199 	"inter",
200 	"iso",
201 	"kilo",
202 	"magneto",
203 	"meta",
204 	"micro",
205 	"milli",
206 	"mis",
207 	"mono",
208 	"multi",
209 	"non",
210 	"out",
211 	"over",
212 	"photo",
213 	"poly",
214 	"pre",
215 	"pseudo",
216 	"re",
217 	"semi",
218 	"stereo",
219 	"sub",
220 	"super",
221 	"thermo",
222 	"ultra",
223 	"under",	/* must precede un */
224 	"un",
225 	NULL
226 };
227 
228 struct wlist {
229 	int fd;
230 	unsigned char *front;
231 	unsigned char *back;
232 } *wlists;
233 
234 int vflag;
235 int xflag;
236 char word[LINE_MAX];
237 char original[LINE_MAX];
238 char *deriv[40];
239 char affix[40];
240 
241 /*
242  * The spellprog utility accepts a newline-delimited list of words
243  * on stdin.  For arguments it expects the path to a word list and
244  * the path to a file in which to store found words.
245  *
246  * In normal usage, spell is called twice.  The first time it is
247  * called with a stop list to flag commonly mispelled words.  The
248  * remaining words are then passed to spell again, this time with
249  * the dictionary file as the first (non-flag) argument.
250  *
251  * Unlike historic versions of spellprog, this one does not use
252  * hashed files.  Instead it simply requires that files be sorted
253  * lexigraphically and uses the same algorithm as the look utility.
254  *
255  * Note that spellprog should be called via the spell shell script
256  * and is not meant to be invoked directly by the user.
257  */
258 
259 int
260 main(int argc, char **argv)
261 {
262 	char *ep, *cp, *dp;
263 	char *outfile;
264 	int ch, fold, i;
265 	struct stat sb;
266 	FILE *file, *found;
267 
268 	setlocale(LC_ALL, "");
269 
270 	outfile = NULL;
271 	while ((ch = getopt(argc, argv, "bvxo:")) != -1) {
272 		switch (ch) {
273 		case 'b':
274 			/* Use British dictionary and convert ize -> ise. */
275 			ise();
276 			break;
277 		case 'o':
278 			outfile = optarg;
279 			break;
280 		case 'v':
281 			/* Also write derivations to "found" file. */
282 			vflag++;
283 			break;
284 		case 'x':
285 			/* Print plausible stems to stdout. */
286 			xflag++;
287 			break;
288 		default:
289 			usage();
290 		}
291 
292 	}
293 	argc -= optind;
294 	argv += optind;
295 	if (argc < 1)
296 		usage();
297 
298 	/* Open and mmap the word/stop lists. */
299 	if ((wlists = malloc(sizeof(struct wlist) * (argc + 1))) == NULL)
300 		err(1, "malloc");
301 	for (i = 0; argc--; i++) {
302 		wlists[i].fd = open(argv[i], O_RDONLY, 0);
303 		if (wlists[i].fd == -1 || fstat(wlists[i].fd, &sb) != 0)
304 			err(1, "%s", argv[i]);
305 		if (sb.st_size > SIZE_T_MAX)
306 			errx(1, "%s: %s", argv[i], strerror(EFBIG));
307 		wlists[i].front = mmap(NULL, (size_t)sb.st_size, PROT_READ,
308 		    MAP_PRIVATE, wlists[i].fd, (off_t)0);
309 		if (wlists[i].front == MAP_FAILED)
310 			err(1, "%s", argv[i]);
311 		wlists[i].back = wlists[i].front + sb.st_size;
312 	}
313 	wlists[i].fd = -1;
314 
315 	/* Open file where found words are to be saved. */
316 	if (outfile == NULL)
317 		found = NULL;
318 	else if ((found = fopen(outfile, "w")) == NULL)
319 		err(1, "cannot open %s", outfile);
320 
321 	for (;; print_word(file)) {
322 		affix[0] = '\0';
323 		file = found;
324 		for (ep = word; (*ep = ch = getchar()) != '\n'; ep++) {
325 			if (ep - word == sizeof(word) - 1) {
326 				*ep = '\0';
327 				warnx("word too long (%s)", word);
328 				while ((ch = getchar()) != '\n')
329 					;	/* slurp until EOL */
330 			}
331 			if (ch == EOF) {
332 				if (found != NULL)
333 					fclose(found);
334 				exit(0);
335 			}
336 		}
337 		for (cp = word, dp = original; cp < ep; )
338 			*dp++ = *cp++;
339 		*dp = '\0';
340 		fold = 0;
341 		for (cp = word; cp < ep; cp++)
342 			if (islower(*cp))
343 				goto lcase;
344 		if (trypref(ep, ".", 0))
345 			continue;
346 		++fold;
347 		for (cp = original + 1, dp = word + 1; dp < ep; dp++, cp++)
348 			*dp = tolower(*cp);
349 lcase:
350 		if (trypref(ep, ".", 0) || suffix(ep, 0))
351 			continue;
352 		if (isupper(word[0])) {
353 			for (cp = original, dp = word; (*dp = *cp++); dp++) {
354 				if (fold)
355 					*dp = tolower(*dp);
356 			}
357 			word[0] = tolower(word[0]);
358 			goto lcase;
359 		}
360 		file = stdout;
361 	}
362 
363 	exit(0);
364 }
365 
366 void
367 print_word(FILE *f)
368 {
369 
370 	if (f != NULL) {
371 		if (vflag && affix[0] != '\0' && affix[0] != '.')
372 			fprintf(f, "%s\t%s\n", affix, original);
373 		else
374 			fprintf(f, "%s\n", original);
375 	}
376 }
377 
378 /*
379  * For each matching suffix in suftab, call the function associated
380  * with that suffix (p1 and p2).
381  */
382 int
383 suffix(char *ep, int lev)
384 {
385 	struct suftab *t;
386 	char *cp, *sp;
387 
388 	lev += DLEV;
389 	deriv[lev] = deriv[lev-1] = 0;
390 	for (t = suftab; (sp = t->suf); t++) {
391 		cp = ep;
392 		while (*sp) {
393 			if (*--cp != *sp++)
394 				goto next;
395 		}
396 		for (sp = cp; --sp >= word && !vowel(*sp);)
397 			;	/* nothing */
398 		if (sp < word)
399 			return (0);
400 		if ((*t->p1)(ep-t->n1, t->d1, t->a1, lev+1))
401 			return (1);
402 		if (t->p2 != NULL) {
403 			deriv[lev] = deriv[lev+1] = '\0';
404 			return ((*t->p2)(ep-t->n2, t->d2, t->a2, lev));
405 		}
406 		return (0);
407 next:		;
408 	}
409 	return (0);
410 }
411 
412 int
413 nop(void)
414 {
415 
416 	return (0);
417 }
418 
419 int
420 strip(char *ep, char *d, char *a, int lev)
421 {
422 
423 	return (trypref(ep, a, lev) || suffix(ep, lev));
424 }
425 
426 int
427 s(char *ep, char *d, char *a, int lev)
428 {
429 
430 	if (lev > DLEV + 1)
431 		return (0);
432 	if (*ep == 's' && ep[-1] == 's')
433 		return (0);
434 	return (strip(ep, d, a, lev));
435 }
436 
437 int
438 an(char *ep, char *d, char *a, int lev)
439 {
440 
441 	if (!isupper(*word))	/* must be proper name */
442 		return (0);
443 	return (trypref(ep,a,lev));
444 }
445 
446 int
447 ize(char *ep, char *d, char *a, int lev)
448 {
449 
450 	*ep++ = 'e';
451 	return (strip(ep ,"", d, lev));
452 }
453 
454 int
455 y_to_e(char *ep, char *d, char *a, int lev)
456 {
457 	char c = *ep;
458 
459 	*ep++ = 'e';
460 	if (strip(ep, "", d, lev))
461 		return (1);
462 	ep[-1] = c;
463 	return (0);
464 }
465 
466 int
467 ily(char *ep, char *d, char *a, int lev)
468 {
469 
470 	if (ep[-1] == 'i')
471 		return (i_to_y(ep, d, a, lev));
472 	else
473 		return (strip(ep, d, a, lev));
474 }
475 
476 int
477 ncy(char *ep, char *d, char *a, int lev)
478 {
479 
480 	if (skipv(skipv(ep-1)) < word)
481 		return (0);
482 	ep[-1] = 't';
483 	return (strip(ep, d, a, lev));
484 }
485 
486 int
487 bility(char *ep, char *d, char *a, int lev)
488 {
489 
490 	*ep++ = 'l';
491 	return (y_to_e(ep, d, a, lev));
492 }
493 
494 int
495 i_to_y(char *ep, char *d, char *a, int lev)
496 {
497 
498 	if (ep[-1] == 'i') {
499 		ep[-1] = 'y';
500 		a = d;
501 	}
502 	return (strip(ep, "", a, lev));
503 }
504 
505 int
506 es(char *ep, char *d, char *a, int lev)
507 {
508 
509 	if (lev > DLEV)
510 		return (0);
511 
512 	switch (ep[-1]) {
513 	default:
514 		return (0);
515 	case 'i':
516 		return (i_to_y(ep, d, a, lev));
517 	case 's':
518 	case 'h':
519 	case 'z':
520 	case 'x':
521 		return (strip(ep, d, a, lev));
522 	}
523 }
524 
525 int
526 metry(char *ep, char *d, char *a, int lev)
527 {
528 
529 	ep[-2] = 'e';
530 	ep[-1] = 'r';
531 	return (strip(ep, d, a, lev));
532 }
533 
534 int
535 tion(char *ep, char *d, char *a, int lev)
536 {
537 
538 	switch (ep[-2]) {
539 	case 'c':
540 	case 'r':
541 		return (trypref(ep, a, lev));
542 	case 'a':
543 		return (y_to_e(ep, d, a, lev));
544 	}
545 	return (0);
546 }
547 
548 /*
549  * Possible consonant-consonant-e ending.
550  */
551 int
552 CCe(char *ep, char *d, char *a, int lev)
553 {
554 
555 	switch (ep[-1]) {
556 	case 'l':
557 		if (vowel(ep[-2]))
558 			break;
559 		switch (ep[-2]) {
560 		case 'l':
561 		case 'r':
562 		case 'w':
563 			break;
564 		default:
565 			return (y_to_e(ep, d, a, lev));
566 		}
567 		break;
568 	case 's':
569 		if (ep[-2] == 's')
570 			break;
571 	case 'c':
572 	case 'g':
573 		if (*ep == 'a')
574 			return (0);
575 	case 'v':
576 	case 'z':
577 		if (vowel(ep[-2]))
578 			break;
579 	case 'u':
580 		if (y_to_e(ep, d, a, lev))
581 			return (1);
582 		if (!(ep[-2] == 'n' && ep[-1] == 'g'))
583 			return (0);
584 	}
585 	return (VCe(ep, d, a, lev));
586 }
587 
588 /*
589  * Possible consonant-vowel-consonant-e ending.
590  */
591 int
592 VCe(char *ep, char *d, char *a, int lev)
593 {
594 	char c;
595 
596 	c = ep[-1];
597 	if (c == 'e')
598 		return (0);
599 	if (!vowel(c) && vowel(ep[-2])) {
600 		c = *ep;
601 		*ep++ = 'e';
602 		if (trypref(ep, d, lev) || suffix(ep, lev))
603 			return (1);
604 		ep--;
605 		*ep = c;
606 	}
607 	return (strip(ep, d, a, lev));
608 }
609 
610 char *
611 lookuppref(char **wp, char *ep)
612 {
613 	char **sp;
614 	char *bp,*cp;
615 
616 	for (sp = preftab; *sp; sp++) {
617 		bp = *wp;
618 		for (cp = *sp; *cp; cp++, bp++) {
619 			if (tolower(*bp) != *cp)
620 				goto next;
621 		}
622 		for (cp = bp; cp < ep; cp++) {
623 			if (vowel(*cp)) {
624 				*wp = bp;
625 				return (*sp);
626 			}
627 		}
628 next:		;
629 	}
630 	return (0);
631 }
632 
633 /*
634  * If the word is not in the dictionary, try stripping off prefixes
635  * until the word is found or we run out of prefixes to check.
636  */
637 int
638 trypref(char *ep, char *a, int lev)
639 {
640 	char *cp;
641 	char *bp;
642 	char *pp;
643 	int val = 0;
644 	char space[20];
645 
646 	deriv[lev] = a;
647 	if (tryword(word, ep, lev))
648 		return (1);
649 	bp = word;
650 	pp = space;
651 	deriv[lev+1] = pp;
652 	while ((cp = lookuppref(&bp, ep))) {
653 		*pp++ = '+';
654 		while ((*pp = *cp++))
655 			pp++;
656 		if (tryword(bp, ep, lev+1)) {
657 			val = 1;
658 			break;
659 		}
660 		if (pp - space >= sizeof(space))
661 			return (0);
662 	}
663 	deriv[lev+1] = deriv[lev+2] = '\0';
664 	return (val);
665 }
666 
667 int
668 tryword(char *bp, char *ep, int lev)
669 {
670 	int i, j;
671 	char duple[3];
672 
673 	if (ep-bp <= 1)
674 		return (0);
675 	if (vowel(*ep) && monosyl(bp, ep))
676 		return (0);
677 
678 	i = dict(bp, ep);
679 	if (i == 0 && vowel(*ep) && ep[-1] == ep[-2] && monosyl(bp, ep-1)) {
680 		ep--;
681 		deriv[++lev] = duple;
682 		duple[0] = '+';
683 		duple[1] = *ep;
684 		duple[2] = '\0';
685 		i = dict(bp, ep);
686 	}
687 	if (vflag == 0 || i == 0)
688 		return (i);
689 
690 	/* Also tack on possible derivations. (XXX - warn on truncation?) */
691 	for (j = lev; j > 0; j--) {
692 		if (deriv[j])
693 			strlcat(affix, deriv[j], sizeof(affix));
694 	}
695 	return (i);
696 }
697 
698 int
699 monosyl(char *bp, char *ep)
700 {
701 
702 	if (ep < bp + 2)
703 		return (0);
704 	if (vowel(*--ep) || !vowel(*--ep) || ep[1] == 'x' || ep[1] == 'w')
705 		return (0);
706 	while (--ep >= bp)
707 		if (vowel(*ep))
708 			return (0);
709 	return (1);
710 }
711 
712 char *
713 skipv(char *s)
714 {
715 
716 	if (s >= word && vowel(*s))
717 		s--;
718 	while (s >= word && !vowel(*s))
719 		s--;
720 	return (s);
721 }
722 
723 int
724 vowel(int c)
725 {
726 
727 	switch (tolower(c)) {
728 	case 'a':
729 	case 'e':
730 	case 'i':
731 	case 'o':
732 	case 'u':
733 	case 'y':
734 		return (1);
735 	}
736 	return (0);
737 }
738 
739 /*
740  * Crummy way to Britishise.
741  */
742 void
743 ise(void)
744 {
745 	struct suftab *tab;
746 
747 	for (tab = suftab; tab->suf; tab++) {
748 		/* Assume that suffix will contain 'z' if a1 or d1 do */
749 		if (strchr(tab->suf, 'z')) {
750 			tab->suf = estrdup(tab->suf);
751 			ztos(tab->suf);
752 			if (strchr(tab->d1, 'z')) {
753 				tab->d1 = estrdup(tab->d1);
754 				ztos(tab->d1);
755 			}
756 			if (strchr(tab->a1, 'z')) {
757 				tab->a1 = estrdup(tab->a1);
758 				ztos(tab->a1);
759 			}
760 		}
761 	}
762 }
763 
764 void
765 ztos(char *s)
766 {
767 
768 	for (; *s; s++)
769 		if (*s == 'z')
770 			*s = 's';
771 }
772 
773 char *
774 estrdup(const char *s)
775 {
776 	char *d;
777 
778 	if ((d = strdup(s)) == NULL)
779 		err(1, "strdup");
780 	return (d);
781 }
782 
783 /*
784  * Look up a word in the dictionary.
785  * Returns 1 if found, 0 if not.
786  */
787 int
788 dict(char *bp, char *ep)
789 {
790 	char c;
791 	int i, rval;
792 
793 	c = *ep;
794 	*ep = '\0';
795 	if (xflag)
796 		printf("=%s\n", bp);
797 	for (i = rval = 0; wlists[i].fd != -1; i++) {
798 		if ((rval = look((unsigned char *)bp, wlists[i].front,
799 		    wlists[i].back)) == 1)
800 			break;
801 	}
802 	*ep = c;
803 	return (rval);
804 }
805 
806 __dead void
807 usage(void)
808 {
809 	extern char *__progname;
810 
811 	fprintf(stderr, "usage: %s [-bvx] [-o found-words] word-list ...\n",
812 	    __progname);
813 	exit(1);
814 }
815