/*
   Phonewrd - given a phone number, find all words or phrases in a dictionary
     which fit it.

   version 1.1 - 4/5/93

Ported To Amiga.
	Christopher Klaus, cklaus@hotsun.nersc.gov or gt6468c@hydra.gatech.edu
	26468 GaTech Station
	Atlanta, GA 30332
	(404)-676-9622


   (c) copyright 1993, Eric Haines, all rights reserved (erich@eye.com)

   See the man page for details.
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

/*============= default related =========================================== */

/* path to dictionary */
#define	DICT_PATH	"dict"

/* number of digits in phone number */
#define	NUM_DIGITS	7

/* how many numerals are allowed in our phrase? */
#define	NUMERALS_ALLOWED	0

/* minimum length of words: note that 1 always gets bumped to 2, because of
 * OneLetter below.
 */
#define	MIN_LENGTH	1

/* one letter words that are acceptable */
#define	ONE_LETTER	"aio"
/* If you like things like "CDB" (see the bee) or "IM4U, use or modify the
 * array below.  Yes, "j" and "k" are potential names, "m" is only in "I am",
 * "q" is queue, etc - you decide...  This option tends to generate lots of
 * useless goop, but it's good if you're desperate.
 */
/* #define ONE_LETTER	"abcdgimoptuxy248" */

#define	NO_NUMBER	-2

/* to make Q and Z not be anything, set them to NO_NUMBER, else set them
 * to the digit you want (e.g. 0 or 1)
 */
#define	Q	NO_NUMBER
#define	Z	NO_NUMBER

/* translation table for letters to numbers */
int	Letter2Numeral[] =
	/* 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 */
	 { 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 } ;


/*============= storage space related ===================================== */

#define MAX_DIGITS	14
#define	MAX_INDICES	(((MAX_DIGITS+1)*MAX_DIGITS)/2)

/* increment for word list memory space */
#define	WORD_LIST_SIZE	50

/*============= internal use constants ==================================== */

#define	FIXED_LETTER	-1

#define	BUFSIZE		256
#ifndef	TRUE
#define	TRUE	1
#define	FALSE	0
#endif

/*============= structures ================================================ */

/* array of words, indexed by digit location and length of word.  This list is
 * built up in the first part of the program, as each dictionary word is
 * categorized and put in its list.  Then this structure is traversed to make
 * up the combinations.
 */
typedef	struct {
	char	**p_word ;	/* list of pointers to words */
	int	count ;		/* current count */
	int	size ;		/* allocated size */
} wordlist, *p_wordlist ;

/*============= data and macros =========================================== */

#define	IndexWL( digit_loc, length )	( WLoffset[digit_loc] + length - 1 )

wordlist WL[MAX_INDICES] ;
int	WLoffset[MAX_DIGITS] ;
int	PhoneNum[MAX_DIGITS+1] ;	/* phone number translation */
char	PhoneStr[MAX_DIGITS+1] ;	/* phone number letter, if specified */
int	NumeralMapped[10] ;		/* if TRUE, then index # is mapped */
char	OneLetter[BUFSIZE] ;
char	DictPath[BUFSIZE] ;
char	*ProgName ;

int	NumDigits ;
int	NumIndices ;
int	NumNumerals ;
int	MinLength ;
int	RollOwn ;
int	Concat ;
int	Verbose ;
int	MatchTot ;
int	Wildcard ;
int	TotBrk ;

int	HoldSize ;
int	HoldCount ;
char	**HoldWord ;

char	OutputGrid[MAX_DIGITS][27] ;

#define	USAGE	fprintf( stderr, "usage: %s [options] phone#[*...]\n"	\
	" [*...] - extra *'s at the end mean optional wildcard letters\n" \
	"  -l # - minimum length of words (cur. == %d)\n"		\
	"  -n # - number of numerals allowed in phrase (cur. == %d)\n"	\
	"  -q # - mapping of q (cur. == %d)\n"				\
	"  -z # - mapping of z (cur. == %d)\n"				\
	"  -d string - dictionary path (cur. == %s)\n"			\
	"  -s string - allowed single letter words (cur. == \"%s\")\n"	\
	"  -m char[26] - mapping of entire alphabet\n"			\
	"  -r - output the corresponding letters in an array\n"		\
	"  -c - output all combinations (no dictionary)\n"		\
	"  -v - verbose output (show words that do fit)\n"		\
	, ProgName							\
	, MinLength							\
	, NumNumerals							\
	, Q								\
	, Z								\
	, DictPath							\
	, OneLetter							\
	) ;

