ftp.nice.ch/pub/next/unix/network/infosystems/webster.s.tar.gz#/webster/webster.c

This is webster.c in view mode; [Download] [Up]

/*
 * webster - look words up in the dictionary
 *
 * This program connects to a Webster server to get
 * definitions of words.  Words may be given on the command line, or, if
 * no arguments are given, the program runs interactively.
 *
 * In either mode, a word may include the wildcard characters '%' and '*'.
 * The '%' character matches exactly one character, while the '*' matches
 * zero or more characters.  If wildcards are used, the program will
 * return either "No match" or a list of matching words.
 *
 * In interactive mode only, Tenex-style command completion may also be
 * used.  Typing a '?' following part of a word will cause the program
 * to print all words which begin with the partial word, or the program
 * will beep if nothing matches.  Typing an ESCape character causes the
 * program to attempt to complete the word.  If the word can be completed,
 * the new word is printed; otherwise, the program beeps.  Wildcards
 * may be used to specify the partial words.
 *
 * If the flag -t is given, a server providing thesaurus lookup is
 * sought, and the program obtains and prints the thesaurus entry
 * for the word(s).
 *
 * This program runs under 4.3BSD and System V Unix.
 *
 * NOTES TO INSTALLERS:
 *	The program goes through a list of known webster servers
 *      in order, connecting to the first one which responds that offers
 *      the type of service desired (dictionary/thesaurus lookup).
 *	The list of servers is set below in serverlist.
 *
 *	Define USG if you are running a Sys V derivative
 *
 *      A connection attempt is aborted if a server does not respond
 *      within CONNTIMEO seconds.  CONNTIMEO is #defined below.
 *	An interactive connection is closed if the user does not type
 *      anything for USERTIMEO seconds, as #defined below.
 *      
 *
 *		
 * David A. Curry, davy@purdue-ecn
 * Purdue Engineering Computer Network
 *
 * Anil Gangolli, gangolli@wolvesden.stanford.edu
 *   -fixed some bugs in interactive mode under 4.3
 *   -changed connection routine to go through list of webster servers
 *   -added INDEX options for thesaurus/dictionary/dictionary-full
 *      selection on NeXT-box webster servers.
 *   -fixed connection routine to continue when first server connect fails
 *    (bug found by Scott Seligman).
 *   -added USG conditional stuff to suspend() routine which Eric missed
 *    (code from Scott Seligman)
 *   -added connect attempt time out stuff to connectup()
 *
 * Eric P. Scott, eps@sutro.sfsu.edu
 *   -added USG conditional for HP/UX, various minor fixes
 *
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/errno.h>
#include <netinet/in.h>
#include <signal.h>
#ifdef USG
#include <sys/termio.h>
#else
#include <sgtty.h>
#endif
#include <netdb.h>
#include <ctype.h>
#include <stdio.h>

#define INDEX_DICT 01
#define INDEX_THES 02
#define INDEX_FULL 04
#define INDEX_ALL 07

typedef struct {
  char hostname[50];		/* hostname of webster server */
  int  websterport;		/* internet port on which it listens */
  int  indicesoffered;		/* which indices are offered by server
				   INDEX_DICT /THES /FULL bits set
				   accordingly */
} SERVERENTRY;

SERVERENTRY serverlist[3] = {
  {"next.stanford.edu", 103, INDEX_ALL},
  {"gsb-why.stanford.edu", 103, INDEX_DICT},
  {"",0,0}  /* this trailer entry is needed */
};


char *indexname[5] = {
  "",
  "dictionary",
  "thesaurus",
  "",
  "dictionary-full"
};
    

#define CONNTIMEO 5   /* time out period in seconds for connect() attempts */
#define USERTIMEO 180 /* close-down if user types nothing for 3 minutes */

/* #define NOINTERACT */      		/* define if interactive mode not
					   desired */
#ifndef CEOF
#define CEOF		04		/* EOF character             ^D */
#endif
#define BACKSPACE	010		/* backspace character       ^H */
#define DELETE		0177		/* backspace character       DEL */
#define WORDERASE	027		/* word erase character      ^W */
#define LINEERASE	025		/* line kill character       ^U */
#define LINERTYPE	022		/* line reprint character    ^R */
#define COMPLETE	033		/* word completion character ESC */
#define ENDINGS		'?'		/* print matches character      */
#define EOFCH		0200		/* EOF record from the server   */

