/* Copyright (C) 2003 Nathan I. Laredo

   This program is modifiable/redistributable under the terms
   of the GNU General Public Licence.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
   Send your comments and all your spare pocket change to
   the address found in "whois tinycode.com"

   Returns random word from dictionary
 */
/* Location of an ascii list of words separated by newline */
#define DICTIONARY_FILE "/usr/share/dict/words"
/* Maximum number of word matches per pass */
#define MAX_MATCHES 20000
#define MAXWORDLENGTH 80
#undef NO_STATUS

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


char *xlate[MAX_MATCHES], *xlate2[MAX_MATCHES], **windex, **in_word;
char xlc[26]; /* initial translate matrix supplied by user */
/* hold starts of various length words in the dictionary */
int windexlen[MAXWORDLENGTH];
int windexlen_end[MAXWORDLENGTH];
int in, dict_size;

void do_exit(i)
int i;
{
    printf("</CENTER></BODY></HTML>\n");
    exit(i);
}
int count_words(source, nchars)
char *source;
int nchars;
{
    int i, count, state;

    for (i = state = count = 0; i < nchars; i++)
	if (isalpha(source[i]))
	    state = 1;
	else if (state)
	    (state = 0, count++);

    return count;
}

int make_wordlist(index, source, nchars)
char **index, *source;
int nchars;
{
    int i, count, state;

    for (i = state = count = 0; i < nchars; i++)
	if (isalpha(source[i])) {
	    if (!state)
		(state = 1, index[count++] = &source[i]);
	} else if (state)
	    (source[i] = '\0', state = 0);
    return count;
}

int read_dict()
{
    FILE *f;
    long fsize, count;
    char *words;

    if ((f = fopen(DICTIONARY_FILE, "r")) == NULL) {
	perror(DICTIONARY_FILE);
	exit(1);
    }
    if (fseek(f, 0L, SEEK_END)) {
	perror(DICTIONARY_FILE);
	exit(1);
    }
    fsize = ftell(f);
    if (fseek(f, 0L, SEEK_SET)) {
	perror(DICTIONARY_FILE);
	exit(1);
    }
    /* save room to null terminate last value if no newline */
    if ((words = malloc(fsize + 1)) == NULL) {
	fprintf(stderr, "malloc failed for %ld bytes\n", fsize);
	do_exit(1);
    }
    if ((count = fread(words, 1, fsize, f)) < fsize) {
	fprintf(stderr, "fread error: %ld of %ld read\n", fsize, count);
	do_exit(1);
    }
    if (fclose(f))
	perror(DICTIONARY_FILE);

    count = count_words(words, fsize);

    if ((windex = malloc(count * sizeof(char *))) == NULL) {
	fprintf(stderr, "malloc failed for %ld bytes\n",
		count * sizeof(char *));
	do_exit(1);
    }
    return make_wordlist(windex, words, fsize);
}

/* for qsort: sort longest to shortest strings */
int strlencmp(a, b)
char **a, **b;
{
    int i, j;

    if ((i = strlen(*a)) == (j = strlen(*b)))
	return 0;
    if (i > j)
	return -1;
    return 1;
}

int findchar(s, c)
char *s;
int c;
{
    int i;
    for (i = 0; i < 26; i++)
	if (s[i] == c)
	    return 1;
    return 0;
}

#ifndef NO_STATUS
static unsigned long int num_compares = 0;
static int mst = 0, wst = 0, mnst = 0;
#endif

/* return zero if pattern of characters matches */
int pcompare(crypted, plain, xl, l)
char *crypted, *plain, *xl;
int l;
{
    register int i;
#ifndef NO_STATUS
    num_compares++;
    /* print status every 65536 compares */
    if (!(num_compares & 0xFFFF)) {
	i = (num_compares & 0x30000) >> 16;
	printf("[%03d/%06d/%06d]%c\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b",
	       wst, mnst, mst,
	       i == 0 ? '|' : i == 1 ? '/' : i == 2 ? '-' : '\\');
	fflush(stdout);
    }
#endif

    /* string length already compared */
    for (i = 0; i < l; i++) {
	if (xl[crypted[i] - 'A'] == '_' && !findchar(xl, plain[i]))
	    xl[crypted[i] - 'A'] = plain[i];
	if (xl[crypted[i] - 'A'] != plain[i])
	    return 1;
    }
    return 0;
}