/*============= procedure declarations ==================================== */

int phone_check() ;
void roll_own() ;
void concat_it() ;
void concat_letter_out() ;
void concat_letter_breaks_out() ;
void init_wl() ;
int fit_word() ;
void search_for_match() ;
void hold_word() ;
void store_word() ;
void free_wl() ;
int scan_options() ;

/*============= procedures ================================================ */

main(argc,argv)
int argc;  char *argv[];
{
int	nargc ;
char	*nargv[BUFSIZE], *targv ;
int	i, hdr_out ;

    ProgName = argv[1] ;

    HoldSize = HoldCount = 0 ;

    NumNumerals = NUMERALS_ALLOWED ;
    MinLength = MIN_LENGTH ;
    RollOwn = FALSE ;
    Concat = 0 ;
    Verbose = FALSE ;

    strcpy( OneLetter, ONE_LETTER ) ;
    strcpy( DictPath, DICT_PATH ) ;

    /* translate phone number */
    if ( argc < 2 ) {
	fprintf( stderr, "Not enough arguments.\n" ) ;
	USAGE ;
	return( 1 ) ;
    }

    if ( !scan_options( argc, argv, &nargc, nargv ) ) {
	return( 1 ) ;
    }

    if ( nargc < 1 ) {
	fprintf( stderr, "No phone number found.\n" ) ;
	USAGE ;
	return( 1 ) ;
    }

    /* loop through phone number(s), output headers if more than one */
    hdr_out = ( nargc > 1 ) ;
    for ( i = 0 ; i < nargc ; i++ ) {
	if ( i ) {
	    /* output carriage returns before 2nd, 3rd, etc */
	    fprintf( stdout, "\n" ) ;
	}
	targv = nargv[i] ;
	if ( hdr_out ) {
	    fprintf( stdout, "# %s\n", targv ) ;
	}
	if ( phone_check( targv ) ) {
	    /* something very bad happened, so quit */
	    return( 1 ) ;
	}
    }

    return( 0 ) ;
}

