home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume38 / phonewrd / part01 / phonewrd.c < prev    next >
C/C++ Source or Header  |  1993-07-14  |  35KB  |  1,365 lines

  1. /*
  2.    Phonewrd - given a phone number, find all words or phrases in a dictionary
  3.     which fit it.  See the man page for details, type "phonewrd -?" for
  4.     options.
  5.  
  6.    version 2.0 - 6/23/93
  7.  
  8.    (c) copyright 1993, Eric Haines, all rights reserved (erich@eye.com)
  9.  
  10.  
  11.    History:
  12.  
  13.    2.0 - release for comp.sources.misc
  14.  */
  15.  
  16. #include <stdlib.h>    /* if you don't have stdlib, use malloc.h instead */
  17. #include <stdio.h>
  18.  
  19. /* define USE_STRINGS_H if you use <strings.h> instead of <string.h> */
  20. #ifdef USE_STRINGS_H
  21.     /* BSD string routines. */
  22. #   include <strings.h>
  23. /* Really, should not define USE_STRINGS_H if __STDC__, but be safe. */
  24. #ifndef __STDC__
  25. #   define strchr index
  26. #   define strrchr rindex
  27. #endif /* not __STDC__ */
  28. #else
  29.     /* SYS V string routines. */
  30. #   include <string.h>
  31. #endif /* USE_STRINGS_H */
  32.  
  33. #include "patchlevel.h"
  34.  
  35. /*============= default related =========================================== */
  36.  
  37. /* path to dictionary */
  38. #define    DICT_PATH    "/usr/dict/words"
  39.  
  40. /* number of digits in phone number */
  41. #define    NUM_DIGITS    7
  42.  
  43. /* how many numerals are allowed in our phrase? */
  44. #define    NUMERALS_ALLOWED    0
  45.  
  46. /* minimum length of words: note that 1 always gets bumped to 2, because of
  47.  * OneLetter below.
  48.  */
  49. #define    MIN_LENGTH    1
  50.  
  51. /* one letter words that are acceptable */
  52. #define    ONE_LETTER    "aio"
  53. /* If you like things like "CDB" (see the bee) or "IM4U, use or modify the
  54.  * array below.  Yes, "j" and "k" are potential names, "m" is only in "I am",
  55.  * "q" is queue, etc - you decide...  This option tends to generate lots of
  56.  * useless goop, but it's good if you're desperate.
  57.  */
  58. /* #define ONE_LETTER    "abcdgimoptuxy248" */
  59.  
  60. #define    NO_NUMBER    -2
  61.  
  62. /* to make Q and Z not be anything, set them to NO_NUMBER, else set them
  63.  * to the digit you want (e.g. 0 or 1)
  64.  */
  65. #define    Q    NO_NUMBER
  66. #define    Z    NO_NUMBER
  67.  
  68. /* translation table for letters to numbers */
  69. int    Letter2Numeral[] =
  70.     /* a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z */
  71.      { 2,2,2,3,3,3,4,4,4,5,5,5,6,6,6,7,Q,7,7,8,8,8,9,9,9,Z } ;
  72.  
  73. /* true if a character is a vowel */
  74. #define    Vowel(c)    strchr( "aeiou", (c) )
  75.  
  76. /*============= storage space related ===================================== */
  77.  
  78. #define MAX_DIGITS    14
  79. #define    MAX_INDICES    (((MAX_DIGITS+1)*MAX_DIGITS)/2)
  80.  
  81. /* increment for word list memory space */
  82. #define    WORD_LIST_SIZE    50
  83.  
  84. /*============= internal use constants ==================================== */
  85.  
  86. #define    FIXED_LETTER    -1
  87.  
  88. #define    BUFSIZE        256
  89. #ifndef    TRUE
  90. #define    TRUE    1
  91. #define    FALSE    0
  92. #endif
  93.  
  94. /*============= structures ================================================ */
  95.  
  96. /* array of words, indexed by digit location and length of word.  This list is
  97.  * built up in the first part of the program, as each dictionary word is
  98.  * categorized and put in its list.  Then this structure is traversed to make
  99.  * up the combinations.
  100.  */
  101. typedef    struct {
  102.     char    **p_word ;    /* list of pointers to words */
  103.     int    count ;        /* current count */
  104.     int    size ;        /* allocated size */
  105. } wordlist, *p_wordlist ;
  106.  
  107. /*============= data and macros =========================================== */
  108.  
  109. #define    IndexWL( digit_loc, length )    ( WLoffset[digit_loc] + length - 1 )
  110.  
  111. wordlist WL[MAX_INDICES] ;
  112. int    WLoffset[MAX_DIGITS] ;
  113. int    PhoneNum[MAX_DIGITS+1] ;    /* phone number translation */
  114. char    PhoneStr[MAX_DIGITS+1] ;    /* phone number letter, if specified */
  115. int    NumeralMapped[10] ;        /* if TRUE, then index # is mapped */
  116. char    OneLetter[BUFSIZE] ;
  117. char    *DictPath[100] ;
  118. int    DictTot ;
  119. char    *ProgName ;
  120.  
  121. int    NumDigits ;
  122. int    NumIndices ;
  123. int    NumNumerals ;
  124. int    MinLength ;
  125. int    RollOwn ;
  126. int    Concat ;
  127. int    Verbose ;
  128. int    MatchTot ;
  129. int    Wildcard ;
  130. int    TotBrk ;
  131.  
  132. int    HoldSize ;
  133. int    HoldCount ;
  134. char    **HoldWord ;
  135.  
  136. char    OutputGrid[MAX_DIGITS][27] ;
  137.  
  138. /*============= procedure declarations ==================================== */
  139.  
  140. int phone_check() ;
  141. void roll_own() ;
  142. void concat_it() ;
  143. void concat_letter_out() ;
  144. void concat_letter_breaks_out() ;
  145. void init_wl() ;
  146. int fit_word() ;
  147. int permute_word() ;
  148. void search_for_match() ;
  149. void hold_word() ;
  150. int store_word() ;
  151. void free_wl() ;
  152. int scan_options() ;
  153. char *str_duplicate() ;
  154.  
  155. /*============= procedures ================================================ */
  156.  
  157. main(argc,argv)
  158. int argc;  char *argv[];
  159. {
  160. int    nargc ;
  161. char    *nargv[BUFSIZE], *targv ;
  162. int    i, hdr_out, cont_flag ;
  163.  
  164.     ProgName = argv[0] ;
  165.  
  166.     HoldSize = HoldCount = 0 ;
  167.  
  168.     NumNumerals = NUMERALS_ALLOWED ;
  169.     MinLength = MIN_LENGTH ;
  170.     RollOwn = FALSE ;
  171.     Concat = 0 ;
  172.     Verbose = FALSE ;
  173.  
  174.     strcpy( OneLetter, ONE_LETTER ) ;
  175.     *DictPath = str_duplicate( DICT_PATH ) ;
  176.     if ( !(*DictPath) ) {
  177.     fprintf( stderr, "Ugh, we're out of memory!\n" ) ;
  178.     exit(1) ;
  179.     }
  180.     DictTot = 1 ;
  181.  
  182.     /* translate phone number */
  183.     if ( argc < 2 ) {
  184.     fprintf( stderr, "Not enough arguments.\n" ) ;
  185.     usage() ;
  186.     return( 1 ) ;
  187.     }
  188.  
  189.     cont_flag = scan_options( argc, argv, &nargc, nargv ) ;
  190.     if ( Verbose ) {
  191.     /* always print out the version, etc, even if errors occur */
  192.     fprintf( stdout, "%s %s, patch level %d\n",
  193.         argv[0], VERSION, PATCHLEVEL ) ;
  194.     }
  195.  
  196.     if ( !cont_flag ) {
  197.     /* bad scan option, so quit */
  198.     return( 1 ) ;
  199.     }
  200.  
  201.     if ( nargc < 1 ) {
  202.     fprintf( stderr, "No phone number found.\n" ) ;
  203.     usage() ;
  204.     return( 1 ) ;
  205.     }
  206.  
  207.     /* loop through phone number(s), output headers if more than one */
  208.     hdr_out = ( nargc > 1 ) ;
  209.     for ( i = 0 ; i < nargc ; i++ ) {
  210.     if ( i ) {
  211.         /* output carriage returns before 2nd, 3rd, etc */
  212.         fprintf( stdout, "\n" ) ;
  213.     }
  214.     targv = nargv[i] ;
  215.     if ( hdr_out ) {
  216.         fprintf( stdout, "# %s\n", targv ) ;
  217.     }
  218.     if ( phone_check( targv ) ) {
  219.         /* something very bad happened, so quit */
  220.         return( 1 ) ;
  221.     }
  222.     }
  223.  
  224.     return( 0 ) ;
  225. }
  226.  
  227. usage()
  228. {
  229.     fprintf(stderr, "usage: %s [options] phone#[*...]\n", ProgName);
  230.     fprintf(stderr, " [*...] - extra *'s at the end mean optional wildcard letters\n");
  231.     fprintf(stderr, "  -l # - minimum length of words (cur. == %d)\n", MinLength);
  232.     fprintf(stderr, "  -n # - number of numerals allowed in phrase (cur. == %d)\n", NumNumerals);
  233.     if ( Letter2Numeral['q'-'a'] == NO_NUMBER ) {
  234.     fprintf(stderr, "  -q # - mapping of q (none currently)\n" ) ;
  235.     } else {
  236.     fprintf(stderr, "  -q # - mapping of q (cur. == %d)\n",
  237.             Letter2Numeral['q'-'a'] ) ;
  238.     }
  239.     if ( Letter2Numeral['z'-'a'] == NO_NUMBER ) {
  240.     fprintf(stderr, "  -z # - mapping of z (none currently)\n" ) ;
  241.     } else {
  242.     fprintf(stderr, "  -z # - mapping of z (cur. == %d)\n",
  243.             Letter2Numeral['z'-'a'] ) ;
  244.     }
  245.     fprintf(stderr, "  -d string - dictionary path (cur. == %s)\n", *DictPath);
  246.     fprintf(stderr, "  -s string - allowed single letter words (cur. == \"%s\")\n", OneLetter);
  247.     fprintf(stderr, "  -m char[26] - mapping of entire alphabet\n");
  248.     fprintf(stderr, "  -r - output the corresponding letters in an array (no dictionary)\n");
  249.     fprintf(stderr, "  -c - output all combinations (no dictionary)\n");
  250.     fprintf(stderr, "  -C - output all combinations and spacings (no dictionary)\n");
  251.     fprintf(stderr, "  -v - verbose output (show words that do fit)\n");
  252. }
  253.  
  254.  
  255. int
  256. phone_check( numeral_string )
  257. char    *numeral_string ;
  258. {
  259. int    i, tot, word_count, length, min_length, found_digit, val, nn ;
  260. char    tchr, *tstr, **dp ;
  261. char    dict_word[BUFSIZE] ;
  262. char    out_string[BUFSIZE] ;
  263. p_wordlist    p_wl ;
  264. FILE    *infile ;
  265.  
  266.     NumDigits = 0 ;
  267.     TotBrk = -1 ;
  268.  
  269.     tstr = numeral_string ;
  270.     tchr = *tstr++ ;
  271.     found_digit = FALSE ;
  272.     while ( tchr != '\0' && tchr != '*' && (NumDigits <= MAX_DIGITS ) ) {
  273.     if ( ( tchr >= '0' ) && ( tchr <= '9' ) ) {
  274.         /* convert to digit */
  275.         PhoneNum[NumDigits] = (int)(tchr - '0') ;
  276.         PhoneStr[NumDigits++] = tchr ;
  277.         found_digit = TRUE ;
  278.     } else if ( ( (tchr >= 'A') && (tchr <= 'Z') ) ||
  279.             ( (tchr >= 'a') && (tchr <= 'z') ) ) {
  280.         PhoneNum[NumDigits] = FIXED_LETTER ;
  281.         PhoneStr[NumDigits++] = tolower( tchr ) ;
  282.     }
  283.     tchr = *tstr++ ;
  284.     }
  285.     PhoneStr[NumDigits]='\0' ;
  286.  
  287.     if ( NumDigits <= 0 ) {
  288.     fprintf( stderr, "Sorry, no digits input in `%s'\n", numeral_string ) ;
  289.     fprintf( stderr, "Recompile with a higher MAX_DIGITS setting.\n" ) ;
  290.     usage() ;
  291.     return(0) ;
  292.     }
  293.  
  294.     /* check if we're in roll own mode */
  295.     if ( RollOwn ) {
  296.     roll_own() ;
  297.     return(0) ;
  298.     }
  299.  
  300.     /* check if we're in concat output mode */
  301.     if ( Concat ) {
  302.     concat_it() ;
  303.     return(0) ;
  304.     }
  305.  
  306.     /* check if any numbers were input */
  307.     if ( !found_digit ) {
  308.     /* entirely alphabetic input, so translate to number */
  309.     for ( i = 0 ; i < NumDigits ; i++ ) {
  310.         val = Letter2Numeral[PhoneStr[i]-'a'] ;
  311.         if ( val != NO_NUMBER ) {
  312.         fprintf( stdout, "%d", val ) ;
  313.         } else {
  314.         fprintf( stdout, "*" ) ;
  315.         }
  316.     }
  317.     fprintf( stdout, "\n" ) ;
  318.     return(0) ;
  319.     }
  320.  
  321.     if ( tchr == '*' ) {
  322.     /* the phone number ends in a wild card - phrases can be longer */
  323.     Wildcard = 1 ;
  324.     while ( *tstr++ == '*' ) {
  325.         Wildcard++ ;
  326.     }
  327.     } else {
  328.     Wildcard = 0 ;
  329.     }
  330.  
  331.     if ( NumDigits > MAX_DIGITS ) {
  332.     fprintf(stderr, "Sorry, too many digits input in `%s'; ",
  333.         numeral_string);
  334.     fprintf(stderr, "only %d allowed.\n", MAX_DIGITS);
  335.     usage() ;
  336.     return(0) ;
  337.     }
  338.  
  339.     /* set up offset array for indexing WL */
  340.     tot = 0 ;
  341.     for ( i = 0 ; i < NumDigits ; i++ ) {
  342.     WLoffset[i] = tot ;
  343.     tot += (NumDigits-i) ;
  344.     }
  345.     NumIndices = ((NumDigits+1)*NumDigits)/2 ;
  346.  
  347.     /* build WL */
  348.     word_count = 0 ;
  349.     init_wl() ;
  350.     dp = DictPath ;
  351.     i = DictTot ;
  352.     while ( i-- ) {
  353.     infile = fopen( *dp, "r") ;
  354.     min_length = MinLength > 1 ? MinLength : 2 ;
  355.     if ( infile != NULL ) {
  356.         while ( fgets( dict_word, BUFSIZE, infile ) ) {
  357.         word_count += fit_word( dict_word, min_length ) ;
  358.         }
  359.     } else {
  360.         fprintf( stderr, "Sorry, couldn't find dictionary at `%s'.\n",
  361.             *dp ) ;
  362.         return(1) ;
  363.     }
  364.     fclose( infile ) ;
  365.     dp++ ;
  366.     }
  367.  
  368.     if ( Verbose ) {
  369.     for ( i = 0 ; i < NumDigits ; i++ ) {
  370.         for ( length = 1 ; length <= NumDigits-i ; length++ ) {
  371.         p_wl = &WL[IndexWL( i, length )] ;
  372.         if ( p_wl->count ) {
  373.             fprintf( stdout, "\nDigit %d, length %d:\n", i+1, length ) ;
  374.             for ( tot = 0 ; tot < p_wl->count ; tot++ ) {
  375.             fprintf( stdout, " %s\n", p_wl->p_word[tot] ) ;
  376.             }
  377.         }
  378.         }
  379.     }
  380.     }
  381.  
  382.     MatchTot = 0 ;    /* number of words matched */
  383.  
  384.     /* save NumNumerals around in case it gets incremented */
  385.     nn = NumNumerals ;
  386.  
  387.     /* search through WL for paths */
  388.     if ( word_count ) {
  389.     /* good, there's something to work with: search away! */
  390.     *out_string = '\0' ;
  391.     while ( !MatchTot && ( NumNumerals <= NumDigits ) ) {
  392.         search_for_match( 0, out_string, out_string, 0 ) ;
  393.         if ( !MatchTot ) {
  394.         fprintf( stderr, "No matches with -n %d", NumNumerals ) ;
  395.         if ( NumNumerals < NumDigits ) {
  396.             NumNumerals++ ;
  397.             fprintf( stderr,
  398.                 ", trying again with -n %d\n", NumNumerals ) ;
  399.         } else {
  400.             fprintf( stderr, "\n" ) ;
  401.         }
  402.         }
  403.     }
  404.     if ( !MatchTot ) {
  405.         fprintf( stderr,
  406.     "Bad luck: dictionary words fit, but there's too much numeral goop.\n");
  407.         fprintf( stderr,
  408. "Maybe try again with the option `-s abcdgimoptuxy248' or try wildcarding.\n"
  409.             ) ;
  410.     }
  411.     } else {
  412.     fprintf( stderr,
  413.         "Worst luck: no dictionary words fit anywhere in %s.\n",
  414.         PhoneStr ) ;
  415.     fprintf( stderr,
  416. "Maybe try again with the option `-s abcdgimoptuxy248' or try wildcarding.\n"
  417.             ) ;
  418.     }
  419.  
  420.     /* restore NumNumerals (yeah, this is sloppy - sue me) */
  421.     NumNumerals = nn ;
  422.  
  423.     free_wl() ;
  424.  
  425.     return(0) ;
  426. }
  427.  
  428. void
  429. roll_own()
  430. {
  431. int    digit_letter[MAX_DIGITS], found_one, search, i, j, k ;
  432.  
  433.     for ( i = 0 ; i < NumDigits ; i++ ) {
  434.     if ( PhoneNum[i] == FIXED_LETTER ) {
  435.         digit_letter[i] = -2 ;
  436.     } else {
  437.         digit_letter[i] = -1 ;
  438.     }
  439.     }
  440.     found_one = TRUE ;
  441.     for ( i = 0 ; i < 26 && found_one ; i++ ) {
  442.     found_one = FALSE ;
  443.     for ( j = 0 ; j < NumDigits ; j++ ) {
  444.         if ( digit_letter[j] >= 26 ) {
  445.         /* done with this one */
  446.         fprintf( stdout, "   " ) ;
  447.         } else {
  448.         if ( digit_letter[j] == -2 ) {
  449.             found_one = TRUE ;
  450.             digit_letter[j] = 26 ;
  451.             fprintf( stdout, "  %c", PhoneStr[j] ) ;
  452.         } else {
  453.             for ( k = digit_letter[j] + 1, search = TRUE
  454.             ; k < 26 && search
  455.             ; k++ ) {
  456.  
  457.             if ( Letter2Numeral[k] == PhoneNum[j] ) {
  458.                 search = FALSE ;
  459.                 found_one = TRUE ;
  460.                 fprintf( stdout, "  %c", (char)(k+'a') ) ;
  461.                 digit_letter[j] = k ;
  462.             }
  463.             }
  464.             if ( search ) {
  465.             /* no further match found */
  466.             if ( digit_letter[j] < 0 ) {
  467.                 /* numeral is not mapped, so output it directly */
  468.                 fprintf( stdout, "  %c", PhoneStr[j] ) ;
  469.             }
  470.             digit_letter[j] = 26 ;
  471.             }
  472.         }
  473.         }
  474.     }
  475.     fprintf( stdout, "\n" ) ;
  476.     }
  477. }
  478.  
  479. void
  480. concat_it()
  481. {
  482. int    i, j, tot ;
  483. char    full_string[MAX_DIGITS+1] ;
  484.  
  485.     for ( i = 0 ; i < NumDigits ; i++ ) {
  486.     if ( PhoneNum[i] == FIXED_LETTER ) {
  487.         OutputGrid[i][0] = PhoneStr[i] ;
  488.         OutputGrid[i][1] = '\0' ;
  489.     } else {
  490.         tot = 0 ;
  491.         for ( j = 0 ; j < 26 ; j++ ) {
  492.         if ( Letter2Numeral[j] == PhoneNum[i] ) {
  493.             OutputGrid[i][tot++] = (char)(j+'a') ;
  494.         }
  495.         }
  496.         if ( !tot ) {
  497.         /* no match found */
  498.         OutputGrid[i][tot++] = PhoneStr[i] ;
  499.         }
  500.         OutputGrid[i][tot] = '\0' ;
  501.     }
  502.     }
  503.  
  504.     if ( Concat == 1 ) {
  505.     concat_letter_out( 0, full_string ) ;
  506.     } else {
  507.     concat_letter_breaks_out( 0, full_string ) ;
  508.     }
  509. }
  510.  
  511. void
  512. concat_letter_out( digit, full_string )
  513. int    digit ;
  514. char    *full_string ;
  515. {
  516. int    i, length ;
  517. char    *cstr ;
  518.  
  519.     if ( digit >= NumDigits ) {
  520.     full_string[digit] = '\0' ;
  521.     fprintf( stdout, "%s\n", full_string ) ;
  522.     } else {
  523.     length = strlen( cstr = OutputGrid[digit] ) ;
  524.     for ( i = 0 ; i < length ; i++ ) {
  525.         full_string[digit] = *cstr++ ;
  526.         concat_letter_out( digit+1, full_string ) ;
  527.     }
  528.     }
  529. }
  530.  
  531. void
  532. concat_letter_breaks_out( digit, full_string )
  533. int    digit ;
  534. char    *full_string ;
  535. {
  536. char    brk_string[MAX_DIGITS*2] ;
  537. int    i, j, bny, index, length ;
  538. char    *cstr ;
  539.  
  540.     if ( digit >= NumDigits ) {
  541.     if ( TotBrk == -1 ) {
  542.         TotBrk = 1 ;
  543.         for ( i = 1 ; i < NumDigits ; i++ ) {
  544.         TotBrk *= 2 ;
  545.         }
  546.     }
  547.     for ( i = 0 ; i < TotBrk ; i++ ) {
  548.         bny = i ;
  549.         index = 0 ;
  550.         for ( j = 0 ; j < NumDigits ; j++ ) {
  551.         brk_string[index++] = full_string[j] ;
  552.         if ( bny & 0x1 ) {
  553.             brk_string[index++] = ' ' ;
  554.         }
  555.         bny = bny >> 1 ;
  556.         }
  557.         brk_string[index] = '\0' ;
  558.         fprintf( stdout, "%s\n", brk_string ) ;
  559.     }
  560.     } else {
  561.     length = strlen( cstr = OutputGrid[digit] ) ;
  562.     for ( i = 0 ; i < length ; i++ ) {
  563.         full_string[digit] = *cstr++ ;
  564.         concat_letter_breaks_out( digit+1, full_string ) ;
  565.     }
  566.     }
  567. }
  568.  
  569. /* create structures needed for the word list, etc */
  570. void
  571. init_wl()
  572. {
  573. p_wordlist p_wl ;
  574. int    i, j, length, search ;
  575. char    tstr[2] ;
  576.  
  577.     for ( i = 0, p_wl = WL ; i < NumIndices ; i++, p_wl++ ) {
  578.     p_wl->p_word = NULL ;
  579.     p_wl->count = 0 ;
  580.     p_wl->size = 0 ;
  581.     }
  582.  
  583.     /* figure out which numerals don't have any letter translations */
  584.     for ( i = 0 ; i < 10 ; i++ ) {
  585.     for ( j = 0, search = TRUE ; j < 26 && search ; j++ ) {
  586.         if ( i == Letter2Numeral[j] ) {
  587.         search = FALSE ;
  588.         }
  589.     }
  590.     /* set to TRUE if numeral is mapped by something */
  591.     NumeralMapped[i] = !search ;
  592.     }
  593.  
  594.     tstr[1] = '\0' ;
  595.     /* add one number values as possible */
  596.     /* we rely on the fact that the first word in the single letter word
  597.      * lists is actually a numeral, so change the following at your own
  598.      * peril.
  599.      */
  600.     for ( i = 0 ; i < 10 ; i++ ) {
  601.     tstr[0] = (char)i + '0' ;
  602.     (void)fit_word( tstr, 1 ) ;
  603.     }
  604.  
  605.     /* add one letter words as possible */
  606.     length = strlen( OneLetter ) ;
  607.     for ( i = 0 ; i < length ; i++ ) {
  608.     tstr[0] = OneLetter[i] ;
  609.     (void)fit_word( tstr, 1 ) ;
  610.     }
  611. }
  612.  
  613. /* see if the word fits in the database anywhere */
  614. int
  615. fit_word( dict_word, min_length )
  616. char    *dict_word ;
  617. int    min_length ;
  618. {
  619. int    length, compare_length, true_length, index_length, nl ;
  620. int    i, j, num_match ;
  621. int    word_val[MAX_DIGITS], hit[MAX_DIGITS], *wv, check_length, match, tot ;
  622. char    lc_dict_word[MAX_DIGITS], *wc, *lc, tchr, *cc ;
  623. char    clean_dict_word[BUFSIZE], new_word[BUFSIZE] ;
  624.  
  625.     /* set new_word to empty string to initialize permute_word */
  626.     tot = 0 ;
  627.     *new_word = '\0' ;
  628.     while( permute_word( dict_word, new_word ) ) {
  629.     /* convert word to numbers */
  630.     length = strlen( new_word ) ;
  631.     compare_length = true_length = 0 ;
  632.     for ( i = 0, wv = word_val, wc = new_word, lc = lc_dict_word,
  633.           cc = clean_dict_word
  634.         ; ( i < length ) && ( compare_length < NumDigits )
  635.         ; i++, wc++ ) {
  636.  
  637.         /* remove apostrophes, hyphens, etc for matching */
  638.         tchr = tolower(*wc) ;
  639.         if ( ( tchr >= 'a' ) && ( tchr <= 'z' ) ) {
  640.         *wv++ = Letter2Numeral[(*lc++ = tchr) -'a'] ;
  641.         compare_length++ ;
  642.         } else if ( ( tchr >= '0' ) && ( tchr <= '9' ) ) {
  643.         *wv++ = (*lc++ = tchr) -'0' ;
  644.         compare_length++ ;
  645.         }
  646.         *cc++ = *wc ;
  647.     }
  648.     true_length = compare_length ;
  649.  
  650.     while ( *wc ) {
  651.         tchr = tolower( *wc++ ) ;
  652.         /* get true length of word in valid characters */
  653.         if ( ( tchr >= 'a' ) && ( tchr <= 'z' ) ) {
  654.         true_length++ ;
  655.         } else if ( ( tchr >= '0' ) && ( tchr <= '9' ) ) {
  656.         true_length++ ;
  657.         }
  658.         /* copy the rest of the word */
  659.         *cc++ = tchr ;
  660.     }
  661.     /* end the cleaned word */
  662.     *cc = '\0' ;
  663.  
  664.     /* is the word too long? */
  665.     if ( true_length > NumDigits + Wildcard ) {
  666.         /* the word was too long: for ispell words we know that the
  667.          * first word is going to be as short as it gets, so we bail
  668.          * out of successive word generation here now.
  669.          */
  670.         goto LastWord ;
  671.     }
  672.  
  673.     /* is the word too short? */
  674.     if ( true_length < min_length ) {
  675.         /* the word was too short */
  676.         goto NextWord ;
  677.     }
  678.  
  679.     /* now look for matches */
  680.     if ( Wildcard ) {
  681.         check_length = NumDigits - true_length + Wildcard ;
  682.         if ( check_length >= NumDigits ) {
  683.         /* cannot index into array at higher than NumDigits-1,
  684.          * so reduce length.
  685.          */
  686.         check_length = NumDigits - 1 ;
  687.         }
  688.     } else {
  689.         check_length = NumDigits - true_length ;
  690.     }
  691.  
  692.     for ( i = 0, num_match = 0 ; i <= check_length ; i++ ) {
  693.         index_length = compare_length+i ;
  694.         if ( Wildcard ) {
  695.         if ( index_length > NumDigits ) {
  696.             /* cut comparisons down to numerals available */
  697.             index_length = NumDigits ;
  698.         }
  699.         }
  700.         for ( j = i, wv = word_val, match = TRUE
  701.         ; match && (j < index_length)
  702.         ; j++, wv++ ) {
  703.  
  704.         /* numerical match? */
  705.         if ( PhoneNum[j] != *wv ) {
  706.             /* no; exact letter match? */
  707.             if ( ( PhoneNum[j] != FIXED_LETTER ) ||
  708.              ( PhoneStr[j] != lc_dict_word[j-i] ) ) {
  709.             /* no match, stop testing */
  710.             match = FALSE ;
  711.             }
  712.         }
  713.         }
  714.         if ( match ) {
  715.         /* word fits, store index */
  716.         hit[num_match++] = i ;
  717.         }
  718.     }
  719.     /* were there any matches? */
  720.     if ( num_match ) {
  721.         /* make one copy of the word */
  722.         wc = str_duplicate( clean_dict_word ) ;
  723.         if ( !wc ) {
  724.         fprintf( stderr, "Ugh, we're out of memory!\n" ) ;
  725.         exit(1) ;
  726.         }
  727.         hold_word( wc ) ;
  728.         for ( i = 0 ; i < num_match ; i++ ) {
  729.         if ( Wildcard ) {
  730.             if ( hit[i] + true_length <= NumDigits ) {
  731.             nl = true_length ;
  732.             } else {
  733.             nl = NumDigits - hit[i] ;
  734.             }
  735.             if ( !store_word( wc, hit[i], nl ) ) {
  736.             /* duplicate word, so free it and go to next word */
  737.             free( wc ) ;
  738.             goto NextWord ;
  739.             }
  740.         } else {
  741.             if ( !store_word( wc, hit[i], true_length ) ) {
  742.             /* duplicate word, so free it and go to next word */
  743.             free( wc ) ;
  744.             goto NextWord ;
  745.             }
  746.         }
  747.         }
  748.         /* got here, so the word must have been stored */
  749.         tot++ ;
  750.     }
  751.     NextWord: ;
  752.     }
  753.     LastWord: ;
  754.     return( tot ) ;
  755. }
  756.  
  757. /* take permutations of word if "/" GNU ispell format detected, else just
  758.  * strip out invalid characters and return new word
  759.  */
  760. int
  761. permute_word( dict_word, new_word )
  762. char    *dict_word ;
  763. char    *new_word ;
  764. {
  765. int    search_word ;
  766. char    *ppc, *psc, *dc, *wc, tchr, ttchr ;
  767. static char perm_word[BUFSIZE], perm_pre_word[BUFSIZE] ;
  768. static char perm_pre_cmd[BUFSIZE], perm_suf_cmd[BUFSIZE] ;
  769. static int  perm_pre_count, perm_suf_count ;
  770. static int  perm_cur_pre_count, perm_cur_suf_count, pre_length ;
  771.  
  772.     if ( *new_word ) {
  773.     /* on successive call of this routine by calling procedure, so get
  774.      * a permutation (if available) and return it.
  775.      */
  776.  
  777.     /* subtract previous output permutation from count and see if done */
  778.     search_word = 0 ;
  779.     do {
  780.         if ( perm_cur_suf_count <= 0 ) {
  781.         if ( perm_cur_pre_count <= 0 ) {
  782.             /* no more permutations */
  783.             return( 0 ) ;
  784.         }
  785.         perm_cur_suf_count = perm_suf_count ;
  786.         switch( perm_pre_cmd[--perm_cur_pre_count] ) {
  787.             case 'A':    /* enter -> reenter */
  788.             strcpy( perm_pre_word, "re" ) ;
  789.             strcat( perm_pre_word, perm_word ) ;
  790.             break ;
  791.             case 'I':    /* disposed -> indisposed */
  792.             strcpy( perm_pre_word, "re" ) ;
  793.             strcat( perm_pre_word, perm_word ) ;
  794.             break ;
  795.             case 'U':    /* natural -> unnatural */
  796.             strcpy( perm_pre_word, "un" ) ;
  797.             strcat( perm_pre_word, perm_word ) ;
  798.             break ;
  799.             default:
  800.             fprintf( stderr,
  801.             "warning:  unrecognized word flag `%c' for word `%s'\n",
  802.                 tchr, perm_word ) ;
  803.             return( 0 ) ;
  804.         }
  805.         pre_length = strlen( perm_pre_word ) ;
  806.         /* for the first prefix addition just return rest of word
  807.          * without any suffix
  808.          */
  809.         strcpy( new_word, perm_pre_word ) ;
  810.         } else {
  811.         strcpy( new_word, perm_pre_word ) ;
  812.         switch( perm_suf_cmd[--perm_cur_suf_count] ) {
  813.             case 'V':    /* create -> creative */
  814.             /* use only when there is no prefix */
  815.             if ( perm_cur_pre_count == perm_pre_count ) {
  816.                 tchr = tolower(new_word[pre_length-1]) ;
  817.                 if ( tchr == 'e' ) {
  818.                 new_word[pre_length-1] = '\0' ;
  819.                 }
  820.                 strcat( new_word, "ive" ) ;
  821.             } else {
  822.                 /* invalid combination, so continue */
  823.                 search_word = 1 ;
  824.             }
  825.             break ;
  826.             case 'N':    /* create -> creation */
  827.             tchr = tolower(new_word[pre_length-1]) ;
  828.             if ( tchr == 'e' ) {
  829.                 new_word[pre_length-1] = '\0' ;
  830.                 strcat( new_word, "ion" ) ;
  831.             } else if ( tchr == 'y' ) {
  832.                 new_word[pre_length-1] = '\0' ;
  833.                 strcat( new_word, "ication" ) ;
  834.             } else {
  835.                 strcat( new_word, "en" ) ;
  836.             }
  837.             break ;
  838.             case 'X':    /* create -> creations */
  839.             tchr = tolower(new_word[pre_length-1]) ;
  840.             if ( tchr == 'e' ) {
  841.                 new_word[pre_length-1] = '\0' ;
  842.                 strcat( new_word, "ions" ) ;
  843.             } else if ( tchr == 'y' ) {
  844.                 new_word[pre_length-1] = '\0' ;
  845.                 strcat( new_word, "ications" ) ;
  846.             } else {
  847.                 strcat( new_word, "ens" ) ;
  848.             }
  849.             break ;
  850.             case 'H':    /* twenty -> twentieth */
  851.             /* use only when there is no prefix */
  852.             if ( perm_cur_pre_count == perm_pre_count ) {
  853.                 tchr = tolower(new_word[pre_length-1]) ;
  854.                 if ( tchr == 'y' ) {
  855.                 new_word[pre_length-1] = '\0' ;
  856.                 strcat( new_word, "ieth" ) ;
  857.                 } else {
  858.                 strcat( new_word, "th" ) ;
  859.                 }
  860.             } else {
  861.                 /* invalid combination, so continue */
  862.                 search_word = 1 ;
  863.             }
  864.             break ;
  865.             case 'Y':    /* quick -> quickly */
  866.             strcat( new_word, "ly" ) ;
  867.             break ;
  868.             case 'G':    /* file -> filing */
  869.             tchr = tolower(new_word[pre_length-1]) ;
  870.             if ( tchr == 'e' ) {
  871.                 new_word[pre_length-1] = '\0' ;
  872.             }
  873.             strcat( new_word, "ing" ) ;
  874.             break ;
  875.             case 'J':    /* file -> filings */
  876.             tchr = tolower(new_word[pre_length-1]) ;
  877.             if ( tchr == 'e' ) {
  878.                 new_word[pre_length-1] = '\0' ;
  879.             }
  880.             strcat( new_word, "ings" ) ;
  881.             break ;
  882.             case 'D':    /* create -> created */
  883.             tchr = tolower(new_word[pre_length-1]) ;
  884.             if ( tchr == 'e' ) {
  885.                 strcat( new_word, "d" ) ;
  886.             } else if ( tchr == 'y' ) {
  887.                 ttchr = tolower(new_word[pre_length-2]) ;
  888.                 if ( Vowel(ttchr) ) {
  889.                 strcat( new_word, "ed" ) ;
  890.                 } else {
  891.                 new_word[pre_length-1] = '\0' ;
  892.                 strcat( new_word, "ied" ) ;
  893.                 }
  894.             } else {
  895.                 strcat( new_word, "ed" ) ;
  896.             }
  897.             break ;
  898.             case 'T':    /* late -> latest */
  899.             /* use only when there is no prefix */
  900.             if ( perm_cur_pre_count == perm_pre_count ) {
  901.                 tchr = tolower(new_word[pre_length-1]) ;
  902.                 if ( tchr == 'e' ) {
  903.                 strcat( new_word, "st" ) ;
  904.                 } else if ( tchr == 'y' ) {
  905.                 ttchr = tolower(new_word[pre_length-2]) ;
  906.                 if ( Vowel(ttchr) ) {
  907.                     strcat( new_word, "est" ) ;
  908.                 } else {
  909.                     new_word[pre_length-1] = '\0' ;
  910.                     strcat( new_word, "iest" ) ;
  911.                 }
  912.                 } else {
  913.                 strcat( new_word, "est" ) ;
  914.                 }
  915.             } else {
  916.                 /* invalid combination, so continue */
  917.                 search_word = 1 ;
  918.             }
  919.             break ;
  920.             case 'R':    /* late -> later */
  921.             tchr = tolower(new_word[pre_length-1]) ;
  922.             if ( tchr == 'e' ) {
  923.                 strcat( new_word, "r" ) ;
  924.             } else if ( tchr == 'y' ) {
  925.                 ttchr = tolower(new_word[pre_length-2]) ;
  926.                 if ( Vowel(ttchr) ) {
  927.                 strcat( new_word, "er" ) ;
  928.                 } else {
  929.                 new_word[pre_length-1] = '\0' ;
  930.                 strcat( new_word, "ier" ) ;
  931.                 }
  932.             } else {
  933.                 strcat( new_word, "er" ) ;
  934.             }
  935.             break ;
  936.             case 'Z':    /* skate ->skaters */
  937.             tchr = tolower(new_word[pre_length-1]) ;
  938.             if ( tchr == 'e' ) {
  939.                 strcat( new_word, "rs" ) ;
  940.             } else if ( tchr == 'y' ) {
  941.                 ttchr = tolower(new_word[pre_length-2]) ;
  942.                 if ( Vowel(ttchr) ) {
  943.                 strcat( new_word, "ers" ) ;
  944.                 } else {
  945.                 new_word[pre_length-1] = '\0' ;
  946.                 strcat( new_word, "iers" ) ;
  947.                 }
  948.             } else {
  949.                 strcat( new_word, "ers" ) ;
  950.             }
  951.             break ;
  952.             case 'S':    /* imply -> implies */
  953.             tchr = tolower(new_word[pre_length-1]) ;
  954.             if ( tchr == 'y' ) {
  955.                 ttchr = tolower(new_word[pre_length-2]) ;
  956.                 if ( Vowel(ttchr) ) {
  957.                 strcat( new_word, "s" ) ;
  958.                 } else {
  959.                 new_word[pre_length-1] = '\0' ;
  960.                 strcat( new_word, "ies" ) ;
  961.                 }
  962.             } else {
  963.                 if ( strchr( "sxzh", tchr ) ) {
  964.                 strcat( new_word, "es" ) ;
  965.                 } else {
  966.                 strcat( new_word, "s" ) ;
  967.                 }
  968.             }
  969.             break ;
  970.             case 'P':    /* cloudy -> cloudiness */
  971.             tchr = tolower(new_word[pre_length-1]) ;
  972.             if ( tchr == 'y' ) {
  973.                 ttchr = tolower(new_word[pre_length-2]) ;
  974.                 if ( Vowel(ttchr) ) {
  975.                 strcat( new_word, "ness" ) ;
  976.                 } else {
  977.                 new_word[pre_length-1] = '\0' ;
  978.                 strcat( new_word, "iness" ) ;
  979.                 }
  980.             } else {
  981.                 strcat( new_word, "ness" ) ;
  982.             }
  983.             break ;
  984.             case 'M':    /* dog -> dog's */
  985.             strcat( new_word, "'s" ) ;
  986.             break ;
  987.             default:
  988.             fprintf( stderr,
  989.             "warning:  unrecognized word flag `%c' for word `%s'\n",
  990.                 tchr, perm_word ) ;
  991.             return( 0 ) ;
  992.         }
  993.         }
  994.     } while ( search_word ) ;
  995.     } else {
  996.     /* first call, so check if we need to permute */
  997.     if ( wc = strchr( dict_word, '/' ) ) {
  998.         /* GNU ispell format detected, so save word and permutations */
  999.         strncpy( perm_word, dict_word, wc-dict_word ) ;
  1000.         perm_word[wc-dict_word] = '\0' ;
  1001.  
  1002.         wc++ ;
  1003.         ppc = perm_pre_cmd ;
  1004.         psc = perm_suf_cmd ;
  1005.         perm_pre_count = 0 ;
  1006.         perm_suf_count = 0 ;
  1007.         while ( tchr = toupper( *wc++ ) ) {
  1008.         /* save only permutation characters */
  1009.         if ( ( tchr >= 'A' ) && ( tchr <= 'Z' ) ) {
  1010.             if ( (tchr == 'A') || (tchr == 'I') || (tchr == 'U') ) {
  1011.             *ppc++ = tchr ;
  1012.             perm_pre_count++ ;
  1013.             } else {
  1014.             *psc++ = tchr ;
  1015.             perm_suf_count++ ;
  1016.             }
  1017.         }
  1018.         }
  1019.         *ppc = '\0' ;
  1020.         *psc = '\0' ;
  1021.         perm_cur_pre_count = perm_pre_count ;
  1022.         perm_cur_suf_count = perm_suf_count ;
  1023.         strcpy( perm_pre_word, perm_word ) ;
  1024.         pre_length = strlen( perm_pre_word ) ;
  1025.  
  1026.         /* and finally, copy the basic word for use */
  1027.         strcpy( new_word, perm_word ) ;
  1028.     } else {
  1029.         /* no permutation, so set permutation list to empty */
  1030.         perm_cur_pre_count = 0 ;
  1031.         perm_cur_suf_count = 0 ;
  1032.  
  1033.         /* cull out line feeds, etc */
  1034.         wc = dict_word ;
  1035.         dc = new_word ;
  1036.         while ( tchr = *wc++ ) {
  1037.         if ( tchr > 13 ) {
  1038.             *dc++ = tchr ;
  1039.         }
  1040.         }
  1041.         *dc = '\0' ;
  1042.     }
  1043.     }
  1044.     return( 1 ) ;
  1045. }
  1046.  
  1047. /* yeah, I could combine this with store_word - call me lazy... */
  1048. void
  1049. hold_word( word )
  1050. char    *word ;
  1051. {
  1052.     if ( HoldSize <= HoldCount ) {
  1053.     if ( HoldSize ) {
  1054.         HoldSize += WORD_LIST_SIZE * NumDigits ;
  1055.         HoldWord = (char **)realloc( (void *)HoldWord,
  1056.             HoldSize * sizeof(char *)) ;
  1057.     } else {
  1058.         HoldSize = WORD_LIST_SIZE * NumDigits ;
  1059.         HoldWord =
  1060.             (char **)malloc( HoldSize * sizeof(char *)) ;
  1061.     }
  1062.     if ( HoldWord == NULL ) {
  1063.         fprintf( stderr, "Ugh, we're out of memory!\n" ) ;
  1064.         exit(1) ;
  1065.     }
  1066.     }
  1067.     HoldWord[HoldCount++] = word ;
  1068. }
  1069.  
  1070. /* store word in the given location, return 1 if word not a duplicate */
  1071. int
  1072. store_word( word, digit, length )
  1073. char    *word ;
  1074. int    digit ;
  1075. int    length ;
  1076. {
  1077. p_wordlist    p_wl ;
  1078. int    i ;
  1079.  
  1080.     p_wl = &WL[IndexWL( digit, length )] ;
  1081.     /* check if word is already on list */
  1082.     for ( i = 0 ; i < p_wl->count ; i++ ) {
  1083.     if ( !strcmp( word, p_wl->p_word[i] ) ) {
  1084.         /* duplicate word - don't store it */
  1085.         return( 0 ) ;
  1086.     }
  1087.     }
  1088.  
  1089.     /* check storage space */
  1090.     if ( p_wl->size <= p_wl->count ) {
  1091.     if ( p_wl->size ) {
  1092.         p_wl->size += WORD_LIST_SIZE ;
  1093.         p_wl->p_word = (char **)realloc( (void *)p_wl->p_word,
  1094.             p_wl->size * sizeof(char *)) ;
  1095.     } else {
  1096.         p_wl->size = WORD_LIST_SIZE ;
  1097.         p_wl->p_word =
  1098.             (char **)malloc( p_wl->size * sizeof(char *)) ;
  1099.     }
  1100.     if ( p_wl->p_word == NULL ) {
  1101.         fprintf( stderr, "Ugh, we're out of memory!\n" ) ;
  1102.         exit(1) ;
  1103.     }
  1104.     }
  1105.     /* store word in structure */
  1106.     p_wl->p_word[p_wl->count++] = word ;
  1107.  
  1108.     return( 1 ) ;
  1109. }
  1110.  
  1111. /* search through the stored words for matches */
  1112. void
  1113. search_for_match( digit, full_string, suffix_loc, numeral_count )
  1114. int    digit ;
  1115. char    *full_string ;
  1116. char    *suffix_loc ;
  1117. int    numeral_count ;
  1118. {
  1119. int    length, tot_len, tot_word, wn, add_num, val ;
  1120. p_wordlist    p_wl ;
  1121. char    **p_word ;
  1122.  
  1123.     tot_len = NumDigits - digit ;
  1124.     /* loop through all possible word lengths from this point */
  1125.     /* count down so that the longer strings are output first */
  1126.     for ( length = tot_len ; length > 0 ; length-- ) {
  1127.     p_wl = &WL[IndexWL( digit, length )] ;
  1128.     tot_word = p_wl->count ;
  1129.     /* now go through all words on the list */
  1130.     if ( ( length == 1 ) && tot_word ) {
  1131.         val = **(p_wl->p_word) - '0' ;
  1132.         /* is the first word a numeral, and is it mapped? */
  1133.         if ( ( val >= 0 ) && ( val <= 9 ) && NumeralMapped[val] ) {
  1134.         /* don't include first word (a mapped numeral) if we've used
  1135.          * up our quota.
  1136.          */
  1137.         wn = (numeral_count >= NumNumerals ) ;
  1138.         add_num = 1 ;
  1139.         } else {
  1140.         /* an unmapped numeral, so definitely do it */
  1141.         wn = 0 ;
  1142.         add_num = 0 ;
  1143.         }
  1144.     } else {
  1145.         /* not on the one letter word list, so do all words */
  1146.         wn = 0 ;
  1147.         add_num = 0 ;
  1148.     }
  1149.     for ( p_word = p_wl->p_word + wn
  1150.         ; wn < tot_word
  1151.         ; wn++, p_word++ ) {
  1152.  
  1153.         strcpy( suffix_loc, *p_word ) ;
  1154.         if ( length == tot_len ) {
  1155.         /* finished - output it! */
  1156.         fprintf( stdout, "%s\n", full_string ) ;
  1157.         MatchTot++ ;
  1158.         } else {
  1159.         strcat( suffix_loc, " " ) ;
  1160.         /* Add one to numeral_count only if numeral is used */
  1161.         search_for_match( digit+length, full_string,
  1162.             suffix_loc + strlen( suffix_loc ),
  1163.             numeral_count + add_num ) ;
  1164.         }
  1165.     }
  1166.     }
  1167. }
  1168.  
  1169. void
  1170. free_wl()
  1171. {
  1172. int i ;
  1173. p_wordlist p_wl ;
  1174.  
  1175.     for ( i = 0, p_wl = WL ; i < NumIndices ; i++, p_wl++ ) {
  1176.     if ( p_wl->size ) {
  1177.         p_wl->size = 0 ;
  1178.         p_wl->count = 0 ;
  1179.         free( p_wl->p_word ) ;
  1180.     }
  1181.     }
  1182.  
  1183.     /* the only function of HoldWord is to be able to free the word memory */
  1184.     if ( HoldSize ) {
  1185.     for ( i = 0 ; i < HoldCount ; i++ ) {
  1186.         free( HoldWord[i] ) ;
  1187.     }
  1188.     free( HoldWord ) ;
  1189.     }
  1190.     HoldCount = HoldSize = 0 ;
  1191. }
  1192.  
  1193. /* strip out all valid "-" arguments and their values */
  1194. int
  1195. scan_options( argc, argv, nargc, nargv )
  1196. int    argc ;
  1197. char    *argv[] ;
  1198. int    *nargc ;
  1199. char    *nargv[] ;
  1200. {
  1201. int    num_arg ;
  1202. int    i, first_dict ;
  1203. char    str[BUFSIZE] ;
  1204.  
  1205.     *nargc = num_arg = 0 ;
  1206.     first_dict = 1 ;    /* haven't received a dictionary path yet */
  1207.  
  1208.     while ( ++num_arg < argc ) {
  1209.     if ( *argv[num_arg] == '-' ) {
  1210.         switch( argv[num_arg][1] ) {
  1211.         case 'l':    /* minimum length of words */
  1212.             if ( ++num_arg < argc ) {
  1213.             sscanf( argv[num_arg], "%d", &i ) ;
  1214.             if ( i < 1 ) {
  1215.                 fprintf( stderr,
  1216.                     "Minimum word length too low!\n" ) ;
  1217.                 usage() ;
  1218.                 return( FALSE ) ;
  1219.             }
  1220.             MinLength = i ;
  1221.             } else {
  1222.             fprintf( stderr, "No minimum length given for -l.\n" ) ;
  1223.             usage() ;
  1224.             return( FALSE ) ;
  1225.             }
  1226.             break ;
  1227.         case 'n':    /* number of numerals allowed */
  1228.             if ( ++num_arg < argc ) {
  1229.             sscanf( argv[num_arg], "%d", &i ) ;
  1230.             if ( i < 0 ) {
  1231.                 fprintf( stderr, "Number of numerals too low!\n" ) ;
  1232.                 usage() ;
  1233.                 return( FALSE ) ;
  1234.             }
  1235.             NumNumerals = i ;
  1236.             } else {
  1237.             fprintf( stderr,
  1238.                 "No number of numerals given for -n.\n" ) ;
  1239.             usage() ;
  1240.             return( FALSE ) ;
  1241.             }
  1242.             break ;
  1243.         case 'q':    /* mapping of q */
  1244.             if ( ++num_arg < argc ) {
  1245.             sscanf( argv[num_arg], "%d", &i ) ;
  1246.             if ( (i >= 0) && (i <= 9) ) {
  1247.                 Letter2Numeral['q'-'a'] = i ;
  1248.             }
  1249.             } else {
  1250.             fprintf( stderr, "No mapped number given for -q.\n" ) ;
  1251.             usage() ;
  1252.             return( FALSE ) ;
  1253.             }
  1254.             break ;
  1255.         case 'z':    /* mapping of z */
  1256.             if ( ++num_arg < argc ) {
  1257.             sscanf( argv[num_arg], "%d", &i ) ;
  1258.             if ( (i >= 0) && (i <= 9) ) {
  1259.                 Letter2Numeral['z'-'a'] = i ;
  1260.             }
  1261.             } else {
  1262.             fprintf( stderr, "No mapped number given for -z.\n" ) ;
  1263.             usage() ;
  1264.             return( FALSE ) ;
  1265.             }
  1266.             break ;
  1267.         case 'd':    /* dictionary path */
  1268.             if ( ++num_arg < argc ) {
  1269.             if ( first_dict ) {
  1270.                 first_dict = 0 ;
  1271.                 /* erase first dictionary, since we're
  1272.                  * overriding by giving a path.
  1273.                  */
  1274.                 DictTot = 0 ;
  1275.                 free( *DictPath ) ;
  1276.             }
  1277.             sscanf( argv[num_arg], "%s", str ) ;
  1278.             if ( (str[0] == '.') && ( str[1] == NULL ) ) {
  1279.                 /* for '.' use the default dictionary */
  1280.                 strcpy( str, DICT_PATH ) ;
  1281.             }
  1282.             DictPath[DictTot] = str_duplicate( str ) ;
  1283.             if ( !DictPath[DictTot++] ) {
  1284.                 fprintf( stderr, "Ugh, we're out of memory!\n" ) ;
  1285.                 exit(1) ;
  1286.             }
  1287.             } else {
  1288.             fprintf( stderr,
  1289.                 "No dictionary path given for -d.\n" ) ;
  1290.             usage() ;
  1291.             return( FALSE ) ;
  1292.             }
  1293.             break ;
  1294.         case 's':    /* allowed single letter words */
  1295.             if ( ++num_arg < argc ) {
  1296.             sscanf( argv[num_arg], "%s", OneLetter ) ;
  1297.             } else {
  1298.             fprintf( stderr,
  1299.                 "No single letter words given for -s.\n" ) ;
  1300.             usage() ;
  1301.             return( FALSE ) ;
  1302.             }
  1303.             break ;
  1304.         case 'm':    /* mapping of entire alphabet */
  1305.             if ( ++num_arg < argc ) {
  1306.             sscanf( argv[num_arg], "%s", str ) ;
  1307.             if ( strlen(str) != 26 ) {
  1308.                 fprintf( stderr, "Must input all 26 digits\n" ) ;
  1309.                 usage() ;
  1310.                 return( FALSE ) ;
  1311.             }
  1312.             for ( i = 0 ; i < 26 ; i++ ) {
  1313.                 if ( (str[i] >= '0') && (str[i] <= '9') ) {
  1314.                 Letter2Numeral[i] = str[i] - '0' ;
  1315.                 } else {
  1316.                 Letter2Numeral[i] = NO_NUMBER ;
  1317.                 }
  1318.             }
  1319.             if ( Verbose ) {
  1320.                 fprintf( stderr, "here's your banana: )\n" ) ;
  1321.             }
  1322.             } else {
  1323.             fprintf( stderr, "No digit map given for -m.\n" ) ;
  1324.             usage() ;
  1325.             return( FALSE ) ;
  1326.             }
  1327.             break ;
  1328.         case 'r':    /* output concatenation */
  1329.             RollOwn = TRUE ;
  1330.             break ;
  1331.         case 'c':    /* output concatenation */
  1332.             Concat = 1 ;
  1333.             break ;
  1334.         case 'C':    /* output spacing & concatenation */
  1335.             Concat = 2 ;
  1336.             break ;
  1337.         case 'v':    /* verbose output */
  1338.             Verbose = TRUE ;
  1339.             break ;
  1340.         default:
  1341.             fprintf( stderr, "No such option `-%c'.\n",
  1342.                 argv[num_arg][1] ) ;
  1343.             usage() ;
  1344.             return( FALSE ) ;
  1345.         }
  1346.     } else {
  1347.         /* not an argument, so pass it on */
  1348.         nargv[(*nargc)++] = argv[num_arg] ;
  1349.     }
  1350.     }
  1351.     return( TRUE ) ;
  1352. }
  1353.  
  1354. /* strdup for the masses, since strdup is not standard */
  1355. char *str_duplicate( s )
  1356. char    *s ;
  1357. {
  1358. char *ps ;
  1359.  
  1360.     if ( ps = malloc( strlen( s ) + 1 ) ) {
  1361.     strcpy( ps, s ) ;
  1362.     }
  1363.     return( ps ) ;
  1364. }
  1365.