[comp.emacs] webster mode for GNU Emacs

spolsky-joel@CS.Yale.EDU (Joel Spolsky) (03/14/89)

Here is Jason Glasgow's webster.el. This allows you to look up a word
in the Websters seventh edition via a server with Websters on line.
There are servers at Boston University, Purdue, and others, although I
do not know where nor do I know how one goes about becoming a webster
server. Don't even bother asking me about such esoterica.

Needless to say you must have telnet for this to work. This is only an
"alpha test" version: comments, suggestions, and bug fixes are encouraged.

+----------------+----------------------------------------------------------+
|  Joel Spolsky  | bitnet: spolsky@yalecs.bitnet     uucp: ...!yale!spolsky |
|                | internet: spolsky@cs.yale.edu     voicenet: 203-436-1483 |
+----------------+----------------------------------------------------------+

+----------------+----------------------------------------------------------+
| Jason Glasgow  | bitnet: glasgow@yalecs.bitnet     uucp: ...!yale!glasgow |
|                | internet: glasgow@cs.yale.edu     voicenet: 203-436-1437 |
+----------------+----------------------------------------------------------+

-----------------snip snip snip snip--------------------
;; Copyright (C) 1989 Free Software Foundation

;; This file is part of GNU Emacs.

;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY.  No author or distributor
;; accepts responsibility to anyone for the consequences of using it
;; or for whether it serves any particular purpose or works at all,
;; unless he says so in writing.  Refer to the GNU Emacs General Public
;; License for full details.