int
phone_check( numeral_string )
char	*numeral_string ;
{
int	i, tot, word_count, length, min_length, found_digit, val ;
char	tchr, *tstr ;
char	dict_word[BUFSIZE] ;
char	out_string[BUFSIZE] ;
p_wordlist	p_wl ;
FILE	*infile ;

    NumDigits = 0 ;
    TotBrk = -1 ;

    tstr = numeral_string ;
    tchr = *tstr++ ;
    found_digit = FALSE ;
    while ( tchr != '\0' && tchr != '*' && (NumDigits <= MAX_DIGITS ) ) {
	if ( ( tchr >= '0' ) && ( tchr <= '9' ) ) {
	    /* convert to digit */
	    PhoneNum[NumDigits] = (int)(tchr - '0') ;
	    PhoneStr[NumDigits++] = tchr ;
	    found_digit = TRUE ;
	} else if ( ( (tchr >= 'A') && (tchr <= 'Z') ) ||
		    ( (tchr >= 'a') && (tchr <= 'z') ) ) {
	    PhoneNum[NumDigits] = FIXED_LETTER ;
	    PhoneStr[NumDigits++] = tolower( tchr ) ;
	}
	tchr = *tstr++ ;
    }
    PhoneStr[NumDigits]='\0' ;

    if ( NumDigits <= 0 ) {
	fprintf( stderr, "Sorry, no digits input in `%s'\n", numeral_string ) ;
	fprintf( stderr, "Recompile with a higher MAX_DIGITS setting.\n" ) ;
	USAGE ;
	return(0) ;
    }

    /* check if we're in roll own mode */
    if ( RollOwn ) {
	roll_own() ;
	return(0) ;
    }

    /* check if we're in concat output mode */
    if ( Concat ) {
	concat_it() ;
	return(0) ;
    }

    /* check if any numbers were input */
    if ( !found_digit ) {
	/* entirely alphabetic input, so translate to number */
	for ( i = 0 ; i < NumDigits ; i++ ) {
	    val = Letter2Numeral[PhoneStr[i]-'a'] ;
	    if ( val != NO_NUMBER ) {
		fprintf( stdout, "%d", val ) ;
	    } else {
		fprintf( stdout, "*" ) ;
	    }
	}
	fprintf( stdout, "\n" ) ;
	return(0) ;
    }

    if ( tchr == '*' ) {
	/* the phone number ends in a wild card - phrases can be longer */
	Wildcard = 1 ;
	while ( *tstr++ == '*' ) {
	    Wildcard++ ;
	}
    } else {
	Wildcard = 0 ;
    }

    if ( NumDigits > MAX_DIGITS ) {
	fprintf( stderr, "Sorry, too many digits input in `%s'; "
		"only %d allowed.\n", numeral_string, NumDigits ) ;
	USAGE ;
	return(0) ;
    }

    /* set up offset array for indexing WL */
    tot = 0 ;
    for ( i = 0 ; i < NumDigits ; i++ ) {
	WLoffset[i] = tot ;
	tot += (NumDigits-i) ;
    }
    NumIndices = ((NumDigits+1)*NumDigits)/2 ;

    /* build WL */
    word_count = 0 ;
    init_wl() ;
    infile = fopen( DictPath, "r") ;
    min_length = MinLength > 1 ? MinLength : 2 ;
    if ( infile != NULL ) {
	while ( fgets( dict_word, BUFSIZE, infile ) ) {
	    word_count += fit_word( dict_word, min_length ) ;
	}
    } else {
	fprintf( stderr, "Sorry, couldn't find dictionary at `%s'.\n",
		DictPath ) ;
	return(1) ;
    }
    fclose( infile ) ;

    if ( Verbose ) {
	for ( i = 0 ; i < NumDigits ; i++ ) {
	    for ( length = 1 ; length <= NumDigits-i ; length++ ) {
		p_wl = &WL[IndexWL( i, length )] ;
		if ( p_wl->count ) {
		    fprintf( stdout, "\nDigit %d, length %d:\n", i+1, length ) ;
		    for ( tot = 0 ; tot < p_wl->count ; tot++ ) {
			fprintf( stdout, " %s\n", p_wl->p_word[tot] ) ;
		    }
		}
	    }
	}
    }

    MatchTot = 0 ;	/* number of words matched */

    /* search through WL for paths */
    if ( word_count ) {
	/* good, there's something to work with: search away! */
	*out_string = '\0' ;
	search_for_match( 0, out_string, out_string, 0 ) ;
	if ( !MatchTot ) {
	    fprintf( stderr,
		"Bad luck: dictionary words fit, but there's too much "
		"numeral goop.\n" ) ;
	    fprintf( stderr,
		"I'd try again with the option `-n %d' or so.\n",
		NumNumerals+1 ) ;
	}
    } else {
	fprintf( stderr,
		"Worst luck: no dictionary words fit anywhere in %s.\n",
		PhoneStr ) ;
	fprintf( stderr,
		"Maybe try again with the option `-s abcdgimoptuxy248'.\n" ) ;
    }

    free_wl() ;

    return(0) ;
}

void
roll_own()
{
int	digit_letter[MAX_DIGITS], found_one, search, i, j, k ;

    for ( i = 0 ; i < NumDigits ; i++ ) {
	if ( PhoneNum[i] == FIXED_LETTER ) {
	    digit_letter[i] = -2 ;
	} else {
	    digit_letter[i] = -1 ;
	}
    }
    found_one = TRUE ;
    for ( i = 0 ; i < 26 && found_one ; i++ ) {
	found_one = FALSE ;
	for ( j = 0 ; j < NumDigits ; j++ ) {
	    if ( digit_letter[j] >= 26 ) {
		/* done with this one */
		fprintf( stdout, "   " ) ;
	    } else {
		if ( digit_letter[j] == -2 ) {
		    found_one = TRUE ;
		    digit_letter[j] = 26 ;
		    fprintf( stdout, "  %c", PhoneStr[j] ) ;
		} else {
		    for ( k = digit_letter[j] + 1, search = TRUE
			; k < 26 && search
			; k++ ) {

			if ( Letter2Numeral[k] == PhoneNum[j] ) {
			    search = FALSE ;
			    found_one = TRUE ;
			    fprintf( stdout, "  %c", (char)(k+'a') ) ;
			    digit_letter[j] = k ;
			}
		    }
		    if ( search ) {
			/* no further match found */
			if ( digit_letter[j] < 0 ) {
			    /* numeral is not mapped, so output it directly */
			    fprintf( stdout, "  %c", PhoneStr[j] ) ;
			}
			digit_letter[j] = 26 ;
		    }
		}
	    }
	}
	fprintf( stdout, "\n" ) ;
    }
}