/* try to narrow down match list to a smaller list
 * compares all matches with specified word for new list of matches
 * runs through mB matches in xlateB, creating new list xlateA with
 * mA matches.   Return number of new matches found.
 * For first pass, xlateB = NULL and supplied translation (if any)
 * is used.
 */
int narrow_matches(xlateA, xlateB, mB, word)
char **xlateA, **xlateB;
int mB, word;
{
    int i, k, mA, start, len, end;

    len = strlen(in_word[word]);
    if ((start = windexlen[len]) < 0)
	return 0;
    end = windexlen_end[len];
#ifndef NO_STATUS
    mnst = 0;
    wst = word;
#endif
    for (k = mA = 0; k < mB && mA < MAX_MATCHES; k++) {
#ifndef NO_STATUS
	mst = mB - k;
#endif
	for (i = start; i < end && mA < MAX_MATCHES; i++) {
	    if (xlateB != NULL)
		memcpy(xlateA[mA], xlateB[k], 26);
	    else
		memcpy(xlateA[mA], xlc, 26);
	    if (!pcompare(in_word[word], windex[i], xlateA[mA], len))
#ifndef NO_STATUS
		mnst =
#endif
		    mA++;
	}
    }
    return mA;

}
void frequencycount(s, out)
char *s, *out;
{
    int i;

    memcpy(out, "@@@@@@@@@@@@@@@@@@@@@@@@@@", 26);
    for (i = 0; i < strlen(s); i++)
	if (isalpha(s[i]))
	    out[toupper(s[i]) - 'A']++;
}

int  freqcmp(in, out)
char *in, *out;
{
    int i;
    for  (i = 0; i < 26; i++)
	if (out[i] > in[i])
	   return 1;
    return 0;
}

int main(argc, argv)
int argc;
char **argv;
{
    char instring[32767], oldstr[32767], *tmp, dowhat;
    int i, j, k, bytesread, count, use, start, end, len;
    int m1 = 0, m2 = 0, roothit = -1;

    memset(xlc, '_', 26);
    for (i=1; i < argc; i++)
	if (strlen(argv[i]) == 3 && argv[i][1] == '=' && isalpha(*argv[i]))
	    xlc[toupper(*argv[i]) - 'A'] = toupper(argv[i][2]);

    printf("Content-type: text/html\n\n<HTML>\n<TITLE>"
	   "Random Word Generator</TITLE><BODY BG=\"#FFFFFF\"><CENTER>\n");

    /* An ugly method to allocate a string, but simplifies other stuff */

    if ((tmp = malloc(26 * MAX_MATCHES * 2)) == NULL) {
	fprintf(stderr, "malloc failed for %d bytes\n", 26 * MAX_MATCHES * 2);
	do_exit(1);
    }
    for (i = 0; i < MAX_MATCHES; i++) {
	xlate[i] = tmp;
	tmp += 26;
    }
    for (i = 0; i < MAX_MATCHES; i++) {
	xlate2[i] = tmp;
	tmp += 26;
    }
    /* setup initial translation and modify by command line args if any */
    /* almost every clue provided will exponentially decrease runtime */
    dict_size = read_dict();
    qsort(windex, dict_size, sizeof(char *), strlencmp);

    /* index dictionary by word length */
    for (i = 0; i < MAXWORDLENGTH; i++)
	windexlen[i] = windexlen_end[i] = -1;

    for (i = j = 0; i < dict_size; i++) {
	k = strlen(windex[i]);
	if (j != k) {
	    if (j)		/* this is buggy */
		windexlen_end[j] = i;
	    windexlen[j = k] = i;
	}
    }
    srand(time(NULL));
    for (i = 0; i < 20 ; i++) {
	j = rand() % dict_size;
	printf("<a href=\"http://m-w.com/cgi-bin/dictionary?book=Dictionary&va=%s\">%s</a><br>\n", windex[j], windex[j]);
    }
    do_exit(0);
}
