/*
 * functions.c - functions for the webster server
 *
 * David A. Curry
 * Purdue University
 * Engineering Computer Network
 * April, 1986
 *
 * $Log: functions.c,v $
 * Revision 1.11  1993/04/05  01:51:35  jik
 * A little fix to the April Foolery.
 *
 * Revision 1.10  1992/03/31  00:52:38  jik
 * Just a little something to wile away  the hours.
 *
 * Revision 1.9  1991/07/09  00:21:23  ambar
 * reformatted again.
 *
 * Revision 1.8  91/07/08  23:08:58  ambar
 * added missing declaration of malloc()
 * 
 * 
 * Revision 1.7  91/07/08  22:48:17  ambar
 * changed where stdio.h is included from
 * added strdup()
 * 
 * Revision 1.6  90/04/04  12:30:50  jik
 * missing semicolon
 * 
 * Revision 1.5  88/02/29  04:53:47  ambar
 * char *stash changed to char *curstash.
 * 
 * Revision 1.4  88/02/25  22:39:51  ambar
 * changed locations of our header files.
 * 
 * Revision 1.3  88/02/21  15:30:07  ambar
 * removed unused variable in dwim().
 * 
 * Revision 1.2  88/02/20  07:02:03  ambar
 * ifdef'ed for SUNRPC
 * 
 * Revision 1.1  87/06/16  20:26:18  jtkohl
 * Initial revision
 * 
 * Revision 1.2  86/12/26  22:02:17  davy
 * Changed to work with DBM files.
 * 
 */
#ifndef lint
static char *rcs_functions_c = "$Header: /afs/athena.mit.edu/user/j/i/jik/sipbsrc/src/webster/src/server/RCS/functions.c,v 1.11 1993/04/05 01:51:35 jik Exp $";
#endif

#include <sys/types.h>
#include <ctype.h>
#include <stdio.h>
#include <index.h>
#include <webster.h>
#ifdef SUNRPC
#include <sunrpcweb.h>
#endif SUNRPC

#ifndef SUNRPC
extern FILE *output;
#endif !SUNRPC
extern char *wordbuf;			/* word storage buffer		*/
extern char *websterhelp;		/* path to help file		*/

extern int nanswers;			/* number of answers in answers	*/
extern int answers_valid;		/* non-zero if numbers work	*/
extern struct answer answers[MAXANSWERS]; /* answers found in search	*/

int comp();
#ifdef SUNRPC
char *dwim();
extern char *error(), *wildsearch();
#endif SUNRPC

#ifndef SUNRPC
#ifdef APRIL_FOOL
#undef APRIL_FOOL
#endif
#endif

#ifdef APRIL_FOOL
extern  int april_fool;
#endif

/*
 * define - find and return the definition of a word.
 */
#ifdef SUNRPC
char *
#endif SUNRPC
define(argcount, argv)
int argcount;
char **argv;
{
	register char *s;
	register int i, j;
	char word[BUFSIZ];
#ifdef SUNRPC
	register char *err;
	char output[BUFSIZ];
	extern char *printdef();
#endif SUNRPC
	
	s = word;
	*s = NULL;

	for (i = 1; i < argcount; i++) {
		(void) strcat(word, argv[i]);

		if ((argcount - i) > 1)
			(void) strcat(word, " ");
	}

	/*
	 * Make the word all lower case.
	 */
	while (*s) {
		if (isascii(*s) && isupper(*s))
			*s = tolower(*s);
		s++;
	}

#ifdef APRIL_FOOL
	if (april_fool && (! strncmp(word, "gull", 4))
	    && ((word[4] == 'i') || (word[4] == 'a')) &&
	    (! (strcmp(&word[5], "ble")))) {
	     return("SPELLING 0\n");
	}
#endif
	
	/* 
	 * If the answers are valid and the user sent a number,
	 * define the word in answers[number].
	 */
	if (answers_valid && isnumber(word)) {
		i = atoi(word);

		if (i > nanswers) {
#ifdef SUNRPC			
			return(error(0, "%d: out of range.", i));
#else !SUNRPC
			error(0, "%d: out of range.", i);
			return(-1);
#endif !SUNRPC
		}
		
		argv[1] = answers[i-1].a_word;
		answers_valid = 0;

		return(define(argcount, argv));
	}

	/*
	 * If the word has wildcards in it, look it up
	 * and send back a WILD response.
	 */
	if (haswild(word)) {
#ifdef SUNRPC
		if ((err = wildsearch(word, 0)) != (char *) NULL)
			return(err);
#else !SUNRPC
		wildsearch(word, 0);
#endif !SUNRPC
		
		if (nanswers == 0) {
#ifdef SUNRPC
			(void) sprintf(output, "WILD 0\n");
			return(output);
#else !SUNRPC
			fprintf(output, "WILD 0\r\n");
			return(1);
#endif !SUNRPC
		}
		/*
		 * Sort the answers.
		 */
		qsort((char *)answers, nanswers, sizeof(struct answer), comp);
#ifdef SUNRPC
		(void) sprintf(output, "WILD\n");
		addtodef(output);
#else !SUNRPC
		fprintf(output, "WILD\r\n");

#endif !SUNRPC
		/*
		 * Output words and word numbers.
		 */
		j = 1;
		for (i = 0; i < nanswers; i++) {
			if ((i > 0) && (strcmp(answers[i].a_word,
					       answers[i-1].a_word) == 0))
				continue;
#ifdef SUNRPC
			(void) sprintf(output, "%d %s\n", j++,
				       answers[i].a_word);
			addtodef(output);
#else !SUNRPC
			fprintf(output, "%d %s\r\n", j++, answers[i].a_word);
#endif SUNRPC
		}
#ifdef SUNRPC
		/*
		 * Send the output.
		 */
		*curptr++ = '\0';
		howlong = maxdeflen = 0;

		answers_valid = 1;
		return(curstash);
#else !SUNRPC
		/*
 		 * End output with an EOF marker.
  		 */
 		putc(EOFCH, output);
 		answers_valid = 1;

 		return(1);
#endif !SUNRPC
	}

	/*
	 * Look the word up the easy way.
	 */
	easysearch(word, 0);

	/*
	 * If we found it, print the definition.
	 */
	if (nanswers == 1) {
		answers_valid = 0;
#ifdef SUNRPC
		return(printdef(&answers[0]));
#else !SUNRPC
		printdef(&answers[0]);
		return(1);
#endif !SUNRPC
	}

	/*
	 * Do What I Mean.
	 */
	return(dwim(word));
}

