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.