#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <webster.h>
#ifdef APRIL_FOOL
#include <time.h>
#endif

extern char	*whoami;
extern char	**hostlist;

#ifdef _AIX
#include <sys/select.h>
#endif

#ifdef SUNRPC
#include <rpc/rpc.h>
#include <sunrpcweb.h>

#ifndef NeXT
char	*malloc();
#endif
#else !SUNRPC
#include <netinet/in.h>

extern FILE *WebsterSock;
#endif !SUNRPC

#ifdef XCLIENT
extern char	*curstash;
void addtodef(), addctodef();
struct sizes *popup();
#endif XCLIENT
char	*listlines();

#ifdef SUNRPC
int	RPC_exit_on_RPC_error = 1;
#endif

#if defined(POSIX) && !defined(index) && !defined(strchr)
#define index strchr
#endif

/*
 * define - try to define a word and print its definition.
 */
void
define(word)
char	*word;
{
	int	refs, byebye();

#ifndef SUNRPC
	int	c;
	char	buf[BUFSIZ], returnbuf[BUFSIZ];
#else /* SUNRPC */
	char	*returnbuf, *wordptr;

	/* 9 = '\n' + '\0' + strlen("DEFINE ") */
	if ((wordptr = malloc((unsigned)sizeof(char *) * (strlen(word) +
	    9))) == (char *) NULL)
		memerror();
#endif /* SUNRPC */

#ifndef SUNRPC
	/*
	 * Command is "DEFINE<space>word<nl>".
	 */
	(void) sprintf(buf, "DEFINE %s\r\n", word);

	/*
	 * Send the command.
	 */
	if (send(fileno(WebsterSock), buf, strlen(buf), 0) < 0) {
		perror("webster: send");
		byebye();
	}

	/*
	 * Read the first line back from the server.  This
	 * line tells us what the result of our DEFINE
	 * request was.
	 */
	(void) getline(returnbuf);

	/* in some weird conditions, the server will return NULL. */
	if (returnbuf[0] == '\0') {
		server_broken_message();
	}
#else SUNRPC

	(void) sprintf(wordptr, "DEFINE %s\n", word);

	if (dorpc(wordptr, &returnbuf)) {
	     return;
	}
#endif SUNRPC

	/*
	 * "WILD<space>0<nl>" means they used wild cards and no
	 * matches were found.
	 */
	if (!strncmp(returnbuf, "WILD 0", 6)) {
#ifdef TTYCLIENT
		printf("No matching words found.\n");
#else XCLIENT
		popup("Error", "No matching words found.\n");
#endif
	}

	/*
	 * "WILD<nl>" means that the wildcard matched, so we
	 * print a list of possible matches.
	 */
	else if (!strncmp(returnbuf, "WILD", 4)) {
#ifdef TTYCLIENT
		printf("Possible matches are:\n");
#else XCLIENT
		char	buf[BUFSIZ];
		(void) sprintf(buf, "Possible matches are:\n");
		addtodef(buf);
#endif
		/*
		 * List lines.
		 */
#ifndef SUNRPC
		(void) listlines(0, 1);
#else SUNRPC
		(void) listlines((index(returnbuf, '\n') + 1), 0,
		     1);
#endif SUNRPC

#ifdef TTYCLIENT
		(void) putchar('\n');
#else XCLIENT
		addctodef('\n');
		popup(word, curstash);
#endif
	}

	/*
	 * "SPELLING<space>0<nl>" means the word is not defined,
	 * and there are no alternate spellings.
	 */
	else if (!strncmp(returnbuf, "SPELLING 0", 10)) {
#ifdef TTYCLIENT
		printf("No definition for '%s'.\n", word);
#else XCLIENT
		char	buf[BUFSIZ];

		(void) sprintf(buf, "No definition for '%s'.\n",
		    word);
		popup("Error", buf);
#endif
	}

	/*
	 * "SPELLING<nl>" means the word is not defined, but
	 * some alternate spellings were found.  Print
	 * them out.
	 */
	else if (!strncmp(returnbuf, "SPELLING", 8)) {
#ifdef TTYCLIENT
		printf("No definition for '%s'.  Maybe you mean:\n",
		     word);
#else XCLIENT
		char	buf[BUFSIZ];

		(void) sprintf(buf, "No definition for '%s'.  Maybe you \
mean:\n",
		     word);
		addtodef(buf);
#endif
		/*
		 * List lines.
		 */
#ifndef SUNRPC
		(void) listlines(0, 1);
#else SUNRPC
		(void) listlines((index(returnbuf, '\n') + 1), 0,
		     1);
#endif SUNRPC

#ifdef TTYCLIENT
		(void) putchar('\n');
#else XCLIENT
		addctodef('\0');
		popup(word, curstash);
#endif
	}

	/*
	 * "DEFINITION<space>n<nl>" means the word is defined,
	 * and there are n cross-references.
	 */
	else if (!strncmp(returnbuf, "DEFINITION", 10)) {
	     char *newdef = returnbuf;

	     (void) sscanf(newdef + 11, "%d", &refs);

	     /*
	      * Print any cross references.
	      */
	     if (refs > 0) {
#ifdef TTYCLIENT
		  printf("Cross references:\n");
#else XCLIENT
		  char	buf[BUFSIZ];

		  (void) sprintf(buf, "Cross references:\n");
		  addtodef(buf);
#endif
		  /*
		   * List lines.
		   */
#ifndef SUNRPC
		  (void) listlines(refs, 1);
#else SUNRPC
		  newdef = listlines((index(newdef, '\n') +
					 1), refs, 1);
#endif SUNRPC
	     }
#ifndef SUNRPC
	     /*
	      * Print the definition.
	      */
	     while ((c = getc(WebsterSock)) != EOF) {
		  if (c == EOFCH)
		       break;

		  c &= 0177;
		  /* nucleate carriage returns */
		  if (c != '\r')
#ifdef TTYCLIENT
		       (void) putchar(c);
#else XCLIENT
		  addctodef(c);
#endif
	     }
#else SUNRPC
	     if (refs > 0) {
		  /* we already skipped the "code" once */
#ifdef TTYCLIENT
		  printf(newdef);
		  (void) putchar('\n');
#else XCLIENT
		  addtodef(newdef);
		  addctodef('\0');
		  popup(word, curstash);
#endif
	     } else {
#ifdef TTYCLIENT
		  printf(index(newdef, '\n') + 1);
		  (void) putchar('\n');
#else XCLIENT
		  addtodef(index(newdef, '\n') + 1);
		  addctodef('\0');
		  popup(word, curstash);
#endif
	     }
#endif SUNRPC
	}

	/* An error message. */
	else if (!strncmp(returnbuf, "ERROR ", 6)) {
		if (!strncmp(returnbuf + 6, "FATAL", 5)) {
			fprintf(stderr, "%s: %s\n", whoami, returnbuf +
			    11);
			byebye();
		} else {
			fprintf(stderr, "%s: %s\n", whoami, returnbuf +
			    17);
		}
	}
#ifndef SUNRPC
	/*
	 * Should never get here.
	 */
	else {
	     while (((c = getc(WebsterSock)) != EOF) && (c != EOFCH))
		  ;
	}
#else /* SUNRPC */
	freerpc(&returnbuf);
#endif /* SUNRPC */
}