/*
 * complete - attempt to do Tenex-style word completion.
 */
#ifdef SUNRPC
char *
#endif SUNRPC
complete(argcount, argv)
int argcount;
char **argv;
{
	register int i;
	register char *s;
	char word[BUFSIZ];
#ifdef SUNRPC
	char output[BUFSIZ];
	register char *err;
#endif SUNRPC
	
	s = word;
	*s = NULL;

	for (i = 1; i < argcount; i++) {
		(void) strcat(word, argv[i]);

		if ((argcount - i) > 1)
			(void) strcat(word, " ");
	}

	/*
	 * Convert to lower case.
	 */
	while (*s) {
		if (isupper(*s))
			*s = tolower(*s);
		s++;
	}

	/*
	 * Add a "match 0 or more" wildcard.
	 */
	*s++ = MANYCH;
	*s = NULL;

	/*
	 * Search for the word.
	 */
#ifdef SUNRPC
	if ((err = wildsearch(word, 0)) != (char *) NULL)
		return(err);
#else !SUNRPC
	wildsearch(word, 0);
#endif !SUNRPC
	
	answers_valid = 0;

	/*
	 * It's either ambiguous, meaning we didn't find "just one",
	 * or we complete it.
	 */
	if (nanswers != 1) {
#ifdef SUNRPC
		(void) sprintf(output, "AMBIGUOUS %d\n", nanswers);
		addtodef(output);
		/* clean up and go */
		*curptr++ = '\0';
		howlong = maxdeflen = 0;
		return(curstash);
#else !SUNRPC
		fprintf(output, "AMBIGUOUS %d\n", nanswers);
		return(0);
#endif !SUNRPC
	}
	
#ifdef SUNRPC
	(void) sprintf(output, "COMPLETION %s\n", answers[0].a_word);
	addtodef(output);
	/* clean up and go */
	*curptr++ = '\0';
	howlong = maxdeflen = 0;
	return(curstash);
#else !SUNRPC
	fprintf(output, "COMPLETION %s\r\n", answers[0].a_word);
 	return(1);
#endif SUNRPC
	
}

/*
 * endings - do Tenex-style endings stuff.
 */