void
concat_it()
{
int	i, j, tot ;
char	full_string[MAX_DIGITS+1] ;

    for ( i = 0 ; i < NumDigits ; i++ ) {
	if ( PhoneNum[i] == FIXED_LETTER ) {
	    OutputGrid[i][0] = PhoneStr[i] ;
	    OutputGrid[i][1] = '\0' ;
	} else {
	    tot = 0 ;
	    for ( j = 0 ; j < 26 ; j++ ) {
		if ( Letter2Numeral[j] == PhoneNum[i] ) {
		    OutputGrid[i][tot++] = (char)(j+'a') ;
		}
	    }
	    if ( !tot ) {
		/* no match found */
		OutputGrid[i][tot++] = PhoneStr[i] ;
	    }
	    OutputGrid[i][tot] = '\0' ;
	}
    }

    if ( Concat == 1 ) {
	concat_letter_out( 0, full_string ) ;
    } else {
	concat_letter_breaks_out( 0, full_string ) ;
    }
}

void
concat_letter_out( digit, full_string )
int	digit ;
char	*full_string ;
{
int	i, length ;
char	*cstr ;

    if ( digit >= NumDigits ) {
	full_string[digit] = '\0' ;
	fprintf( stdout, "%s\n", full_string ) ;
    } else {
	length = strlen( cstr = OutputGrid[digit] ) ;
	for ( i = 0 ; i < length ; i++ ) {
	    full_string[digit] = *cstr++ ;
	    concat_letter_out( digit+1, full_string ) ;
	}
    }
}

void
concat_letter_breaks_out( digit, full_string )
int	digit ;
char	*full_string ;
{
char	brk_string[MAX_DIGITS*2] ;
int	i, j, bny, index, length ;
char	*cstr ;

    if ( digit >= NumDigits ) {
	if ( TotBrk == -1 ) {
	    TotBrk = 1 ;
	    for ( i = 1 ; i < NumDigits ; i++ ) {
		TotBrk *= 2 ;
	    }
	}
	for ( i = 0 ; i < TotBrk ; i++ ) {
	    bny = i ;
	    index = 0 ;
	    for ( j = 0 ; j < NumDigits ; j++ ) {
		brk_string[index++] = full_string[j] ;
		if ( bny & 0x1 ) {
		    brk_string[index++] = ' ' ;
		}
		bny = bny >> 1 ;
	    }
	    brk_string[index] = '\0' ;
	    fprintf( stdout, "%s\n", brk_string ) ;
	}
    } else {
	length = strlen( cstr = OutputGrid[digit] ) ;
	for ( i = 0 ; i < length ; i++ ) {
	    full_string[digit] = *cstr++ ;
	    concat_letter_breaks_out( digit+1, full_string ) ;
	}
    }
}

/* create structures needed for the word list, etc */
void
init_wl()
{
p_wordlist p_wl ;
int	i, j, length, search ;
char	tstr[2] ;

    for ( i = 0, p_wl = WL ; i < NumIndices ; i++, p_wl++ ) {
	p_wl->p_word = NULL ;
	p_wl->count = 0 ;
	p_wl->size = 0 ;
    }

    /* figure out which numerals don't have any letter translations */
    for ( i = 0 ; i < 10 ; i++ ) {
	for ( j = 0, search = TRUE ; j < 26 && search ; j++ ) {
	    if ( i == Letter2Numeral[j] ) {
		search = FALSE ;
	    }
	}
	/* set to TRUE if numeral is mapped by something */
	NumeralMapped[i] = !search ;
    }

    tstr[1] = '\0' ;
    /* add one number values as possible */
    /* we rely on the fact that the first word in the single letter word
     * lists is actually a numeral, so change the following at your own
     * peril.
     */
    for ( i = 0 ; i < 10 ; i++ ) {
	tstr[0] = (char)i + '0' ;
	(void)fit_word( tstr, 1 ) ;
    }

    /* add one letter words as possible */
    length = strlen( OneLetter ) ;
    for ( i = 0 ; i < length ; i++ ) {
	tstr[0] = OneLetter[i] ;
	(void)fit_word( tstr, 1 ) ;
    }
}