/*
 * spell - look up a word and see if it's spelled correctly.
 */
spell(word)
char	*word;
{
#ifndef SUNRPC
	int	c;
	char	buf[BUFSIZ], returnbuf[BUFSIZ];
#else SUNRPC
	char	*returnbuf, *wordptr;

	/* 8 = '\n' + '\0' + strlen("SPELL ") */
	if ((wordptr = malloc((unsigned)sizeof(char *) * (strlen(word) +
	    8))) == (char *) NULL)
		memerror();

#endif SUNRPC

#ifndef SUNRPC
	/*
	 * Command is "SPELL<space>word<nl>".
	 */
	(void) sprintf(buf, "SPELL %s\r\n", word);

	/*
	 * Send the command.
	 */
	if (send(fileno(WebsterSock), buf, strlen(buf), 0) < 0) {
		perror("webster: send");
		byebye();
	}

	/*
	 * Read the first line back from the server.  This
	 * line tells us what the result of our SPELL
	 * request was.
	 */
	(void) getline(returnbuf);
#else SUNRPC
	(void) sprintf(wordptr, "SPELL %s\n", word);

	if (dorpc(wordptr, &returnbuf)) {
	     return;
	}
#endif SUNRPC

	/*
	 * "SPELLING<space>0<nl>" means the word is not spelled correctly,
	 * and there are no alternate spellings.
	 */
	if (!strncmp(returnbuf, "SPELLING 0", 10)) {
		char	buf[BUFSIZ];
		(void) sprintf(buf, "'%s' is not a correct spelling.\n",
		     word);
		write(1, buf, strlen(buf));
	}

	/*
	 * "SPELLING<space>1<nl>" means the word is spelled correctly.
	 */
	else if (!strncmp(returnbuf, "SPELLING 1", 10)) {
		char	buf[BUFSIZ];
		(void) sprintf(buf, "'%s' is spelled correctly.\n",
		     word);
		write(1, buf, strlen(buf));
	}

	/*
	 * "SPELLING<nl>" means the word is not spelled correctly, but
	 * some alternate spellings were found.  Print them out.
	 */
	else if (!strncmp(returnbuf, "SPELLING", 8)) {
		char	buf[BUFSIZ];
		(void) sprintf(buf, "No spelling for '%s'.  Maybe you mean:\n",
		     word);
		write(1, buf, strlen(buf));

		/*
		 * List lines.
		 */
#ifndef SUNRPC
		(void) listlines(0, 1);
#else SUNRPC
		(void) listlines((index(returnbuf, '\n') + 1), 0,
		     1);
#endif SUNRPC
		(void) putchar('\n');
	}

	/*
	 * An error message.
	 */
	else if (!strncmp(returnbuf, "ERROR ", 6)) {
		if (!strncmp(returnbuf + 6, "FATAL", 5)) {
			printf("%s\n", returnbuf + 11);
			byebye();
		} else {
			printf("%s\n", returnbuf + 17);
		}
	}
#ifndef SUNRPC
	/*
	 * Should never get here.
	 */
	else {
	     while (((c = getc(WebsterSock)) != EOF) && (c != EOFCH))
		;
	}
#else
	freerpc(&returnbuf);
#endif /* SUNRPC */
}