#ifdef SUNRPC
char *
#endif SUNRPC
endings(argcount, argv)
int argcount;
char **argv;
{
	register char *s;
	register int i, j;
	char word[BUFSIZ];
#ifdef SUNRPC
	char output[BUFSIZ];
	register char *err;
#endif SUNRPC
	
	s = word;
	*s = NULL;

	for (i = 1; i < argcount; i++) {
		(void) strcat(word, argv[i]);

		if ((argcount - i) > 1)
			(void) strcat(word, " ");
	}

	/*
	 * Convert to lower case.
	 */
	while (*s) {
		if (isupper(*s))
			*s = tolower(*s);
		s++;
	}

	/*
	 * Add a "match zero or more" wildcard.
	 */
	*s++ = MANYCH;
	*s = NULL;

	/*
	 * Search for the word.
	 */
#ifdef SUNRPC
	if ((err = wildsearch(word, 0)) != (char *) NULL)
		return(err);
#else !SUNRPC
	wildsearch(word, 0);
#endif !SUNRPC

	/*
	 * Not found.
	 */
	if (nanswers == 0) {
#ifdef SUNRPC
		(void) sprintf(output, "MATCHS 0\n");
		return(output);
#else !SUNRPC
		fprintf(output, "MATCHS 0\r\n");
		return(0);
#endif !SUNRPC
	}
	
	/* Print the matches, along with numbers. */
#ifdef SUNRPC
	(void) sprintf(output, "MATCHS\n");
	addtodef(output);
#else !SUNRPC
	fprintf(output, "MATCHS\n");
#endif !SUNRPC

	qsort((char *)answers, nanswers, sizeof(struct answer), comp);

	j = 1;
	for (i = 0; i < nanswers; i++) {
		if ((i > 0) && (strcmp(answers[i].a_word,
				       answers[i-1].a_word) == 0))
			continue;
#ifdef SUNRPC
		(void) sprintf(output, "%d %s\n", j++, answers[i].a_word);
		addtodef(output);
#else !SUNRPC
		fprintf(output, "%d %s\n", j++, answers[i].a_word);
#endif !SUNRPC
		
	}
	answers_valid = 1;

#ifdef SUNRPC
	*curptr++ = '\0';
	howlong = maxdeflen = 0;
	return(curstash);
#else !SUNRPC
	putc(EOFCH, output);
	return(1);
#endif !SUNRPC
	
}

/*
 * spell - check the spelling of a word.
 */
#ifdef SUNRPC
char *
#endif SUNRPC
spell(argcount, argv)
int argcount;
char **argv;
{
	register int i;
	register char *s;
	char word[BUFSIZ];
#ifdef SUNRPC
	char output[BUFSIZ];
#endif SUNRPC
	
	s = word;
	*s = NULL;

	for (i = 1; i < argcount; i++) {
		(void) strcat(word, argv[i]);

		if ((argcount - i) > 1)
			(void) strcat(word, " ");
	}

	/*
	 * Convert to lower case.
	 */
	while (*s) {
		if (isupper(*s))
			*s = tolower(*s);
		s++;
	}

#ifdef APRIL_FOOL
	if (april_fool && (! strncmp(word, "gull", 4))
	    && ((word[4] == 'i') || (word[4] == 'a')) &&
	    (! (strcmp(&word[5], "ble")))) {
	     nanswers = answers_valid = 0;
	     return("SPELLING 0\n");
	}
#endif

	/*
	 * No wildcards.
	 */
	if (haswild(word)) {
#ifdef SUNRPC
		return(error(0,
			     "Wildcards not allowed in SPELL requests.",
			     (char *) NULL));
#else !SUNRPC
         	error(0, "Wildcards not allowed in SPELL requests.",
		      (char *) NULL);
        	return(-1);
#endif !SUNRPC
	}
	
	/*
	 * Look it up the easy way.
	 */
	easysearch(word, 0);

	/*
	 * Spelled correctly.
	 */
	if (nanswers == 1) {
		answers_valid = 0;
#ifdef SUNRPC
		(void) sprintf(output, "SPELLING 1\n");
		return(output);
#else !SUNRPC
		fprintf(output, "SPELLING 1\r\n");
		return(nanswers);
#endif !SUNRPC
	}
	
	/*
	 * Look for possible alternate spellings.
	 */
	return(dwim(word));
}

/*
 * help - print the protocol document.
 */
/* ARGSUSED */
#ifdef SUNRPC
char *
#endif SUNRPC
help(argcount, argv)
int argcount;
char **argv;
{
	FILE *fp;
	register int c;
	
	if ((fp = fopen(websterhelp, "r")) == NULL) {
#ifdef SUNRPC	
		return(error(0, "Cannot open \"%s\".", websterhelp));
#else !SUNRPC
		error(0, "Cannot open \"%s\".", websterhelp);
		return(-1);
#endif !SUNRPC
	}
	
	while ((c = getc(fp)) != EOF) {
#ifdef SUNRPC
		addctodef(c);
#else !SUNRPC
 		if (c == '\n')
 			putc('\r', output);

		putc(c, output);
#endif !SUNRPC
	}

	if (fclose(fp) == EOF) {
#ifdef SUNRPC
		return(error(0, "Cannot close \"%s\".", websterhelp));
#else !SUNRPC
		error(0, "Cannot close \"%s\".", websterhelp);
		return(-1);
#endif SUNRPC
	}
	
#ifdef SUNRPC	
	/* build the string and return it */
	*curptr++ = '\0';
	howlong = maxdeflen = 0;
	return(curstash);

#else !SUNRPC
	putc(EOFCH, output);
	return(1);
#endif !SUNRPC
	
}

