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