1*8d9348aaSnia /* $NetBSD: spellprog.c,v 1.10 2021/11/09 09:41:05 nia Exp $ */
2fcc96823Sperry
3fcc96823Sperry /* derived from OpenBSD: spellprog.c,v 1.4 2003/06/03 02:56:16 millert Exp */
4fcc96823Sperry
5fcc96823Sperry /*
6fcc96823Sperry * Copyright (c) 1991, 1993
7fcc96823Sperry * The Regents of the University of California. All rights reserved.
8fcc96823Sperry *
9fcc96823Sperry * Redistribution and use in source and binary forms, with or without
10fcc96823Sperry * modification, are permitted provided that the following conditions
11fcc96823Sperry * are met:
12fcc96823Sperry * 1. Redistributions of source code must retain the above copyright
13fcc96823Sperry * notice, this list of conditions and the following disclaimer.
14fcc96823Sperry * 2. Redistributions in binary form must reproduce the above copyright
15fcc96823Sperry * notice, this list of conditions and the following disclaimer in the
16fcc96823Sperry * documentation and/or other materials provided with the distribution.
17fcc96823Sperry * 3. Neither the name of the University nor the names of its contributors
18fcc96823Sperry * may be used to endorse or promote products derived from this software
19fcc96823Sperry * without specific prior written permission.
20fcc96823Sperry *
21fcc96823Sperry * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22fcc96823Sperry * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23fcc96823Sperry * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24fcc96823Sperry * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25fcc96823Sperry * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26fcc96823Sperry * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27fcc96823Sperry * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28fcc96823Sperry * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29fcc96823Sperry * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30fcc96823Sperry * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31fcc96823Sperry * SUCH DAMAGE.
32fcc96823Sperry *
33fcc96823Sperry * @(#)spell.h 8.1 (Berkeley) 6/6/93
34fcc96823Sperry */
35fcc96823Sperry /*
36fcc96823Sperry * Copyright (C) Caldera International Inc. 2001-2002.
37fcc96823Sperry * All rights reserved.
38fcc96823Sperry *
39fcc96823Sperry * Redistribution and use in source and binary forms, with or without
40fcc96823Sperry * modification, are permitted provided that the following conditions
41fcc96823Sperry * are met:
42fcc96823Sperry * 1. Redistributions of source code and documentation must retain the above
43fcc96823Sperry * copyright notice, this list of conditions and the following disclaimer.
44fcc96823Sperry * 2. Redistributions in binary form must reproduce the above copyright
45fcc96823Sperry * notice, this list of conditions and the following disclaimer in the
46fcc96823Sperry * documentation and/or other materials provided with the distribution.
47fcc96823Sperry * 3. All advertising materials mentioning features or use of this software
48fcc96823Sperry * must display the following acknowledgement:
49fcc96823Sperry * This product includes software developed or owned by Caldera
50fcc96823Sperry * International, Inc.
51fcc96823Sperry * 4. Neither the name of Caldera International, Inc. nor the names of other
52fcc96823Sperry * contributors may be used to endorse or promote products derived from
53fcc96823Sperry * this software without specific prior written permission.
54fcc96823Sperry *
55fcc96823Sperry * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
56fcc96823Sperry * INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
57fcc96823Sperry * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
58fcc96823Sperry * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
59fcc96823Sperry * IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE FOR ANY DIRECT,
60fcc96823Sperry * INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
61fcc96823Sperry * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
62fcc96823Sperry * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
63fcc96823Sperry * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
64fcc96823Sperry * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
65fcc96823Sperry * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
66fcc96823Sperry * POSSIBILITY OF SUCH DAMAGE.
67fcc96823Sperry */
68fcc96823Sperry
6936c7456dSperry #include <sys/cdefs.h>
7036c7456dSperry
71fcc96823Sperry #ifndef lint
72fcc96823Sperry static const char copyright[] =
73fcc96823Sperry "@(#) Copyright (c) 1991, 1993\n\
74fcc96823Sperry The Regents of the University of California. All rights reserved.\n";
75fcc96823Sperry #endif /* not lint */
76fcc96823Sperry
77fcc96823Sperry #ifndef lint
78fcc96823Sperry #if 0
79fcc96823Sperry static const char sccsid[] = "@(#)spell.c 8.1 (Berkeley) 6/6/93";
80fcc96823Sperry #else
81fcc96823Sperry #endif
82fcc96823Sperry static const char rcsid[] = "$OpenBSD: spellprog.c,v 1.4 2003/06/03 02:56:16 millert Exp $";
83fcc96823Sperry #endif /* not lint */
84fcc96823Sperry
85fcc96823Sperry #include <sys/param.h>
86fcc96823Sperry #include <sys/mman.h>
87fcc96823Sperry #include <sys/stat.h>
88fcc96823Sperry
89fcc96823Sperry #include <ctype.h>
90fcc96823Sperry #include <err.h>
91fcc96823Sperry #include <errno.h>
92fcc96823Sperry #include <fcntl.h>
93fcc96823Sperry #include <limits.h>
94fcc96823Sperry #include <locale.h>
95fcc96823Sperry #include <stdio.h>
96fcc96823Sperry #include <stdlib.h>
97fcc96823Sperry #include <string.h>
98fcc96823Sperry #include <unistd.h>
99cdab3a7aSchristos #include <util.h>
100fcc96823Sperry
10197ccbe26Schristos #include "extern.h"
10297ccbe26Schristos
103fcc96823Sperry #define DLEV 2
104fcc96823Sperry
10597ccbe26Schristos static int dict(char *, char *);
10697ccbe26Schristos static int trypref(char *, const char *, size_t);
10797ccbe26Schristos static int tryword(char *, char *, size_t);
10897ccbe26Schristos static int suffix(char *, size_t);
10997ccbe26Schristos static int vowel(int);
11097ccbe26Schristos static const char *lookuppref(char **, char *);
11197ccbe26Schristos static char *skipv(char *);
11297ccbe26Schristos static void ise(void);
11397ccbe26Schristos static void print_word(FILE *);
11497ccbe26Schristos static void ztos(char *);
11597ccbe26Schristos static int monosyl(char *, char *);
1168b0f9554Sperry static void usage(void) __dead;
11797ccbe26Schristos static void getderiv(size_t);
118fcc96823Sperry
11997ccbe26Schristos static int an(char *, const char *, const char *, size_t);
12097ccbe26Schristos static int bility(char *, const char *, const char *, size_t);
12197ccbe26Schristos static int es(char *, const char *, const char *, size_t);
12297ccbe26Schristos static int i_to_y(char *, const char *, const char *, size_t);
12397ccbe26Schristos static int ily(char *, const char *, const char *, size_t);
12497ccbe26Schristos static int ize(char *, const char *, const char *, size_t);
12597ccbe26Schristos static int metry(char *, const char *, const char *, size_t);
12697ccbe26Schristos static int ncy(char *, const char *, const char *, size_t);
12797ccbe26Schristos static int nop(char *, const char *, const char *, size_t);
12897ccbe26Schristos static int s(char *, const char *, const char *, size_t);
12997ccbe26Schristos static int strip(char *, const char *, const char *, size_t);
13097ccbe26Schristos static int tion(char *, const char *, const char *, size_t);
13197ccbe26Schristos static int y_to_e(char *, const char *, const char *, size_t);
13297ccbe26Schristos static int CCe(char *, const char *, const char *, size_t);
13397ccbe26Schristos static int VCe(char *, const char *, const char *, size_t);
134fcc96823Sperry
1357fc92b02Schristos /*
1367fc92b02Schristos * This cannot be const because we modify it when we choose british
1377fc92b02Schristos * spelling.
1387fc92b02Schristos */
13997ccbe26Schristos static struct suftab {
14097ccbe26Schristos const char *suf;
14197ccbe26Schristos int (*p1)(char *, const char *, const char *, size_t);
142fcc96823Sperry int n1;
14397ccbe26Schristos const char *d1;
14497ccbe26Schristos const char *a1;
14597ccbe26Schristos int (*p2)(char *, const char *, const char *, size_t);
146fcc96823Sperry int n2;
14797ccbe26Schristos const char *d2;
14897ccbe26Schristos const char *a2;
149fcc96823Sperry } suftab[] = {
1507284ae46Schristos { .suf = "ssen", .p1 = ily, .n1 = 4,
1517284ae46Schristos .d1 = "-y+iness", .a1 = "+ness" },
1527284ae46Schristos { .suf = "ssel", .p1 = ily, .n1 = 4,
1537284ae46Schristos .d1 = "-y+i+less", .a1 = "+less" },
1547284ae46Schristos { .suf = "se", .p1 = s, .n1 = 1,
1557284ae46Schristos .d1 = "", .a1 = "+s", .p2 = es,
1567284ae46Schristos .n2 = 2, .d2 = "-y+ies", .a2 = "+es" },
1577284ae46Schristos { .suf = "s'", .p1 = s, .n1 = 2,
1587284ae46Schristos .d1 = "", .a1 = "+'s" },
1597284ae46Schristos { .suf = "s", .p1 = s, .n1 = 1,
1607284ae46Schristos .d1 = "", .a1 = "+s" },
1617284ae46Schristos { .suf = "ecn", .p1 = ncy, .n1 = 1,
1627284ae46Schristos .d1 = "", .a1 = "-t+ce" },
1637284ae46Schristos { .suf = "ycn", .p1 = ncy, .n1 = 1,
1647284ae46Schristos .d1 = "", .a1 = "-cy+t" },
1657284ae46Schristos { .suf = "ytilb", .p1 = nop, .n1 = 0,
1667284ae46Schristos .d1 = "", .a1 = "" },
1677284ae46Schristos { .suf = "ytilib", .p1 = bility, .n1 = 5,
1687284ae46Schristos .d1 = "-le+ility", .a1 = "" },
1697284ae46Schristos { .suf = "elbaif", .p1 = i_to_y, .n1 = 4,
1707284ae46Schristos .d1 = "-y+iable", .a1 = "" },
1717284ae46Schristos { .suf = "elba", .p1 = CCe, .n1 = 4,
1727284ae46Schristos .d1 = "-e+able", .a1 = "+able" },
1737284ae46Schristos { .suf = "yti", .p1 = CCe, .n1 = 3,
1747284ae46Schristos .d1 = "-e+ity", .a1 = "+ity" },
1757284ae46Schristos { .suf = "ylb", .p1 = y_to_e, .n1 = 1,
1767284ae46Schristos .d1 = "-e+y", .a1 = "" },
1777284ae46Schristos { .suf = "yl", .p1 = ily, .n1 = 2,
1787284ae46Schristos .d1 = "-y+ily", .a1 = "+ly" },
1797284ae46Schristos { .suf = "laci", .p1 = strip, .n1 = 2,
1807284ae46Schristos .d1 = "", .a1 = "+al" },
1817284ae46Schristos { .suf = "latnem", .p1 = strip, .n1 = 2,
1827284ae46Schristos .d1 = "", .a1 = "+al" },
1837284ae46Schristos { .suf = "lanoi", .p1 = strip, .n1 = 2,
1847284ae46Schristos .d1 = "", .a1 = "+al" },
1857284ae46Schristos { .suf = "tnem", .p1 = strip, .n1 = 4,
1867284ae46Schristos .d1 = "", .a1 = "+ment" },
1877284ae46Schristos { .suf = "gni", .p1 = CCe, .n1 = 3,
1887284ae46Schristos .d1 = "-e+ing", .a1 = "+ing" },
1897284ae46Schristos { .suf = "reta", .p1 = nop, .n1 = 0,
1907284ae46Schristos .d1 = "", .a1 = "" },
1917284ae46Schristos { .suf = "re", .p1 = strip, .n1 = 1,
1927284ae46Schristos .d1 = "", .a1 = "+r", .p2 = i_to_y,
1937284ae46Schristos .n2 = 2, .d2 = "-y+ier", .a2 = "+er" },
1947284ae46Schristos { .suf = "de", .p1 = strip, .n1 = 1,
1957284ae46Schristos .d1 = "", .a1 = "+d", .p2 = i_to_y,
1967284ae46Schristos .n2 = 2, .d2 = "-y+ied", .a2 = "+ed" },
1977284ae46Schristos { .suf = "citsi", .p1 = strip, .n1 = 2,
1987284ae46Schristos .d1 = "", .a1 = "+ic" },
1997284ae46Schristos { .suf = "cihparg", .p1 = i_to_y, .n1 = 1,
2007284ae46Schristos .d1 = "-y+ic", .a1 = "" },
2017284ae46Schristos { .suf = "tse", .p1 = strip, .n1 = 2,
2027284ae46Schristos .d1 = "", .a1 = "+st", .p2 = i_to_y,
2037284ae46Schristos .n2 = 3, .d2 = "-y+iest",.a2 = "+est" },
2047284ae46Schristos { .suf = "cirtem", .p1 = i_to_y, .n1 = 1,
2057284ae46Schristos .d1 = "-y+ic", .a1 = "" },
2067284ae46Schristos { .suf = "yrtem", .p1 = metry, .n1 = 0,
2077284ae46Schristos .d1 = "-ry+er", .a1 = "" },
2087284ae46Schristos { .suf = "cigol", .p1 = i_to_y, .n1 = 1,
2097284ae46Schristos .d1 = "-y+ic", .a1 = "" },
2107284ae46Schristos { .suf = "tsigol", .p1 = i_to_y, .n1 = 2,
2117284ae46Schristos .d1 = "-y+ist", .a1 = "" },
2127284ae46Schristos { .suf = "tsi", .p1 = VCe, .n1 = 3,
2137284ae46Schristos .d1 = "-e+ist", .a1 = "+ist" },
2147284ae46Schristos { .suf = "msi", .p1 = VCe, .n1 = 3,
2157284ae46Schristos .d1 = "-e+ism", .a1 = "+ist" },
2167284ae46Schristos { .suf = "noitacif", .p1 = i_to_y, .n1 = 6,
2177284ae46Schristos .d1 = "-y+ication", .a1 = "" },
2187284ae46Schristos { .suf = "noitazi", .p1 = ize, .n1 = 5,
2197284ae46Schristos .d1 = "-e+ation", .a1 = "" },
2207284ae46Schristos { .suf = "rota", .p1 = tion, .n1 = 2,
2217284ae46Schristos .d1 = "-e+or", .a1 = "" },
2227284ae46Schristos { .suf = "noit", .p1 = tion, .n1 = 3,
2237284ae46Schristos .d1 = "-e+ion", .a1 = "+ion" },
2247284ae46Schristos { .suf = "naino", .p1 = an, .n1 = 3,
2257284ae46Schristos .d1 = "", .a1 = "+ian" },
2267284ae46Schristos { .suf = "na", .p1 = an, .n1 = 1,
2277284ae46Schristos .d1 = "", .a1 = "+n" },
2287284ae46Schristos { .suf = "evit", .p1 = tion, .n1 = 3,
2297284ae46Schristos .d1 = "-e+ive", .a1 = "+ive" },
2307284ae46Schristos { .suf = "ezi", .p1 = CCe, .n1 = 3,
2317284ae46Schristos .d1 = "-e+ize", .a1 = "+ize" },
2327284ae46Schristos { .suf = "pihs", .p1 = strip, .n1 = 4,
2337284ae46Schristos .d1 = "", .a1 = "+ship" },
2347284ae46Schristos { .suf = "dooh", .p1 = ily, .n1 = 4,
2357284ae46Schristos .d1 = "-y+hood", .a1 = "+hood" },
2367284ae46Schristos { .suf = "ekil", .p1 = strip, .n1 = 4,
2377284ae46Schristos .d1 = "", .a1 = "+like" },
2387284ae46Schristos { .suf = NULL, }
239fcc96823Sperry };
240fcc96823Sperry
24197ccbe26Schristos static const char *preftab[] = {
242fcc96823Sperry "anti",
243fcc96823Sperry "bio",
244fcc96823Sperry "dis",
245fcc96823Sperry "electro",
246fcc96823Sperry "en",
247fcc96823Sperry "fore",
248fcc96823Sperry "hyper",
249fcc96823Sperry "intra",
250fcc96823Sperry "inter",
251fcc96823Sperry "iso",
252fcc96823Sperry "kilo",
253fcc96823Sperry "magneto",
254fcc96823Sperry "meta",
255fcc96823Sperry "micro",
256fcc96823Sperry "milli",
257fcc96823Sperry "mis",
258fcc96823Sperry "mono",
259fcc96823Sperry "multi",
260fcc96823Sperry "non",
261fcc96823Sperry "out",
262fcc96823Sperry "over",
263fcc96823Sperry "photo",
264fcc96823Sperry "poly",
265fcc96823Sperry "pre",
266fcc96823Sperry "pseudo",
267fcc96823Sperry "re",
268fcc96823Sperry "semi",
269fcc96823Sperry "stereo",
270fcc96823Sperry "sub",
271fcc96823Sperry "super",
272fcc96823Sperry "thermo",
273fcc96823Sperry "ultra",
274fcc96823Sperry "under", /* must precede un */
275fcc96823Sperry "un",
276fcc96823Sperry NULL
277fcc96823Sperry };
278fcc96823Sperry
27997ccbe26Schristos static struct wlist {
280fcc96823Sperry int fd;
281fcc96823Sperry unsigned char *front;
282fcc96823Sperry unsigned char *back;
283fcc96823Sperry } *wlists;
284fcc96823Sperry
28597ccbe26Schristos static int vflag;
28697ccbe26Schristos static int xflag;
28797ccbe26Schristos static char word[LINE_MAX];
28897ccbe26Schristos static char original[LINE_MAX];
28997ccbe26Schristos static char affix[LINE_MAX];
29097ccbe26Schristos static struct {
29197ccbe26Schristos const char **buf;
29297ccbe26Schristos size_t maxlev;
29397ccbe26Schristos } deriv;
294fcc96823Sperry
295fcc96823Sperry /*
296fcc96823Sperry * The spellprog utility accepts a newline-delimited list of words
297fcc96823Sperry * on stdin. For arguments it expects the path to a word list and
298fcc96823Sperry * the path to a file in which to store found words.
299fcc96823Sperry *
300fcc96823Sperry * In normal usage, spell is called twice. The first time it is
301fcc96823Sperry * called with a stop list to flag commonly mispelled words. The
302fcc96823Sperry * remaining words are then passed to spell again, this time with
303fcc96823Sperry * the dictionary file as the first (non-flag) argument.
304fcc96823Sperry *
305fcc96823Sperry * Unlike historic versions of spellprog, this one does not use
306fcc96823Sperry * hashed files. Instead it simply requires that files be sorted
307fcc96823Sperry * lexigraphically and uses the same algorithm as the look utility.
308fcc96823Sperry *
309fcc96823Sperry * Note that spellprog should be called via the spell shell script
310fcc96823Sperry * and is not meant to be invoked directly by the user.
311fcc96823Sperry */
312fcc96823Sperry
313fcc96823Sperry int
main(int argc,char ** argv)314fcc96823Sperry main(int argc, char **argv)
315fcc96823Sperry {
316fcc96823Sperry char *ep, *cp, *dp;
317fcc96823Sperry char *outfile;
318fcc96823Sperry int ch, fold, i;
319fcc96823Sperry struct stat sb;
320fcc96823Sperry FILE *file, *found;
321fcc96823Sperry
322fcc96823Sperry setlocale(LC_ALL, "");
323fcc96823Sperry
324fcc96823Sperry outfile = NULL;
325fcc96823Sperry while ((ch = getopt(argc, argv, "bvxo:")) != -1) {
326fcc96823Sperry switch (ch) {
327fcc96823Sperry case 'b':
328fcc96823Sperry /* Use British dictionary and convert ize -> ise. */
329fcc96823Sperry ise();
330fcc96823Sperry break;
331fcc96823Sperry case 'o':
332fcc96823Sperry outfile = optarg;
333fcc96823Sperry break;
334fcc96823Sperry case 'v':
335fcc96823Sperry /* Also write derivations to "found" file. */
336fcc96823Sperry vflag++;
337fcc96823Sperry break;
338fcc96823Sperry case 'x':
339fcc96823Sperry /* Print plausible stems to stdout. */
340fcc96823Sperry xflag++;
341fcc96823Sperry break;
342fcc96823Sperry default:
343fcc96823Sperry usage();
344fcc96823Sperry }
345fcc96823Sperry
346fcc96823Sperry }
347fcc96823Sperry argc -= optind;
348fcc96823Sperry argv += optind;
349fcc96823Sperry if (argc < 1)
350fcc96823Sperry usage();
351fcc96823Sperry
352fcc96823Sperry /* Open and mmap the word/stop lists. */
353fcc96823Sperry if ((wlists = malloc(sizeof(struct wlist) * (argc + 1))) == NULL)
354fcc96823Sperry err(1, "malloc");
35597ccbe26Schristos
356fcc96823Sperry for (i = 0; argc--; i++) {
357fcc96823Sperry wlists[i].fd = open(argv[i], O_RDONLY, 0);
358fcc96823Sperry if (wlists[i].fd == -1 || fstat(wlists[i].fd, &sb) != 0)
359fcc96823Sperry err(1, "%s", argv[i]);
360fcc96823Sperry if (sb.st_size > SIZE_T_MAX)
361fcc96823Sperry errx(1, "%s: %s", argv[i], strerror(EFBIG));
362fcc96823Sperry wlists[i].front = mmap(NULL, (size_t)sb.st_size, PROT_READ,
363fcc96823Sperry MAP_PRIVATE, wlists[i].fd, (off_t)0);
364fcc96823Sperry if (wlists[i].front == MAP_FAILED)
365fcc96823Sperry err(1, "%s", argv[i]);
36697ccbe26Schristos wlists[i].back = wlists[i].front + (size_t)sb.st_size;
367fcc96823Sperry }
368fcc96823Sperry wlists[i].fd = -1;
369fcc96823Sperry
370fcc96823Sperry /* Open file where found words are to be saved. */
371fcc96823Sperry if (outfile == NULL)
372fcc96823Sperry found = NULL;
373fcc96823Sperry else if ((found = fopen(outfile, "w")) == NULL)
374fcc96823Sperry err(1, "cannot open %s", outfile);
375fcc96823Sperry
376fcc96823Sperry for (;; print_word(file)) {
377fcc96823Sperry affix[0] = '\0';
378fcc96823Sperry file = found;
379fcc96823Sperry for (ep = word; (*ep = ch = getchar()) != '\n'; ep++) {
380fcc96823Sperry if (ep - word == sizeof(word) - 1) {
381fcc96823Sperry *ep = '\0';
382fcc96823Sperry warnx("word too long (%s)", word);
383fcc96823Sperry while ((ch = getchar()) != '\n')
384fcc96823Sperry ; /* slurp until EOL */
385fcc96823Sperry }
386fcc96823Sperry if (ch == EOF) {
387fcc96823Sperry if (found != NULL)
388fcc96823Sperry fclose(found);
389fcc96823Sperry exit(0);
390fcc96823Sperry }
391fcc96823Sperry }
392fcc96823Sperry for (cp = word, dp = original; cp < ep; )
393fcc96823Sperry *dp++ = *cp++;
394fcc96823Sperry *dp = '\0';
395fcc96823Sperry fold = 0;
396fcc96823Sperry for (cp = word; cp < ep; cp++)
397fcc96823Sperry if (islower((unsigned char)*cp))
398fcc96823Sperry goto lcase;
399fcc96823Sperry if (trypref(ep, ".", 0))
400fcc96823Sperry continue;
401fcc96823Sperry ++fold;
402fcc96823Sperry for (cp = original + 1, dp = word + 1; dp < ep; dp++, cp++)
403fcc96823Sperry *dp = tolower((unsigned char)*cp);
404fcc96823Sperry lcase:
405fcc96823Sperry if (trypref(ep, ".", 0) || suffix(ep, 0))
406fcc96823Sperry continue;
407fcc96823Sperry if (isupper((unsigned char)word[0])) {
408fcc96823Sperry for (cp = original, dp = word; (*dp = *cp++); dp++) {
409fcc96823Sperry if (fold)
410fcc96823Sperry *dp = tolower((unsigned char)*dp);
411fcc96823Sperry }
412fcc96823Sperry word[0] = tolower((unsigned char)word[0]);
413fcc96823Sperry goto lcase;
414fcc96823Sperry }
415fcc96823Sperry file = stdout;
416fcc96823Sperry }
417fcc96823Sperry }
418fcc96823Sperry
41997ccbe26Schristos static void
print_word(FILE * f)420fcc96823Sperry print_word(FILE *f)
421fcc96823Sperry {
422fcc96823Sperry
423fcc96823Sperry if (f != NULL) {
424fcc96823Sperry if (vflag && affix[0] != '\0' && affix[0] != '.')
425fcc96823Sperry fprintf(f, "%s\t%s\n", affix, original);
426fcc96823Sperry else
427fcc96823Sperry fprintf(f, "%s\n", original);
428fcc96823Sperry }
429fcc96823Sperry }
430fcc96823Sperry
431fcc96823Sperry /*
432fcc96823Sperry * For each matching suffix in suftab, call the function associated
433fcc96823Sperry * with that suffix (p1 and p2).
434fcc96823Sperry */
43597ccbe26Schristos static int
suffix(char * ep,size_t lev)43697ccbe26Schristos suffix(char *ep, size_t lev)
437fcc96823Sperry {
4387fc92b02Schristos const struct suftab *t;
43997ccbe26Schristos char *cp;
44097ccbe26Schristos const char *sp;
441fcc96823Sperry
442fcc96823Sperry lev += DLEV;
44397ccbe26Schristos getderiv(lev + 1);
44497ccbe26Schristos deriv.buf[lev] = deriv.buf[lev - 1] = 0;
44597ccbe26Schristos for (t = suftab; (sp = t->suf) != NULL; t++) {
446fcc96823Sperry cp = ep;
447fcc96823Sperry while (*sp) {
448fcc96823Sperry if (*--cp != *sp++)
449fcc96823Sperry goto next;
450fcc96823Sperry }
451fcc96823Sperry for (sp = cp; --sp >= word && !vowel(*sp);)
452fcc96823Sperry ; /* nothing */
453fcc96823Sperry if (sp < word)
45497ccbe26Schristos return 0;
455fcc96823Sperry if ((*t->p1)(ep - t->n1, t->d1, t->a1, lev + 1))
45697ccbe26Schristos return 1;
457fcc96823Sperry if (t->p2 != NULL) {
458eb3bd1d5Sjoerg deriv.buf[lev] = deriv.buf[lev + 1] = NULL;
45997ccbe26Schristos return (*t->p2)(ep - t->n2, t->d2, t->a2, lev);
460fcc96823Sperry }
46197ccbe26Schristos return 0;
462fcc96823Sperry next: ;
463fcc96823Sperry }
46497ccbe26Schristos return 0;
465fcc96823Sperry }
466fcc96823Sperry
46797ccbe26Schristos static int
46897ccbe26Schristos /*ARGSUSED*/
nop(char * ep,const char * d,const char * a,size_t lev)46997ccbe26Schristos nop(char *ep, const char *d, const char *a, size_t lev)
470fcc96823Sperry {
471fcc96823Sperry
47297ccbe26Schristos return 0;
473fcc96823Sperry }
474fcc96823Sperry
47597ccbe26Schristos static int
47697ccbe26Schristos /*ARGSUSED*/
strip(char * ep,const char * d,const char * a,size_t lev)47797ccbe26Schristos strip(char *ep, const char *d, const char *a, size_t lev)
478fcc96823Sperry {
479fcc96823Sperry
48097ccbe26Schristos return trypref(ep, a, lev) || suffix(ep, lev);
481fcc96823Sperry }
482fcc96823Sperry
48397ccbe26Schristos static int
s(char * ep,const char * d,const char * a,const size_t lev)48497ccbe26Schristos s(char *ep, const char *d, const char *a, const size_t lev)
485fcc96823Sperry {
486fcc96823Sperry
487fcc96823Sperry if (lev > DLEV + 1)
48897ccbe26Schristos return 0;
489fcc96823Sperry if (*ep == 's' && ep[-1] == 's')
49097ccbe26Schristos return 0;
49197ccbe26Schristos return strip(ep, d, a, lev);
492fcc96823Sperry }
493fcc96823Sperry
49497ccbe26Schristos static int
49597ccbe26Schristos /*ARGSUSED*/
an(char * ep,const char * d,const char * a,size_t lev)49697ccbe26Schristos an(char *ep, const char *d, const char *a, size_t lev)
497fcc96823Sperry {
498fcc96823Sperry
499fcc96823Sperry if (!isupper((unsigned char)*word)) /* must be proper name */
50097ccbe26Schristos return 0;
50197ccbe26Schristos return trypref(ep, a, lev);
502fcc96823Sperry }
503fcc96823Sperry
50497ccbe26Schristos static int
50597ccbe26Schristos /*ARGSUSED*/
ize(char * ep,const char * d,const char * a,size_t lev)50697ccbe26Schristos ize(char *ep, const char *d, const char *a, size_t lev)
507fcc96823Sperry {
508fcc96823Sperry
509fcc96823Sperry *ep++ = 'e';
51097ccbe26Schristos return strip(ep ,"", d, lev);
511fcc96823Sperry }
512fcc96823Sperry
51397ccbe26Schristos static int
51497ccbe26Schristos /*ARGSUSED*/
y_to_e(char * ep,const char * d,const char * a,size_t lev)51597ccbe26Schristos y_to_e(char *ep, const char *d, const char *a, size_t lev)
516fcc96823Sperry {
517fcc96823Sperry char c = *ep;
518fcc96823Sperry
519fcc96823Sperry *ep++ = 'e';
520fcc96823Sperry if (strip(ep, "", d, lev))
52197ccbe26Schristos return 1;
522fcc96823Sperry ep[-1] = c;
52397ccbe26Schristos return 0;
524fcc96823Sperry }
525fcc96823Sperry
52697ccbe26Schristos static int
ily(char * ep,const char * d,const char * a,size_t lev)52797ccbe26Schristos ily(char *ep, const char *d, const char *a, size_t lev)
528fcc96823Sperry {
529fcc96823Sperry
530fcc96823Sperry if (ep[-1] == 'i')
53197ccbe26Schristos return i_to_y(ep, d, a, lev);
532fcc96823Sperry else
53397ccbe26Schristos return strip(ep, d, a, lev);
534fcc96823Sperry }
535fcc96823Sperry
53697ccbe26Schristos static int
ncy(char * ep,const char * d,const char * a,size_t lev)53797ccbe26Schristos ncy(char *ep, const char *d, const char *a, size_t lev)
538fcc96823Sperry {
539fcc96823Sperry
540fcc96823Sperry if (skipv(skipv(ep - 1)) < word)
54197ccbe26Schristos return 0;
542fcc96823Sperry ep[-1] = 't';
54397ccbe26Schristos return strip(ep, d, a, lev);
544fcc96823Sperry }
545fcc96823Sperry
54697ccbe26Schristos static int
bility(char * ep,const char * d,const char * a,size_t lev)54797ccbe26Schristos bility(char *ep, const char *d, const char *a, size_t lev)
548fcc96823Sperry {
549fcc96823Sperry
550fcc96823Sperry *ep++ = 'l';
55197ccbe26Schristos return y_to_e(ep, d, a, lev);
552fcc96823Sperry }
553fcc96823Sperry
55497ccbe26Schristos static int
i_to_y(char * ep,const char * d,const char * a,size_t lev)55597ccbe26Schristos i_to_y(char *ep, const char *d, const char *a, size_t lev)
556fcc96823Sperry {
557fcc96823Sperry
558fcc96823Sperry if (ep[-1] == 'i') {
559fcc96823Sperry ep[-1] = 'y';
560fcc96823Sperry a = d;
561fcc96823Sperry }
56297ccbe26Schristos return strip(ep, "", a, lev);
563fcc96823Sperry }
564fcc96823Sperry
56597ccbe26Schristos static int
es(char * ep,const char * d,const char * a,size_t lev)56697ccbe26Schristos es(char *ep, const char *d, const char *a, size_t lev)
567fcc96823Sperry {
568fcc96823Sperry
569fcc96823Sperry if (lev > DLEV)
57097ccbe26Schristos return 0;
571fcc96823Sperry
572fcc96823Sperry switch (ep[-1]) {
573fcc96823Sperry default:
57497ccbe26Schristos return 0;
575fcc96823Sperry case 'i':
57697ccbe26Schristos return i_to_y(ep, d, a, lev);
577fcc96823Sperry case 's':
578fcc96823Sperry case 'h':
579fcc96823Sperry case 'z':
580fcc96823Sperry case 'x':
58197ccbe26Schristos return strip(ep, d, a, lev);
582fcc96823Sperry }
583fcc96823Sperry }
584fcc96823Sperry
58597ccbe26Schristos static int
metry(char * ep,const char * d,const char * a,size_t lev)58697ccbe26Schristos metry(char *ep, const char *d, const char *a, size_t lev)
587fcc96823Sperry {
588fcc96823Sperry
589fcc96823Sperry ep[-2] = 'e';
590fcc96823Sperry ep[-1] = 'r';
59197ccbe26Schristos return strip(ep, d, a, lev);
592fcc96823Sperry }
593fcc96823Sperry
59497ccbe26Schristos static int
tion(char * ep,const char * d,const char * a,size_t lev)59597ccbe26Schristos tion(char *ep, const char *d, const char *a, size_t lev)
596fcc96823Sperry {
597fcc96823Sperry
598fcc96823Sperry switch (ep[-2]) {
599fcc96823Sperry case 'c':
600fcc96823Sperry case 'r':
60197ccbe26Schristos return trypref(ep, a, lev);
602fcc96823Sperry case 'a':
60397ccbe26Schristos return y_to_e(ep, d, a, lev);
604fcc96823Sperry }
60597ccbe26Schristos return 0;
606fcc96823Sperry }
607fcc96823Sperry
608fcc96823Sperry /*
609fcc96823Sperry * Possible consonant-consonant-e ending.
610fcc96823Sperry */
61197ccbe26Schristos static int
CCe(char * ep,const char * d,const char * a,size_t lev)61297ccbe26Schristos CCe(char *ep, const char *d, const char *a, size_t lev)
613fcc96823Sperry {
614fcc96823Sperry
615fcc96823Sperry switch (ep[-1]) {
616fcc96823Sperry case 'l':
617fcc96823Sperry if (vowel(ep[-2]))
618fcc96823Sperry break;
619fcc96823Sperry switch (ep[-2]) {
620fcc96823Sperry case 'l':
621fcc96823Sperry case 'r':
622fcc96823Sperry case 'w':
623fcc96823Sperry break;
624fcc96823Sperry default:
62597ccbe26Schristos return y_to_e(ep, d, a, lev);
626fcc96823Sperry }
627fcc96823Sperry break;
628fcc96823Sperry case 's':
629fcc96823Sperry if (ep[-2] == 's')
630fcc96823Sperry break;
63197ccbe26Schristos /*FALLTHROUGH*/
632fcc96823Sperry case 'c':
633fcc96823Sperry case 'g':
634fcc96823Sperry if (*ep == 'a')
63597ccbe26Schristos return 0;
63697ccbe26Schristos /*FALLTHROUGH*/
637fcc96823Sperry case 'v':
638fcc96823Sperry case 'z':
639fcc96823Sperry if (vowel(ep[-2]))
640fcc96823Sperry break;
64197ccbe26Schristos /*FALLTHROUGH*/
642fcc96823Sperry case 'u':
643fcc96823Sperry if (y_to_e(ep, d, a, lev))
64497ccbe26Schristos return 1;
645fcc96823Sperry if (!(ep[-2] == 'n' && ep[-1] == 'g'))
64697ccbe26Schristos return 0;
647fcc96823Sperry }
64897ccbe26Schristos return VCe(ep, d, a, lev);
649fcc96823Sperry }
650fcc96823Sperry
651fcc96823Sperry /*
652fcc96823Sperry * Possible consonant-vowel-consonant-e ending.
653fcc96823Sperry */
65497ccbe26Schristos static int
VCe(char * ep,const char * d,const char * a,size_t lev)65597ccbe26Schristos VCe(char *ep, const char *d, const char *a, size_t lev)
656fcc96823Sperry {
657fcc96823Sperry char c;
658fcc96823Sperry
659fcc96823Sperry c = ep[-1];
660fcc96823Sperry if (c == 'e')
66197ccbe26Schristos return 0;
662fcc96823Sperry if (!vowel(c) && vowel(ep[-2])) {
663fcc96823Sperry c = *ep;
664fcc96823Sperry *ep++ = 'e';
665fcc96823Sperry if (trypref(ep, d, lev) || suffix(ep, lev))
66697ccbe26Schristos return 1;
667fcc96823Sperry ep--;
668fcc96823Sperry *ep = c;
669fcc96823Sperry }
67097ccbe26Schristos return strip(ep, d, a, lev);
671fcc96823Sperry }
672fcc96823Sperry
67397ccbe26Schristos static const char *
lookuppref(char ** wp,char * ep)674fcc96823Sperry lookuppref(char **wp, char *ep)
675fcc96823Sperry {
67697ccbe26Schristos const char **sp, *cp;
67797ccbe26Schristos char *bp;
678fcc96823Sperry
679fcc96823Sperry for (sp = preftab; *sp; sp++) {
680fcc96823Sperry bp = *wp;
681fcc96823Sperry for (cp = *sp; *cp; cp++, bp++) {
682fcc96823Sperry if (tolower((unsigned char)*bp) != *cp)
683fcc96823Sperry goto next;
684fcc96823Sperry }
685fcc96823Sperry for (cp = bp; cp < ep; cp++) {
686fcc96823Sperry if (vowel(*cp)) {
687fcc96823Sperry *wp = bp;
68897ccbe26Schristos return *sp;
689fcc96823Sperry }
690fcc96823Sperry }
691fcc96823Sperry next: ;
692fcc96823Sperry }
69397ccbe26Schristos return 0;
694fcc96823Sperry }
695fcc96823Sperry
696fcc96823Sperry /*
697fcc96823Sperry * If the word is not in the dictionary, try stripping off prefixes
698fcc96823Sperry * until the word is found or we run out of prefixes to check.
699fcc96823Sperry */
70097ccbe26Schristos static int
trypref(char * ep,const char * a,size_t lev)70197ccbe26Schristos trypref(char *ep, const char *a, size_t lev)
702fcc96823Sperry {
70397ccbe26Schristos const char *cp;
704fcc96823Sperry char *bp;
705fcc96823Sperry char *pp;
706fcc96823Sperry int val = 0;
707fcc96823Sperry char space[20];
708fcc96823Sperry
70997ccbe26Schristos getderiv(lev + 2);
71097ccbe26Schristos deriv.buf[lev] = a;
711fcc96823Sperry if (tryword(word, ep, lev))
71297ccbe26Schristos return 1;
713fcc96823Sperry bp = word;
714fcc96823Sperry pp = space;
71597ccbe26Schristos deriv.buf[lev + 1] = pp;
71697ccbe26Schristos while ((cp = lookuppref(&bp, ep)) != NULL) {
717fcc96823Sperry *pp++ = '+';
718fcc96823Sperry while ((*pp = *cp++))
719fcc96823Sperry pp++;
720fcc96823Sperry if (tryword(bp, ep, lev + 1)) {
721fcc96823Sperry val = 1;
722fcc96823Sperry break;
723fcc96823Sperry }
724fcc96823Sperry if (pp - space >= sizeof(space))
72597ccbe26Schristos return 0;
726fcc96823Sperry }
727eb3bd1d5Sjoerg deriv.buf[lev + 1] = deriv.buf[lev + 2] = NULL;
72897ccbe26Schristos return val;
729fcc96823Sperry }
730fcc96823Sperry
73197ccbe26Schristos static int
tryword(char * bp,char * ep,size_t lev)73297ccbe26Schristos tryword(char *bp, char *ep, size_t lev)
733fcc96823Sperry {
73497ccbe26Schristos size_t i, j;
735fcc96823Sperry char duple[3];
736fcc96823Sperry
737fcc96823Sperry if (ep-bp <= 1)
73897ccbe26Schristos return 0;
739fcc96823Sperry if (vowel(*ep) && monosyl(bp, ep))
74097ccbe26Schristos return 0;
741fcc96823Sperry
742fcc96823Sperry i = dict(bp, ep);
74397ccbe26Schristos if (i == 0 && vowel(*ep) && ep[-1] == ep[-2] &&
74497ccbe26Schristos monosyl(bp, ep - 1)) {
745fcc96823Sperry ep--;
74697ccbe26Schristos getderiv(++lev);
74797ccbe26Schristos deriv.buf[lev] = duple;
748fcc96823Sperry duple[0] = '+';
749fcc96823Sperry duple[1] = *ep;
750fcc96823Sperry duple[2] = '\0';
751fcc96823Sperry i = dict(bp, ep);
752fcc96823Sperry }
753fcc96823Sperry if (vflag == 0 || i == 0)
75497ccbe26Schristos return i;
755fcc96823Sperry
756fcc96823Sperry /* Also tack on possible derivations. (XXX - warn on truncation?) */
757fcc96823Sperry for (j = lev; j > 0; j--) {
75897ccbe26Schristos if (deriv.buf[j])
75997ccbe26Schristos (void)strlcat(affix, deriv.buf[j], sizeof(affix));
760fcc96823Sperry }
76197ccbe26Schristos return i;
762fcc96823Sperry }
763fcc96823Sperry
76497ccbe26Schristos static int
monosyl(char * bp,char * ep)765fcc96823Sperry monosyl(char *bp, char *ep)
766fcc96823Sperry {
767fcc96823Sperry
768fcc96823Sperry if (ep < bp + 2)
76997ccbe26Schristos return 0;
770fcc96823Sperry if (vowel(*--ep) || !vowel(*--ep) || ep[1] == 'x' || ep[1] == 'w')
77197ccbe26Schristos return 0;
772fcc96823Sperry while (--ep >= bp)
773fcc96823Sperry if (vowel(*ep))
77497ccbe26Schristos return 0;
77597ccbe26Schristos return 1;
776fcc96823Sperry }
777fcc96823Sperry
77897ccbe26Schristos static char *
skipv(char * st)77997ccbe26Schristos skipv(char *st)
780fcc96823Sperry {
781fcc96823Sperry
78297ccbe26Schristos if (st >= word && vowel(*st))
78397ccbe26Schristos st--;
78497ccbe26Schristos while (st >= word && !vowel(*st))
78597ccbe26Schristos st--;
78697ccbe26Schristos return st;
787fcc96823Sperry }
788fcc96823Sperry
78997ccbe26Schristos static int
vowel(int c)790fcc96823Sperry vowel(int c)
791fcc96823Sperry {
792fcc96823Sperry
793fcc96823Sperry switch (tolower(c)) {
794fcc96823Sperry case 'a':
795fcc96823Sperry case 'e':
796fcc96823Sperry case 'i':
797fcc96823Sperry case 'o':
798fcc96823Sperry case 'u':
799fcc96823Sperry case 'y':
80097ccbe26Schristos return 1;
801fcc96823Sperry }
80297ccbe26Schristos return 0;
803fcc96823Sperry }
804fcc96823Sperry
805fcc96823Sperry /*
806fcc96823Sperry * Crummy way to Britishise.
807fcc96823Sperry */
80897ccbe26Schristos static void
ise(void)809fcc96823Sperry ise(void)
810fcc96823Sperry {
8112d45339eSchristos struct suftab *tab;
81297ccbe26Schristos char *cp;
813fcc96823Sperry
814fcc96823Sperry for (tab = suftab; tab->suf; tab++) {
815fcc96823Sperry /* Assume that suffix will contain 'z' if a1 or d1 do */
816fcc96823Sperry if (strchr(tab->suf, 'z')) {
81797ccbe26Schristos tab->suf = cp = estrdup(tab->suf);
81897ccbe26Schristos ztos(cp);
819fcc96823Sperry if (strchr(tab->d1, 'z')) {
82097ccbe26Schristos tab->d1 = cp = estrdup(tab->d1);
82197ccbe26Schristos ztos(cp);
822fcc96823Sperry }
823fcc96823Sperry if (strchr(tab->a1, 'z')) {
82497ccbe26Schristos tab->a1 = cp = estrdup(tab->a1);
82597ccbe26Schristos ztos(cp);
826fcc96823Sperry }
827fcc96823Sperry }
828fcc96823Sperry }
829fcc96823Sperry }
830fcc96823Sperry
83197ccbe26Schristos static void
ztos(char * st)83297ccbe26Schristos ztos(char *st)
833fcc96823Sperry {
834fcc96823Sperry
83597ccbe26Schristos for (; *st; st++)
83697ccbe26Schristos if (*st == 'z')
83797ccbe26Schristos *st = 's';
838fcc96823Sperry }
839fcc96823Sperry
840fcc96823Sperry /*
841fcc96823Sperry * Look up a word in the dictionary.
842fcc96823Sperry * Returns 1 if found, 0 if not.
843fcc96823Sperry */
84497ccbe26Schristos static int
dict(char * bp,char * ep)845fcc96823Sperry dict(char *bp, char *ep)
846fcc96823Sperry {
847fcc96823Sperry char c;
848fcc96823Sperry int i, rval;
849fcc96823Sperry
850fcc96823Sperry c = *ep;
851fcc96823Sperry *ep = '\0';
852fcc96823Sperry if (xflag)
853fcc96823Sperry printf("=%s\n", bp);
854fcc96823Sperry for (i = rval = 0; wlists[i].fd != -1; i++) {
855fcc96823Sperry if ((rval = look((unsigned char *)bp, wlists[i].front,
856fcc96823Sperry wlists[i].back)) == 1)
857fcc96823Sperry break;
858fcc96823Sperry }
859fcc96823Sperry *ep = c;
86097ccbe26Schristos return rval;
861fcc96823Sperry }
862fcc96823Sperry
86397ccbe26Schristos static void
getderiv(size_t lev)86497ccbe26Schristos getderiv(size_t lev)
86597ccbe26Schristos {
86697ccbe26Schristos if (deriv.maxlev < lev) {
867*8d9348aaSnia if (reallocarr(&deriv.buf, lev, sizeof(*deriv.buf)) != 0)
86897ccbe26Schristos err(1, "Cannot grow array");
86997ccbe26Schristos deriv.maxlev = lev;
87097ccbe26Schristos }
87197ccbe26Schristos }
87297ccbe26Schristos
87397ccbe26Schristos
87497ccbe26Schristos static void
usage(void)875fcc96823Sperry usage(void)
876fcc96823Sperry {
87797ccbe26Schristos (void)fprintf(stderr,
87897ccbe26Schristos "Usage: %s [-bvx] [-o found-words] word-list ...\n",
87997ccbe26Schristos getprogname());
880fcc96823Sperry exit(1);
881fcc96823Sperry }
882