#ifdef USG
struct termio ttyb;			/* tty modes when interactive	*/
struct termio rttyb;			/* original tty modes		*/
#else
struct sgttyb sgttyb;			/* tty modes when interactive	*/
struct sgttyb rsgttyb;			/* original tty modes		*/
#endif

FILE *WebsterSock;			/* for reading from the server	*/
int interactive = 0;			/* 1 when running interactive	*/
int timedout = 0;			/* 1 when connect attempt timed out */
int idx = INDEX_DICT;			/* set to desired index in main() */


main(argc, argv)
int argc;
char **argv;
{
       /* check for alternate index flag */
       if ((argc > 1) && (strcmp(argv[1],"-t")==0))
	 {
	   idx = INDEX_THES;   /* use thesaurus instead of dictionary */
	   argc--;
	   argv++;
	 }
#ifdef NOTDEF
/* Disabled because all occurrences searches are expensive for server
   and generally worthless */ 
       else if ((argc > 1) && (strcmp(argv[1],"-f")==0))
	 {
	   idx = INDEX_FULL;  /* use dict with "fullword" lookup */
	   argc--;
	   argv++;
	 }
#endif
       else if ((argc > 1) && (argv[1][0]=='-'))
	 {
	   fprintf(stderr,"usage: webster [-t] word [word ...]\n");
	   exit(1);
	 }

	/*
	 * Connect to the server.
	 */
	connectup();

        if (idx != INDEX_DICT) selectindex(idx);

	/*
	 * If we were given command line arguments, just
	 * try to define each word.
	 */
	if (argc > 1) {
		while (--argc) {
			/*
			 * Define the word.
			 */
			define(*++argv);
		}

		exit(0);
	}

#ifndef NOINTERACT
	/*
	 * If no arguments were given, set up the
	 * terminal modes and run interactively.
	 */
	setup();
	interact();
#endif
#ifdef NOINTERACT
	fprintf(stderr,"usage: webster [-t|-f] word [word ...]\n");
#endif
}

/*
 * connectup - connects to the Webster server.
 */
connectup()
{
	register int s=0;
	struct sockaddr_in sin;
	register struct hostent *hp;
	struct hostent *gethostbyname();
	int i;

	/* variables for connection time out handling */
	int connectval;		  /* value returned by connect() */
	int gotconnection = 0;    /* set to 1 when we get a connection */
	extern int cnncttimeo();  /* connection time out handler */
	extern int errno;	  /* Unix standard error number */

       /* Run through the list of server hosts till we find one
	  that offers the service we want and accepts a connection.
	  If we don't get one fail.
       */

	for(i=0; (!gotconnection) && (serverlist[i].hostname[0] != 0); i++) {
	/* skip the entry if doesn't offer the index type we want. */
	if (!(serverlist[i].indicesoffered & idx)) break;
	
	/*
	 * Look up the host
	 */
	if ((hp = gethostbyname(serverlist[i].hostname)) == NULL) {
		fprintf(stderr, "webster: unknown host in server table!");
		exit(1);
	}

	bzero(&sin, sizeof(struct sockaddr_in));

	/*
	 * Build the server's address.
	 */
	sin.sin_family = AF_INET;
	sin.sin_port = htons(serverlist[i].websterport);
	bcopy(hp->h_addr, &sin.sin_addr, hp->h_length);

	/*
	 * Get a TCP socket.
	 */
	if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
		break;
	}

	/*
	 * Try to connect.  If connect fails, or if timer times out,
	   move to next host in list.
	 */
	connectval = 1;
	timedout = 0;
	signal(SIGALRM, cnncttimeo);
	printf("webster: trying server at %s...", serverlist[i].hostname);
	fflush(stdout);
	alarm(CONNTIMEO);
	connectval = connect(s, &sin, sizeof(struct sockaddr_in));
	alarm(0);
	signal(SIGALRM, SIG_DFL);

	if (timedout) {
	  /* connect interrupted by alarm */
	  printf("timed out.\n");
	  /* move to next host in list */
	  continue;
	}

	if (connectval != 0) {
	  /* otherwise connection failed for some other reason */
	  switch (errno) {
	  case ECONNREFUSED:
	    printf("refused.\n");
	    break;
	  default:
	    printf("failed.\n");
	  }
	  /* move on to next host in list */
	  continue;
	}
	
	/* otherwise we got a connection. */
	gotconnection = 1;
	printf("connected.\n\n");

	/* in any case get output to user */
	fflush(stdout);
      }


      if (!gotconnection) {
	printf("webster: couldn't find an appropriate server.\n");
	exit(1);
      }
	

	/*
	 * Open the socket for stdio.
	 */
	WebsterSock = fdopen(s, "r");
}