/* see if the word fits in the database anywhere */
int
fit_word( dict_word, min_length )
char	*dict_word ;
int	min_length ;
{
int	length, compare_length, true_length, index_length, nl ;
int	i, j ;
int	word_val[MAX_DIGITS], hit[MAX_DIGITS], *wv, check_length, match, tot ;
char	lc_dict_word[MAX_DIGITS], *wc, *lc, tchr, *cc ;
char	clean_dict_word[BUFSIZE] ;

    /* convert word to numbers */
    length = strlen( dict_word ) ;
    compare_length = true_length = 0 ;
    for ( i = 0, wv = word_val, wc = dict_word, lc = lc_dict_word,
	  cc = clean_dict_word
	; ( i < length ) && ( compare_length < NumDigits )
	; i++, wc++ ) {

	/* remove apostrophes, hyphens, etc for matching */
	tchr = tolower(*wc) ;
	if ( ( tchr >= 'a' ) && ( tchr <= 'z' ) ) {
	    *wv++ = Letter2Numeral[(*lc++ = tchr) -'a'] ;
	    compare_length++ ;
	} else if ( ( tchr >= '0' ) && ( tchr <= '9' ) ) {
	    *wv++ = (*lc++ = tchr) -'0' ;
	    compare_length++ ;
	}

	/* clean the word of any carriage return junk */
	if ( tchr > 13 ) {
	    *cc++ = *wc ;
	}
    }
    true_length = compare_length ;

    while ( *wc ) {
	tchr = tolower( *wc++ ) ;
	/* get true length of word in valid characters */
	if ( ( tchr >= 'a' ) && ( tchr <= 'z' ) ) {
	    true_length++ ;
	} else if ( ( tchr >= '0' ) && ( tchr <= '9' ) ) {
	    true_length++ ;
	}
	/* copy the rest of the word */
	if ( tchr > 13 ) {
	    *cc++ = tchr ;
	}
    }
    /* end the cleaned word */
    *cc = '\0' ;

    /* is the word too long? */
    if ( true_length > NumDigits + Wildcard ) {
	/* the word was too long */
	return( 0 ) ;
    }

    /* is the word too short? */
    if ( true_length < min_length ) {
	/* the word was too short */
	return( 0 ) ;
    }

    /* now look for matches */
    if ( Wildcard ) {
	check_length = NumDigits - true_length + Wildcard ;
	if ( check_length >= NumDigits ) {
	    /* cannot index into array at higher than NumDigits-1, so reduce */
	    check_length = NumDigits - 1 ;
	}
    } else {
	check_length = NumDigits - true_length ;
    }

    for ( i = 0, tot = 0 ; i <= check_length ; i++ ) {
	index_length = compare_length+i ;
	if ( Wildcard ) {
	    if ( index_length > NumDigits ) {
		/* cut comparisons down to numerals available */
		index_length = NumDigits ;
	    }
	}
	for ( j = i, wv = word_val, match = TRUE
	    ; match && (j < index_length)
	    ; j++, wv++ ) {

	    /* numerical match? */
	    if ( PhoneNum[j] != *wv ) {
		/* no; exact letter match? */
		if ( ( PhoneNum[j] != FIXED_LETTER ) ||
		     ( PhoneStr[j] != lc_dict_word[j-i] ) ) {
		    /* no match, stop testing */
		    match = FALSE ;
		}
	    }
	}
	if ( match ) {
	    /* word fits, store index */
	    hit[tot++] = i ;
	}
    }
    /* were there any matches? */
    if ( tot ) {
	/* make one copy of the word */
	wc = strdup( clean_dict_word ) ;
	hold_word( wc ) ;
	for ( i = 0 ; i < tot ; i++ ) {
	    if ( Wildcard ) {
		if ( hit[i] + true_length <= NumDigits ) {
		    nl = true_length ;
		} else {
		    nl = NumDigits - hit[i] ;
		}
		store_word( wc, hit[i], nl ) ;
	    } else {
		store_word( wc, hit[i], true_length ) ;
	    }
	}
    }

    return( tot ) ;
}