/*
 * complete - try to complete the word.
 */
complete(word)
char	*word;
{
	register char	*s;
#ifndef SUNRPC
	int	c;
	char	buf[BUFSIZ], returnbuf[BUFSIZ];
#else SUNRPC
	char	*returnbuf, *wordptr;

	/* 11 = '\n' + '\0' + strlen("COMPLETE ") */
	if ((wordptr = malloc((unsigned)sizeof(char *) * (strlen(word) +
	    11))) == (char *) NULL)
		memerror();

#endif SUNRPC

#ifndef SUNRPC
	/*
	 * Command is "COMPLETE<space>word<nl>".
	 */
	(void) sprintf(buf, "COMPLETE %s\r\n", word);

	/*
	 * Send the command.
	 */
	if (send(fileno(WebsterSock), buf, strlen(buf), 0) < 0) {
		perror("webster: send");
		byebye();
	}

	/*
	 * Get the first line from the server, which tells
	 * us the reult of our request.
	 */
	(void) getline(returnbuf);

#else SUNRPC
	(void) sprintf(wordptr, "COMPLETE %s\n", word);

	if (dorpc(wordptr, &returnbuf)) {
	     return;
	}
#endif SUNRPC
	/*
	 * "AMBIGUOUS<space>n<nl>" means the word is ambiguous,
	 * with n possible matches.  We ignore the n, and just
	 * beep.
	 */
	if (!strncmp(returnbuf, "AMBIGUOUS", 9)) {
		(void) write(1, "\007", 1);
	}

	/*
	 * "COMPLETION<space>full-word<nl>" means the
	 * word was completed.  Erase what they typed
	 * and print the new word over it.  This takes
	 * care of things if they used wildcards.
	 */
	else if (!strncmp(returnbuf, "COMPLETION", 10)) {
		for (s = word; *s; s++)
			(void) write(1, "\b", 1);

		s = returnbuf + 11;
		while (((*s & 0177) != '\r') && ((*s & 0177) != '\n') &&
		    ((*s & 0177) != 0)) {
			(void) write(1, s, 1);
			s++;
		}

		/*
		 * Put the new word back into word.  This
		 * gets rid of the wildcards here.
		 */
		*s = '\0';
		(void) strcpy(word, returnbuf + 11);

	}

	/*
	 * An error message.
	 */
	else if (!strncmp(returnbuf, "ERROR ", 6)) {
		if (!strncmp(returnbuf + 6, "FATAL", 5)) {
			printf("%s\n", returnbuf + 11);
			byebye();
		} else {
			printf("%s\n", returnbuf + 17);
		}
	}
#ifndef SUNRPC
	/*
	 * Should never get here.
	 */
	else {
	     while (((c = getc(WebsterSock)) != EOF) && (c != EOFCH))
		;
	}
#else
	freerpc(&returnbuf);
#endif /* !SUNRPC */
}


/*
 * endings - find possible endings for a word.
 */