/*
 * setup - turns on CBREAK, turns off ECHO.  Also trap signals.
 */
setup()
{
	extern int byebye();
	extern int suspend();

	interactive = 1;
#ifdef USG
	ioctl(0, TCGETA, &ttyb);

	rttyb = ttyb;
#else
	ioctl(0, TIOCGETP, &sgttyb);

	rsgttyb = sgttyb;
#endif

	signal(SIGINT, byebye);
	signal(SIGQUIT, byebye);

#ifdef SIGTSTP
	signal(SIGTSTP, suspend);
#endif

#ifdef USG
	ttyb.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL);
	ttyb.c_cc[VMIN]=1, ttyb.c_cc[VTIME]=0;
	ioctl(0, TCSETAW, &ttyb);
#else
	sgttyb.sg_flags |= CBREAK;
	sgttyb.sg_flags &= ~ECHO;
	ioctl(0, TIOCSETP, &sgttyb);
#endif
}

/*
 * interact - interact with the user.
 */
interact()
{
  	char c;				/* WAS int c -- bug, gangolli */
	char buf[1024];
	register char *s, *t;
	extern int byebye();

	/*
	 * Forever...
	 */
	for (;;) {
		/*
		 * Prompt for a word.
		 */
		s = buf;
		write(1, "Word: ", 6);

		/*
		 * Forever... read characters.  We
		 * break out of this from inside.
		 */
		for (;;) {
		  	/* time out on lack of input after USERTIMEO seconds */
  		        signal(SIGALRM,byebye);
			alarm(USERTIMEO);
			if (read(0, &c, 1) <= 0)
				byebye();
			alarm(0);
			signal(SIGALRM,SIG_DFL);

			switch (c) {
			case CEOF:
				byebye();
				break;
			case DELETE:
			case BACKSPACE:
				/*
				 * If not at the beginning of a line,
				 * back up one character.
				 */
			         /* really erase -- gangolli */
				if (s > buf) {
					write(1, "\b \b", 3);
					*--s = 0;
				}
				continue;
			case WORDERASE:
				/*
				 * Until we hit beginning of line
				 * or beginning of word, back up.
				 */
				/* skip trailing spaces first -- gangolli */
				while ((s > buf) && (*(s-1) == ' ')) {
				  write(1, "\b", 1);
				  *--s = 0;
				}
			        while ((s > buf) && (*(s-1) != ' ')) {
				  write(1, "\b \b", 3);
				  *--s = 0;
				}
				continue;
			case LINEERASE:
				/*
				 * Until we hit beginning of line,
				 * back up.
				 */
				/* really zero buffer -- gangolli,
				   don't erase more than the input. */
				while (s > buf) {
				  write(1, "\b \b", 3);
				  *--s = 0;
				}
				continue;
			case LINERTYPE:
				/*
				 * Retype the line.
				 */
				write(1, "\r\nWord: ", 8);

				for (t=buf; t < s; t++)
					write(1, t, 1);
				continue;
			case COMPLETE:
				/*
				 * Try to complete what they typed
				 * so far.  Put the pointer at the
				 * end of the new word.
				 */
				*s = NULL;
				complete(buf);
				for (s=buf; *s; s++)
					;
				continue;
			case ENDINGS:
				/*
				 * If it's the first character,
				 * then print some help.  Otherwise,
				 * try to find endings for the word.
				 * endings() returns 1 if no endings
				 * were found, 0 if some were found.
				 * This tells us whether to reprint
				 * the current word or not.
				 */
				if (s == buf) {
					help();
				}
				else {
					*s = NULL;
					if (endings(buf) == 0) {
						write(1, "Word: ", 6);

						for (s=buf; *s; s++)
							write(1, s, 1);
					}

					continue;
				}
				break;
			case '\n':
				/*
				 * If at the start of a word,
				 * newline is exit.
				 */
				if (s == buf)
					byebye();

				/*
				 * Otherwise, try to define
				 * the word.
				 */
				*s = NULL;
				write(1, "\n", 1);
				define(buf);
				break;
			default:
				/*
				 * Echo the character and copy it.
				 */
				/* only printables -- gangolli */
				if isprint(c) {
				  write(1, &c, 1);
				  *s++ = c;
				}
				else /* beep */
				  write(1, "\007", 1);
				continue;
			}
			break;
		}
	}
}



