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