endings(word)
char	*word;
{
	int returnval = 0;
#ifndef SUNRPC
	int	c;
	char	buf[BUFSIZ], returnbuf[BUFSIZ];

#else SUNRPC
	char	*returnbuf, *wordptr;

	/* 10 = '\n' + '\0' + strlen("ENDINGS ") */
	if ((wordptr = malloc((unsigned)sizeof(char *) * (strlen(word) +
	    10))) == (char *) NULL)
		memerror();
#endif SUNRPC
#ifndef SUNRPC
	/*
	 * Command is "ENDINGS<space>word<nl>".
	 */
	(void) sprintf(buf, "ENDINGS %s\r\n", word);

	/*
	 * Send the command.
	 */
	if (send(fileno(WebsterSock), buf, strlen(buf), 0) < 0) {
		perror("webster: send");
		byebye();
	}

	/*
	 * Get the first line from the server, which tells
	 * us the result of the search.
	 */
	(void) getline(returnbuf);

#else SUNRPC
	(void) sprintf(wordptr, "ENDINGS %s\n", word);

	if (dorpc(wordptr, &returnbuf)) {
	     return(returnval);
	}
#endif SUNRPC

	/*
	 * "MATCHS<space>0<nl>" means nothing matched,
	 * so we beep at them.
	 */
	if (!strncmp(returnbuf, "MATCHS 0", 8)) {
		(void) write(1, "\007", 1);
		returnval = 1;
	}

	/*
	 * "MATCHS<nl>" means there were matches, so
	 * print them out.
	 */
	else if (!strncmp(returnbuf, "MATCHS", 6)) {
		printf("\nMaybe you mean:\n");

		/*
		 * List lines.
		 */
#ifndef SUNRPC
		(void) listlines(0, 0);
#else SUNRPC
		(void) listlines((index(returnbuf, '\n') + 1), 0,
		     0);
#endif SUNRPC
		(void) putchar('\n');
	}

	/*
	 * An error message.
	 */
	else if (!strncmp(returnbuf, "ERROR ", 6)) {
		if (!strncmp(returnbuf + 6, "FATAL", 5)) {
			printf("%s\n", returnbuf + 11);
			byebye();
		} else {
			printf("%s\n", returnbuf + 17);
		}
	}
#ifndef SUNRPC
	/*
	 * Should never get here.
	 */
	else {
	     while (((c = getc(WebsterSock)) != EOF) && (c != EOFCH))
		;
	}
#else
	freerpc(&returnbuf);
#endif /* !SUNRPC */
	return(returnval);
}



#ifndef SUNRPC
/*
 * getline - read one line from the server and put it in s.
 */
getline(s)
register char	*s;
{
	register int	c;

	/* Read in chars.  If we hit EOFCH, return 0. */
	while ((c = getc(WebsterSock)) != EOF) {
		if (c == EOFCH)
			return(0);

		c &= 0177;

		if (c == '\r')
			continue;
		if (c == '\n')
			break;

		*s++ = c;
	}

	*s = '\0';
	return(1);
}


/*
 * listlines - list WILD-style lines on the screen.
 */
listlines(flag, num)
register int	flag, num;
{
	char	buf[BUFSIZ];
	register int	col = 0;

	printf(" ");

	/*
	 * If n is non-zero, we only want to list n lines.
	 * Otherwise, we go till we hit EOFCH.  Lines are
	 * printed in four columns.
	 */
	if (flag) {
		while (flag-- > 0) {
			(void) getline(buf);
			putline(buf, num);

			if (++col == 3) {
				printf("\n ");
				col = 0;
			}
		}
	} else {
		while (getline(buf) > 0) {
			putline(buf, num);

			if (++col == 3) {
				printf("\n ");
				col = 0;
			}
		}
	}

	if (col)
		(void) putchar('\n');
}


/*
 * putline - put out a line, if flag is 0, skip the line number.
 */
putline(buf, flag)
char	*buf;
int	flag;
{
	int	lnum;
	char	line[BUFSIZ];

	(void) sscanf(buf, "%d %[^\n]", &lnum, line);

	if (flag)
		printf("%3d. %-20s", lnum, line);
	else
		printf("%-25s", line);
}


#else SUNRPC
/*
 * listlines - list WILD-style lines on the screen.  If flag is zero,
 * the lines will not be numbered.  Return the current (ie,
 * undealt-with) returnbuf.
 */