selectindex(i)
int i;
{
  char buf[1024];

  sprintf(buf, "INDEX %s\r\n", indexname[idx]);
	  
  if (send(fileno(WebsterSock), buf, strlen(buf), 0) < 0) {
    perror("webster: send");
    byebye();
  }
}

/*
 * define - try to define a word and print its definition.
 */
define(word)
char *word;
{
	int c, refs;
	char buf[1024];

	/*
	 * Command is "DEFINE<space>word<nl>".
	 */
	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.
	 */
	getline(buf);

	/*
	 * "WILD<space>0<nl>" means they used wild cards and no
	 * matches were found.
	 */
	if (!strncmp(buf, "WILD 0", 6)) {
		printf("No match.\n");
		return;
	}

	/*
	 * "WORD<nl>" means that the wildcard matched, so we
	 * print a list of possible matches.
	 */
	if (!strncmp(buf, "WILD", 4)) {
		printf("Possible matches are:\n");

		/*
		 * List lines.
		 */
		listlines(0, 1);
		putchar('\n');
		return;
	}

	/*
	 * "SPELLING<space>0<nl>" means the word is not defined,
	 * and there are no alternate spellings.
	 */
	if (!strncmp(buf, "SPELLING 0", 10)) {
		printf("No definition for '%s'.\n", word);
		return;
	}

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

		/*
		 * List lines.
		 */
		listlines(0, 1);
		putchar('\n');
		return;
	}

	/*
	 * "DEFINITION<space>n<nl>" means the word is defined,
	 * and there are n cross-references.
	 */
	if (!strncmp(buf, "DEFINITION", 10)) {
		sscanf(buf+11, "%d", &refs);

		/*
		 * Print any cross references.
		 */
		if (refs > 0) {
			printf("Cross references:\n");

			/*
			 * List lines.
			 */
			listlines(refs, 1);
			putchar('\n');
		}

		/*
		 * Print the definition.
		 */
		while ((c = getc(WebsterSock)) != EOF) {
			if (c == EOFCH)
				break;

			c &= 0177;
			if (c != '\r')
				putchar(c);
		}

		putchar('\n');
		return;
	}

	/*
	 * "ERROR . . ." means the server didn't like our input
	 */
	if (!strncmp(buf, "ERROR", 5)) {
		printf("Sorry, '%s' isn't understood.\n", word);
		return;
	}

	/*
	 * Should never get here.
	 */
	while (((c = getc(WebsterSock)) != EOF) && (c != EOFCH))
		;
}

/*
 * complete - try to complete the word.
 */
complete(word)
char *word;
{
	int c;
	char buf[1024];
	register char *s;

	/*
	 * Command is "COMPLETE<space>word<nl>".
	 */
	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.
	 */
	getline(buf);

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

	/*
	 * "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.
	 */
	if (!strncmp(buf, "COMPLETION", 10)) {
		for (s=word; *s; s++)
			write(1, "\b", 1);

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

		/*
		 * Put the new word back into word.  This
		 * gets rid of the wildcards here.
		 */
		*s = NULL;
		strcpy(word, buf+11);

		return;
	}

	/*
	 * "ERROR . . ." means the server didn't like our input
	 */
	if (!strncmp(buf, "ERROR", 5)) {
		printf("Sorry, '%s' isn't understood.\n", word);
		return;
	}

	/*
	 * Should never get here.
	 */
	while (((c = getc(WebsterSock)) != EOF) && (c != EOFCH))
		;
}