/* yeah, I could combine this with store_word - call me lazy... */
void
hold_word( word )
char	*word ;
{
    if ( HoldSize <= HoldCount ) {
	if ( HoldSize ) {
	    HoldSize += WORD_LIST_SIZE * NumDigits ;
	    HoldWord = (char **)realloc( (void *)HoldWord,
		    HoldSize * sizeof(char *)) ;
	} else {
	    HoldSize = WORD_LIST_SIZE * NumDigits ;
	    HoldWord =
		    (char **)malloc( HoldSize * sizeof(char *)) ;
	}
	if ( HoldWord == NULL ) {
	    fprintf( stderr, "Ugh, we're out of memory!\n" ) ;
	    exit(1) ;
	}
    }
    HoldWord[HoldCount++] = word ;
}

/* store word in the given location */
void
store_word( word, digit, length )
char	*word ;
int	digit ;
int	length ;
{
p_wordlist	p_wl ;

    p_wl = &WL[IndexWL( digit, length )] ;
    /* check storage space */
    if ( p_wl->size <= p_wl->count ) {
	if ( p_wl->size ) {
	    p_wl->size += WORD_LIST_SIZE ;
	    p_wl->p_word = (char **)realloc( (void *)p_wl->p_word,
		    p_wl->size * sizeof(char *)) ;
	} else {
	    p_wl->size = WORD_LIST_SIZE ;
	    p_wl->p_word =
		    (char **)malloc( p_wl->size * sizeof(char *)) ;
	}
	if ( p_wl->p_word == NULL ) {
	    fprintf( stderr, "Ugh, we're out of memory!\n" ) ;
	    exit(1) ;
	}
    }
    /* store word in structure */
    p_wl->p_word[p_wl->count++] = word ;
}

/* search through the stored words for matches */
void
search_for_match( digit, full_string, suffix_loc, numeral_count )
int	digit ;
char	*full_string ;
char	*suffix_loc ;
int	numeral_count ;
{
int	length, tot_len, tot_word, wn, add_num, add_val ;
p_wordlist	p_wl ;
char	**p_word ;

    tot_len = NumDigits - digit ;
    /* loop through all possible word lengths from this point */
    /* count down so that the longer strings are output first */
    for ( length = tot_len ; length > 0 ; length-- ) {
	p_wl = &WL[IndexWL( digit, length )] ;
	tot_word = p_wl->count ;
	/* now go through all words on the list */
	if ( length == 1 ) {
	    /* is the numeral of the first word mapped? */
	    if ( NumeralMapped[**(p_wl->p_word)-'0'] ) {
		/* don't include first word (a mapped numeral) if we've used
		 * up our quota.
		 */
		wn = (numeral_count >= NumNumerals ) ;
		add_num = 1 ;
	    } else {
		/* an unmapped numeral, so definitely do it */
		wn = 0 ;
		add_num = 0 ;
	    }
	} else {
	    /* not on the one letter word list, so do all words */
	    wn = 0 ;
	    add_num = 0 ;
	}
	for ( p_word = p_wl->p_word + wn
	    ; wn < tot_word
	    ; wn++, p_word++ ) {

	    strcpy( suffix_loc, *p_word ) ;
	    if ( length == tot_len ) {
		/* finished - output it! */
		fprintf( stdout, "%s\n", full_string ) ;
		MatchTot++ ;
	    } else {
		strcat( suffix_loc, " " ) ;
		/* Add one to numeral_count only if numeral is used */
		search_for_match( digit+length, full_string,
		        suffix_loc + strlen( suffix_loc ),
			numeral_count + add_num ) ;
	    }
	}
    }
}