char	*
listlines(returnbuf, n, flag)
char	*returnbuf;
register int	n;
int	flag;
{
	register int	col = 0;
	register char	*new;
	char	buf[BUFSIZ], line[BUFSIZ];
	int	linenum;

	/*
	 * If n is non-zero, we only want to list n lines.  Otherwise,
	 * we go till we hit EOFCH, or '\0'.  Lines are printed in
	 * four columns.
	 */
	if (n) {
		while (n-- > 0) {
			/*
			 * Put from returnbuf to '\n' into buf;
			 * move returnbuf to past the \n.
			 * 
			 * Trust that the flag is correct about the
			 * number of lines we have available.
			 */
			new = index(returnbuf, '\n');

			*new = '\0';
			(void) strcpy(buf, returnbuf);
			returnbuf = ++new;

			(void) sscanf(buf, "%d %[^\n]", &linenum,
			     line);

			if (flag)
				(void) sprintf(buf, "%3d. %-20s",
				     linenum, line);
			else
				(void) sprintf(buf, "%-25s", line);
#ifdef TTYCLIENT
			printf(buf);
#else XCLIENT
			addtodef(buf);
#endif
			if (++col == 3) {
#ifdef TTYCLIENT
				printf("\n");
#else XCLIENT
				addtodef("\n");
#endif
				col = 0;
			}
		}
	} else {
		while ((new = index(returnbuf, '\n')) != 0) {

			*new = '\0';
			(void) strcpy(buf, returnbuf);
			returnbuf = ++new;

			(void) sscanf(buf, "%d %[^\n]", &linenum,
			     line);

			if (flag)
				(void) sprintf(buf, "%3d. %-20s",
				     linenum, line);
			else
				(void) sprintf(buf, "%-25s", line);

#ifdef TTYCLIENT
			printf(buf);
#else XCLIENT
			addtodef(buf);
#endif
			if (++col == 3) {
#ifdef TTYCLIENT
				printf("\n");
#else XCLIENT
				addtodef("\n");
#endif
				col = 0;
			}
		}
	}
#ifdef TTYCLIENT
	(void) putchar('\n');
#else XCLIENT
	addctodef('\n');
#endif
	return(returnbuf);
}


#endif SUNRPC


#ifdef APRIL_FOOL
april_fool()
{
     time_t clock;
     struct tm *loctime;

     (void) time(&clock);
     loctime = localtime(&clock);

     return((loctime->tm_mon == 3) && (loctime->tm_mday == 1));
}
#endif

	  
server_broken_message()
{
	fprintf(stderr, "%s\n%s\n%s%s%s\n%s\n%s\n%s\n", "The webster servers have failed to answer the request to look up your word.",
	     	     "This could be because the webster server is down, or because your word is",
	     "too long and the server can't find it.  Try looking up \"",
#ifdef APRIL_FOOL
		april_fool() ? "gullible" :
#endif
		"tree", "\" -- if that",
	     	     "works, then you've probably misspelled your word.  If it fails, then send",
	     "mail to bug-sipb@ATHENA.MIT.EDU to let the SIPB know that the servers are",
	     	     "broken.");
}


#ifdef SUNRPC
dorpc(arg, returnbuf)
char *arg, **returnbuf;
{
     int hostsleft;
     static int hostnum = -1, numhosts;
     bool_t xdr_longstring();
     int returncode = 0;

     if (hostnum < 0) {
	  while (hostlist[numhosts]) numhosts++;
	  hostnum = getpid() % numhosts;
     }	  

     hostsleft = numhosts;

     *returnbuf = 0;

     while (hostsleft--) {
	  int	retry = 3;

	  do {
#ifdef DEBUG
	       fprintf(stderr, "Trying RPC %s, retry number %d.\n",
		       hostlist[hostnum], retry);
#endif
	       returncode = callrpc_(hostlist[hostnum], WEBPROG,
				    WEBVERS, WEBSTER, xdr_longstring,
				    (char *) &arg, xdr_longstring,
				    (char *) returnbuf);
	       if (*returnbuf == 0)
		    returncode = -1;
	  } while (--retry && returncode);

	  if (returncode) {
	       hostnum = (hostnum + 1) % numhosts;
	       continue;
	  } else
	       break;
     }

     if (returncode) {
	  server_broken_message();
#ifndef DEBUG
	  if (RPC_exit_on_RPC_error)
	       byebyeval(-1);
#endif !DEBUG
     }

     return(returncode);
}

freerpc(returnbuf)
char **returnbuf;
{
     bool_t xdr_longstring();
     free_answer(xdr_longstring, (char *)returnbuf);
}
#endif