/*
 * dwim - Do What I Mean.  We try to find words that are "almost"
 *	  what the user wanted.  This code is a crock, but it's neato.
 */
#ifdef SUNRPC
char *
#endif SUNRPC
dwim(word)
char *word;
{
	int i, j;
	char buf[BUFSIZ];
	register char *p, *s, *t;
#ifdef SUNRPC
	char output[BUFSIZ];
	register char *err;
#endif SUNRPC

	nanswers = 0;
	/*
	 * Try searching as if they transposed two letters.
	 */
	s = word;
	while (*(s + 1)) {
		p = buf;
		t = word;

		while (*t) {
			if (t == s) {
				*p++ = *(t + 1);
				*p++ = *t;
				t += 2;
			} else {
				*p++ = *t++;
			}
		}

		*p = NULL;
		easysearch(buf, nanswers);

		s++;
	}

	/*
	 * Try searching as if they didn't give us one
	 * letter.
	 */
	s = word + 1;
	while (*s) {
		p = buf;
		t = word;

		while (t < s)
			*p++ = *t++;
		*p++ = ONECH;

		while (*t)
			*p++ = *t++;

		*p = NULL;
#ifdef SUNRPC
		if ((err = wildsearch(buf, nanswers)) != (char *) NULL)
			return(err);
#else !SUNRPC
		wildsearch(buf, nanswers);
#endif SUNRPC
		
		s++;
	}

	(void) strcat(strcpy(buf, word), "%");
#ifdef SUNRPC
	if ((err = wildsearch(buf, nanswers)) != (char *) NULL)
		return(err);
#else !SUNRPC
		wildsearch(buf, nanswers);
#endif SUNRPC

	/*
	 * Try searching as if they had given us an extra letter.
	 */
	s = word;
	while (*s) {
		p = buf;
		t = word;

		while (*t) {
			if (t != s)
				*p++ = *t;
			t++;
		}

		*p = NULL;
		easysearch(buf, nanswers);

		s++;
	}

	/*
	 * Try searching as if they gave us one letter wrong.
	 * Assume the first letter is right though.
	 */
	s = word + 1;
	while (*s) {
		p = buf;
		t = word;

		while (*t) {
			if (t == s)
				*p++ = ONECH;
			else
				*p++ = *t;
			t++;
		}

		*p = NULL;
#ifdef SUNRPC
		if ((err = wildsearch(buf, nanswers)) != (char *) NULL)
			return(err);
#else !SUNRPC
		wildsearch(buf, nanswers);
#endif SUNRPC

		s++;
	}

	/*
	 * All that work, and we didn't find anything.
	 */
	if (nanswers == 0) {
		answers_valid = 0;
#ifdef SUNRPC
		(void) sprintf(output, "SPELLING 0\n");
		return(output);
#else !SUNRPC
		fprintf(output, "SPELLING 0\r\n");
		return(0);
#endif SUNRPC
	}

	/*
	 * Show them what we found.
	 */
#ifdef SUNRPC
	(void) sprintf(output, "SPELLING\n");
	addtodef(output);
#else !SUNRPC
	fprintf(output, "SPELLING\r\n");
#endif SUNRPC
	
	qsort((char*)answers, nanswers, sizeof(struct answer), comp);

	j = 1;
	for (i = 0; i < nanswers; i++) {
		if ((i > 0) && (strcmp(answers[i].a_word,
				       answers[i-1].a_word) == 0))
			continue;
#ifdef SUNRPC
		(void) sprintf(output, "%d %s\r\n", j++, answers[i].a_word);
		addtodef(output);
#else !SUNRPC
		fprintf(output, "%d %s\r\n", j++, answers[i].a_word);
#endif !SUNRPC
	}
	answers_valid = 1;

#ifdef SUNRPC
	*curptr++ = '\0';
	howlong = maxdeflen = 0;
	return(curstash);
#else !SUNRPC
	putc(EOFCH, output);
	return(1);
#endif !SUNRPC
}

comp(a, b)
struct answer *a, *b;
{
	return(strcmp(a->a_word, b->a_word));
}

char *
strdup(s)
char *s;
{
	extern char *malloc();
	char *p = malloc((unsigned)(strlen(s) + 1));

	if (p == NULL)
		return error(1, "malloc failed", "");
	(void) strcpy(p, s);
	return (p);
}