void
free_wl()
{
int i ;
p_wordlist p_wl ;

    for ( i = 0, p_wl = WL ; i < NumIndices ; i++, p_wl++ ) {
	if ( p_wl->size ) {
	    p_wl->size = 0 ;
	    p_wl->count = 0 ;
	    free( p_wl->p_word ) ;
	}
    }

    /* the only function of HoldWord is to be able to free the word memory */
    if ( HoldSize ) {
	for ( i = 0 ; i < HoldCount ; i++ ) {
	    free( HoldWord[i] ) ;
	}
	free( HoldWord ) ;
    }
    HoldCount = HoldSize = 0 ;
}

/* strip out all valid "-" arguments and their values */
int
scan_options( argc, argv, nargc, nargv )
int	argc ;
char	*argv[] ;
int	*nargc ;
char	*nargv[] ;
{
int	num_arg ;
int	i ;
char	str[BUFSIZE] ;

    *nargc = num_arg = 0 ;

    while ( ++num_arg < argc ) {
	if ( *argv[num_arg] == '-' ) {
	    switch( argv[num_arg][1] ) {
		case 'l':	/* minimum length of words */
		    if ( ++num_arg < argc ) {
			sscanf( argv[num_arg], "%d", &i ) ;
			if ( i < 1 ) {
			    fprintf( stderr,
				    "Minimum word length too low!\n" ) ;
			    USAGE ;
			    return( FALSE ) ;
			}
			MinLength = i ;
		    }
		    break ;
		case 'n':	/* number of numerals allowed */
		    if ( ++num_arg < argc ) {
			sscanf( argv[num_arg], "%d", &i ) ;
			if ( i < 0 ) {
			    fprintf( stderr, "Number of numerals too low!\n" ) ;
			    USAGE ;
			    return( FALSE ) ;
			}
			NumNumerals = i ;
		    }
		    break ;
		case 'q':	/* mapping of q */
		    if ( ++num_arg < argc ) {
			sscanf( argv[num_arg], "%d", &i ) ;
			if ( (i >= 0) && (i <= 9) ) {
			    Letter2Numeral['q'-'a'] = i ;
			}
		    }
		    break ;
		case 'z':	/* mapping of z */
		    if ( ++num_arg < argc ) {
			sscanf( argv[num_arg], "%d", &i ) ;
			if ( (i >= 0) && (i <= 9) ) {
			    Letter2Numeral['z'-'a'] = i ;
			}
		    }
		    break ;
		case 'd':	/* dictionary path */
		    if ( ++num_arg < argc ) {
			sscanf( argv[num_arg], "%s", DictPath ) ;
		    }
		    break ;
		case 's':	/* allowed single letter words */
		    if ( ++num_arg < argc ) {
			sscanf( argv[num_arg], "%s", OneLetter ) ;
		    }
		    break ;
		case 'm':	/* mapping of entire alphabet */
		    if ( ++num_arg < argc ) {
			sscanf( argv[num_arg], "%s", str ) ;
			if ( strlen(str) != 26 ) {
			    fprintf( stderr, "Must input all 26 digits\n" ) ;
			    USAGE ;
			    return( FALSE ) ;
			}
			for ( i = 0 ; i < 26 ; i++ ) {
			    if ( (str[i] >= '0') && (str[i] <= '9') ) {
				Letter2Numeral[i] = str[i] - '0' ;
			    } else {
				Letter2Numeral[i] = NO_NUMBER ;
			    }
			}
			if ( Verbose ) {
			    fprintf( stderr, "here's your banana: )\n" ) ;
			}
		    }
		    break ;
		case 'r':	/* output concatenation */
		    RollOwn = TRUE ;
		    break ;
		case 'c':	/* output concatenation */
		    Concat = 1 ;
		    break ;
		case 'C':	/* output spacing & concatenation */
		    Concat = 2 ;
		    break ;
		case 'v':	/* verbose output */
		    Verbose = TRUE ;
		    break ;
		default:
		    fprintf( stderr, "No such option `-%c'\n",
			    argv[num_arg][1] ) ;
		    USAGE ;
		    return( FALSE ) ;
	    }
	} else {
	    /* not an argument, so pass it on */
	    nargv[(*nargc)++] = argv[num_arg] ;
	}
    }
    return( TRUE ) ;
}
