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