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