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
*
* Benoit Grange, ben@fizz.fdn.org
* -added __NEXT__ conditional,
* added some prototypes to enable -Wall compilation
*
*/
#ifdef __NeXT__
/* Will sort all our declaration problems */
#import <libc.h>
#endif
#include <sys/types.h>
#include <sys/ioctl.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>
#ifdef __NeXT__
typedef void signalFunctionType;
#else
typedef int signalFunctionType;
#endif
#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}, */
{"localhost", 2627, INDEX_DICT|INDEX_THES},
{"", 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
void connectup(), selectindex(), define(), setup(), interact(), complete(), help();
void listlines(), putline();
int endings(), getline();
signalFunctionType byebye(), cnncttimeo(), suspend();
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() */
int 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");
return (1);
#endif
return(0);
}
/*
* connectup - connects to the Webster server.
*/
void 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 signalFunctionType 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 %s in server table!", serverlist[i].hostname);
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, (struct sockaddr*)&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.
*/
void setup()
{
extern signalFunctionType byebye();
extern signalFunctionType 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.
*/
void interact()
{
char c; /* WAS int c -- bug, gangolli */
char buf[1024];
register char *s, *t;
extern signalFunctionType 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 = 0;
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 = 0;
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 = 0;
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;
}
}
}
void 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.
*/
void 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.
*/
void 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) != 0)) {
write(1, s, 1);
s++;
}
/*
* Put the new word back into word. This
* gets rid of the wildcards here.
*/
*s = 0;
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.
*/
int 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(0);
}
/*
* 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.
*/
int 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.
*/
void 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.
*/
void 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.
*/
void 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
*/
signalFunctionType cnncttimeo()
{
timedout = 1;
}
/*
* byebye - called on exit.
*/
signalFunctionType 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.
*/
signalFunctionType suspend()
{
long blocked;
/*
* 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.