/*
 * endings - find possible endings for a word.
 */
endings(word)
char *word;
{
	int c;
	char buf[1024];

	/*
	 * Command is "ENDINGS<space>word<nl>".
	 */
	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.
	 */
	getline(buf);

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

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

		/*
		 * List lines.
		 */
		listlines(0, 0);
		putchar('\n');
		return(0);
	}

	/*
	 * "ERROR . . ." means the server didn't like our input
	 */
	if (!strncmp(buf, "ERROR", 5)) {
		printf("Sorry, '%s' isn't understood.\n", word);
		return;
	}

	/*
	 * Should never get here.
	 */
	while (((c = getc(WebsterSock)) != EOF) && (c != EOFCH))
		;

	return(0);
}

/*
 * 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 = NULL;
	return(1);
}

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

	putchar(' ');

	/*
	 * 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 (n) {
		col = 0;
		while (n-- > 0) {
			getline(buf);
			putline(buf, num);

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

			if (++col == 3) {
				fputs("\n ", stdout);
				col = 0;
			}
		}
	}

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

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

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

	if (num)
		printf("%2d. %-22s", lnum, line);
	else
		printf("%-26s", line);
}

/*
 * help - print a help message.
 */
help()
{
	printf("\n   Type in the word you want defined, or a blank line to exit. Additionally,\n");
	printf("Webster can match words using wildcards.  The character '%%' in a word means\n");
	printf("match exactly one character; while the character '*' means match zero or more\n");
	printf("characters.\n");
	printf("   Typing a partial word followed by '?' will print all the words in the\n");
	printf("dictionary which match your partial word. Typing a partial word followed by an\n");
	printf("ESCape character will try to complete the word for you.  If the partial word\n");
	printf("is ambiguous, Webster will beep at you.  Note that you can use the wildcards\n");
	printf("along with ESC and ?.  For example (the underlined parts are typed by the\n");
	printf("user, the rest by Webster),\n");
	printf("\n");
	printf("Word: balla?   Maybe you mean:\n");
	printf("      ------\n");
	printf("  1. ballad           2. ballade          3. baladry         4. ballast\n");
	printf("Word: pluria<ESC>xial\n");
	printf("      --------------\n");
	printf("Word: plu*x<ESC>\n");
	printf("      -------------\n");
	printf("Word: pluriaxial\n");
	printf("\n---- End of Help Message ----\n\n");
}

/*
 * cnncttimeo -- called if connect does not return before timeout period
 */
cnncttimeo()
{
  timedout = 1;
}




/*
 * byebye - called on exit.
 */
byebye()
{
	/*
	 * If interactive, reset the tty modes.
	 */
	if (interactive) {
#ifdef USG
	  ioctl(0, TCSETAW, &rttyb);
#else
	  ioctl(0, TIOCSETP, &rsgttyb);
#endif
	  write(1,"\nwebster: Closing connection.",
		strlen("\nwebster: Closing connection."));
	}
	/*
	 * Close the socket and exit.
	 */
	fclose(WebsterSock);
	write(1, "\n", 1);
	exit(0);
}

#ifdef SIGTSTP
/*
 * suspend - reset tty modes and suspend ourselves.
 */
suspend()
{
	long blocked;
	extern int suspend();

	/*
	 * Reset tty modes and suspend.
	 */
#ifdef USG  /* -ss */
	ioctl(0, TCSETAW, &rttyb);
#else
	ioctl(0, TIOCSETP, &rsgttyb);
#endif
	signal(SIGTSTP, SIG_DFL);
	blocked = sigsetmask(0);
	kill(0, SIGTSTP);

	/*
	 * We come here on SIGCONT.  Reset
	 * the signal mask and tty modes.
	 */
	sigsetmask(blocked);
	signal(SIGTSTP, suspend);
#ifdef USG  /* -ss */
	ioctl(0, TCSETAW, &ttyb);
#else
	ioctl(0, TIOCSETP, &sgttyb);	
#endif
}
#endif

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.