;; Everyone is granted permission to copy, modify and redistribute
;; GNU Emacs, but only under the conditions described in the
;; GNU Emacs General Public License.   A copy of this license is
;; supposed to have been given to you along with GNU Emacs so you
;; can know your rights and responsibilities.  It should be in a
;; file named COPYING.  Among other things, the copyright notice
;; and this notice must be preserved on all copies.
;;
;; Author Jason R. Glasgow (glasgow@cs.yale.edu)
;; Modified from telnet.el by William F. Schelter
;; But almost entirely different
;;
;; To use this, you might want to add this line to your .emacs file:
;;
;;  (autoload 'webster "webster" "look up a word in Webster's 7th edition" t)
;;
;; Then just hit M-x webster to look up a word.
;;

(defvar webster-host "128.197.2.40"
  "Host that is a webster server (Boston U). Try also 26.0.0.73, which is sri-nic")
(defvar webster-port "103"
  "The port to connect to. Either 103 or 2627")
(defvar webster-request "DEFINE"
  "The type of request to use.  Try DEFINE SPELLING ENDINGS")

;;;
;;; Initial filter for ignoring information until successfully connected
;;;
(defun webster-initial-filter (proc string)
  (cond ((string-match "No such host" string)
	 (kill-buffer (process-buffer proc))
	 (error "No such host."))
	((string-match "]" string)
	 (set-process-filter proc 'webster-filter)
	 (sleep-for 2)              ;;; wait for good connection
	 (message (concat "Looking up word " webster-word "..."))
	 (send-string (get-buffer-process (current-buffer))
		      (concat webster-request " " webster-word "\n")))))

(defun webster-filter (proc string)
  (let ((place (string-match "\200" string)))
    (cond (place
	   (webster-filter proc (concat (substring string 0 (- place 1))
					"\n\n"))
	   (message "Closing connection...")
	   (progn
	     (kill-process proc)
	     (set-buffer (process-buffer proc))
	     (replace-regexp "Process webster killed" "" nil)
	     (goto-char 1))
	   (message "Done."))
	  ((string-match "SPELLING 0" string)
	   (message "Word not found in webster")
	   (set-buffer (process-buffer proc))
	   (goto-char (point-max))
	   (insert "SPELLING 0")
	   (progn (kill-process proc)
		  (replace-regexp "Process webster killed" "" nil)))
	  (t (save-excursion
	       (set-buffer (process-buffer proc))
	       (goto-char (point-max))
	       (let ((now (point)))
		 (insert string)
		 (delete-char-in-region now (point) ?\^m ?\ ))
	       (if (process-mark proc)
		   (set-marker (process-mark proc) (point))))))))

;;;
;;; delete char1 and char2 if it precedes char1
;;; used to get rid of <space><return>
(defun delete-char-in-region (start end char1 char2)
  (goto-char start)
  (while (search-forward (char-to-string char1) end t)
    (backward-delete-char 1)
    (if (equal (char-after (- (point) 1)) char2)
	(backward-delete-char 1))))

(defun webster (arg)
  "Open a network login connection to a webster host. And look
lookup a word using DEFINE WORD.
Communication with host is recorded in a buffer *webster*.
The process will end after a \200 is sent."
  (interactive "sLook up word in webster: ")
  (require 'shell)
  (let ((name "webster"))
    (cond ((not (eq (get-buffer-window (concat "*" name "*")) nil))
	   (select-window (get-buffer-window (concat "*" name "*"))))
	  ((one-window-p)
	   (split-window-vertically)
	   (other-window 1)))
    (message (concat "Attempting to connect to server " webster-host "..."))
    (switch-to-buffer (make-shell name "telnet"))
    (set-process-filter (get-process name) 'webster-initial-filter)
    (erase-buffer)
    (send-string  name (concat "open " webster-host " " webster-port "\n"))
    (webster-mode)
    (setq webster-word arg)))

(defun webster-endings (arg)
  "Call webster looking for endings"
  (interactive "sFind endings for word in webster: ")
  (webster arg)
  (setq webster-request "ENDINGS"))

(defun webster-spell (arg)
  "Call webster looking for endings"
  (interactive "sTry to spell word in webster: ")
  (webster arg)
  (setq webster-request "SPELL"))
  
(defun webster-mode ()
  "Basically a mode that does nothing. Any characters should make
this window disapper, but not yet implemented."
  (interactive)
  (kill-all-local-variables)
  (setq major-mode 'webster-mode)
  (setq mode-name "Webster")
  (make-local-variable 'webster-request)
  (setq webster-request "DEFINE")
  (make-local-variable 'webster-word)
  (setq webster-word "jason")
  (setq fill-column 80))

dsill@RELAY.NSWC.NAVY.MIL (03/14/89)

Has anyone gotten a (usable) response from either of the servers
listed in the code?  Anyone know of any other webster servers?

felix@AI.SRI.COM (Francois Felix INGRAND) (03/17/89)

In article <37179@bbn.COM>, dsill@RELAY writes:
>Has anyone gotten a (usable) response from either of the servers
>listed in the code?  Anyone know of any other webster servers?

It works, except that sri-nic.arpa is 10.0.0.51 (this is what I am using
and it works fine)

But, the Webster code need some fixes... After querying a word, all
attempt to get another word fail until I kill the Webster buffer. I have
no time to investigate so you will have to live with it.

Anyway, this is an interesting service. 
-- 
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Francois Felix INGRAND                          SRI International, AIC
felix@AI.SRI.COM                                333, Ravenswood Avenue
felix%AI.SRI.COM@UUNET.UU.NET                   MENLO PARK, CA 94025, USA
"Pourquoi tant de haine..." (Edika)      "Read my Lisp... No new syntax" (nil)

jr@bbn.com (John Robinson) (03/18/89)

In article <28684@sri-unix.SRI.COM>, felix@AI (Francois Felix INGRAND) writes:
>But, the Webster code need some fixes... After querying a word, all
>attempt to get another word fail until I kill the Webster buffer. I have
>no time to investigate so you will have to live with it.

The problem was reported on another list, with a fix.  Add a test for
a NULL in the beginning of the function webster-filter:

(defun webster-filter (proc string)
  (let ((place (or (string-match "\200" string) (string-match "\0" string))))
    (cond (place

Some connections are only 7 bits wide.
--
/jr
jr@bbn.com or bbn!jr
C'mon big money!

grunwald@flute.cs.uiuc.edu (03/18/89)

Try this hack of webster.el instead.

I've modified it to keep the connection open
and be a little more intelligent in determining when a connect has been
opened -- the previous version just slept for 2 seconds.

Now, once you establish a connection, you'll get very fast response.
If the connection closes, you'll have to restart it, but it's fairly
invisible (except for the delay).

Definitions are kept around in the buffer, and you're always moved to the
bottom -- you can delete it using kill-region or whatever.

;; Copyright (C) 1989 Free Software Foundation

;; This file is part of GNU Emacs.

;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY.  No author or distributor
;; accepts responsibility to anyone for the consequences of using it
;; or for whether it serves any particular purpose or works at all,
;; unless he says so in writing.  Refer to the GNU Emacs General Public
;; License for full details.

;; Everyone is granted permission to copy, modify and redistribute
;; GNU Emacs, but only under the conditions described in the
;; GNU Emacs General Public License.   A copy of this license is
;; supposed to have been given to you along with GNU Emacs so you
;; can know your rights and responsibilities.  It should be in a
;; file named COPYING.  Among other things, the copyright notice
;; and this notice must be preserved on all copies.
;;
;; Author Jason R. Glasgow (glasgow@cs.yale.edu)
;; Modified from telnet.el by William F. Schelter
;;
;; Modified by Dirk Grunwald to maintain an open connect
;; But almost entirely different
;;
;; To use this, you might want to add this line to your .emacs file:
;;
;;  (autoload 'webster "webster" "look up a word in Webster's 7th edition" t)
;;
;; Then just hit M-x webster to look up a word.
;;

(defvar webster-host "128.197.2.40"
  "Host that is a webster server (Boston U). Try also 26.0.0.73, which is sri-nic")
(defvar webster-port "103"
  "The port to connect to. Either 103 or 2627")

(defvar webster-request "DEFINE SPELLING ENDINGS"
  "The type of request to use.  Try DEFINE SPELLING ENDINGS")

(defvar webster-process nil
  "The current webster process")

(defvar webster-start-delay 1
  "Seconds to delay before talking to webster server")

(defvar webster-running nil
  "Used to determine when connection is established")

;;;
;;; Initial filter for ignoring information until successfully connected
;;;
(defun webster-initial-filter (proc string)
  (cond
   ((not (eq (process-status webster-process) 'run))
    (progn
      (setq webster-running t)
      (message "webster died")))

   ((string-match "No such host" string)
    (progn
      (setq webster-running t)
      (kill-buffer (process-buffer proc))
      (error "No such host.")))

   ((string-match "]" string)
    (progn
      (setq webster-running t)
      (set-process-filter proc 'webster-filter)))))

(defun webster-filter (proc string)
  (let
      ((closed-message (string-match "Connection closed" string))
       (end-def-message (or (string-match "\200" string) (string-match "\0" string))))
    (cond

     ((not (eq (process-status webster-process) 'run))
      (message "Webster died"))

     (closed-message
      (progn
	(message "Closing webster connection...")
	(kill-process proc)
	(save-excursion
	  (set-buffer (process-buffer proc))
	  (replace-regexp "Process webster killed" "" nil)
	  (goto-char 1))
	(message "Done.")))
     
     ((string-match "SPELLING 0" string)
      (message "Word not found in webster"))
     
     ((string-match "SPELLING 1" string)
      (message "Spelled correctly"))

     (end-def-message
      (progn
	(webster-filter proc (concat (substring string 0 (- end-def-message 1)) "\n\n"))
	(goto-char (point-max))
	(recenter -1)))

     (t (save-excursion
	  (set-buffer (process-buffer proc))
	  (goto-char (point-max))
	  (let ((now (point)))
	    (insert string)
	    (delete-char-in-region now (point) ?\^m ?\ ))
	  (if (process-mark proc)
	      (set-marker (process-mark proc) (point))))))))

;;;
;;; delete char1 and char2 if it precedes char1
;;; used to get rid of <space><return>
(defun delete-char-in-region (start end char1 char2)
  (goto-char start)
  (while (search-forward (char-to-string char1) end t)
    (backward-delete-char 1)
    (if (equal (char-after (- (point) 1)) char2)
	(backward-delete-char 1))))

(defun webster (arg)
  "Open a network login connection to a webster host. And look
lookup a word using DEFINE WORD.
Communication with host is recorded in a buffer *webster*.
The process will end after a \200 is sent."
  (interactive "sLook up word in webster: ")
  (webster-send-request "DEFINE" arg))

(defun webster-endings (arg)
  "Call webster looking for endings"
  (interactive "sFind endings for word in webster: ")
  (webster-send-request "ENDINGS" arg))

(defun webster-spell (arg)
  "Call webster looking for endings"
  (interactive "sTry to spell word in webster: ")
  (webster-send-request "SPELL" arg))

(defun webster-send-request (kind word)
  (require 'shell)
  (let ((name "webster")
	(webster-command (concat "open " webster-host " " webster-port "\n")))

    (cond ((not (eq (get-buffer-window (concat "*" name "*")) nil))
	   (select-window (get-buffer-window (concat "*" name "*"))))
	  ((one-window-p)
	   (split-window-vertically)
	   (other-window 1)))
    (if (or (not webster-process)
	    (not (eq (process-status webster-process) 'run)))
	(progn
	  (message
	   (concat "Attempting to connect to server " webster-host "..."))
	  (switch-to-buffer (make-shell name "telnet"))
	  (setq webster-process (get-process name))
	  (set-process-filter webster-process 'webster-initial-filter)
	  (process-send-string  webster-process webster-command)
	  ;
	  ; wait for feedback before sending query
	  ;
	  (setq webster-running nil)
	  (while (not webster-running)
	    (accept-process-output))))
    (process-send-string webster-process (concat kind " " word "\n"))))

--
Dirk Grunwald
Univ. of Illinois
grunwald@flute.cs.uiuc.edu

Ram-Ashwin@cs.yale.edu (Ashwin Ram) (03/19/89)

In article <GRUNWALD.89Mar17144501@flute.cs.uiuc.edu>, grunwald@flute.cs.uiuc.edu writes:
> Try this hack of webster.el instead.
> 
> I've modified it to keep the connection open
> and be a little more intelligent in determining when a connect has been
> opened -- the previous version just slept for 2 seconds.
> 
> Now, once you establish a connection, you'll get very fast response.
> If the connection closes, you'll have to restart it, but it's fairly
> invisible (except for the delay).

I've added a webster-mode to this version for the *webster* buffer:

webster-mode:
Major mode for interacting with on-line Webster's dictionary.

s               webster-spell
q               webster-quit
e               webster-endings
d               webster
?               describe-mode

Use webster-mode-hook for customization.

I've added a webster-quit command to encourage users not to keep open
connections around for days, thus tying up the port unnecessarily.  I've also
fixed some of the doc strings.

Here's the new version of webster.el:

-----------------------------------------------------------------------------
;; Copyright (C) 1989 Free Software Foundation

;; This file is part of GNU Emacs.

;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY.  No author or distributor
;; accepts responsibility to anyone for the consequences of using it
;; or for whether it serves any particular purpose or works at all,
;; unless he says so in writing.  Refer to the GNU Emacs General Public
;; License for full details.

;; Everyone is granted permission to copy, modify and redistribute
;; GNU Emacs, but only under the conditions described in the
;; GNU Emacs General Public License.   A copy of this license is
;; supposed to have been given to you along with GNU Emacs so you
;; can know your rights and responsibilities.  It should be in a
;; file named COPYING.  Among other things, the copyright notice
;; and this notice must be preserved on all copies.
;;
;; Author Jason R. Glasgow (glasgow@cs.yale.edu)
;; Modified from telnet.el by William F. Schelter
;;
;; Modified by Dirk Grunwald to maintain an open connect
;; But almost entirely different
;;
;; 3/18/89 Ashwin Ram <Ram-Ashwin@yale.edu>
;; Added webster-mode.
;; Fixed documentation.
;; 
;; To use this, you might want to add this line to your .emacs file:
;;
;;  (autoload 'webster "webster" "look up a word in Webster's 7th edition" t)
;;
;; Then just hit M-x webster to look up a word.
;;

(defvar webster-host "128.197.2.40"
  "Host that is a webster server (Boston U). Try also 26.0.0.73, which is sri-nic")
(defvar webster-port "103"
  "The port to connect to. Either 103 or 2627")

(defvar webster-request "DEFINE SPELLING ENDINGS"
  "The type of request to use.  Try DEFINE SPELLING ENDINGS")

(defvar webster-process nil
  "The current webster process")

(defvar webster-start-delay 1
  "Seconds to delay before talking to webster server")

(defvar webster-running nil
  "Used to determine when connection is established")

;;;
;;; Initial filter for ignoring information until successfully connected
;;;
(defun webster-initial-filter (proc string)
  (cond
   ((not (eq (process-status webster-process) 'run))
    (progn
      (setq webster-running t)
      (message "webster died")))

   ((string-match "No such host" string)
    (progn
      (setq webster-running t)
      (kill-buffer (process-buffer proc))
      (error "No such host.")))

   ((string-match "]" string)
    (progn
      (setq webster-running t)
      (set-process-filter proc 'webster-filter)))))

(defun webster-filter (proc string)
  (let
      ((closed-message (string-match "Connection closed" string))
       (end-def-message (or (string-match "\200" string) (string-match "\0" string))))
    (cond

     ((not (eq (process-status webster-process) 'run))
      (message "Webster died"))

     (closed-message
      (progn
	(message "Closing webster connection...")
	(kill-process proc)
	(save-excursion
	  (set-buffer (process-buffer proc))
	  (replace-regexp "Process webster killed" "" nil)
	  (goto-char 1))
	(message "Done.")))
     
     ((string-match "SPELLING 0" string)
      (message "Word not found in webster"))
     
     ((string-match "SPELLING 1" string)
      (message "Spelled correctly"))

     (end-def-message
      (progn
	(webster-filter proc (concat (substring string 0 (- end-def-message 1)) "\n\n"))
	(goto-char (point-max))
	(recenter -1)))

     (t (save-excursion
	  (set-buffer (process-buffer proc))
	  (goto-char (point-max))
	  (let ((now (point)))
	    (insert string)
	    (delete-char-in-region now (point) ?\^m ?\ ))
	  (if (process-mark proc)
	      (set-marker (process-mark proc) (point))))))))

;;;
;;; delete char1 and char2 if it precedes char1
;;; used to get rid of <space><return>
(defun delete-char-in-region (start end char1 char2)
  (goto-char start)
  (while (search-forward (char-to-string char1) end t)
    (backward-delete-char 1)
    (if (equal (char-after (- (point) 1)) char2)
	(backward-delete-char 1))))

(defun webster (arg)
  "Look up a word in the Webster's dictionary.
Open a network login connection to a webster host if necessary.
Communication with host is recorded in a buffer *webster*.
The process will end after a \200 is sent."
  (interactive "sLook up word in webster: ")
  (webster-send-request "DEFINE" arg))

(defun webster-endings (arg)
  "Look up endings for a word in the Webster's dictionary.
Open a network login connection to a webster host if necessary.
Communication with host is recorded in a buffer *webster*."
  (interactive "sFind endings for word in webster: ")
  (webster-send-request "ENDINGS" arg))

(defun webster-spell (arg)
  "Look up the spelling for a word in the Webster's dictionary.
Open a network login connection to a webster host if necessary.
Communication with host is recorded in a buffer *webster*."
  (interactive "sSpell word in webster: ")
  (webster-send-request "SPELL" arg))

(defun webster-send-request (kind word)
  (require 'shell)
  (let ((name "webster")
	(webster-command (concat "open " webster-host " " webster-port "\n")))

    (cond ((not (eq (get-buffer-window (concat "*" name "*")) nil))
	   (select-window (get-buffer-window (concat "*" name "*"))))
	  ((one-window-p)
	   (split-window-vertically)
	   (other-window 1)))
    (if (or (not webster-process)
	    (not (eq (process-status webster-process) 'run)))
	(progn
	  (switch-to-buffer (make-shell name "telnet"))
          (webster-mode)
          (sit-for 0)
	  (message "Connecting to %s..." webster-host)
	  (setq webster-process (get-process name))
	  (set-process-filter webster-process 'webster-initial-filter)
	  (process-send-string  webster-process webster-command)
	  ;
	  ; wait for feedback before sending query
	  ;
	  (setq webster-running nil)
	  (while (not webster-running)
            (sit-for 1)
	    (accept-process-output))
	  (message "Connecting to %s...done" webster-host)))
    (process-send-string webster-process (concat kind " " word "\n"))))

; Buffer is buried but not deleted since user may want the info in the buffer.
(defun webster-quit ()
   "Close connection and quit webster-mode."
   (interactive)
   (message "Closing connection to %s..." webster-host)
   (kill-process webster-process)
   (message "Closing connection to %s...done" webster-host)
   (bury-buffer))

(defun webster-mode ()
  "Major mode for interacting with on-line Webster's dictionary.
\\{webster-mode-map}
Use webster-mode-hook for customization."
  (interactive)
  (kill-all-local-variables)
  (setq major-mode 'webster-mode)
  (setq mode-name "Webster")
  (use-local-map webster-mode-map)
  (run-hooks 'webster-mode-hook))

(defvar webster-mode-map nil)
(if webster-mode-map
    nil
  (setq webster-mode-map (make-sparse-keymap))
  (define-key webster-mode-map "?" 'describe-mode)
  (define-key webster-mode-map "d" 'webster)
  (define-key webster-mode-map "e" 'webster-endings)
  (define-key webster-mode-map "q" 'webster-quit)
  (define-key webster-mode-map "s" 'webster-spell))
-----------------------------------------------------------------------------

-- Ashwin.

ARPA:    Ram-Ashwin@cs.yale.edu
UUCP:    {decvax,ucbvax,harvard,cmcl2,...}!yale!Ram-Ashwin
BITNET:  Ram@yalecs