This is xwebster.c in view mode; [Download] [Up]
/* -*-C-*- ******************************************************************************** * * File: xwebster.c * RCS: $Header: /home/everest1/cattelan/work/xwebster.motif/RCS/xwebster.c,v 1.1 1992/03/21 22:39:43 cattelan Exp $ * Description: X11 + HP-Xwidgets interface to a webster dictionary server * Author: Niels Mayer, HPLabs * Created: Wed Aug 31 14:09:08 1988 * Modified: Mon Mar 6 03:43:14 1989 (Niels Mayer) mayer@hplnpm * Language: C * Package: N/A * Status: G-Job * * xwebster - dictionary browser * * Copyright 1988 Hewlett-Packard Company * * Permission to use, copy, modify, and distribute this software and its * documentation for any purpose and without fee is hereby granted, provided * that the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of HP not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. HP makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * Please send any improvements, bug fixes, useful modifications, and comments * to mayer@hplabs.hp.com. ******************************************************************************** */ static char rcs_identity[] = "@(#)$Header: /home/everest1/cattelan/work/xwebster.motif/RCS/xwebster.c,v 1.1 1992/03/21 22:39:43 cattelan Exp $"; char xwebster_version[] = "Xwebster, version 2.0"; /* * Some aspects of this program were inspired by the first * C-language translation of the webster client program, webster.c, done by: * "David A. Curry * Purdue University * Engineering Computer Network * April, 1986" * The only copyright notice appearing in the webster.c source is: * "Webster's 7th Collegiate Dictionary, Copyright (C) 1963 by Merriam-Webster, * Inc. No part of this information may be copied or reprinted without the * express written consent of the publisher." */ #include "xwebster.h" #include "user_prefs.h" #include <stdio.h> #include <ctype.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> /* #include <memory.h> */ #include <Xm/PanedW.h> #include <X11/cursorfont.h> static XtInputCallbackProc Webster_Get_Server_Output(); static void Process_Webster_Line(); static int Webster_Connect_To_Server(); static void Webster_Reconnect(); static XtInputCallbackProc Webster_Handle_Server_Exception(); static void Webster_Disconnect(); int webster_Socket = NULL; static XtInputId webster_xinput; static XtInputId webster_xexcept; static Widget toplevel_Wgt = NULL; /* must be global */ /* change this later when add the EXTERN macro */ XtAppContext webster_app; Display* display; String fallbacks[] = { "*.panel.background: white", "*.control_panel.word_input.columns: 30", "*.panel.wordlist_scroller.height: 100", "*.display_scroller.display.rows: 24", "*.display_scroller.display.columns: 80", "*.wordlist_scroller.wordlist.numColumns: 4", NULL }; USER_PREFS_DATA user_prefs; /* extern declared in user_prefs.h, really here */ /* * Data on how user-customization resources are interpreted: * this must be kept up to date with data structure USER_PREFS_DATA_PTR * in user_prefs.h * * My philosophy is to place all my intended defaults in * APPDEFAULTSDIR/Xwebster, so that the program doesn't have to * be recompiled just to change a simple default. Thus I don't always have * the best defaults in this structure. */ static XtResource resources[] = { {"helpText", "HelpText", XtRString, sizeof(String), XtOffset(USER_PREFS_DATA_PTR, help_text), XtRString, "Warning -- Someone forgot to install APPDEFAULTSDIR/Xwebster"}, {"hostAddrList", "HostAddrList", XtRString, sizeof(String), XtOffset(USER_PREFS_DATA_PTR, host_addr_list), XtRString, ""}, {"hostPort", "HostPort", XtRInt, sizeof(int), XtOffset(USER_PREFS_DATA_PTR, host_port), XtRString, ""}, {"hostServiceName", "HostServiceName", XtRString, sizeof(String), XtOffset(USER_PREFS_DATA_PTR, host_service_name), XtRString, "webster"}, {"numWordsBeforeRefresh", "NumWordsBeforeRefresh", XtRInt, sizeof(int), XtOffset(USER_PREFS_DATA_PTR, num_words_before_refresh), XtRString, "20"}, {"dictionarySource","DictonarySource", XtRString, sizeof(String), XtOffset(USER_PREFS_DATA_PTR, dictionary_source), XtRString, "next"} }; /****************************************************************************** * ******************************************************************************/ main(argc, argv) int argc; char **argv; { /* * Initialize the toolkit, place xwebster-defined resources in user_prefs. */ toplevel_Wgt = XtVaAppInitialize(&webster_app,"Xwebster",NULL,0,&argc,argv,fallbacks,NULL); XtVaGetApplicationResources(toplevel_Wgt, &user_prefs, resources, XtNumber(resources), NULL); { Widget panel_Wgt = XtVaCreateManagedWidget("panel", xmPanedWindowWidgetClass, toplevel_Wgt, NULL); Controlpanel_Titlebar_Init(panel_Wgt); Wordlist_Init(panel_Wgt); Controlpanel_Init(panel_Wgt); Display_Def_Init(panel_Wgt); } XtRealizeWidget(toplevel_Wgt); display = XtDisplay(toplevel_Wgt); XDefineCursor(display, XtWindow(toplevel_Wgt), XCreateFontCursor(display, XC_plus)); XtAppMainLoop(webster_app); } /****************************************************************************** * This procedure is called indirectly, via XtAddInput() callback from * XtMainLoop() whenever new input appears on webster_Socket. This callback * is setup in procedure Webster_Reconnect(). * This procedure will read all the data from webster_Socket, and then call * Process_Webster_Line() for each line of input received. If an incomplete * line of text is received, then this procedure will buffer that line until * the next time it gets called from the callback. ******************************************************************************/ static XtInputCallbackProc Webster_Get_Server_Output(client_data, source_fildes, id) caddr_t client_data; int source_fildes; XtInputId id; { unsigned nbytes; char readbuf[BUFSIZ]; static char buf[2*BUFSIZ]; /* overkill -- buf shouldn't get longer than BUFSIZ+ the size of any remaining line (max 80). */ static char linebuf[BUFSIZ]; static int buf_end_idx = 0; /* init value only on first call */ int prev_buf_end_idx; int prev_buf_idx; register int buf_idx; register int linebuf_idx; if (webster_Socket == NULL) Webster_Reconnect(); if (webster_Socket == NULL) return; if ((nbytes = read(webster_Socket, readbuf, BUFSIZ)) <= 0) { Webster_Disconnect(); return; } memcpy(&(buf[buf_end_idx]), readbuf, nbytes); /* append new input to buf */ buf_end_idx += nbytes; buf[buf_end_idx] = '\000'; /* NULL marks end of buf */ prev_buf_end_idx = buf_end_idx; buf_idx = 0; while (1) { /* process lines till none left: exits w/ break*/ linebuf_idx = 0; prev_buf_idx = buf_idx; /** try to copy a line of characters to linebuf, quitting on EOF or \000 **/ while (((buf[buf_idx] & 0177) != '\n') && (buf[buf_idx] != '\000') && (buf[buf_idx] != '\200')) /* webster server's <EOF> char */ linebuf[linebuf_idx++] = buf[buf_idx++] & 0177; if ((buf[buf_idx] & 0177) == '\n') { /* copy to linebuf stopped at LF */ linebuf[linebuf_idx-1] = '\n'; /* overwrite CR with LF */ linebuf[linebuf_idx] = '\000'; /* NULL terminate linebuf */ Process_Webster_Line(linebuf); /* process a line of input */ buf_idx++; /* skip over \n in buf */ buf_end_idx -= linebuf_idx + 1; /* shorten by len(linebuf)+len(\n) */ } else if (buf[buf_idx] == '\200') { /* copy to linebuf stopped at EOF */ Process_Webster_Line(NULL); /* signal special case of EOF */ buf_idx++; /* skip over \200 in buf */ buf_end_idx -= linebuf_idx + 1; /* shorten by len(linebuf)+len(\200) */ } else { /* copy to linebuf stopped at \000 (end of buf) */ /** IF characters were transferred from buf to linebuf... **/ if (prev_buf_end_idx != buf_end_idx) /** ...THEN shiftLeft characters that weren't transferred. **/ memccpy(&(buf[0]), &(buf[prev_buf_idx]), '\000', 2*BUFSIZ); break; /*@@@---EXIT THE WHILE LOOP---@@@*/ /* Buf_end_idx is now really end of buf: on the next call to this proc, new input will append after remaining unprocessed input in buf.*/ } } } /****************************************************************************** * This is a "state machine" procedure that is called for every line of text * received from the webster server. The state of the webster program is * contained in the static variable webster_state which lets this procedure * handle the current line of text in a way that depends on what the previous * line of text was. For more info on the webster protocol that is embodied in * this procedure, do "/usr/bin/telnet 10.0.0.51 103" and then type "HELP<cr>". ******************************************************************************/ #define WEBSTER_READY 0 #define GET_WORDLIST 1 #define GET_XREFS 2 #define GET_DEFINITION 3 /*-----------------------------------------------------------------------------*/ static void Process_Webster_Line(line) char* line; { static int num_xrefs; static int webster_state = WEBSTER_READY; static int current_wordlist_is_xrefs = FALSE; switch(webster_state) { case WEBSTER_READY: if (line == NULL) { Wordlist_Reset(); Controlpanel_Reactivate(); /* ready for user input */ } else if (strncmp(line, "AMBIGUOUS ", 10) == 0) { /* returned by COMPLETE */ int num_ambiguities = 0; sscanf(line, "AMBIGUOUS %d\n", &num_ambiguities); sprintf(temptext, "Ambiguous! Matches %d other words.", num_ambiguities); Controlpanel_Titlebar_Set_Label(temptext); XBell(display, 100); Wordlist_Reset(); Controlpanel_Reactivate(); /* ready for user input */ /* webster_state = WEBSTER_READY; */ } else if (strncmp(line, "COMPLETION ", 11) == 0) { /* returned by COMPLETE */ sscanf(line, "COMPLETION %s\n", temptext); Controlpanel_Set_Input_Word(temptext); Controlpanel_Titlebar_Set_Label("\000"); Wordlist_Reset(); Controlpanel_Reactivate(); /* ready for user input */ /* webster_state = WEBSTER_READY; */ } else if (strncmp(line, "MATCHS 0\n", 9) == 0) { /* returned by ENDINGS */ XBell(display, 100); Controlpanel_Titlebar_Set_Label("No matching words!"); Wordlist_Reset(); Controlpanel_Reactivate(); /* ready for user input */ /* webster_state = WEBSTER_READY; */ } else if (strncmp(line, "MATCHS\n", 7) == 0) { /* returned by ENDINGS */ Controlpanel_Titlebar_Set_Label("Choose a word:"); Wordlist_Reset(); webster_state = GET_WORDLIST; current_wordlist_is_xrefs = FALSE; } else if (strncmp(line, "SPELLING 0\n", 11) == 0) { /* returned by DEFINE and SPELL */ Controlpanel_Titlebar_Set_Label("No such word!"); XBell(display, 100); Wordlist_Reset(); Controlpanel_Reactivate(); /* ready for user input */ /* webster_state = WEBSTER_READY; */ } else if (strncmp(line, "SPELLING\n", 9) == 0) { /* returned by DEFINE */ Controlpanel_Titlebar_Set_Label("Word not in dictionary. Do you mean:"); Wordlist_Reset(); webster_state = GET_WORDLIST; current_wordlist_is_xrefs = FALSE; } else if (strncmp(line, "WILD 0\n", 7) == 0) { /* returned by DEFINE */ Controlpanel_Titlebar_Set_Label("No Match!"); XBell(display, 100); Wordlist_Reset(); Controlpanel_Reactivate(); /* webster_state = WEBSTER_READY; */ } else if (strncmp(line, "WILD\n", 5) == 0) { /* returned by DEFINE */ Controlpanel_Titlebar_Set_Label("Choose a word:"); Wordlist_Reset(); webster_state = GET_WORDLIST; current_wordlist_is_xrefs = FALSE; } else if (strncmp(line, "DEFINITION ", 11) == 0) { /* returned by DEFINE */ sscanf(line, "DEFINITION %d\n", &num_xrefs); if (num_xrefs == 0) { /** don't clear the wordlist if last define req came from the wordlist **/ if (Controlpanel_Cur_Word_Is_From_TextEdit()) { Wordlist_Reset(); Controlpanel_Titlebar_Set_Label("\000"); } webster_state = GET_DEFINITION; } else { /** don't clear old xrefs if new set of xrefs gend from a word in old xrefs **/ if (Controlpanel_Cur_Word_Is_From_TextEdit() || !current_wordlist_is_xrefs) Wordlist_Reset(); Controlpanel_Titlebar_Set_Label("Cross References:"); webster_state = GET_XREFS; current_wordlist_is_xrefs = TRUE; } } else if (strncmp(line, "ERROR RECOVERABLE", 17) == 0) { Controlpanel_Titlebar_Set_Label("Error:"); Display_Def_Text_Reset(); Display_Def_Text_Append(line); Display_Def_Refresh(); XBell(display, 100); XBell(display, 100); Wordlist_Reset(); Controlpanel_Reactivate(); /* webster_state = WEBSTER_READY; */ } else if (strncmp(line, "ERROR FATAL", 11) == 0) { XBell(display, 100); XBell(display, 100); XBell(display, 100); XBell(display, 100); Display_Def_Text_Reset(); Display_Def_Text_Append(line); Display_Def_Refresh(); Webster_Disconnect(); Controlpanel_Reactivate(); } else { sprintf(temptext, "unknown server message: %s", line); Controlpanel_Titlebar_Set_Label(temptext); XBell(display, 100); XBell(display, 100); Wordlist_Reset(); Controlpanel_Reactivate(); /* webster_state = WEBSTER_READY; */ } break; case GET_WORDLIST: /* get words, one per line, till EOF reached */ if (line == NULL) { /* EOF reached */ Wordlist_Show(); Controlpanel_Reactivate(); /* ready for user input */ webster_state = WEBSTER_READY; } else { sscanf(line, "%*d %[^\n]", temptext); Wordlist_Add(temptext); /* webster_state = GET_WORDLIST; */ } break; case GET_XREFS: if (line == NULL) { Display_Def_Refresh(); /* show the definition */ Controlpanel_Reactivate(); webster_state = WEBSTER_READY; break; } sscanf(line, "%*d %[^\n]", temptext); Wordlist_Add(temptext); num_xrefs--; if (num_xrefs == 0) { Wordlist_Show(); webster_state = GET_DEFINITION; } /* else webster_state = GET_XREFS; */ break; case GET_DEFINITION: if (line == NULL) { /* EOF reached */ /* * Hmm why is this here? Everytime word is selected from the buttons it sets * the input window word to "" not friendly in my opinion RMC */ /* Controlpanel_Set_Input_Word(""); */ Display_Def_Refresh(); /* show the definition */ Controlpanel_Reactivate(); /* ready for user input */ webster_state = WEBSTER_READY; } else { Display_Def_Text_Append(line); /* webster_state = GET_DEFINITION; */ } break; default: XBell(display, 100); Controlpanel_Titlebar_Set_Label("error -- xwebster programmer goofed"); Controlpanel_Reactivate(); webster_state = WEBSTER_READY; break; } } /****************************************************************************** * returns 0 if it couldn't send, else 1. ******************************************************************************/ int Webster_Send(buf) char *buf; { extern int sys_nerr; extern char *sys_errlist[]; extern int errno; if (webster_Socket == NULL) Webster_Reconnect(); if (webster_Socket == NULL) return(0); if (send(webster_Socket, buf, strlen(buf), 0) < 0) { Display_Def_Text_Reset(); if (errno < sys_nerr) (void) sprintf(temptext, "Error sending to webster server: %s\n", sys_errlist[errno]); else (void) strcpy(temptext, "Error sending to webster server: unknown error\n"); Display_Def_Text_Append(temptext); Display_Def_Refresh(); return(0); } return(1); } /****************************************************************************** * originally from webster.c, but by now, highly modified. ******************************************************************************/ static int Webster_Connect_To_Server(host_addr, host_port, host_service_name) char* host_addr; int host_port; char* host_service_name; { struct sockaddr_in sin; register int s; register struct servent *sp; /* Look up the host in the host file. */ memset( (char*) &sin, '\000', sizeof(struct sockaddr_in)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = inet_addr(host_addr); if (sin.sin_addr.s_addr == -1) { fprintf(stderr, "xwebster: %s: unknown host.\n", host_addr); return(-1); } if ((sp = getservbyname(host_service_name, "tcp")) == NULL) sin.sin_port = htons(host_port); else sin.sin_port = sp->s_port; /* Get a TCP socket. */ if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) { return(-1); } /* Try to connect. */ if (connect(s, &sin, sizeof(struct sockaddr_in)) < 0) { return(-1); } return(s); } /****************************************************************************** * Try to connect up to a webster server. Xwebster will try each host address * in the Xdefault hostAddrList successively until it connects succesfully * or until it runs out of hosts to try to connect. ******************************************************************************/ static void Webster_Reconnect() { extern int sys_nerr; extern char* sys_errlist[]; extern int errno; extern char* strtok(); /* if you don't have this, get the source from Henry Spencer's public domain strings package */ char* host_addr = strtok(user_prefs.host_addr_list, " \t"); /* If more than one host compiled into the default list strtok core dumps */ /* find out why */ Display_Def_Text_Reset(); do { sprintf(temptext, "Trying webster server at address %s ...\n", host_addr); Display_Def_Text_Append(temptext); Display_Def_Refresh(); XSync(display, FALSE); while (XtPending()) { /* since we can't get back to XtMainLoop yet-*/ XEvent event; /*-we process the events here... (yech) */ XtNextEvent(&event); XtDispatchEvent(&event); } webster_Socket = Webster_Connect_To_Server(host_addr, user_prefs.host_port, user_prefs.host_service_name); if (webster_Socket < 0) { Display_Def_Text_Reset(); (void) sprintf(temptext, "Problem with server host %s\n", host_addr); Display_Def_Text_Append(temptext); if (errno < sys_nerr) (void) sprintf(temptext, "webster server error: %s\n", sys_errlist[errno]); else (void) strcpy(temptext, "webster server error: unknown error\n"); Display_Def_Text_Append(temptext); } } while ((webster_Socket < 0) && ((host_addr = strtok(NULL, " \t")) != NULL)); if (webster_Socket < 0) { Controlpanel_Titlebar_Set_Label("Error:"); Display_Def_Text_Reset(); Display_Def_Text_Append("Couldn't access a webster host. Be sure that the following Xdefaults\n"); Display_Def_Text_Append("have been set correctly for accessing your Webster server:\n"); Display_Def_Text_Append("*hostAddrList *hostPort *hostServiceName\n"); sprintf(temptext, "See %s/Xwebster for details.\n", APPDEFAULTSDIR); Display_Def_Text_Append(temptext); Display_Def_Refresh(); webster_Socket = NULL; /* indicates we still don't have server connection */ } else { Controlpanel_Titlebar_Set_Label(xwebster_version); Display_Def_Text_Reset(); webster_xinput = XtAppAddInput(webster_app, webster_Socket, (XtPointer)XtInputReadMask, (XtInputCallbackProc)Webster_Get_Server_Output, NULL); webster_xexcept = XtAppAddInput(webster_app, webster_Socket, (XtPointer)XtInputExceptMask, (XtInputCallbackProc)Webster_Handle_Server_Exception, NULL); } } /****************************************************************************** * This procedure is called indirectly, via XtAddInput() callback from * XtMainLoop() whenever an exception occurs on webster_Socket. This callback * is setup in procedure Webster_Reconnect(). ******************************************************************************/ static XtInputCallbackProc Webster_Handle_Server_Exception(client_data, source_fildes, id) caddr_t client_data; int source_fildes; XtInputId id; { Webster_Disconnect(); } /****************************************************************************** * ******************************************************************************/ static void Webster_Disconnect() { extern int sys_nerr; extern char* sys_errlist[]; extern int errno; Controlpanel_Titlebar_Set_Label("Webster Server Error:"); Display_Def_Text_Reset(); if (errno < sys_nerr) (void) sprintf(temptext, "Webster server error: %s\n", sys_errlist[errno]); else (void) strcpy(temptext, "Webster server error: unknown error\n"); Display_Def_Text_Append(temptext); Display_Def_Text_Append("Webster server connection closed.\n"); Display_Def_Refresh(); XtRemoveInput(webster_xinput); XtRemoveInput(webster_xexcept); close(webster_Socket); webster_Socket = NULL; Controlpanel_Reactivate(); /* No point reconnecting - we'll wait till we try to do something. */ /* Well it seems that the next websterd will close if given a null word */ /* while this does seem to be a problem with the websterd we do want to give */ /* the user another chance to type a word soooo go aheand and reactivevate */ /* the controlpanel RMC*/ }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.