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