xref: /illumos-gate/usr/src/cmd/spell/spellprog.c (revision 66582b606a8194f7f3ba5b3a3a6dca5b0d346361)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2015 Gary Mills
24  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
25  * Use is subject to license terms.
26  */
27 
28 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
29 /*	  All Rights Reserved  	*/
30 
31 #include <stdlib.h>
32 #include <unistd.h>
33 #include <limits.h>
34 #include <string.h>
35 #include <stdio.h>
36 #include <ctype.h>
37 #include <locale.h>
38 #include "hash.h"
39 
40 #define	Tolower(c) (isupper(c)?tolower(c):c)
41 #define	DLEV 2
42 
43 /*
44  * ANSI prototypes
45  */
46 static int	ily(char *, char *, char *, int);
47 static int	s(char *, char *, char *, int);
48 static int	es(char *, char *, char *, int);
49 static int	subst(char *, char *, char *, int);
50 static int	nop(void);
51 static int	bility(char *, char *, char *, int);
52 static int	i_to_y(char *, char *, char *, int);
53 static int	CCe(char *, char *, char *, int);
54 static int	y_to_e(char *, char *, char *, int);
55 static int	strip(char *, char *, char *, int);
56 static int	ize(char *, char *, char *, int);
57 static int	tion(char *, char *, char *, int);
58 static int	an(char *, char *, char *, int);
59 int		prime(char *);
60 static int	tryword(char *, char *, int);
61 static int	trypref(char *, char *, int);
62 static int	trysuff(char *, int);
63 static int	vowel(int);
64 static int	dict(char *, char *);
65 static int	monosyl(char *, char *);
66 static int	VCe(char *, char *, char *, int);
67 static char	*skipv(char *);
68 
69 struct suftab {
70 	char *suf;
71 	int (*p1)();
72 	int n1;
73 	char *d1;
74 	char *a1;
75 	int (*p2)();
76 	int n2;
77 	char *d2;
78 	char *a2;
79 };
80 
81 static struct suftab sufa[] = {
82 	{"ssen", ily, 4, "-y+iness", "+ness" },
83 	{"ssel", ily, 4, "-y+i+less", "+less" },
84 	{"se", s, 1, "", "+s", 	es, 2, "-y+ies", "+es" },
85 	{"s'", s, 2, "", "+'s"},
86 	{"s", s, 1, "", "+s"},
87 	{"ecn", subst, 1, "-t+ce", ""},
88 	{"ycn", subst, 1, "-t+cy", ""},
89 	{"ytilb", nop, 0, "", ""},
90 	{"ytilib", bility, 5, "-le+ility", ""},
91 	{"elbaif", i_to_y, 4, "-y+iable", ""},
92 	{"elba", CCe, 4, "-e+able", "+able"},
93 	{"yti", CCe, 3, "-e+ity", "+ity"},
94 	{"ylb", y_to_e, 1, "-e+y", ""},
95 	{"yl", ily, 2, "-y+ily", "+ly"},
96 	{"laci", strip, 2, "", "+al"},
97 	{"latnem", strip, 2, "", "+al"},
98 	{"lanoi", strip, 2, "", "+al"},
99 	{"tnem", strip, 4, "", "+ment"},
100 	{"gni", CCe, 3, "-e+ing", "+ing"},
101 	{"reta", nop, 0, "", ""},
102 	{"retc", nop, 0, "", ""},
103 	{"re", strip, 1, "", "+r", i_to_y, 2, "-y+ier", "+er"},
104 	{"de", strip, 1, "", "+d", i_to_y, 2, "-y+ied", "+ed"},
105 	{"citsi", strip, 2, "", "+ic"},
106 	{"citi", ize, 1, "-ic+e", ""},
107 	{"cihparg", i_to_y, 1, "-y+ic", ""},
108 	{"tse", strip, 2, "", "+st", 	i_to_y, 3, "-y+iest", "+est"},
109 	{"cirtem", i_to_y, 1, "-y+ic", ""},
110 	{"yrtem", subst, 0, "-er+ry", ""},
111 	{"cigol", i_to_y, 1, "-y+ic", ""},
112 	{"tsigol", i_to_y, 2, "-y+ist", ""},
113 	{"tsi", CCe, 3, "-e+ist", "+ist"},
114 	{"msi", CCe, 3, "-e+ism", "+ist"},
115 	{"noitacifi", i_to_y, 6, "-y+ication", ""},
116 	{"noitazi", ize, 4, "-e+ation", ""},
117 	{"rota", tion, 2, "-e+or", ""},
118 	{"rotc", tion, 2, "", "+or"},
119 	{"noit", tion, 3, "-e+ion", "+ion"},
120 	{"naino", an, 3, "", "+ian"},
121 	{"na", an, 1, "", "+n"},
122 	{"evi", subst, 0, "-ion+ive", ""},
123 	{"ezi", CCe, 3, "-e+ize", "+ize"},
124 	{"pihs", strip, 4, "", "+ship"},
125 	{"dooh", ily, 4, "-y+ihood", "+hood"},
126 	{"luf", ily, 3, "-y+iful", "+ful"},
127 	{"ekil", strip, 4, "", "+like"},
128 	0
129 };
130 
131 static struct suftab sufb[] = {
132 	{"ssen", ily, 4, "-y+iness", "+ness" },
133 	{"ssel", ily, 4, "-y+i+less", "+less" },
134 	{"se", s, 1, "", "+s", 	es, 2, "-y+ies", "+es" },
135 	{"s'", s, 2, "", "+'s"},
136 	{"s", s, 1, "", "+s"},
137 	{"ecn", subst, 1, "-t+ce", ""},
138 	{"ycn", subst, 1, "-t+cy", ""},
139 	{"ytilb", nop, 0, "", ""},
140 	{"ytilib", bility, 5, "-le+ility", ""},
141 	{"elbaif", i_to_y, 4, "-y+iable", ""},
142 	{"elba", CCe, 4, "-e+able", "+able"},
143 	{"yti", CCe, 3, "-e+ity", "+ity"},
144 	{"ylb", y_to_e, 1, "-e+y", ""},
145 	{"yl", ily, 2, "-y+ily", "+ly"},
146 	{"laci", strip, 2, "", "+al"},
147 	{"latnem", strip, 2, "", "+al"},
148 	{"lanoi", strip, 2, "", "+al"},
149 	{"tnem", strip, 4, "", "+ment"},
150 	{"gni", CCe, 3, "-e+ing", "+ing"},
151 	{"reta", nop, 0, "", ""},
152 	{"retc", 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 	{"citi", ize, 1, "-ic+e", ""},
157 	{"cihparg", i_to_y, 1, "-y+ic", ""},
158 	{"tse", strip, 2, "", "+st", 	i_to_y, 3, "-y+iest", "+est"},
159 	{"cirtem", i_to_y, 1, "-y+ic", ""},
160 	{"yrtem", subst, 0, "-er+ry", ""},
161 	{"cigol", i_to_y, 1, "-y+ic", ""},
162 	{"tsigol", i_to_y, 2, "-y+ist", ""},
163 	{"tsi", CCe, 3, "-e+ist", "+ist"},
164 	{"msi", CCe, 3, "-e+ism", "+ist"},
165 	{"noitacifi", i_to_y, 6, "-y+ication", ""},
166 	{"noitasi", ize, 4, "-e+ation", ""},
167 	{"rota", tion, 2, "-e+or", ""},
168 	{"rotc", tion, 2, "", "+or"},
169 	{"noit", tion, 3, "-e+ion", "+ion"},
170 	{"naino", an, 3, "", "+ian"},
171 	{"na", an, 1, "", "+n"},
172 	{"evi", subst, 0, "-ion+ive", ""},
173 	{"esi", CCe, 3, "-e+ise", "+ise"},
174 	{"pihs", strip, 4, "", "+ship"},
175 	{"dooh", ily, 4, "-y+ihood", "+hood"},
176 	{"luf", ily, 3, "-y+iful", "+ful"},
177 	{"ekil", strip, 4, "", "+like"},
178 	0
179 };
180 
181 static char *preftab[] = {
182 	"anti",
183 	"auto",
184 	"bio",
185 	"counter",
186 	"dis",
187 	"electro",
188 	"en",
189 	"fore",
190 	"geo",
191 	"hyper",
192 	"intra",
193 	"inter",
194 	"iso",
195 	"kilo",
196 	"magneto",
197 	"meta",
198 	"micro",
199 	"mid",
200 	"milli",
201 	"mis",
202 	"mono",
203 	"multi",
204 	"non",
205 	"out",
206 	"over",
207 	"photo",
208 	"poly",
209 	"pre",
210 	"pseudo",
211 	"psycho",
212 	"re",
213 	"semi",
214 	"stereo",
215 	"sub",
216 	"super",
217 	"tele",
218 	"thermo",
219 	"ultra",
220 	"under",	/* must precede un */
221 	"un",
222 	0
223 };
224 
225 static int bflag;
226 static int vflag;
227 static int xflag;
228 static struct suftab *suftab;
229 static char *prog;
230 static char word[LINE_MAX];
231 static char original[LINE_MAX];
232 static char *deriv[LINE_MAX];
233 static char affix[LINE_MAX];
234 static FILE *file, *found;
235 /*
236  *	deriv is stack of pointers to notes like +micro +ed
237  *	affix is concatenated string of notes
238  *	the buffer size 141 stems from the sizes of original and affix.
239  */
240 
241 /*
242  *	in an attempt to defray future maintenance misunderstandings, here is
243  *	an attempt to describe the input/output expectations of the spell
244  *	program.
245  *
246  *	spellprog is intended to be called from the shell file spell.
247  *	because of this, there is little error checking (this is historical, not
248  *	necessarily advisable).
249  *
250  *	spellprog options hashed-list pass
251  *
252  *	the hashed-list is a list of the form made by spellin.
253  *	there are 2 types of hashed lists:
254  *		1. a stop list: this specifies words that by the rules embodied
255  *		   in spellprog would be recognized as correct, BUT are really
256  *		   errors.
257  *		2. a dictionary of correctly spelled words.
258  *	the pass number determines how the words found in the specified
259  *	hashed-list are treated. If the pass number is 1, the hashed-list is
260  *	treated as the stop-list, otherwise, it is treated as the regular
261  *	dictionary list. in this case, the value of "pass" is a filename. Found
262  *	words are written to this file.
263  *
264  *	In the normal case, the filename = /dev/null. However, if the v option
265  *	is specified, the derivations are written to this file.
266  *	The spellprog looks up words in the hashed-list; if a word is found, it
267  *	is printed to the stdout. If the hashed-list was the stop-list, the
268  *	words found are presumed to be misspellings. in this case,
269  *	a control character is printed ( a "-" is appended to the word.
270  *	a hyphen will never occur naturally in the input list because deroff
271  *	is used in the shell file before calling spellprog.)
272  *	If the regualar spelling list was used (hlista or hlistb), the words
273  *	are correct, and may be ditched. (unless the -v option was used -
274  *	see the manual page).
275  *
276  *	spellprog should be called twice : first with the stop-list, to flag all
277  *	a priori incorrectly spelled words; second with the dictionary.
278  *
279  *	spellprog hstop 1 |\
280  *	spellprog hlista /dev/null
281  *
282  *	for a complete scenario, see the shell file: spell.
283  *
284  */
285 
286 int
287 main(int argc, char **argv)
288 {
289 	char *ep, *cp;
290 	char *dp;
291 	int fold;
292 	int c, j;
293 	int pass;
294 
295 	/* Set locale environment variables local definitions */
296 	(void) setlocale(LC_ALL, "");
297 #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
298 #define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it wasn't */
299 #endif
300 	(void) textdomain(TEXT_DOMAIN);
301 
302 
303 	prog = argv[0];
304 	while ((c = getopt(argc, argv, "bvx")) != EOF) {
305 		switch (c) {
306 		case 'b':
307 			bflag++;
308 			break;
309 		case 'v':
310 			vflag++;
311 			break;
312 		case 'x':
313 			xflag++;
314 			break;
315 		}
316 	}
317 
318 	argc -= optind;
319 	argv = &argv[optind];
320 
321 	if ((argc < 2) || !prime(*argv)) {
322 		(void) fprintf(stderr,
323 		    gettext("%s: cannot initialize hash table\n"), prog);
324 		exit(1);
325 	}
326 	argc--;
327 	argv++;
328 
329 	/* Select the correct suffix table */
330 	suftab = (bflag == 0) ? sufa : sufb;
331 
332 /*
333  *	if pass is not 1, it is assumed to be a filename.
334  *	found words are written to this file.
335  */
336 	pass = **argv;
337 	if (pass != '1')
338 		found = fopen(*argv, "w");
339 
340 	for (;;) {
341 		affix[0] = 0;
342 		file = stdout;
343 		for (ep = word; (*ep = j = getchar()) != '\n'; ep++)
344 			if (j == EOF)
345 				exit(0);
346 /*
347  *	here is the hyphen processing. these words were found in the stop
348  *	list. however, if they exist as is, (no derivations tried) in the
349  *	dictionary, let them through as correct.
350  *
351  */
352 		if (ep[-1] == '-') {
353 			*--ep = 0;
354 			if (!tryword(word, ep, 0))
355 				(void) fprintf(file, "%s\n", word);
356 			continue;
357 		}
358 		for (cp = word, dp = original; cp < ep; )
359 			*dp++ = *cp++;
360 		*dp = 0;
361 		fold = 0;
362 		for (cp = word; cp < ep; cp++)
363 			if (islower(*cp))
364 				goto lcase;
365 		if (((ep - word) == 1) &&
366 		    ((word[0] == 'A') || (word[0] == 'I')))
367 			continue;
368 		if (trypref(ep, ".", 0))
369 			goto foundit;
370 		++fold;
371 		for (cp = original+1, dp = word+1; dp < ep; dp++, cp++)
372 			*dp = Tolower(*cp);
373 lcase:
374 		if (((ep - word) == 1) && (word[0] == 'a'))
375 			continue;
376 		if (trypref(ep, ".", 0)||trysuff(ep, 0))
377 			goto foundit;
378 		if (isupper(word[0])) {
379 			for (cp = original, dp = word; *dp = *cp++; dp++)
380 				if (fold) *dp = Tolower(*dp);
381 			word[0] = Tolower(word[0]);
382 			goto lcase;
383 		}
384 		(void) fprintf(file, "%s\n", original);
385 		continue;
386 
387 foundit:
388 		if (pass == '1')
389 			(void) fprintf(file, "%s-\n", original);
390 		else if (affix[0] != 0 && affix[0] != '.') {
391 			file = found;
392 			(void) fprintf(file, "%s\t%s\n", affix,
393 			    original);
394 		}
395 	}
396 }
397 
398 /*
399  *	strip exactly one suffix and do
400  *	indicated routine(s), which may recursively
401  *	strip suffixes
402  */
403 
404 static int
405 trysuff(char *ep, int lev)
406 {
407 	struct suftab	*t;
408 	char *cp, *sp;
409 
410 	lev += DLEV;
411 	deriv[lev] = deriv[lev-1] = 0;
412 	for (t = &suftab[0]; (t != 0 && (sp = t->suf) != 0); t++) {
413 		cp = ep;
414 		while (*sp)
415 			if (*--cp != *sp++)
416 				goto next;
417 		for (sp = cp; --sp >= word && !vowel(*sp); )
418 			;
419 		if (sp < word)
420 			return (0);
421 		if ((*t->p1)(ep-t->n1, t->d1, t->a1, lev+1))
422 			return (1);
423 		if (t->p2 != 0) {
424 			deriv[lev] = deriv[lev+1] = 0;
425 			return ((*t->p2)(ep-t->n2, t->d2, t->a2, lev));
426 		}
427 		return (0);
428 next:;
429 	}
430 	return (0);
431 }
432 
433 static int
434 nop(void)
435 {
436 	return (0);
437 }
438 
439 /* ARGSUSED */
440 static int
441 strip(char *ep, char *d, char *a, int lev)
442 {
443 	return (trypref(ep, a, lev)||trysuff(ep, lev));
444 }
445 
446 static int
447 s(char *ep, char *d, char *a, int lev)
448 {
449 	if (lev > DLEV+1)
450 		return (0);
451 	if (*ep == 's' && ep[-1] == 's')
452 		return (0);
453 	return (strip(ep, d, a, lev));
454 }
455 
456 /* ARGSUSED */
457 static int
458 an(char *ep, char *d, char *a, int lev)
459 {
460 	if (!isupper(*word))	/* must be proper name */
461 		return (0);
462 	return (trypref(ep, a, lev));
463 }
464 
465 /* ARGSUSED */
466 static int
467 ize(char *ep, char *d, char *a, int lev)
468 {
469 	ep[-1] = 'e';
470 	return (strip(ep, "", d, lev));
471 }
472 
473 /* ARGSUSED */
474 static int
475 y_to_e(char *ep, char *d, char *a, int lev)
476 {
477 	*ep++ = 'e';
478 	return (strip(ep, "", d, lev));
479 }
480 
481 static int
482 ily(char *ep, char *d, char *a, int lev)
483 {
484 	if (ep[-1] == 'i')
485 		return (i_to_y(ep, d, a, lev));
486 	else
487 		return (strip(ep, d, a, lev));
488 }
489 
490 static int
491 bility(char *ep, char *d, char *a, int lev)
492 {
493 	*ep++ = 'l';
494 	return (y_to_e(ep, d, a, lev));
495 }
496 
497 static int
498 i_to_y(char *ep, char *d, char *a, int lev)
499 {
500 	if (ep[-1] == 'i') {
501 		ep[-1] = 'y';
502 		a = d;
503 	}
504 	return (strip(ep, "", a, lev));
505 }
506 
507 static int
508 es(char *ep, char *d, char *a, int lev)
509 {
510 	if (lev > DLEV)
511 		return (0);
512 	switch (ep[-1]) {
513 	default:
514 		return (0);
515 	case 'i':
516 		return (i_to_y(ep, d, a, lev));
517 	case 's':
518 	case 'h':
519 	case 'z':
520 	case 'x':
521 		return (strip(ep, d, a, lev));
522 	}
523 }
524 
525 /* ARGSUSED */
526 static int
527 subst(char *ep, char *d, char *a, int lev)
528 {
529 	char *u, *t;
530 
531 	if (skipv(skipv(ep-1)) < word)
532 		return (0);
533 	for (t = d; *t != '+'; t++)
534 		continue;
535 	for (u = ep; *--t != '-'; )
536 		*--u = *t;
537 	return (strip(ep, "", d, lev));
538 }
539 
540 
541 static int
542 tion(char *ep, char *d, char *a, int lev)
543 {
544 	switch (ep[-2]) {
545 	case 'c':
546 	case 'r':
547 		return (trypref(ep, a, lev));
548 	case 'a':
549 		return (y_to_e(ep, d, a, lev));
550 	}
551 	return (0);
552 }
553 
554 /*	possible consonant-consonant-e ending */
555 static int
556 CCe(char *ep, char *d, char *a, int lev)
557 {
558 	switch (ep[-1]) {
559 	case 'r':
560 		if (ep[-2] == 't')
561 			return (y_to_e(ep, d, a, lev));
562 		break;
563 	case 'l':
564 		if (vowel(ep[-2]))
565 			break;
566 		switch (ep[-2]) {
567 		case 'l':
568 		case 'r':
569 		case 'w':
570 			break;
571 		default:
572 			return (y_to_e(ep, d, a, lev));
573 		}
574 		break;
575 	case 's':
576 		if (ep[-2] == 's')
577 			break;
578 		if (*ep == 'a')
579 			return (0);
580 		if (vowel(ep[-2]))
581 			break;
582 		if (y_to_e(ep, d, a, lev))
583 			return (1);
584 		if (!(ep[-2] == 'n' && ep[-1] == 'g'))
585 			return (0);
586 		break;
587 	case 'c':
588 	case 'g':
589 		if (*ep == 'a')
590 			return (0);
591 		if (vowel(ep[-2]))
592 			break;
593 		if (y_to_e(ep, d, a, lev))
594 			return (1);
595 		if (!(ep[-2] == 'n' && ep[-1] == 'g'))
596 			return (0);
597 		break;
598 	case 'v':
599 	case 'z':
600 		if (vowel(ep[-2]))
601 			break;
602 		if (y_to_e(ep, d, a, lev))
603 			return (1);
604 		if (!(ep[-2] == 'n' && ep[-1] == 'g'))
605 			return (0);
606 		break;
607 	case 'u':
608 		if (y_to_e(ep, d, a, lev))
609 			return (1);
610 		if (!(ep[-2] == 'n' && ep[-1] == 'g'))
611 			return (0);
612 		break;
613 	}
614 	return (VCe(ep, d, a, lev));
615 }
616 
617 /*	possible consonant-vowel-consonant-e ending */
618 static int
619 VCe(char *ep, char *d, char *a, int lev)
620 {
621 	char c;
622 	c = ep[-1];
623 	if (c == 'e')
624 		return (0);
625 	if (!vowel(c) && vowel(ep[-2])) {
626 		c = *ep;
627 		*ep++ = 'e';
628 		if (trypref(ep, d, lev)||trysuff(ep, lev))
629 			return (1);
630 		ep--;
631 		*ep = c;
632 	}
633 	return (strip(ep, d, a, lev));
634 }
635 
636 static char *
637 lookuppref(char **wp, char *ep)
638 {
639 	char **sp;
640 	char *bp, *cp;
641 
642 	for (sp = preftab; *sp; sp++) {
643 		bp = *wp;
644 		for (cp = *sp; *cp; cp++, bp++)
645 			if (Tolower(*bp) != *cp)
646 				goto next;
647 		for (cp = bp; cp < ep; cp++)
648 			if (vowel(*cp)) {
649 				*wp = bp;
650 				return (*sp);
651 			}
652 next:;
653 	}
654 	return (0);
655 }
656 
657 /*
658  *	while word is not in dictionary try stripping
659  *	prefixes. Fail if no more prefixes.
660  */
661 static int
662 trypref(char *ep, char *a, int lev)
663 {
664 	char *cp;
665 	char *bp;
666 	char *pp;
667 	int val = 0;
668 	char space[LINE_MAX * 2];
669 	deriv[lev] = a;
670 	if (tryword(word, ep, lev))
671 		return (1);
672 	bp = word;
673 	pp = space;
674 	deriv[lev+1] = pp;
675 	while (cp = lookuppref(&bp, ep)) {
676 		*pp++ = '+';
677 		while (*pp = *cp++)
678 			pp++;
679 		if (tryword(bp, ep, lev+1)) {
680 			val = 1;
681 			break;
682 		}
683 	}
684 	deriv[lev+1] = deriv[lev+2] = 0;
685 	return (val);
686 }
687 
688 static int
689 tryword(char *bp, char *ep, int lev)
690 {
691 	int i, j;
692 	char duple[3];
693 	if (ep-bp <= 1)
694 		return (0);
695 	if (vowel(*ep)) {
696 		if (monosyl(bp, ep))
697 			return (0);
698 	}
699 	i = dict(bp, ep);
700 	if (i == 0 && vowel(*ep) && ep[-1] == ep[-2] && monosyl(bp, ep-1)) {
701 		ep--;
702 		deriv[++lev] = duple;
703 		duple[0] = '+';
704 		duple[1] = *ep;
705 		duple[2] = 0;
706 		i = dict(bp, ep);
707 	}
708 	if (vflag == 0 || i == 0)
709 		return (i);
710 	/*
711 	 *	when derivations are wanted, collect them
712 	 *	for printing
713 	 */
714 	j = lev;
715 	do {
716 		if (deriv[j])
717 			(void) strcat(affix, deriv[j]);
718 	} while (--j > 0);
719 	return (i);
720 }
721 
722 
723 static int
724 monosyl(char *bp, char *ep)
725 {
726 	if (ep < bp+2)
727 		return (0);
728 	if (vowel(*--ep) || !vowel(*--ep) || ep[1] == 'x' || ep[1] == 'w')
729 		return (0);
730 	while (--ep >= bp)
731 		if (vowel(*ep))
732 			return (0);
733 	return (1);
734 }
735 
736 static char *
737 skipv(char *s)
738 {
739 	if (s >= word&&vowel(*s))
740 		s--;
741 	while (s >= word && !vowel(*s))
742 		s--;
743 	return (s);
744 }
745 
746 static int
747 vowel(int c)
748 {
749 	switch (Tolower(c)) {
750 	case 'a':
751 	case 'e':
752 	case 'i':
753 	case 'o':
754 	case 'u':
755 	case 'y':
756 		return (1);
757 	}
758 	return (0);
759 }
760 
761 static int
762 dict(char *bp, char *ep)
763 {
764 	int temp, result;
765 	if (xflag)
766 		(void) fprintf(stdout, "=%.*s\n", ep-bp, bp);
767 	temp = *ep;
768 	*ep = 0;
769 	result = hashlook(bp);
770 	*ep = temp;
771 	return (result);
772 }
773