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