Remove stuff about selection timeout, which is
[bpt/emacs.git] / lisp / textmodes / ispell.el
index 9224021..7290752 100644 (file)
-;;;;;;;;;;;;;;;;;;;;;;;;;;; -*- Mode: emacs-lisp -*- ;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;; GNU EMACS interface for International Ispell Version 3.1 by Geoff Kuenning.
-;;;
-;;;
-;;; Copyright (C) 1994, 1995 Free Software Foundation, Inc.
-;;;
-;;;
-;;; Authors         : Ken Stevens <k.stevens@ieee.org>
-;;; Note: version numbers and time stamp are not updated
-;;;   when this file is edited for release with GNU Emacs.
-;;; Last Modified On: Tue Jun 13 12:05:28 EDT 1995
-;;; Update Revision : 2.37
-;;; Syntax          : emacs-lisp
-;;; Status         : Release with 3.1.12+ ispell.
-;;; Version        : International Ispell Version 3.1 by Geoff Kuenning.
-;;; Bug Reports            : ispell-el-bugs@itcorp.com
-;;;
-;;; Note: version numbers and time stamp are not updated
-;;;   when this file is edited for release with GNU emacs.
-;;;
-;;; This file is part of GNU Emacs.
-;;;
-;;; GNU Emacs is free software; you can redistribute it and/or modify
-;;; it under the terms of the GNU General Public License as published by
-;;; the Free Software Foundation; either version 2, or (at your option)
-;;; any later version.
-;;;
-;;; GNU Emacs is distributed in the hope that it will be useful,
-;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-;;; GNU General Public License for more details.
-;;;
-;;; You should have received a copy of the GNU General Public License
-;;; along with GNU Emacs; see the file COPYING.  If not, write to
-;;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
-;;;
+;;; ispell.el --- Interface to International Ispell Versions 3.1 and 3.2
+
+;; Copyright (C) 1994, 1995, 1997, 1998, 1999, 2000 Free Software Foundation, Inc.
+
+;; Author:          Ken Stevens <k.stevens@ieee.org>
+;; Maintainer:      Ken Stevens <k.stevens@ieee.org>
+;; Stevens Mod Date: Fri Aug  4 09:41:50 PDT 2000
+;; Stevens Revision: 3.4
+;; Status          : Release with 3.1.12+ and 3.2.0+ ispell.
+;; Bug Reports     : ispell-el-bugs@itcorp.com
+;; Web Site        : http://kdstevens.com/~stevens/ispell-page.html
+;; Keywords: unix wp
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 2, or (at your option)
+;; any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs; see the file COPYING.  If not, write to the
+;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
+
+;; Note: version numbers and time stamp are not updated
+;;   when this file is edited for release with GNU emacs.
+
 ;;; Commentary:
-;;;
-;;; INSTRUCTIONS
-;;;
-;;;  This code contains a section of user-settable variables that you should
-;;; inspect prior to installation.  Look past the end of the history list.
-;;; Set them up for your locale and the preferences of the majority of the
-;;; users.  Otherwise the users may need to set a number of variables
-;;; themselves.
-;;;  You particularly may want to change the default dictionary for your
-;;; country and language.
-;;;
-;;;
-;;; To fully install this, add this file to your Emacs Lisp directory and
-;;; compile it with M-X byte-compile-file.  Then add the following to the
-;;; appropriate init file:
-;;;
-;;;  (autoload 'ispell-word "ispell"
-;;;    "Check the spelling of word in buffer." t)
-;;;  (global-set-key "\e$" 'ispell-word)
-;;;  (autoload 'ispell-region "ispell"
-;;;    "Check the spelling of region." t)
-;;;  (autoload 'ispell-buffer "ispell"
-;;;    "Check the spelling of buffer." t)
-;;;  (autoload 'ispell-complete-word "ispell"
-;;;    "Look up current word in dictionary and try to complete it." t)
-;;;  (autoload 'ispell-change-dictionary "ispell"
-;;;    "Change ispell dictionary." t)
-;;;  (autoload 'ispell-message "ispell"
-;;;    "Check spelling of mail message or news post.")
-;;;
-;;;  Depending on the mail system you use, you may want to include these:
-;;;
-;;;  (add-hook 'news-inews-hook 'ispell-message)
-;;;  (add-hook 'mail-send-hook  'ispell-message)
-;;;  (add-hook 'mh-before-send-letter-hook 'ispell-message)
-;;;
-;;;
-;;; Ispell has a TeX parser and a nroff parser (the default).
-;;; The parsing is controlled by the variable ispell-parser.  Currently
-;;; it is just a "toggle" between TeX and nroff, but if more parsers are
-;;; added it will be updated.  See the variable description for more info.
-;;;
-;;;
-;;; TABLE OF CONTENTS
-;;;
-;;;   ispell-word
-;;;   ispell-region
-;;;   ispell-buffer
-;;;   ispell-message
-;;;   ispell-continue
-;;;   ispell-complete-word
-;;;   ispell-complete-word-interior-frag
-;;;   ispell-change-dictionary
-;;;   ispell-kill-ispell
-;;;   ispell-pdict-save
-;;;
-;;;
-;;; Commands in ispell-region:
-;;; Character replacement: Replace word with choice.  May query-replace.
-;;; ' ': Accept word this time.
-;;; 'i': Accept word and insert into private dictionary.
-;;; 'a': Accept word for this session.
-;;; 'A': Accept word and place in buffer-local dictionary.
-;;; 'r': Replace word with typed-in value.  Rechecked.
-;;; 'R': Replace word with typed-in value. Query-replaced in buffer. Rechecked.
-;;; '?': Show these commands
-;;; 'x': Exit spelling buffer.  Move cursor to original point.
-;;; 'X': Exit spelling buffer.  Leave cursor at the current point.
-;;; 'q': Quit spelling session (Kills ispell process).
-;;; 'l': Look up typed-in replacement in alternate dictionary.  Wildcards okay.
-;;; 'u': Like 'i', but the word is lower-cased first.
-;;; 'm': Like 'i', but allows one to include dictionary completion info.
-;;; 'C-l': redraws screen
-;;; 'C-r': recursive edit
-;;; 'C-z': suspend emacs or iconify frame
-;;;
-;;; Buffer-Local features:
-;;; There are a number of buffer-local features that can be used to customize
-;;;  ispell for the current buffer.  This includes language dictionaries,
-;;;  personal dictionaries, parsing, and local word spellings.  Each of these
-;;;  local customizations are done either through local variables, or by
-;;;  including the keyword and argument(s) at the end of the buffer (usually
-;;;  prefixed by the comment characters).  See the end of this file for
-;;;  examples.  The local keywords and variables are:
-;;;
-;;;  ispell-dictionary-keyword   language-dictionary
-;;;      uses local variable ispell-local-dictionary
-;;;  ispell-pdict-keyword        personal-dictionary
-;;;      uses local variable ispell-local-pdict
-;;;  ispell-parsing-keyword      mode-arg extended-char-arg
-;;;  ispell-words-keyword        any number of local word spellings
-;;;
-;;;
-;;; BUGS:
-;;;  Highlighting in version 19 still doesn't work on tty's.
-;;;  On some versions of emacs, growing the minibuffer fails.
-;;;
-;;; HISTORY
-;;;
-;;; Revision 2.37  1995/6/13 12:05:28  stevens
-;;; Removed autoload from ispell-dictionary-alist. *choices* mode-line shows
-;;; misspelled word.  Block skip for pgp & forwarded messages added.
-;;;
-;;; Revision 2.36  1995/2/6 17:39:38   stevens
-;;; Properly adjust screen with different ispell-choices-win-default-height
-;;; settings.  Skips SGML entity references.
-;;;
-;;; Revision 2.35  1995/1/13 14:16:46  stevens
-;;; Skips SGML tags, ispell-change-dictionary fix for add-hook, assure personal
-;;; dictionary is saved when called from the menu
-;;;
-;;; Revision 2.34  1994/12/08 13:17:41  stevens
-;;; Interaction corrected to function with all 3.1 ispell versions.
-;;;
-;;; Revision 2.33  1994/11/24 02:31:20  stevens
-;;; Repaired bug introduced in 2.32 that corrupts buffers when correcting.
-;;; Improved buffer scrolling. Nondestructive buffer selections allowed.
-;;;
-;;; Revision 2.32  1994/10/31 21:10:08  geoff
-;;; Many revisions accepted from RMS/FSF.  I think (though I don't know) that
-;;; this represents an 'official' version.
-;;;
-;;; Revision 2.31  1994/5/31 10:18:17  stevens
-;;; Repaired comments.  buffer-local commands executed in `ispell-word' now.
-;;; German dictionary described for extended character mode.  Dict messages.
-;;;
-;;; Revision 2.30  1994/5/20 22:18:36  stevens
-;;; Continue ispell from ispell-word, C-z functionality fixed.
-;;;
-;;; Revision 2.29  1994/5/12 09:44:33  stevens
-;;; Restored ispell-use-ptys-p, ispell-message aborts sends with interrupt.
-;;; defined fn ispell
-;;;
-;;; Revision 2.28  1994/4/28 16:24:40  stevens
-;;; Window checking when ispell-message put on gnus-inews-article-hook jwz.
-;;; prefixed ispell- to highlight functions and horiz-scroll fn.
-;;; Try and respect case of word in ispell-complete-word.
-;;; Ignore non-char events.  Ispell-use-ptys-p commented out. Lucid menu.
-;;; Better interrupt handling.  ispell-message improvements from Ethan.
-;;;
-;;; Revision 2.27
-;;; version 18 explicit C-g handling disabled as it didn't work. Added
-;;; ispell-extra-args for ispell customization (jwz)
-;;;
-;;; Revision 2.26  1994/2/15 16:11:14  stevens
-;;; name changes for copyright assignment.  Added word-frags in complete-word.
-;;; Horizontal scroll (John Conover). Query-replace matches words now.  bugs.
-;;;
-;;; Revision 2.25
-;;; minor mods, upgraded ispell-message
-;;;
-;;; Revision 2.24
-;;; query-replace more robust, messages, defaults, ispell-change-dict.
-;;;
-;;; Revision 2.23  1993/11/22 23:47:03  stevens
-;;; ispell-message, Fixed highlighting, added menu-bar, fixed ispell-help, ...
-;;;
-;;; Revision 2.22
-;;; Added 'u' command.  Fixed default in ispell-local-dictionary.
-;;; fixed affix rules display.  Tib skipping more robust.  Contributions by
-;;; Per Abraham (parser selection), Denis Howe, and Eberhard Mattes.
-;;;
-;;; Revision 2.21  1993/06/30 14:09:04  stevens
-;;; minor bugs. (nroff word skipping fixed)
-;;;
-;;; Revision 2.20  1993/06/30 14:09:04  stevens
-;;;
-;;; Debugging and contributions by: Boris Aronov, Rik Faith, Chris Moore,
-;;;  Kevin Rodgers, Malcolm Davis.
-;;; Particular thanks to Michael Lipp, Jamie Zawinski, Phil Queinnec
-;;;  and John Heidemann for suggestions and code.
-;;; Major update including many tweaks.
-;;; Many changes were integrations of suggestions.
-;;; lookup-words rehacked to use call-process (Jamie).
-;;; ispell-complete-word rehacked to be compatible with the rest of the
-;;; system for word searching and to include multiple wildcards,
-;;; and it's own dictionary.
-;;; query-replace capability added.  New options 'X', 'R', and 'A'.
-;;; buffer-local modes for dictionary, word-spelling, and formatter-parsing.
-;;; Many random bugs, like commented comments being skipped, fix to
-;;; keep-choices-win, fix for math mode, added pipe mode choice,
-;;; fixed 'q' command, ispell-word checks previous word and leave cursor
-;;; in same location.  Fixed tib code which could drop spelling regions.
-;;; Cleaned up setq calls for efficiency. Gave more context on window overlays.
-;;; Assure context on ispell-command-loop.  Window lossage in look cmd fixed.
-;;; Due to pervasive opinion, common-lisp package syntax removed. Display
-;;; problem when not highlighting.
-;;;
-;;; Revision 2.19  1992/01/10  10:54:08  geoff
-;;; Make another attempt at fixing the "Bogus, dude" problem.  This one is
-;;; less elegant, but has the advantage of working.
-;;;
-;;; Revision 2.18  1992/01/07  10:04:52  geoff
-;;; Fix the "Bogus, Dude" problem in ispell-word.
-;;;
-;;; Revision 2.17  1991/09/12  00:01:42  geoff
-;;; Add some changes to make ispell-complete-word work better, though
-;;; still not perfectly.
-;;;
-;;; Revision 2.16  91/09/04  18:00:52  geoff
-;;; More updates from Sebastian, to make the multiple-dictionary support
-;;; more flexible.
-;;;
-;;; Revision 2.15  91/09/04  17:30:02  geoff
-;;; Sebastian Kremer's tib support
-;;;
-;;; Revision 2.14  91/09/04  16:19:37  geoff
-;;; Don't do set-window-start if the move-to-window-line moved us
-;;; downward, rather than upward.  This prevents getting the buffer all
-;;; confused.  Also, don't use the "not-modified" function to clear the
-;;; modification flag;  instead use set-buffer-modified-p.  This prevents
-;;; extra messages from flashing.
-;;;
-;;; Revision 2.13  91/09/04  14:35:41  geoff
-;;; Fix a spelling error in a comment.  Add code to handshake with the
-;;; ispell process before sending anything to it.
-;;;
-;;; Revision 2.12  91/09/03  20:14:21  geoff
-;;; Add Sebastian Kremer's multiple-language support.
-;;;
-;;;
-;;; Walt Buehring
-;;; Texas Instruments - Computer Science Center
-;;; ARPA:  Buehring%TI-CSL@CSNet-Relay
-;;; UUCP:  {smu, texsun, im4u, rice} ! ti-csl ! buehring
-;;;
-;;; ispell-region and associated routines added by
-;;; Perry Smith
-;;; pedz@bobkat
-;;; Tue Jan 13 20:18:02 CST 1987
-;;;
-;;; extensively modified by Mark Davies and Andrew Vignaux
-;;; {mark,andrew}@vuwcomp
-;;; Sun May 10 11:45:04 NZST 1987
-;;;
-;;; Ken Stevens  ARPA: k.stevens@ieee.org
-;;; Tue Jan  3 16:59:07 PST 1989
-;;; This file has overgone a major overhaul to be compatible with ispell
-;;; version 2.1.  Most of the functions have been totally rewritten, and
-;;; many user-accessible variables have been added.  The syntax table has
-;;; been removed since it didn't work properly anyway, and a filter is
-;;; used rather than a buffer.  Regular expressions are used based on
-;;; ispell's internal definition of characters (see ispell(4)).
-;;; Some new updates:
-;;; - Updated to version 3.0 to include terse processing.
-;;; - Added a variable for the look command.
-;;; - Fixed a bug in ispell-word when cursor is far away from the word
-;;;   that is to be checked.
-;;; - Ispell places the incorrect word or guess in the minibuffer now.
-;;; - fixed a bug with 'l' option when multiple windows are on the screen.
-;;; - lookup-words just didn't work with the process filter.  Fixed.
-;;; - Rewrote the process filter to make it cleaner and more robust
-;;;   in the event of a continued line not being completed.
-;;; - Made ispell-init-process more robust in handling errors.
-;;; - Fixed bug in continuation location after a region has been modified by
-;;;   correcting a misspelling.
-;;; Mon 17 Sept 1990
-;;;
-;;; Sebastian Kremer <sk@thp.uni-koeln.de>
-;;; Wed Aug  7 14:02:17 MET DST 1991
-;;; - Ported ispell-complete-word from Ispell 2 to Ispell 3.
-;;; - Added ispell-kill-ispell command.
-;;; - Added ispell-dictionary and ispell-dictionary-alist variables to
-;;;   support other than default language.  See their docstrings and
-;;;   command ispell-change-dictionary.
-;;; - (ispelled it :-)
-;;; - Added ispell-skip-tib variable to support the tib bibliography
-;;;   program.
-;;;
-;;;
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
+;; INSTRUCTIONS
+
+;;   This code contains a section of user-settable variables that you
+;; should inspect prior to installation.  Look past the end of the history
+;; list.  Set them up for your locale and the preferences of the majority
+;; of the users.  Otherwise the users may need to set a number of variables
+;; themselves.
+;;   You particularly may want to change the default dictionary for your
+;; country and language.
+;;   Most dictionary changes should be made in this file so all users can
+;; enjoy them.  Local or modified dictionaries are supported in your .emacs
+;; file.  Modify the variable `ispell-local-dictionary-alist' to include
+;; these dictionaries, and they will be installed when ispell.el is loaded.
+
+;;  Depending on the mail system you use, you may want to include these:
+;;  (add-hook 'news-inews-hook 'ispell-message)
+;;  (add-hook 'mail-send-hook  'ispell-message)
+;;  (add-hook 'mh-before-send-letter-hook 'ispell-message)
+
+;;   Ispell has a TeX parser and a nroff parser (the default).
+;; The parsing is controlled by the variable ispell-parser.  Currently
+;; it is just a "toggle" between TeX and nroff, but if more parsers are
+;; added it will be updated.  See the variable description for more info.
+
+
+;; TABLE OF CONTENTS
+
+;;   ispell-word
+;;   ispell-region
+;;   ispell-buffer
+;;   ispell-message
+;;   ispell-comments-and-strings
+;;   ispell-continue
+;;   ispell-complete-word
+;;   ispell-complete-word-interior-frag
+;;   ispell-change-dictionary
+;;   ispell-kill-ispell
+;;   ispell-pdict-save
+;;   ispell-skip-region-alist
+
+;; Commands in ispell-region:
+;; Character replacement: Replace word with choice.  May query-replace.
+;; ` ': Accept word this time.
+;; `i': Accept word and insert into private dictionary.
+;; `a': Accept word for this session.
+;; `A': Accept word and place in buffer-local dictionary.
+;; `r': Replace word with typed-in value.  Rechecked.
+;; `R': Replace word with typed-in value. Query-replaced in buffer. Rechecked.
+;; `?': Show these commands
+;; `x': Exit spelling buffer.  Move cursor to original point.
+;; `X': Exit spelling buffer.  Leaves cursor at the current point, and permits
+;;      the check to be completed later.
+;; `q': Quit spelling session (Kills ispell process).
+;; `l': Look up typed-in replacement in alternate dictionary.  Wildcards okay.
+;; `u': Like `i', but the word is lower-cased first.
+;; `m': Place entered value in personal dictionary, then recheck current word.
+;; `C-l': redraws screen
+;; `C-r': recursive edit
+;; `C-z': suspend emacs or iconify frame
+
+;; Buffer-Local features:
+;; There are a number of buffer-local features that can be used to customize
+;;  ispell for the current buffer.  This includes language dictionaries,
+;;  personal dictionaries, parsing, and local word spellings.  Each of these
+;;  local customizations are done either through local variables, or by
+;;  including the keyword and argument(s) at the end of the buffer (usually
+;;  prefixed by the comment characters).  See the end of this file for
+;;  examples.  The local keywords and variables are:
+
+;;  ispell-dictionary-keyword   language-dictionary
+;;      uses local variable ispell-local-dictionary
+;;  ispell-pdict-keyword        personal-dictionary
+;;      uses local variable ispell-local-pdict
+;;  ispell-parsing-keyword      mode-arg extended-char-arg
+;;  ispell-words-keyword        any number of local word spellings
+
+;; Region skipping:
+;;  Place new regular expression definitions of regions you prefer not to
+;;  spell check in `ispell-skip-region-alist'.  Mode-dependent features can
+;;  be added to latex by modifying `ispell-tex-skip-alists'.
+;;  `ispell-message' contains some custom skipping code for e-mail messages.
+
+;; BUGS:
+;;  Need a way to select between different character mappings without separate
+;;    dictionary entries.
+;;  Multi-byte characters if not defined by current dictionary may result in the
+;;    evil "misalignment error" in some versions of MULE emacs.
+;;  On some versions of emacs, growing the minibuffer fails.
+;;    see `ispell-help-in-bufferp'.
+;;  Recursive edits (?C-r or ?R) inside a keyboard text replacement check (?r)
+;;    can cause misalignment errors.
+
+;; HISTORY
+
+;; Modifications made in latest versions:
+
+;; Revision 3.4 2000/8/4 09:41:50      kss
+;; Support new color display functions.
+;; Fixed misalignment offset bug when replacing a string after a shift made.
+;; Set to standard Author/Maintainer heading,
+;; ensure localwords lists are separated from the text by newline. (Dave Love)
+;; Added dictionary definition for Italian (William Deakin)
+;; HTML region skipping greatly improved. (Chuck D. Phillips)
+;; improved menus.  Fixed regexp matching http/email addresses.
+;; one arg always for xemacs sleep-for (gunnar Evermann)
+;; support for synchronous processes (Eli Zaretskii)
+
+;; Revision 3.3  1999/11/29 11:38:34     kss
+;; Only word replacements entered in from the keyboard are rechecked.
+;; This fixes a bug in tex parsing and misalignment.
+;; Exceptions exist for recursive edit and query-replace, with tex error
+;; condition tested.  Recursive editing improved.
+;; XEmacs repair for when `enable-multibyte-characters' defined - Didier Verna.
+;; ispell-help fixed for XEmacs.  Choices minibuffer now displayed in XEmacs.
+;; Only list valid dictionaries in Spell menu.  Russian dictionary doesn't allow
+;; run-together words, and uses koi8-r font.  Don't skip text in html <TT>
+;; fonts.
+
+;; Revision 3.2  1999/5/7 14:25:14     kss
+;; Accept ispell versions 3.X.Y where X>=1
+;; fine tuned latex region skipping.  Fixed bug in ispell-word that did not
+;; point in right place on words < 2 chars.  Simplified ispell-minor-mode.
+;; Fixed bug in TeX parsing when math commands are in the comments.
+;; Removed calls to `when' macro.
+
+;; Revision 3.1  1998/12/1 13:21:52    kss
+;; Improved and fixed customize support.
+;; Improved and fixed comments in variables and messages.
+;; A coding system is now required for all languages.
+;; casechars improved for castellano, castellano8, and norsk dictionaries.
+;; Dictionary norsk7-tex removed.  Dictionary polish added.
+;; Dictionaries redefined at load-time to support dictionary changes.
+;; Menu redefined at load time to support dictionary changes.
+;; ispell-check-version added as an alias for `check-ispell-version'.
+;; Spelling suggestions returned in order generated by ispell.
+;; Small bug fixed in matching ispell error messages.
+;; Robustness added to ensure `case-fold-search' doesn't get redefined.
+;; Fixed bug that didn't respect case of word in `ispell-complete-word'.
+;; Multibyte character coding support added for process interactions.
+;; Ensure ispell process has terminated before starting new process.
+;;  This can otherwise confuse process filters and hang ispell.
+;; Improved skipping support for SGML.
+;; Fixed bug using ^M rather than \r in `ispell-minor-check'.
+;; Improved message reference matching in `ispell-message'.
+;; Fixed bug in returning to nroff mode from tex mode.
+
+
+;;; Code:
+
+;;; Custom.el macros require recompiling this when they are not present.
+;;; Add in backward compatible custom support.
+(eval-when-compile
+  (if (not (fboundp 'defcustom))
+      (defmacro defcustom (symbol value doc &rest args)
+       "Empty replacement for defcustom when not supplied."
+       `(defvar ,symbol ,value ,doc))))
+
+(eval-when-compile
+  (if (fboundp 'defgroup)
+      (defgroup ispell nil
+       "User variables for emacs ispell interface."
+       :group 'applications)))
+
+(if (not (fboundp 'buffer-substring-no-properties))
+    (defun buffer-substring-no-properties (start end)
+      (buffer-substring start end)))
+
+;;;###autoload
+(defconst xemacsp (string-match "Lucid\\|XEmacs" emacs-version)
+  "Non nil if using XEmacs.")
+
+;;;###autoload
+(defconst version18p (string-match "18\\.[0-9]+\\.[0-9]+" emacs-version)
+  "Non nil if using emacs version 18.")
+
+;;;###autoload
+(defconst version20p (string-match "20\\.[0-9]+\\.[0-9]+" emacs-version)
+  "Non nil if using emacs version 20.")
+
+(defconst ispell-graphic-p
+  (if (fboundp 'display-graphic-p)
+      (display-graphic-p)
+    xemacsp)
+  "True if running on a `graphics capable' display.")
 
+(and (not version18p)
+     (not (boundp 'epoch::version))
+     (defalias 'ispell-check-version 'check-ispell-version))
+
+(if (fboundp 'mode-line-window-height-fudge)
+    (defalias 'ispell-mode-line-window-height-fudge
+      'mode-line-window-height-fudge)
+  (defun ispell-mode-line-window-height-fudge ()
+    "Return 1 if running on a `graphics capable' display, otherwise 0."
+    (if ispell-graphic-p 1 0)))
 
 ;;; **********************************************************************
 ;;; The following variables should be set according to personal preference
 ;;; **********************************************************************
 
 
-;;;  ******* THIS FILE IS WRITTEN FOR ISPELL VERSION 3.1
-;;; Code:
+;;;  ******* THIS FILE IS WRITTEN FOR ISPELL VERSION 3.1+
 
-(defvar ispell-highlight-p t
-  "*Highlight spelling errors when non-nil.")
+(defcustom ispell-highlight-p 'block
+  "*Highlight spelling errors when non-nil.
+When set to `block', assumes a block cursor with TTY displays."
+  :type '(choice (const block) (const :tag "off" nil) (const :tag "on" t))
+  :group 'ispell)
 
-(defvar ispell-highlight-face 'highlight
+(defcustom ispell-highlight-face 'highlight
   "*The face used for Ispell highlighting.  For Emacses with overlays.
 Possible values are `highlight', `modeline', `secondary-selection',
 `region', and `underline'.
 This variable can be set by the user to whatever face they desire.
 It's most convenient if the cursor color and highlight color are
-slightly different.")
-
-(defvar ispell-check-comments nil
-  "*Spelling of comments checked when non-nil.")
-
-(defvar ispell-query-replace-choices nil
+slightly different."
+  :type 'face
+  :group 'ispell)
+
+(defcustom ispell-check-comments t
+  "*Spelling of comments checked when non-nil.
+When set to `exclusive', ONLY comments are checked.  (For code comments).
+Warning!  Not checking comments, when a comment start is embedded in strings,
+may produce undesired results."
+  :type '(choice (const exclusive) (const :tag "off" nil) (const :tag "on" t))
+  :group 'ispell)
+
+(defcustom ispell-query-replace-choices nil
   "*Corrections made throughout region when non-nil.
-Uses `query-replace' (\\[query-replace]) for corrections.")
+Uses `query-replace' (\\[query-replace]) for corrections."
+  :type 'boolean
+  :group 'ispell)
 
-(defvar ispell-skip-tib nil
+(defcustom ispell-skip-tib nil
   "*Does not spell check `tib' bibliography references when non-nil.
 Skips any text between strings matching regular expressions
 `ispell-tib-ref-beginning' and `ispell-tib-ref-end'.
 
 TeX users beware:  Any field starting with [. will skip until a .] -- even
 your whole buffer -- unless you set `ispell-skip-tib' to nil.  That includes
-a [.5mm] type of number....")
+a [.5mm] type of number...."
+  :type 'boolean
+  :group 'ispell)
 
 (defvar ispell-tib-ref-beginning "[[<]\\."
   "Regexp matching the beginning of a Tib reference.")
@@ -351,18 +284,24 @@ a [.5mm] type of number....")
 (defvar ispell-tib-ref-end "\\.[]>]"
   "Regexp matching the end of a Tib reference.")
 
-(defvar ispell-keep-choices-win t
+(defcustom ispell-keep-choices-win t
   "*When not nil, the `*Choices*' window remains for spelling session.
-This minimizes redisplay thrashing.")
-
-(defvar ispell-choices-win-default-height 2
-  "*The default size of the `*Choices*' window, including status line.
-Must be greater than 1.")
-
-(defvar ispell-program-name "ispell"
-  "Program invoked by \\[ispell-word] and \\[ispell-region] commands.")
-
-(defvar ispell-alternate-dictionary
+This minimizes redisplay thrashing."
+  :type 'boolean
+  :group 'ispell)
+
+(defcustom ispell-choices-win-default-height 2
+  "*The default size of the `*Choices*' window, including mode line.
+Must be greater than 1."
+  :type 'integer
+  :group 'ispell)
+
+(defcustom ispell-program-name "ispell"
+  "Program invoked by \\[ispell-word] and \\[ispell-region] commands."
+  :type 'string
+  :group 'ispell)
+
+(defcustom ispell-alternate-dictionary
   (cond ((file-exists-p "/usr/dict/web2") "/usr/dict/web2")
        ((file-exists-p "/usr/share/dict/web2") "/usr/share/dict/web2")
        ((file-exists-p "/usr/dict/words") "/usr/dict/words")
@@ -370,147 +309,350 @@ Must be greater than 1.")
        ((file-exists-p "/usr/share/dict/words") "/usr/share/dict/words")
        ((file-exists-p "/sys/dict") "/sys/dict")
        (t "/usr/dict/words"))
-  "*Alternate dictionary for spelling help.")
-
-(defvar ispell-complete-word-dict ispell-alternate-dictionary
-  "*Dictionary used for word completion.")
-
-(defvar ispell-grep-command "egrep"
-  "Name of the grep command for search processes.")
-
-(defvar ispell-grep-options "-i"
+  "*Alternate dictionary for spelling help."
+  :type '(choice file (const :tag "None" nil))
+  :group 'ispell)
+
+(defcustom ispell-complete-word-dict ispell-alternate-dictionary
+  "*Dictionary used for word completion."
+  :type '(choice file (const :tag "None" nil))
+  :group 'ispell)
+
+(defcustom ispell-message-dictionary-alist nil
+  "*List used by `ispell-message' to select a new dictionary.
+It consists of pairs (REGEXP . DICTIONARY).  If REGEXP is found
+in the message headers, `ispell-local-dictionary' will be set to
+DICTIONARY if `ispell-local-dictionary' is not buffer-local.
+E.g. you may use the following value:
+  '((\"^Newsgroups:[ \\t]*de\\\\.\" . \"deutsch8\")
+    (\"^To:[^\\n,]+\\\\.de[ \\t\\n,>]\" . \"deutsch8\"))"
+  :type '(repeat (cons regexp string))
+  :group 'ispell)
+
+
+(defcustom ispell-grep-command "egrep"
+  "Name of the grep command for search processes."
+  :type 'string
+  :group 'ispell)
+
+(defcustom ispell-grep-options "-i"
   "String of options to use when running the program in `ispell-grep-command'.
 Should probably be \"-i\" or \"-e\".
-Some machines (like the NeXT) don't support \"-i\"")
-
-(defvar ispell-look-command "look"
+Some machines (like the NeXT) don't support \"-i\""
+  :type 'string
+  :group 'ispell)
+
+(defcustom ispell-look-command
+  (cond ((file-exists-p "/bin/look") "/bin/look")
+       ((file-exists-p "/usr/local/bin/look") "/usr/local/bin/look")
+       ((file-exists-p "/usr/bin/look") "/usr/bin/look")
+       (t "look"))
   "Name of the look command for search processes.
-This must be an absolute file name.")
+This must be an absolute file name."
+  :type 'file
+  :group 'ispell)
 
-(defvar ispell-look-p (file-exists-p ispell-look-command)
+(defcustom ispell-look-p (file-exists-p ispell-look-command)
   "*Non-nil means use `look' rather than `grep'.
-Default is based on whether `look' seems to be available.")
+Default is based on whether `look' seems to be available."
+  :type 'boolean
+  :group 'ispell)
 
-(defvar ispell-have-new-look nil
-  "*Non-nil means use the `-r' option (regexp) when running `look'.")
+(defcustom ispell-have-new-look nil
+  "*Non-nil means use the `-r' option (regexp) when running `look'."
+  :type 'boolean
+  :group 'ispell)
 
-(defvar ispell-look-options (if ispell-have-new-look "-dfr" "-df")
-  "String of command options for `ispell-look-command'.")
+(defcustom ispell-look-options (if ispell-have-new-look "-dfr" "-df")
+  "String of command options for `ispell-look-command'."
+  :type 'string
+  :group 'ispell)
 
-(defvar ispell-use-ptys-p nil
+(defcustom ispell-use-ptys-p nil
   "When non-nil, Emacs uses ptys to communicate with Ispell.
-When nil, Emacs uses pipes.")
+When nil, Emacs uses pipes."
+  :type 'boolean
+  :group 'ispell)
 
-(defvar ispell-following-word nil
+(defcustom ispell-following-word nil
   "*Non-nil means `ispell-word' checks the word around or after point.
-Otherwise `ispell-word' checks the preceding word.")
+Otherwise `ispell-word' checks the preceding word."
+  :type 'boolean
+  :group 'ispell)
 
-(defvar ispell-help-in-bufferp nil
+(defcustom ispell-help-in-bufferp nil
   "*Non-nil means display interactive keymap help in a buffer.
-Otherwise use the minibuffer.")
-
-(defvar ispell-quietly nil
-  "*Non-nil means suppress messages in `ispell-word'.")
-
-(defvar ispell-format-word (function upcase)
+The following values are supported:
+  nil        Expand the minibuffer and display a short help message
+             there for a couple of seconds.
+  t          Pop up a new buffer and display a short help message there
+             for a couple of seconds.
+  electric   Pop up a new buffer and display a long help message there.
+             User can browse and then exit the help mode."
+  :type '(choice (const electric) (const :tag "off" nil) (const :tag "on" t))
+  :group 'ispell)
+
+(defcustom ispell-quietly nil
+  "*Non-nil means suppress messages in `ispell-word'."
+  :type 'boolean
+  :group 'ispell)
+
+(defcustom ispell-format-word (function upcase)
   "*Formatting function for displaying word being spell checked.
-The function must take one string argument and return a string.")
+The function must take one string argument and return a string."
+  :type 'function
+  :group 'ispell)
+
+(defcustom ispell-use-framepop-p nil
+  "When non-nil ispell uses framepop to display choices in a dedicated frame.
+You can set this variable to dynamically use framepop if you are in a
+window system by evaluating the following on startup to set this variable:
+  (and window-system (condition-case () (require 'framepop) (error nil)))"
+  :type 'boolean
+  :group 'ispell)
 
-(defvar ispell-personal-dictionary nil
+;;;###autoload
+(defcustom ispell-personal-dictionary nil
   "*File name of your personal spelling dictionary, or nil.
 If nil, the default personal dictionary, \"~/.ispell_DICTNAME\" is used,
-where DICTNAME is the name of your default dictionary.")
+where DICTNAME is the name of your default dictionary."
+  :type '(choice file
+                (const :tag "default" nil))
+  :group 'ispell)
 
-(defvar ispell-silently-savep nil
-  "*When non-nil, save the personal dictionary without confirmation.")
+(defcustom ispell-silently-savep nil
+  "*When non-nil, save the personal dictionary without confirmation."
+  :type 'boolean
+  :group 'ispell)
 
-;;; This variable contains the current dictionary being used if the ispell
-;;; process is running.  Otherwise it contains the global default.
-(defvar ispell-dictionary nil
-  "If non-nil, a dictionary to use instead of the default one.
-This is passed to the ispell process using the `-d' switch and is
-used as key in `ispell-dictionary-alist' (which see).
+;;; This is the local dictionary to use.  When nil the default dictionary will
+;;; be used.  Change set-default call to use a new default dictionary.
+(defcustom ispell-local-dictionary nil
+  "If non-nil, the dictionary to be used for Ispell commands.
+The value must be a string dictionary name in `ispell-dictionary-alist'.
+This variable becomes buffer-local when set in any fashion.
+
+Setting `ispell-local-dictionary' to a value has the same effect as
+calling \\[ispell-change-dictionary] with that value.  This variable
+is automatically set when defined in the file with either
+`ispell-dictionary-keyword' or the Local Variable syntax.
+
+To create a non-standard default dictionary (not from `ispell-dictionary-alist')
+call function `set-default' with the new dictionary name."
+  :type '(choice string
+                (const :tag "default" nil))
+  :group 'ispell)
 
-You should set this variable before your first use of Emacs spell-checking
-commands in the Emacs session, or else use the \\[ispell-change-dictionary]
-command to change it.  Otherwise, this variable only takes effect in a newly
-started Ispell process.")
+(make-variable-buffer-local 'ispell-local-dictionary)
 
-(defvar ispell-extra-args nil
+;; Call this function set up the default dictionary if not English.
+;;(set-default 'ispell-local-dictionary nil)
+
+
+(defcustom ispell-extra-args nil
   "*If non-nil, a list of extra switches to pass to the Ispell program.
-For example, '(\"-W\" \"3\") to cause it to accept all 1-3 character
+For example, (\"-W\" \"3\") to cause it to accept all 1-3 character
 words as correct.  See also `ispell-dictionary-alist', which may be used
-for language-specific arguments.")
-
-;;; ispell-dictionary-alist is set up from two subvariables above
-;;; to avoid having very long lines in loaddefs.el.
-(defvar ispell-dictionary-alist
-
-  '((nil                               ; default (english.aff)
-     "[A-Za-z]" "[^A-Za-z]" "[']" nil ("-B") nil)
-    ("english"                         ; make english explicitly selectable
-     "[A-Za-z]" "[^A-Za-z]" "[']" nil ("-B") nil)
-    ("british"                         ; british version
-     "[A-Za-z]" "[^A-Za-z]" "[']" nil ("-B" "-d" "british") nil)
-    ("deutsch"                         ; deutsch.aff
-     "[a-zA-Z\"]" "[^a-zA-Z\"]" "[']" t ("-C") "~tex")
-    ("deutsch8"
-     "[a-zA-Z\304\326\334\344\366\337\374]"
-     "[^a-zA-Z\304\326\334\344\366\337\374]"
-     "[']" t ("-C" "-d" "deutsch") "~latin1")
-    ("nederlands"                              ; nederlands.aff
-     "[A-Za-z\300-\305\307\310-\317\322-\326\331-\334\340-\345\347\350-\357\361\362-\366\371-\374]"
-     "[^A-Za-z\300-\305\307\310-\317\322-\326\331-\334\340-\345\347\350-\357\361\362-\366\371-\374]"
-     "[']" t ("-C") nil)
-    ("nederlands8"                             ; dutch8.aff
-     "[A-Za-z\300-\305\307\310-\317\322-\326\331-\334\340-\345\347\350-\357\361\362-\366\371-\374]"
-     "[^A-Za-z\300-\305\307\310-\317\322-\326\331-\334\340-\345\347\350-\357\361\362-\366\371-\374]"
-     "[']" t ("-C") nil)
-    ("svenska"                         ;7 bit swedish mode
-     "[A-Za-z}{|\\133\\135\\\\]" "[^A-Za-z}{|\\133\\135\\\\]"
-     "[']" nil ("-C") nil)
-    ("svenska8"                                ;8 bit swedish mode
-     "[A-Za-z\345\344\366\305\304\366]"  "[^A-Za-z\345\344\366\305\304\366]"
-     "[']" nil ("-C" "-d" "svenska") "~list") ; Add `"-T" "list"' instead?
-    ("francais7"
-     "[A-Za-z]" "[^A-Za-z]" "[`'^---]" t nil nil)
-    ("francais"                                ; francais.aff
-     "[A-Za-z\300\302\306\307\310\311\312\313\316\317\324\331\333\334\340\342\347\350\351\352\353\356\357\364\371\373\374]"
-     "[^A-Za-z\300\302\306\307\310\311\312\313\316\317\324\331\333\334\340\342\347\350\351\352\353\356\357\364\371\373\374]"
-     "[---']" t nil "~list")
-    ("francais-tex"                    ; francais.aff
-     "[A-Za-z\300\302\306\307\310\311\312\313\316\317\324\331\333\334\340\342\347\350\351\352\353\356\357\364\371\373\374\\]"
-     "[^A-Za-z\300\302\306\307\310\311\312\313\316\317\324\331\333\334\340\342\347\350\351\352\353\356\357\364\371\373\374\\]"
-     "[---'^`\"]" t nil "~tex")
-    ("dansk"                           ; dansk.aff
-     "[A-Z\306\330\305a-z\346\370\345]" "[^A-Z\306\330\305a-z\346\370\345]"
-     "" nil ("-C") nil)
-    )
+for language-specific arguments."
+  :type '(repeat string)
+  :group 'ispell)
+
+
+
+(defcustom ispell-skip-html 'use-mode-name
+  "*Indicates whether ispell should skip spell checking of SGML markup.
+If t, always skip SGML markup; if nil, never skip; if non-t and non-nil,
+guess whether SGML markup should be skipped according to the name of the
+buffer's major mode."
+  :type '(choice (const :tag "always" t) (const :tag "never" nil)
+                (const :tag "use-mode-name" use-mode-name))
+  :group 'ispell)
+
+
+;;; Define definitions here only for personal dictionaries.
+;;;###autoload
+(defcustom ispell-local-dictionary-alist nil
+  "*Contains local or customized dictionary definitions.
+See `ispell-dictionary-alist'."
+  :type '(repeat (list (choice :tag "Dictionary"
+                              (string :tag "Dictionary name")
+                              (const :tag "default" nil))
+                      (regexp :tag "Case characters")
+                      (regexp :tag "Non case characters")
+                      (regexp :tag "Other characters")
+                      (boolean :tag "Many other characters")
+                      (repeat :tag "Ispell command line args"
+                              (string :tag "Arg"))
+                      (choice :tag "Extended character mode"
+                              (const "~tex") (const "~plaintex")
+                              (const "~nroff") (const "~list")
+                              (const "~latin1") (const "~latin3")
+                              (const :tag "default" nil))
+                      (choice :tag "Character set"
+                              (const iso-8859-1)
+                              (const iso-8859-2)
+                              (const koi8-r))))
+  :group 'ispell)
+
+
+;;; split dictionary so line length is smaller in loaddefs.el
+
+;;; First part of dictionary, shortened for loaddefs.el
+;;;###autoload
+(setq
+ ispell-dictionary-alist-1
+ '((nil                                        ; default (English.aff)
+    "[A-Za-z]" "[^A-Za-z]" "[']" nil ("-B") nil iso-8859-1)
+   ("american"                         ; Yankee English
+    "[A-Za-z]" "[^A-Za-z]" "[']" nil ("-B") nil iso-8859-1)
+   ("brasileiro"                       ; Brazilian mode
+    "[A-Z\301\311\315\323\332\300\310\314\322\331\303\325\307\334\302\312\324a-z\341\351\355\363\372\340\350\354\362\371\343\365\347\374\342\352\364]"
+    "[^A-Z\301\311\315\323\332\300\310\314\322\331\303\325\307\334\302\312\324a-z\341\351\355\363\372\340\350\354\362\371\343\365\347\374\342\352\364]"
+    "[']" nil ("-d" "brasileiro") nil iso-8859-1)
+   ("british"                          ; British version
+    "[A-Za-z]" "[^A-Za-z]" "[']" nil ("-B" "-d" "british") nil iso-8859-1)
+   ("castellano"                       ; Spanish mode
+    "[A-Z\301\311\315\321\323\332\334a-z\341\351\355\361\363\372\374]"
+    "[^A-Z\301\311\315\321\323\332\334a-z\341\351\355\361\363\372\374]"
+    "[-]" nil ("-B" "-d" "castellano") "~tex" iso-8859-1)
+   ("castellano8"                      ; 8 bit Spanish mode
+    "[A-Z\301\311\315\321\323\332\334a-z\341\351\355\361\363\372\374]"
+    "[^A-Z\301\311\315\321\323\332\334a-z\341\351\355\361\363\372\374]"
+    "[-]" nil ("-B" "-d" "castellano") "~latin1" iso-8859-1)))
+
+
+;;; Second part of dictionary, shortened for loaddefs.el
+;;;###autoload
+(setq
+ ispell-dictionary-alist-2
+ '(("czech"
+    "[A-Za-z\301\311\314\315\323\332\331\335\256\251\310\330\317\253\322\341\351\354\355\363\372\371\375\276\271\350\370\357\273\362]"
+    "[^A-Za-z\301\311\314\315\323\332\331\335\256\251\310\330\317\253\322\341\351\354\355\363\372\371\375\276\271\350\370\357\273\362]"
+    "" nil ("-B" "-d" "czech") nil iso-8859-2)
+   ("dansk"                            ; Dansk.aff
+    "[A-Z\306\330\305a-z\346\370\345]" "[^A-Z\306\330\305a-z\346\370\345]"
+    "[']" nil ("-C") nil iso-8859-1)
+   ("deutsch"                          ; Deutsch.aff
+    "[a-zA-Z\"]" "[^a-zA-Z\"]" "[']" t ("-C") "~tex" iso-8859-1)
+   ("deutsch8"
+    "[a-zA-Z\304\326\334\344\366\337\374]"
+    "[^a-zA-Z\304\326\334\344\366\337\374]"
+    "[']" t ("-C" "-d" "deutsch") "~latin1" iso-8859-1)
+   ("english"                          ; make English explicitly selectable
+    "[A-Za-z]" "[^A-Za-z]" "[']" nil ("-B") nil iso-8859-1)))
+
+
+;;; Third part of dictionary, shortened for loaddefs.el
+;;;###autoload
+(setq
+ ispell-dictionary-alist-3
+ '(("esperanto"
+    "[A-Za-z\246\254\266\274\306\330\335\336\346\370\375\376]"
+    "[^A-Za-z\246\254\266\274\306\330\335\336\346\370\375\376]"
+    "[-']" t ("-C") "~latin3" iso-8859-1)
+   ("esperanto-tex"
+    "[A-Za-z^\\]" "[^A-Za-z^\\]"
+    "[-'`\"]" t ("-C" "-d" "esperanto") "~tex" iso-8859-1)
+   ("francais7"
+    "[A-Za-z]" "[^A-Za-z]" "[`'^---]" t nil nil iso-8859-1)
+   ("francais"                         ; Francais.aff
+    "[A-Za-z\300\302\306\307\310\311\312\313\316\317\324\331\333\334\340\342\347\350\351\352\353\356\357\364\371\373\374]"
+    "[^A-Za-z\300\302\306\307\310\311\312\313\316\317\324\331\333\334\340\342\347\350\351\352\353\356\357\364\371\373\374]"
+    "[-']" t nil "~list" iso-8859-1)))
+
+
+;;; Fourth part of dictionary, shortened for loaddefs.el
+;;;###autoload
+(setq
+ ispell-dictionary-alist-4
+ '(("francais-tex"                     ; Francais.aff
+    "[A-Za-z\300\302\306\307\310\311\312\313\316\317\324\331\333\334\340\342\347\350\351\352\353\356\357\364\371\373\374\\]"
+    "[^A-Za-z\300\302\306\307\310\311\312\313\316\317\324\331\333\334\340\342\347\350\351\352\353\356\357\364\371\373\374\\]"
+    "[-'^`\"]" t nil "~tex" iso-8859-1)
+   ("italiano"                         ; Italian.aff
+    "[A-Z\300\301\310\311\314\315\322\323\331\332a-z\340\341\350\351\354\355\363\371\372]"
+    "[^A-Z\300\301\310\311\314\315\322\323\331\332a-z\340\341\350\351\354\355\363\371\372]"
+    "[-]" nil ("-B" "-d" "italian") "~tex" iso-8859-1)
+   ("nederlands"                       ; Nederlands.aff
+    "[A-Za-z\300-\305\307\310-\317\322-\326\331-\334\340-\345\347\350-\357\361\362-\366\371-\374]"
+    "[^A-Za-z\300-\305\307\310-\317\322-\326\331-\334\340-\345\347\350-\357\361\362-\366\371-\374]"
+    "[']" t ("-C") nil iso-8859-1)
+   ("nederlands8"                      ; Dutch8.aff
+    "[A-Za-z\300-\305\307\310-\317\322-\326\331-\334\340-\345\347\350-\357\361\362-\366\371-\374]"
+    "[^A-Za-z\300-\305\307\310-\317\322-\326\331-\334\340-\345\347\350-\357\361\362-\366\371-\374]"
+    "[']" t ("-C") nil iso-8859-1)))
+
+
+;;; Fifth part of dictionary, shortened for loaddefs.el
+;;;###autoload
+(setq
+ ispell-dictionary-alist-5
+ '(("norsk"                            ; 8 bit Norwegian mode
+    "[A-Za-z\305\306\307\310\311\322\324\330\345\346\347\350\351\362\364\370]"
+    "[^A-Za-z\305\306\307\310\311\322\324\330\345\346\347\350\351\362\364\370]"
+    "[\"]" nil ("-d" "norsk") "~list" iso-8859-1)
+   ("norsk7-tex"                       ; 7 bit Norwegian TeX mode
+    "[A-Za-z{}\\'^`]" "[^A-Za-z{}\\'^`]"
+    "[\"]" nil ("-d" "norsk") "~plaintex" iso-8859-1)
+   ("polish"                           ; polish mode
+    "[A-Za-z\241\243\246\254\257\261\263\266\274\277\306\312\321\323\346\352\361\363]"
+    "[^A-Za-z\241\243\246\254\257\261\263\266\274\277\306\312\321\323\346\352\361\363]"
+    "" nil ( "-d" "polish") nil iso-8859-2)))
+
+
+;;; Sixth part of dictionary, shortened for loaddefs.el
+;;;###autoload
+(setq
+ ispell-dictionary-alist-6
+ ;; include Russian iso character set too?
+ ;;   "[']" t ("-d" "russian") "~latin1" iso-8859-1
+ '(("russian"                          ; Russian.aff (KOI8-R charset)
+    "[\341\342\367\347\344\345\263\366\372\351\352\353\354\355\356\357\360\362\363\364\365\346\350\343\376\373\375\370\371\377\374\340\361\301\302\327\307\304\305\243\326\332\311\312\313\314\315\316\317\320\322\323\324\325\306\310\303\336\333\335\330\331\337\334\300\321]"
+    "[^\341\342\367\347\344\345\263\366\372\351\352\353\354\355\356\357\360\362\363\364\365\346\350\343\376\373\375\370\371\377\374\340\361\301\302\327\307\304\305\243\326\332\311\312\313\314\315\316\317\320\322\323\324\325\306\310\303\336\333\335\330\331\337\334\300\321]"
+    "" nil ("-d" "russian") nil koi8-r)
+   ("svenska"                          ; Swedish mode
+    "[A-Za-z\345\344\366\351\340\374\350\346\370\347\305\304\326\311\300\334\310\306\330\307]"
+    "[^A-Za-z\345\344\366\351\340\374\350\346\370\347\305\304\326\311\300\334\310\306\330\307]"
+    "[']" nil ("-C") "~list" iso-8859-1)
+   ("portugues"
+    "[a-zA-Z\301\302\311\323\340\341\342\351\352\355\363\343\372]"
+    "[^a-zA-Z\301\302\311\323\340\341\342\351\352\355\363\343\372]"
+    "[']" t ("-C" "-d" "portugues") "~latin1" iso-8859-1)))
 
+;;;###autoload
+(defcustom ispell-dictionary-alist
+  (append ispell-local-dictionary-alist        ; dictionary customizations
+         ispell-dictionary-alist-1 ispell-dictionary-alist-2
+         ispell-dictionary-alist-3 ispell-dictionary-alist-4
+         ispell-dictionary-alist-5 ispell-dictionary-alist-6)
   "An alist of dictionaries and their associated parameters.
 
 Each element of this list is also a list:
 
 \(DICTIONARY-NAME CASECHARS NOT-CASECHARS OTHERCHARS MANY-OTHERCHARS-P
-        ISPELL-ARGS EXTENDED-CHARACTER-MODE\)
+        ISPELL-ARGS EXTENDED-CHARACTER-MODE CHARACTER-SET\)
 
-DICTIONARY-NAME is a possible value of variable `ispell-dictionary', nil
-means the default dictionary.
+DICTIONARY-NAME is a possible string value of variable `ispell-dictionary',
+nil means the default dictionary.
 
 CASECHARS is a regular expression of valid characters that comprise a
 word.
 
 NOT-CASECHARS is the opposite regexp of CASECHARS.
 
-OTHERCHARS is a regular expression of other characters that are valid
-in word constructs.  Otherchars cannot be adjacent to each other in a
-word, nor can they begin or end a word.  This implies we can't check
-\"Stevens'\" as a correct possessive and other correct formations.
-
+OTHERCHARS is a regexp of characters in the NOT-CASECHARS set but which can be
+used to construct words in some special way.  If OTHERCHARS characters follow
+and precede characters from CASECHARS, they are parsed as part of a word,
+otherwise they become word-breaks.  As an example in English, assume the
+regular expression \"[']\" for OTHERCHARS.  Then \"they're\" and
+\"Steven's\" are parsed as single words including the \"'\" character, but
+\"Stevens'\" does not include the quote character as part of the word.
+If you want OTHERCHARS to be empty, use the empty string.
 Hint: regexp syntax requires the hyphen to be declared first here.
 
-MANY-OTHERCHARS-P is non-nil if many otherchars are to be allowed in a
-word instead of only one.
+MANY-OTHERCHARS-P is non-nil when multiple OTHERCHARS are allowed in a word.
+Otherwise only a single OTHERCHARS character is allowed to be part of any
+single word.
 
 ISPELL-ARGS is a list of additional arguments passed to the ispell
 subprocess.
@@ -524,78 +666,272 @@ but the dictionary can control the extended character mode.
 Both defaults can be overruled in a buffer-local fashion. See
 `ispell-parsing-keyword' for details on this.
 
+CHARACTER-SET used for languages with multibyte characters.
+
 Note that the CASECHARS and OTHERCHARS slots of the alist should
 contain the same character set as casechars and otherchars in the
-language.aff file \(e.g., english.aff\).")
+LANGUAGE.aff file \(e.g., english.aff\)."
+  :type '(repeat (list (choice :tag "Dictionary"
+                              (string :tag "Dictionary name")
+                              (const :tag "default" nil))
+                      (regexp :tag "Case characters")
+                      (regexp :tag "Non case characters")
+                      (regexp :tag "Other characters")
+                      (boolean :tag "Many other characters")
+                      (repeat :tag "Ispell command line args"
+                              (string :tag "Arg"))
+                      (choice :tag "Extended character mode"
+                              (const "~tex") (const "~plaintex")
+                              (const "~nroff") (const "~list")
+                              (const "~latin1") (const "~latin3")
+                              (const :tag "default" nil))
+                      (choice :tag "Character set"
+                              (const iso-8859-1)
+                              (const iso-8859-2)
+                              (const koi8-r))))
+  :group 'ispell)
+
+;;; update the dictionaries at load time
+(setq ispell-dictionary-alist
+      (append ispell-local-dictionary-alist    ; dictionary customizations
+             ispell-dictionary-alist-1 ispell-dictionary-alist-2
+             ispell-dictionary-alist-3 ispell-dictionary-alist-4
+             ispell-dictionary-alist-5 ispell-dictionary-alist-6))
+
+
+
+
+;;; **********************************************************************
+;;; The following are used by ispell, and should not be changed.
+;;; **********************************************************************
+
+
+
+;;; The version must be 3.1 or greater for this version of ispell.el
+;;; There is an incompatibility between version 3.1.12 and lower versions.
+(defconst ispell-required-version '(3 1 12)
+  "Ispell versions with which this version of ispell.el is known to work.")
+(defvar ispell-offset -1
+  "Offset that maps protocol differences between ispell 3.1 versions.")
+
+(defconst ispell-version "ispell.el 3.4 -- Fri Aug  4 09:41:50 PDT 2000")
+
+
+(defun check-ispell-version (&optional interactivep)
+  "Ensure that `ispell-program-name' is valid and the correct version.
+Returns version number if called interactively.
+Otherwise returns the library path if defined."
+  ;; This is a little wasteful as we actually launch ispell twice: once
+  ;; to make sure it's the right version, and once for real.  But people
+  ;; get confused by version mismatches *all* the time (and I've got the
+  ;; email to prove it) so I think this is worthwhile.  And the -v[ersion]
+  ;; option is the only way I can think of to do this that works with
+  ;; all versions, since versions earlier than 3.0.09 didn't identify
+  ;; themselves on startup.
+  (interactive "p")
+  (let ((case-fold-search-val case-fold-search)
+       ;; avoid bugs when syntax of `.' changes in various default modes
+       (default-major-mode 'fundamental-mode)
+       result status)
+    (save-excursion
+      (set-buffer (get-buffer-create " *ispell-tmp*"))
+      (erase-buffer)
+      (setq status (call-process ispell-program-name nil t nil "-vv"))
+      (goto-char (point-min))
+      (if interactivep
+         (progn
+           (end-of-line)
+           (setq result (concat (buffer-substring-no-properties (point-min)
+                                                                (point))
+                                ", "
+                                ispell-version))
+           (message result))
+       ;; return library path.
+       (if (re-search-forward "LIBDIR = \\\"\\([^ \t\n]*\\)\\\"" nil t)
+           (setq result (buffer-substring (match-beginning 1) (match-end 1)))))
+      (goto-char (point-min))
+      (if (not (memq status '(0 nil)))
+         (error "%s exited with %s %s" ispell-program-name
+                (if (stringp status) "signal" "code") status))
+      (setq case-fold-search t
+           status (re-search-forward
+                   (concat "\\<\\("
+                           (format "%d" (car ispell-required-version))
+                           "\\)\\.\\([0-9]*\\)\\.\\([0-9]*\\)\\>")
+                   nil t)
+           case-fold-search case-fold-search-val)
+      (if (or (not status)     ; major version mismatch
+             (< (car (read-from-string (buffer-substring-no-properties
+                                        (match-beginning 2) (match-end 2))))
+                (car (cdr ispell-required-version)))) ; minor version mismatch
+         (error "%s version 3 release %d.%d.%d or greater is required"
+                ispell-program-name (car ispell-required-version)
+                (car (cdr ispell-required-version))
+                (car (cdr (cdr ispell-required-version))))
+       ;; check that it is the correct version.
+       (if (and (= (car (read-from-string (buffer-substring-no-properties
+                                           (match-beginning 2)(match-end 2))))
+                   (car (cdr ispell-required-version)))
+                (< (car (read-from-string (buffer-substring-no-properties
+                                           (match-beginning 3)(match-end 3))))
+                   (car (cdr (cdr ispell-required-version)))))
+           (setq ispell-offset 0)))
+      (kill-buffer (current-buffer)))
+    result))
+
+
+
+;;; The preparation of the menu bar menu must be autoloaded
+;;; because otherwise this file gets autoloaded every time Emacs starts
+;;; so that it can set up the menus and determine keyboard equivalents.
 
 
-(defvar ispell-menu-map nil "Key map for ispell menu")
+;;;###autoload
+(defvar ispell-menu-map nil "Key map for ispell menu.")
+;;; redo menu when loading ispell to get dictionary modifications
+(setq ispell-menu-map nil)
 
-(defvar ispell-menu-lucid nil "Spelling menu for Lucid Emacs.")
+;;;###autoload
+(defvar ispell-menu-xemacs nil
+  "Spelling menu for XEmacs.
+If nil when package is loaded, a standard menu will be set,
+and added as a submenu of the \"Edit\" menu.")
 
-;;; Break out lucid menu and split into several calls to avoid having
+;;; Break out XEmacs menu and split into several calls to avoid having
 ;;; long lines in loaddefs.el.  Detect need off following constant.
 
-(defconst ispell-menu-map-needed       ; make sure this is not Lucid Emacs
+;;; Set up dictionary
+;;;###autoload
+(defvar ispell-menu-map-needed
+  ;; only needed when not version 18 and not XEmacs.
   (and (not ispell-menu-map)
-       (string-lessp "19" emacs-version)
-       ;; make sure this isn't Lucid Emacs
-       (not (string-match "Lucid" emacs-version))))
+       (not version18p)
+       (not xemacsp)
+       'reload))
 
+(defvar ispell-library-path (if (or (not (fboundp 'byte-compiling-files-p))
+                                   (not (byte-compiling-files-p)))
+                               (check-ispell-version))
+  "The directory where ispell dictionaries reside.")
+
+(defvar ispell-process nil
+  "The process object for Ispell.")
 
-;;; setup dictionary
-(if ispell-menu-map-needed
+(defvar ispell-async-processp (and (fboundp 'kill-process)
+                                  (fboundp 'process-send-string)
+                                  (fboundp 'accept-process-output)
+                                  ;;(fboundp 'start-process)
+                                  ;;(fboundp 'set-process-filter)
+                                  ;;(fboundp 'process-kill-without-query)
+                                  )
+  "Non-nil means that the OS supports asynchronous processes.")
+
+;;;###autoload
+(if (and ispell-menu-map-needed
+        (or (not (fboundp 'byte-compiling-files-p))
+            (not (byte-compiling-files-p))))
     (let ((dicts (reverse (cons (cons "default" nil) ispell-dictionary-alist)))
-         name)
+         ;; `ispell-library-path' intentionally not defined in autoload
+         (path (and (boundp 'ispell-library-path) ispell-library-path))
+         name load-dict)
       (setq ispell-menu-map (make-sparse-keymap "Spell"))
       ;; add the dictionaries to the bottom of the list.
       (while dicts
        (setq name (car (car dicts))
+             load-dict (car (cdr (member "-d" (nth 5 (car dicts)))))
              dicts (cdr dicts))
-       (if (stringp name)
-           (define-key ispell-menu-map (vector (intern name))
-             (cons (concat "Select " (capitalize name))
-                   (list 'lambda () '(interactive)
-                         (list 'ispell-change-dictionary name))))))))
+       (cond ((not (stringp name))
+              (define-key ispell-menu-map (vector 'default)
+                (cons "Select Default Dict"
+                      (cons "Dictionary for which Ispell was configured"
+                            (list 'lambda () '(interactive)
+                                  (list
+                                    'ispell-change-dictionary "default"))))))
+             ((or (not path)           ; load all if library dir not defined
+                  (file-exists-p (concat path "/" name ".hash"))
+                  (file-exists-p (concat path "/" name ".has"))
+                  (and load-dict
+                       (or (file-exists-p(concat path "/" load-dict ".hash"))
+                           (file-exists-p(concat path "/" load-dict ".has")))))
+              (define-key ispell-menu-map (vector (intern name))
+                (cons (concat "Select " (capitalize name) " Dict")
+                      (list 'lambda () '(interactive)
+                            (list 'ispell-change-dictionary name)))))))))
+
 
 ;;; define commands in menu in opposite order you want them to appear.
-(if ispell-menu-map-needed
+;;;###autoload
+(if (and ispell-menu-map-needed
+        (or (not (fboundp 'byte-compiling-files-p))
+            (not (byte-compiling-files-p))))
     (progn
       (define-key ispell-menu-map [ispell-change-dictionary]
-       '("Change Dictionary" . ispell-change-dictionary))
+       '(menu-item "Change Dictionary..." ispell-change-dictionary
+                   :help "Supply explicit path to dictionary"))
       (define-key ispell-menu-map [ispell-kill-ispell]
-       '("Kill Process" . ispell-kill-ispell))
+       '(menu-item "Kill Process" ispell-kill-ispell
+                   :enable (and (boundp 'ispell-process) ispell-process
+                                (eq (ispell-process-status) 'run))
+                   :help "Terminate Ispell subprocess"))
       (define-key ispell-menu-map [ispell-pdict-save]
-       '("Save Dictionary" . (lambda () (interactive) (ispell-pdict-save t t))))
+       '(menu-item "Save Dictionary"
+                   (lambda () (interactive) (ispell-pdict-save t t))
+                   :help "Save personal dictionary"))
+      (define-key ispell-menu-map [ispell-help]
+       ;; use (x-popup-menu last-nonmenu-event(list "" ispell-help-list)) ?
+       '(menu-item "Help"
+                   (lambda () (interactive) (describe-function 'ispell-help))
+                   :help "Show standard Ispell keybindings and commands"))
       (define-key ispell-menu-map [ispell-complete-word]
-       '("Complete Word" . ispell-complete-word))
+       '(menu-item "Complete Word" ispell-complete-word
+                   :help "Complete word at cursor using dictionary"))
       (define-key ispell-menu-map [ispell-complete-word-interior-frag]
-       '("Complete Word Frag" . ispell-complete-word-interior-frag))))
+       '(menu-item "Complete Word Fragment" ispell-complete-word-interior-frag
+                   :help "Complete word fragment at cursor"))))
 
-(if ispell-menu-map-needed
+;;;###autoload
+(if (and ispell-menu-map-needed
+        (or (not (fboundp 'byte-compiling-files-p))
+            (not (byte-compiling-files-p))))
     (progn
       (define-key ispell-menu-map [ispell-continue]
-       '("Continue Check" . ispell-continue))
+       '(menu-item "Continue Spell-Checking" ispell-continue
+                   :enable (and (boundp 'ispell-region-end)
+                                (marker-position ispell-region-end)
+                                (equal (marker-buffer ispell-region-end)
+                                       (current-buffer)))
+                   :help "Continue spell checking last region"))
       (define-key ispell-menu-map [ispell-word]
-       '("Check Word" . ispell-word))
-      (define-key ispell-menu-map [ispell-region]
-       '("Check Region" . ispell-region))
-      (define-key ispell-menu-map [ispell-buffer]
-       '("Check Buffer" . ispell-buffer))))
+       '(menu-item "Spell-Check Word" ispell-word
+                   :help "Spell-check word at cursor"))
+      (define-key ispell-menu-map [ispell-comments-and-strings]
+       '(menu-item "Spell-Check Comments" ispell-comments-and-strings
+                   :help "Spell-check only comments and strings"))))
 
-(if ispell-menu-map-needed
+;;;###autoload
+(if (and ispell-menu-map-needed
+        (or (not (fboundp 'byte-compiling-files-p))
+            (not (byte-compiling-files-p))))
     (progn
+      (define-key ispell-menu-map [ispell-region]
+       '(menu-item "Spell-Check Region" ispell-region
+                   :enable mark-active
+                   :help "Spell-check text in marked region"))
       (define-key ispell-menu-map [ispell-message]
-       '("Check Message" . ispell-message))
-      (define-key ispell-menu-map [ispell-help]
-       ;; use (x-popup-menu last-nonmenu-event(list "" ispell-help-list)) ?
-       '("Help" . (lambda () (interactive) (describe-function 'ispell-help))))
-      (put 'ispell-region 'menu-enable 'mark-active)
+       '(menu-item "Spell-Check Message" ispell-message
+                   :help "Skip headers and included message text"))
+      (define-key ispell-menu-map [ispell-buffer]
+       '(menu-item "Spell-Check Buffer" ispell-buffer
+                   :help "Check spelling of selected buffer"))
+      ;;(put 'ispell-region 'menu-enable 'mark-active)
       (fset 'ispell-menu-map (symbol-value 'ispell-menu-map))))
 
-;;; Xemacs version 19
-(if (and (string-lessp "19" emacs-version)
-        (string-match "Lucid" emacs-version))
+;;; XEmacs versions 19 & 20
+(if (and xemacsp
+        (not version18p)
+        (featurep 'menubar)
+        (null ispell-menu-xemacs)
+        (not (and (boundp 'infodock-version) infodock-version)))
     (let ((dicts (cons (cons "default" nil) ispell-dictionary-alist))
          (current-menubar (or current-menubar default-menubar))
          (menu
@@ -603,6 +939,7 @@ language.aff file \(e.g., english.aff\).")
             ;;["Help"          (popup-menu ispell-help-list)   t]
             ["Check Message"   ispell-message                  t]
             ["Check Buffer"    ispell-buffer                   t]
+            ["Check Comments"  ispell-comments-and-strings     t]
             ["Check Word"      ispell-word                     t]
             ["Check Region"    ispell-region  (or (not zmacs-regions) (mark))]
             ["Continue Check"  ispell-continue                 t]
@@ -610,52 +947,87 @@ language.aff file \(e.g., english.aff\).")
             ["Complete Word"   ispell-complete-word            t]
             ["Kill Process"    ispell-kill-ispell              t]
             "-"
-            ["Save Dictionary" (ispell-pdict-save t t)         t]
-            ["Change Dictionary" ispell-change-dictionary      t]))
-         name)
+            ["Save Personal Dict"(ispell-pdict-save t t)       t]
+            ["Change Dictionary" ispell-change-dictionary      t]
+            ["Select Default"  (ispell-change-dictionary "default") t]))
+         name load-dict)
       (while dicts
        (setq name (car (car dicts))
+             load-dict (car (cdr (member "-d" (nth 5 (car dicts)))))
              dicts (cdr dicts))
-       (if (stringp name)
+       ;; Include if the dictionary is in the library, or path not defined.
+       (if (and (stringp name)
+                (or (not ispell-library-path)
+                    (file-exists-p (concat ispell-library-path "/"
+                                           name ".hash"))
+                    (file-exists-p (concat ispell-library-path "/"
+                                           name ".has"))
+                    (and load-dict
+                         (or (file-exists-p (concat ispell-library-path "/"
+                                                    load-dict ".hash"))
+                             (file-exists-p (concat ispell-library-path "/"
+                                                    load-dict ".has"))))))
            (setq menu (append menu
                               (list
-                               (vector (concat "Select " (capitalize name))
-                                       (list 'ispell-change-dictionary name)
-                                       t))))))
-      (setq ispell-menu-lucid menu)
+                                (vector (concat "Select " (capitalize name))
+                                        (list 'ispell-change-dictionary name)
+                                        t))))))
+      (setq ispell-menu-xemacs menu)
       (if current-menubar
          (progn
            (delete-menu-item '("Edit" "Spell")) ; in case already defined
-           (add-menu '("Edit") "Spell" ispell-menu-lucid)))))
+           (add-menu '("Edit") "Spell" ispell-menu-xemacs)))))
+
+;;; Allow incrementing characters as integers in XEmacs 20
+(if (and xemacsp
+        (fboundp 'int-char))
+    (fset 'ispell-int-char 'int-char)
+  ;; Emacs and XEmacs 19 or earlier
+  (fset 'ispell-int-char 'identity))
 
 
-;;; **********************************************************************
-;;; The following are used by ispell, and should not be changed.
 ;;; **********************************************************************
 
 
-;;; The version must be 3.1 or greater for this version of ispell.el
-;;; There is an incompatibility between version 3.1.12 and lower versions.
-(defconst ispell-required-version '("3.1." 12)
-  "Ispell versions with which this version of ispell.el is known to work.")
-(defvar ispell-offset 1
-  "Offset that maps protocol differences between ispell 3.1 versions.")
+;;; This variable contains the current dictionary being used if the ispell
+;;; process is running.  Otherwise it contains the global default.
+(defvar ispell-dictionary nil
+  "The name of the current dictionary, or nil for the default.
+When `ispell-local-dictionary' is nil, `ispell-dictionary' is used to select
+the dictionary for new buffers.
+
+This is passed to the ispell process using the `-d' switch and is
+used as key in `ispell-dictionary-alist' (which see).")
+
+(defun ispell-decode-string (str)
+  "Decodes multibyte character strings.
+Protects against bogus binding of `enable-multibyte-characters' in XEmacs."
+  (if (and (or xemacsp
+              (and (boundp 'enable-multibyte-characters)
+                   enable-multibyte-characters))
+          (fboundp 'decode-coding-string)
+          (ispell-get-coding-system))
+      (decode-coding-string str (ispell-get-coding-system))
+    str))
 
 (defun ispell-get-casechars ()
-  (nth 1 (assoc ispell-dictionary ispell-dictionary-alist)))
+  (ispell-decode-string
+   (nth 1 (assoc ispell-dictionary ispell-dictionary-alist))))
 (defun ispell-get-not-casechars ()
-  (nth 2 (assoc ispell-dictionary ispell-dictionary-alist)))
+  (ispell-decode-string
+   (nth 2 (assoc ispell-dictionary ispell-dictionary-alist))))
 (defun ispell-get-otherchars ()
-  (nth 3 (assoc ispell-dictionary ispell-dictionary-alist)))
+  (ispell-decode-string
+   (nth 3 (assoc ispell-dictionary ispell-dictionary-alist))))
 (defun ispell-get-many-otherchars-p ()
   (nth 4 (assoc ispell-dictionary ispell-dictionary-alist)))
 (defun ispell-get-ispell-args ()
   (nth 5 (assoc ispell-dictionary ispell-dictionary-alist)))
 (defun ispell-get-extended-character-mode ()
   (nth 6 (assoc ispell-dictionary ispell-dictionary-alist)))
+(defun ispell-get-coding-system ()
+  (nth 7 (assoc ispell-dictionary ispell-dictionary-alist)))
 
-(defvar ispell-process nil
-  "The process object for Ispell.")
 
 (defvar ispell-pdict-modified-p nil
   "Non-nil means personal dictionary has modifications to be saved.")
@@ -665,20 +1037,32 @@ language.aff file \(e.g., english.aff\).")
 ;;; When numeric, contains cursor location in buffer, and cursor remains there.
 (defvar ispell-quit nil)
 
+(defvar ispell-process-directory nil
+  "The directory where `ispell-process' was started.")
+
 (defvar ispell-filter nil
   "Output filter from piped calls to Ispell.")
 
 (defvar ispell-filter-continue nil
   "Control variable for Ispell filter function.")
 
-(defvar ispell-process-directory nil
-  "The directory where `ispell-process' was started.")
+(defvar ispell-output-buffer nil
+  "Buffer used for reading output of a synchronous Ispell subprocess.")
+
+(defvar ispell-session-buffer nil
+  "Buffer used for passing input to a synchronous Ispell subprocess.")
+
+(defvar ispell-cmd-args nil
+  "Command-line arguments to pass to a synchronous Ispell subprocess.")
 
 (defvar ispell-query-replace-marker (make-marker)
   "Marker for `query-replace' processing.")
 
+(defvar ispell-recursive-edit-marker (make-marker)
+  "Marker for return point from recursive edit.")
+
 (defvar ispell-checking-message nil
-  "Non-nil when we're checking a mail message")
+  "Non-nil when we're checking a mail message.")
 
 (defconst ispell-choices-buffer "*Choices*")
 
@@ -686,47 +1070,88 @@ language.aff file \(e.g., english.aff\).")
 
 ;;; *** Buffer Local Definitions ***
 
-;;; This is the local dictionary to use.  When nil the default dictionary will
-;;; be used.  Do not redefine default value or it will override the global!
-(defvar ispell-local-dictionary nil
-  "If non-nil, a dictionary to use for Ispell commands in this buffer.
-The value must be a string dictionary name in `ispell-dictionary-alist'.
-This variable becomes buffer-local when set in any fashion.
-
-Setting ispell-local-dictionary to a value has the same effect as
-calling \\[ispell-change-dictionary] with that value.  This variable
-is automatically set when defined in the file with either
-`ispell-dictionary-keyword' or the Local Variable syntax.")
-
-(make-variable-buffer-local 'ispell-local-dictionary)
-
-;; Use default directory, unless locally set.
-(set-default 'ispell-local-dictionary nil)
-
-(defconst ispell-words-keyword "LocalWords: "                                
+(defconst ispell-words-keyword "LocalWords: "                                 
   "The keyword for local oddly-spelled words to accept.
 The keyword will be followed by any number of local word spellings.
 There can be multiple of these keywords in the file.")
 
 (defconst ispell-dictionary-keyword "Local IspellDict: "
-  "The keyword for local dictionary definitions.
-There should be only one dictionary keyword definition per file, and it
-should be followed by a correct dictionary name in `ispell-dictionary-alist'.")
+  "The keyword for a local dictionary to use.
+The keyword must be followed by a correct dictionary name in
+`ispell-dictionary-alist'.  When multiple occurrences exist, the last keyword
+definition is used.")
+
+(defconst ispell-pdict-keyword "Local IspellPersDict: "
+  "The keyword for defining buffer local dictionaries.
+Keyword must be followed by the filename of a personal dictionary.
+The last occurring definition in the buffer will be used.")
 
 (defconst ispell-parsing-keyword "Local IspellParsing: "
   "The keyword for overriding default Ispell parsing.
-Determined by the buffer's major mode and extended-character mode as well as
-the default dictionary.
-
 The above keyword string should be followed by `latex-mode' or
 `nroff-mode' to put the current buffer into the desired parsing mode.
 
 Extended character mode can be changed for this buffer by placing
-a `~' followed by an extended-character mode -- such as `~.tex'.")
+a `~' followed by an extended-character mode -- such as `~.tex'.
+The last occurring definition in the buffer will be used.")
+
+;;;###autoload
+(defvar ispell-skip-region-alist
+  '((ispell-words-keyword         forward-line)
+    (ispell-dictionary-keyword    forward-line)
+    (ispell-pdict-keyword         forward-line)
+    (ispell-parsing-keyword       forward-line)
+    ("^---*BEGIN PGP [A-Z ]*--*" . "^---*END PGP [A-Z ]*--*")
+    ("^---* \\(Start of \\)?[Ff]orwarded [Mm]essage"   . "^---* End of [Ff]orwarded [Mm]essage")
+    ;; Matches e-mail addresses, file names, http addresses, etc.  The `-+'
+    ;; pattern necessary for performance reasons when `-' part of word syntax.
+    ("\\(-+\\|\\(/\\|\\(\\(\\w\\|[-_]\\)+[.:@]\\)\\)\\(\\w\\|[-_]\\)*\\([.:/@]+\\(\\w\\|[-_]\\|~\\)+\\)+\\)")
+    ;; This is a pretty complex regexp.  It can be simplified to the following:
+    ;; "\\(\\w\\|[-_]\\)*\\([.:/@]+\\(\\w\\|[-_]\\|~\\)+\\)+"
+    ;; but some valid text will be skipped, e.g. "his/her".  This could be
+    ;; fixed up (at the expense of a moderately more complex regexp)
+    ;; by not allowing "/" to be the character which triggers the
+    ;; identification of the computer name, e.g.:
+    ;; "\\(\\w\\|[-_]\\)+[.:@]\\(\\w\\|[-_]\\)*\\([.:/@]+\\(\\w\\|[-_]\\|~\\)+\\)+"
+    )
+  "Alist expressing beginning and end of regions not to spell check.
+The alist key must be a regular expression.
+Valid forms include:
+  (KEY) - just skip the key.
+  (KEY . REGEXP) - skip to the end of REGEXP.  REGEXP may be string or symbol.
+  (KEY REGEXP) - skip to end of REGEXP.  REGEXP must be a string.
+  (KEY FUNCTION ARGS) - FUNCTION called with ARGS returns end of region.")
+
+
+
+;;;###autoload
+(defvar ispell-tex-skip-alists
+  '((;;("%\\[" . "%\\]") ; AMStex block comment...
+     ;; All the standard LaTeX keywords from L. Lamport's guide:
+     ;; \cite, \hspace, \hspace*, \hyphenation, \include, \includeonly, \input,
+     ;; \label, \nocite, \rule (in ispell - rest included here)
+     ("\\\\addcontentsline"              ispell-tex-arg-end 2)
+     ("\\\\add\\(tocontents\\|vspace\\)" ispell-tex-arg-end)
+     ("\\\\\\([aA]lph\\|arabic\\)"      ispell-tex-arg-end)
+     ;;("\\\\author"                    ispell-tex-arg-end)
+     ("\\\\bibliographystyle"           ispell-tex-arg-end)
+     ("\\\\makebox"                     ispell-tex-arg-end 0)
+     ("\\\\e?psfig"                     ispell-tex-arg-end)
+     ("\\\\document\\(class\\|style\\)" .
+      "\\\\begin[ \t\n]*{[ \t\n]*document[ \t\n]*}"))
+    (;; delimited with \begin.  In ispell: displaymath, eqnarray, eqnarray*,
+     ;; equation, minipage, picture, tabular, tabular* (ispell)
+     ("\\(figure\\|table\\)\\*?"        ispell-tex-arg-end 0)
+     ("list"                            ispell-tex-arg-end 2)
+     ("program"                . "\\\\end[ \t\n]*{[ \t\n]*program[ \t\n]*}")
+     ("verbatim\\*?"   . "\\\\end[ \t\n]*{[ \t\n]*verbatim\\*?[ \t\n]*}")))
+  "*Lists of regions to be skipped in TeX mode.
+First list is used raw.
+Second list has key placed inside \\begin{}.
+
+Delete or add any regions you want to be automatically selected
+for skipping in latex mode.")
 
-(defvar ispell-skip-sgml nil
-  "Skips spell checking of SGML tags and entity references when non-nil.
-This variable is set when major-mode is sgml-mode or html-mode.")
 
 (defvar ispell-local-pdict ispell-personal-dictionary
   "A buffer local variable containing the current personal dictionary.
@@ -740,18 +1165,15 @@ local variable syntax.")
 
 (make-variable-buffer-local 'ispell-local-pdict)
 
-(defconst ispell-pdict-keyword "Local IspellPersDict: "
-  "The keyword for defining buffer local dictionaries.")
-
 (defvar ispell-buffer-local-name nil
   "Contains the buffer name if local word definitions were used.
 Ispell is then restarted because the local words could conflict.")
 
 (defvar ispell-parser 'use-mode-name
    "*Indicates whether ispell should parse the current buffer as TeX Code.
-Special value `use-mode-name' tries to guess using the name of major-mode.
-Default parser is 'nroff.
-Currently the only other valid parser is 'tex.
+Special value `use-mode-name' tries to guess using the name of `major-mode'.
+Default parser is `nroff'.
+Currently the only other valid parser is `tex'.
 
 You can set this variable in hooks in your init file -- eg:
 
@@ -768,12 +1190,109 @@ You can set this variable in hooks in your init file -- eg:
 ;;; **********************************************************************
 
 
-(and (string-lessp "19" emacs-version)
-     (not (boundp 'epoch::version))
-     (defalias 'ispell 'ispell-buffer))
 
 ;;;###autoload
-(define-key global-map "\M-$" 'ispell-word)
+(define-key esc-map "$" 'ispell-word)
+
+
+(defun ispell-accept-output (&optional timeout-secs timeout-msecs)
+  "Wait for output from ispell process, or TIMEOUT-SECS and TIMEOUT-MSECS.
+If asynchronous subprocesses are not supported, call `ispell-filter' and
+pass it the output of the last ispell invocation."
+  (if ispell-async-processp
+      (accept-process-output ispell-process timeout-secs timeout-msecs)
+    (if (null ispell-process)
+       (error "No Ispell process to read output from!")
+      (let ((buf ispell-output-buffer)
+           ispell-output)
+       (if (not (bufferp buf))
+           (setq ispell-filter nil)
+         (save-excursion
+           (set-buffer buf)
+           (setq ispell-output (buffer-substring-no-properties
+                                (point-min) (point-max))))
+         (ispell-filter t ispell-output)
+         (save-excursion
+           (set-buffer buf)
+           (erase-buffer)))))))
+
+
+(defun ispell-send-string (string)
+  "Send the string STRING to the Ispell process."
+  (if ispell-async-processp
+      (process-send-string ispell-process string)
+    ;; Asynchronous subprocesses aren't supported on this losing system.
+    ;; We keep all the directives passed to Ispell during the entire
+    ;; session in a buffer, and pass them anew each time we invoke
+    ;; Ispell to process another chunk of text.  (Yes, I know this is a
+    ;; terrible kludge, and it's a bit slow, but it does get the work done.)
+    (let ((cmd (aref string 0))
+         ;; The following commands are not passed to Ispell until
+         ;; we have a *reall* reason to invoke it.
+         (cmds-to-defer '(?* ?@ ?~ ?+ ?- ?! ?%))
+         (default-major-mode 'fundamental-mode)
+         (session-buf ispell-session-buffer)
+         (output-buf ispell-output-buffer)
+         (ispell-args ispell-cmd-args)
+         (defdir ispell-process-directory)
+         prev-pos)
+      (save-excursion
+       (set-buffer session-buf)
+       (setq prev-pos (point))
+       (setq default-directory defdir)
+       (insert string)
+       (if (not (memq cmd cmds-to-defer))
+           (let (coding-system-for-read coding-system-for-write status)
+             (if (or xemacsp
+                     (and (boundp 'enable-multibyte-characters)
+                          enable-multibyte-characters))
+                 (setq coding-system-for-read (ispell-get-coding-system)
+                       coding-system-for-write (ispell-get-coding-system)))
+             (set-buffer output-buf)
+             (erase-buffer)
+             (set-buffer session-buf)
+             (setq status
+                   (apply 'call-process-region (point-min) (point-max)
+                          ispell-program-name nil
+                          output-buf nil
+                          "-a" "-m" ispell-args))
+             (set-buffer output-buf)
+             (goto-char (point-min))
+             (save-match-data
+               (if (not (looking-at "@(#) "))
+                   (error "Ispell error: %s"
+                          (buffer-substring-no-properties
+                           (point) (progn (end-of-line) (point)))))
+               ;; If STRING is "^Z\n", we just started Ispell and need
+               ;; to retain its version ID line in the output buffer.
+               ;; Otherwise, remove the ID line, as it will confuse
+               ;; `ispell-filter'.
+               (or (string= string "\032\n")
+                   (progn
+                     (forward-line)
+                     (delete-region (point-min) (point))))
+               ;; If STRING begins with ^ or any normal character, we need
+               ;; to remove the last line from the session buffer, since it
+               ;; was just spell-checked, and we don't want to check it again.
+               ;; The same goes for the # command, since Ispell already saved
+               ;; the personal dictionary.
+               (set-buffer session-buf)
+               (delete-region prev-pos (point))
+               ;; Ispell run synchronously saves the personal dictionary
+               ;; after each successful command.  So we can remove any
+               ;; lines in the session buffer that insert words into the
+               ;; dictionary.
+               (if (memq status '(0 nil))
+                   (let ((more-lines t))
+                     (goto-char (point-min))
+                     (while more-lines
+                       (if (looking-at "^\\*")
+                           (let ((start (point)))
+                             (forward-line)
+                             (delete-region start (point)))
+                         (setq more-lines (= 0 (forward-line))))))))))))))
+
+
 
 ;;;###autoload
 (defun ispell-word (&optional following quietly continue)
@@ -781,19 +1300,27 @@ You can set this variable in hooks in your init file -- eg:
 If the word is not found in dictionary, display possible corrections
 in a window allowing you to choose one.
 
-With a prefix argument (or if CONTINUE is non-nil),
-resume interrupted spell-checking of a buffer or region.
-
 If optional argument FOLLOWING is non-nil or if `ispell-following-word'
 is non-nil when called interactively, then the following word
 \(rather than preceding\) is checked when the cursor is not over a word.
 When the optional argument QUIETLY is non-nil or `ispell-quietly' is non-nil
 when called interactively, non-corrective messages are suppressed.
 
+With a prefix argument (or if CONTINUE is non-nil),
+resume interrupted spell-checking of a buffer or region.
+
 Word syntax described by `ispell-dictionary-alist' (which see).
 
 This will check or reload the dictionary.  Use \\[ispell-change-dictionary]
-or \\[ispell-region] to update the Ispell process."
+or \\[ispell-region] to update the Ispell process.
+
+return values:
+nil           word is correct or spelling is accpeted.
+0             word is inserted into buffer-local definitions.
+\"word\"        word corrected from word list.
+\(\"word\" arg\)  word is hand entered.
+quit          spell session exited."
+
   (interactive (list nil nil current-prefix-arg))
   (if continue
       (ispell-continue)
@@ -803,62 +1330,78 @@ or \\[ispell-region] to update the Ispell process."
     (ispell-accept-buffer-local-defs)  ; use the correct dictionary
     (let ((cursor-location (point))    ; retain cursor location
          (word (ispell-get-word following))
-         start end poss replace)
-      ;; destructure return word info list.
+         start end poss new-word replace)
+      ;; De-structure return word info list.
       (setq start (car (cdr word))
            end (car (cdr (cdr word)))
            word (car word))
 
-      ;; now check spelling of word.
-      (or quietly
-         (message "Checking spelling of %s..."
-                  (funcall ispell-format-word word)))
-      (process-send-string ispell-process "%\n") ;put in verbose mode
-      (process-send-string ispell-process (concat "^" word "\n"))
-      ;; wait until ispell has processed word
-      (while (progn
-              (accept-process-output ispell-process)
-              (not (string= "" (car ispell-filter)))))
-      ;;(process-send-string ispell-process "!\n") ;back to terse mode.
-      (setq ispell-filter (cdr ispell-filter))
-      (if (listp ispell-filter)
-         (setq poss (ispell-parse-output (car ispell-filter))))
-      (cond ((eq poss t)
-            (or quietly
-                (message "%s is correct" (funcall ispell-format-word word))))
-           ((stringp poss)
-            (or quietly
-                (message "%s is correct because of root %s"
-                         (funcall ispell-format-word word)
-                         (funcall ispell-format-word poss))))
-           ((null poss) (message "Error in ispell process"))
-           (ispell-check-only          ; called from ispell minor mode.
-            (beep))
-           (t                          ; prompt for correct word.
-            (save-window-excursion
-              (setq replace (ispell-command-loop
-                             (car (cdr (cdr poss)))
-                             (car (cdr (cdr (cdr poss))))
-                             (car poss) start end)))
-            (cond ((equal 0 replace)
-                   (ispell-add-per-file-word-list (car poss)))
-                  (replace
-                   (setq word (if (atom replace) replace (car replace))
-                         cursor-location (+ (- (length word) (- end start))
-                                            cursor-location))
-                   (if (not (equal word (car poss)))
-                       (progn
-                         (delete-region start end)
-                         (insert word)))
-                   (if (not (atom replace)) ; recheck spelling of replacement
-                       (progn
-                         (goto-char cursor-location)
-                         (ispell-word following quietly)))))
-            (if (get-buffer ispell-choices-buffer)
-                (kill-buffer ispell-choices-buffer))))
+      ;; now check spelling of word if it has 3 or more characters.
+      (cond
+       ((> (length word) 2)
+       (or quietly
+           (message "Checking spelling of %s..."
+                    (funcall ispell-format-word word)))
+       (ispell-send-string "%\n")      ; put in verbose mode
+       (ispell-send-string (concat "^" word "\n"))
+       ;; wait until ispell has processed word
+       (while (progn
+                (ispell-accept-output)
+                (not (string= "" (car ispell-filter)))))
+       ;;(ispell-send-string "!\n") ;back to terse mode.
+       (setq ispell-filter (cdr ispell-filter)) ; remove extra \n
+       (if (and ispell-filter (listp ispell-filter))
+           (if (> (length ispell-filter) 1)
+               (error "Ispell and its process have different character maps.")
+             (setq poss (ispell-parse-output (car ispell-filter)))))
+       (cond ((eq poss t)
+              (or quietly
+                  (message "%s is correct"
+                           (funcall ispell-format-word word))))
+             ((stringp poss)
+              (or quietly
+                  (message "%s is correct because of root %s"
+                           (funcall ispell-format-word word)
+                           (funcall ispell-format-word poss))))
+             ((null poss) (message "Error in ispell process"))
+             (ispell-check-only        ; called from ispell minor mode.
+              (beep)
+              (message "%s is incorrect" (funcall ispell-format-word word)))
+             (t                        ; prompt for correct word.
+              (save-window-excursion
+                (setq replace (ispell-command-loop
+                               (car (cdr (cdr poss)))
+                               (car (cdr (cdr (cdr poss))))
+                               (car poss) start end)))
+              (cond ((equal 0 replace)
+                     (ispell-add-per-file-word-list (car poss)))
+                    (replace
+                     (setq new-word (if (atom replace) replace (car replace))
+                           cursor-location (+ (- (length word) (- end start))
+                                              cursor-location))
+                     (if (not (equal new-word (car poss)))
+                         (progn
+                           (delete-region start end)
+                           (setq start (point))
+                           (insert new-word)
+                           (setq end (point))))
+                     (if (not (atom replace)) ;recheck spelling of replacement
+                         (progn
+                           (if (car (cdr replace)) ; query replace requested
+                               (save-window-excursion
+                                 (query-replace word new-word t)))
+                           (goto-char start)
+                           ;; single word could be split into multiple words
+                           (setq ispell-quit (not (ispell-region start end)))
+                           ))))
+              ;; keep if rechecking word and we keep choices win.
+              (if (get-buffer ispell-choices-buffer)
+                  (kill-buffer ispell-choices-buffer))))
+       (ispell-pdict-save ispell-silently-savep)
+       ;; NB: Cancels ispell-quit incorrectly if called from ispell-region
+       (if ispell-quit (setq ispell-quit nil replace 'quit))))
       (goto-char cursor-location)      ; return to original location
-      (ispell-pdict-save ispell-silently-savep)
-      (if ispell-quit (setq ispell-quit nil)))))
+      replace)))
 
 
 (defun ispell-get-word (following &optional extra-otherchars)
@@ -876,8 +1419,8 @@ Word syntax described by `ispell-dictionary-alist' (which see)."
         (ispell-many-otherchars-p (ispell-get-many-otherchars-p))
         (word-regexp (concat ispell-casechars
                              "+\\("
-                             ispell-otherchars
-                             "?"
+                             (if (not (string= "" ispell-otherchars))
+                                 (concat ispell-otherchars "?"))
                              (if extra-otherchars
                                  (concat extra-otherchars "?"))
                              ispell-casechars
@@ -885,7 +1428,7 @@ Word syntax described by `ispell-dictionary-alist' (which see)."
                              (if (or ispell-many-otherchars-p
                                      extra-otherchars)
                                  "*" "?")))
-        did-it-once
+        did-it-once prevpt
         start end word)
     ;; find the word
     (if (not (looking-at ispell-casechars))
@@ -894,28 +1437,34 @@ Word syntax described by `ispell-dictionary-alist' (which see)."
          (re-search-backward ispell-casechars (point-min) t)))
     ;; move to front of word
     (re-search-backward ispell-not-casechars (point-min) 'start)
-    (while (and (or (looking-at ispell-otherchars)
+    (while (and (or (and (not (string= "" ispell-otherchars))
+                        (looking-at ispell-otherchars))
                    (and extra-otherchars (looking-at extra-otherchars)))
                (not (bobp))
                (or (not did-it-once)
-                   ispell-many-otherchars-p))
+                   ispell-many-otherchars-p)
+               (not (eq prevpt (point))))
       (if (and extra-otherchars (looking-at extra-otherchars))
          (progn
            (backward-char 1)
            (if (looking-at ispell-casechars)
                (re-search-backward ispell-not-casechars (point-min) 'move)))
-       (setq did-it-once t)
+       (setq did-it-once t
+             prevpt (point))
        (backward-char 1)
        (if (looking-at ispell-casechars)
            (re-search-backward ispell-not-casechars (point-min) 'move)
          (backward-char -1))))
     ;; Now mark the word and save to string.
-    (or (re-search-forward word-regexp (point-max) t)
-       (error "No word found to check!"))
-    (setq start (match-beginning 0)
-         end (point)
-         word (buffer-substring start end))
-    (list word start end)))
+    (if (not (re-search-forward word-regexp (point-max) t))
+       (if ispell-check-only
+           ;; return dummy word when just flagging misspellings
+           (list "" (point) (point))
+         (error "No word found to check!"))
+      (setq start (match-beginning 0)
+           end (point)
+           word (buffer-substring-no-properties start end))
+      (list word start end))))
 
 
 ;;; Global ispell-pdict-modified-p is set by ispell-command-loop and
@@ -923,6 +1472,7 @@ Word syntax described by `ispell-dictionary-alist' (which see)."
 ;;; a value or a list, whose value is the state of whether the
 ;;; dictionary needs to be saved.
 
+;;; ###autoload
 (defun ispell-pdict-save (&optional no-query force-save)
   "Check to see if the personal dictionary has been modified.
 If so, ask if it needs to be saved."
@@ -932,12 +1482,20 @@ If so, ask if it needs to be saved."
   (if (or ispell-pdict-modified-p force-save)
       (if (or no-query (y-or-n-p "Personal dictionary modified.  Save? "))
          (progn
-           (process-send-string ispell-process "#\n")
+           (ispell-send-string "#\n")  ; save dictionary
            (message "Personal dictionary saved."))))
   ;; unassert variable, even if not saved to avoid questioning.
   (setq ispell-pdict-modified-p nil))
 
 
+(defun ispell-choices-win-default-height ()
+  "Return the default height of the `*Choices*' window for this display.
+This is the value of of the variable `ispell-choices-win-default-height',
+plus a possible fudge factor to work around problems with mode-lines that
+obscure the last buffer line on graphics capable displays."
+  (+ ispell-choices-win-default-height (ispell-mode-line-window-height-fudge)))
+
+
 (defun ispell-command-loop (miss guess word start end)
   "Display possible corrections from list MISS.
 GUESS lists possibly valid affix construction of WORD.
@@ -945,25 +1503,31 @@ Returns nil to keep word.
 Returns 0 to insert locally into buffer-local dictionary.
 Returns string for new chosen word.
 Returns list for new replacement word (will be rechecked).
+  Query-replace when list length is 2.
+  Automatic query-replace when second element is `query-replace'.
 Highlights the word, which is assumed to run from START to END.
 Global `ispell-pdict-modified-p' becomes a list where the only value
 indicates whether the dictionary has been modified when option `a' or `i' is
-used."
-  (let ((textbuf (current-buffer))
-       (count ?0)
-       (line 2)
-       (max-lines (- (window-height) 4)) ; assure 4 context lines.
+used.
+Global `ispell-quit' set to start location to continue spell session."
+  (let ((count ?0)
+       (line (ispell-choices-win-default-height))
+       (max-lines (- (window-height) 4)) ; ensure 4 context lines.
        (choices miss)
        (window-min-height (min window-min-height
-                               ispell-choices-win-default-height))
+                               (ispell-choices-win-default-height)))
        (command-characters '( ?  ?i ?a ?A ?r ?R ?? ?x ?X ?q ?l ?u ?m ))
+       (dedicated (window-dedicated-p (selected-window)))
        (skipped 0)
-       char num result textwin highlighted)
+       char num result textwin dedicated-win)
 
     ;; setup the *Choices* buffer with valid data.
     (save-excursion
       (set-buffer (get-buffer-create ispell-choices-buffer))
       (setq mode-line-format (concat "--  %b  --  word: " word))
+      ;; XEmacs: prevent thick modeline vs increasing height in overlay-window
+      ;;(and (fboundp 'set-specifier)
+      ;;     (set-specifier has-modeline-p (cons (current-buffer) nil)))
       (erase-buffer)
       (if guess
          (progn
@@ -992,48 +1556,32 @@ used."
        ;; not so good if there are over 20 or 30 options, but then, if
        ;; there are that many you don't want to scan them all anyway...
        (while (memq count command-characters) ; skip command characters.
-         (setq count (1+ count)
+         (setq count (ispell-int-char (1+ count))
                skipped (1+ skipped)))
        (insert "(" count ") " (car choices) "  ")
        (setq choices (cdr choices)
-             count (1+ count)))
-      (setq count (- count ?0 skipped)))
+             count (ispell-int-char (1+ count))))
+      (setq count (ispell-int-char (- count ?0 skipped))))
 
-    ;; Assure word is visible
+    ;; ensure word is visible
     (if (not (pos-visible-in-window-p end))
        (sit-for 0))
-    ;; Display choices for misspelled word.
-    (let ((choices-window (get-buffer-window ispell-choices-buffer)))
-      (if choices-window
-         (if (= line (window-height choices-window))
-             (select-window choices-window)
-           ;; *Choices* window changed size.  Adjust the choices window
-           ;; without scrolling the spelled window when possible
-           (let ((window-line (- line (window-height choices-window)))
-                 (visible (progn (forward-line -1) (point))))
-             (if (< line ispell-choices-win-default-height)
-                 (setq window-line (+ window-line
-                                      (- ispell-choices-win-default-height
-                                         line))))
-             (move-to-window-line 0)
-             (forward-line window-line)
-             (set-window-start (selected-window)
-                               (if (> (point) visible) visible (point)))
-             (goto-char end)
-             (select-window (previous-window)) ; *Choices* window
-             (enlarge-window window-line)))
-       ;; Overlay *Choices* window when it isn't showing
-       (ispell-overlay-window (max line ispell-choices-win-default-height)))
-      (switch-to-buffer ispell-choices-buffer)
-      (goto-char (point-min)))
 
+    ;; allow temporary split of dedicated windows...
+    (if dedicated
+       (progn
+         (setq dedicated-win (selected-window))
+         (set-window-dedicated-p dedicated-win nil)))
+
+    ;; Display choices for misspelled word.
+    (ispell-show-choices line end)
     (select-window (setq textwin (next-window)))
 
     ;; highlight word, protecting current buffer status
     (unwind-protect
        (progn
-         (if ispell-highlight-p
-             (ispell-highlight-spelling-error start end t))
+         (and ispell-highlight-p
+              (ispell-highlight-spelling-error start end t))
          ;; Loop until a valid choice is made.
          (while
              (eq
@@ -1042,8 +1590,9 @@ used."
                result
                (progn
                  (undo-boundary)
-                 (message (concat "C-h or ? for more options; SPC to leave "
-                                  "unchanged, Character to replace word"))
+                 (let (message-log-max)
+                   (message (concat "C-h or ? for more options; SPC to leave "
+                                    "unchanged, Character to replace word")))
                  (let ((inhibit-quit t))
                    (setq char (if (fboundp 'read-char-exclusive)
                                   (read-char-exclusive)
@@ -1063,21 +1612,37 @@ used."
                  (cond
                   ((= char ? ) nil)    ; accept word this time only
                   ((= char ?i)         ; accept and insert word into pers dict
-                   (process-send-string ispell-process (concat "*" word "\n"))
+                   (ispell-send-string (concat "*" word "\n"))
                    (setq ispell-pdict-modified-p '(t)) ; dictionary modified!
                    nil)
                   ((or (= char ?a) (= char ?A)) ; accept word without insert
-                   (process-send-string ispell-process (concat "@" word "\n"))
+                   (ispell-send-string (concat "@" word "\n"))
                    (if (null ispell-pdict-modified-p)
                        (setq ispell-pdict-modified-p
                              (list ispell-pdict-modified-p)))
                    (if (= char ?A) 0)) ; return 0 for ispell-add buffer-local
                   ((or (= char ?r) (= char ?R)) ; type in replacement
-                   (if (or (= char ?R) ispell-query-replace-choices)
-                       (list (read-string "Query-replacement for: " word) t)
-                     (cons (read-string "Replacement for: " word) nil)))
+                   (and (eq 'block ispell-highlight-p) ; refresh tty's
+                        (ispell-highlight-spelling-error start end nil t))
+                   (let ((result
+                          (if (or (= char ?R) ispell-query-replace-choices)
+                              (list (read-string
+                                     (format "Query-replacement for %s: "word)
+                                     word)
+                                    t)
+                            (cons (read-string "Replacement for: " word)
+                                  nil))))
+                     (and (eq 'block ispell-highlight-p)
+                          (ispell-highlight-spelling-error start end nil
+                                                           'block))
+                     result))
                   ((or (= char ??) (= char help-char) (= char ?\C-h))
+                   (and (eq 'block ispell-highlight-p)
+                        (ispell-highlight-spelling-error start end nil t))
                    (ispell-help)
+                   (and (eq 'block ispell-highlight-p)
+                        (ispell-highlight-spelling-error start end nil
+                                                         'block))
                    t)
                   ;; Quit and move point back.
                   ((= char ?x)
@@ -1088,12 +1653,11 @@ used."
                   ;; Quit and preserve point.
                   ((= char ?X)
                    (ispell-pdict-save ispell-silently-savep)
-                   (message
+                   (message "%s"
                     (substitute-command-keys
                      (concat "Spell-checking suspended;"
                              " use C-u \\[ispell-word] to resume")))
-                   (setq ispell-quit (max (point-min)
-                                          (- (point) (length word))))
+                   (setq ispell-quit start)
                    nil)
                   ((= char ?q)
                    (if (y-or-n-p "Really kill Ispell process? ")
@@ -1104,10 +1668,11 @@ used."
                                ispell-pdict-modified-p nil))
                      t))               ; continue if they don't quit.
                   ((= char ?l)
+                   (and (eq 'block ispell-highlight-p) ; refresh tty displays
+                        (ispell-highlight-spelling-error start end nil t))
                    (let ((new-word (read-string
                                     "Lookup string (`*' is wildcard): "
-                                    word))
-                         (new-line 2))
+                                    word)))
                      (if new-word
                          (progn
                            (save-excursion
@@ -1120,7 +1685,8 @@ used."
                                                      "--  %b  --  word: "
                                                      new-word)
                                    miss (lookup-words new-word)
-                                   choices miss)
+                                   choices miss
+                                   line (ispell-choices-win-default-height))
                              (while (and choices ; adjust choices window.
                                          (< (if (> (+ 7 (current-column)
                                                       (length (car choices))
@@ -1128,43 +1694,30 @@ used."
                                                    (window-width))
                                                 (progn
                                                   (insert "\n")
-                                                  (setq new-line
-                                                        (1+ new-line)))
-                                              new-line)
+                                                  (setq line (1+ line)))
+                                              line)
                                             max-lines))
                                (while (memq count command-characters)
-                                 (setq count (1+ count)
+                                 (setq count (ispell-int-char (1+ count))
                                        skipped (1+ skipped)))
                                (insert "(" count ") " (car choices) "  ")
                                (setq choices (cdr choices)
-                                     count (1+ count)))
-                             (setq count (- count ?0 skipped)))
-                           (select-window (previous-window))
-                           (if (and (/= new-line line)
-                                    (> (max line new-line)
-                                       ispell-choices-win-default-height))
-                               (let* ((minh ispell-choices-win-default-height)
-                                      (gr-bl (if (< line minh) ; blanks
-                                                 (- minh line)
-                                               0))
-                                      (shr-bl (if (< new-line minh) ; blanks
-                                                  (- minh new-line)
-                                                0)))
-                                 (if (> new-line line)
-                                     (enlarge-window (- new-line line gr-bl))
-                                   (shrink-window (- line new-line shr-bl)))
-                                 (setq line new-line)))
+                                     count (ispell-int-char (1+ count))))
+                             (setq count (ispell-int-char
+                                          (- count ?0 skipped))))
+                           (ispell-show-choices line end)
                            (select-window (next-window)))))
+                   (and (eq 'block ispell-highlight-p)
+                        (ispell-highlight-spelling-error start end nil
+                                                         'block))
                    t)                  ; reselect from new choices
-                  ((= char ?u)
-                   (process-send-string ispell-process
-                                        (concat "*" (downcase word) "\n"))
+                  ((= char ?u)         ; insert lowercase into dictionary
+                   (ispell-send-string (concat "*" (downcase word) "\n"))
                    (setq ispell-pdict-modified-p '(t)) ; dictionary modified!
                    nil)
                   ((= char ?m)         ; type in what to insert
-                   (process-send-string
-                    ispell-process (concat "*" (read-string "Insert: " word)
-                                           "\n"))
+                   (ispell-send-string
+                    (concat "*" (read-string "Insert: " word) "\n"))
                    (setq ispell-pdict-modified-p '(t))
                    (cons word nil))
                   ((and (>= num 0) (< num count))
@@ -1174,17 +1727,82 @@ used."
                   ((= char ?\C-l)
                    (redraw-display) t)
                   ((= char ?\C-r)
-                   (save-window-excursion (recursive-edit)) t)
+                   ;; This may have alignment errors if current line is edited
+                   (if (marker-position ispell-recursive-edit-marker)
+                       (progn
+                         (message "Only one recursive edit session supported")
+                         (beep)
+                         (sit-for 2))
+                     (set-marker ispell-recursive-edit-marker start)
+                     ;;(set-marker ispell-region-end reg-end)
+                     (and ispell-highlight-p           ; unhighlight
+                          (ispell-highlight-spelling-error start end))
+                     (unwind-protect
+                         (progn
+                           (message
+                            "%s"
+                            (substitute-command-keys
+                             (concat "Exit recursive edit with"
+                                     " \\[exit-recursive-edit]")))
+                           (save-window-excursion (save-excursion
+                                                    (recursive-edit))))
+                       ;; protected
+                       (goto-char ispell-recursive-edit-marker)
+                       (if (not (equal (marker-buffer
+                                        ispell-recursive-edit-marker)
+                                       (current-buffer)))
+                           (progn
+                             (set-marker ispell-recursive-edit-marker nil)
+                             (error
+                              "Cannot continue ispell from this buffer.")))
+                       (set-marker ispell-recursive-edit-marker nil)))
+                   (list word nil))    ; recheck starting at this word.
                   ((= char ?\C-z)
                    (funcall (key-binding "\C-z"))
                    t)
                   (t (ding) t))))))
          result)
       ;; protected
-      (if ispell-highlight-p           ; unhighlight
-         (save-window-excursion
-           (select-window textwin)
-           (ispell-highlight-spelling-error start end))))))
+      (and ispell-highlight-p          ; unhighlight
+          (save-window-excursion
+            (select-window textwin)
+            (ispell-highlight-spelling-error start end)))
+      (if dedicated
+         (set-window-dedicated-p dedicated-win t)))))
+
+
+
+(defun ispell-show-choices (line end)
+  "Shows the choices in another buffer or frame."
+  (if (and ispell-use-framepop-p (fboundp 'framepop-display-buffer))
+      (progn
+       (framepop-display-buffer (get-buffer ispell-choices-buffer))
+       (get-buffer-window ispell-choices-buffer t)
+       (select-window (previous-window))) ; *Choices* window
+    ;; standard selection by splitting a small buffer out of this window.
+    (let ((choices-window (get-buffer-window ispell-choices-buffer)))
+      (if choices-window
+         (if (= line (window-height choices-window))
+             (select-window choices-window)
+           ;; *Choices* window changed size.  Adjust the choices window
+           ;; without scrolling the spelled window when possible
+           (let ((window-line (- line (window-height choices-window)))
+                 (visible (progn (vertical-motion -1) (point))))
+             (if (< line (ispell-choices-win-default-height))
+                 (setq window-line (+ window-line
+                                      (- (ispell-choices-win-default-height)
+                                         line))))
+             (move-to-window-line 0)
+             (vertical-motion window-line)
+             (set-window-start (selected-window)
+                               (if (> (point) visible) visible (point)))
+             (goto-char end)
+             (select-window (previous-window)) ; *Choices* window
+             (enlarge-window window-line)))
+       ;; Overlay *Choices* window when it isn't showing
+       (ispell-overlay-window (max line (ispell-choices-win-default-height))))
+      (switch-to-buffer ispell-choices-buffer)
+      (goto-char (point-min)))))
 
 
 ;;;###autoload
@@ -1207,46 +1825,72 @@ SPC:   Accept word this time.
 `q':   Quit spelling session (Kills ispell process).
 `l':   Look up typed-in replacement in alternate dictionary.  Wildcards okay.
 `u':   Like `i', but the word is lower-cased first.
-`m':   Like `i', but allows one to include dictionary completion information.
+`m':   Place typed-in value in personal dictionary, then recheck current word.
 `C-l':  redraws screen
 `C-r':  recursive edit
 `C-z':  suspend emacs or iconify frame"
 
-  (let ((help-1 (concat "[r/R]eplace word; [a/A]ccept for this session; "
-                       "[i]nsert into private dictionary"))
-       (help-2 (concat "[l]ook a word up in alternate dictionary;  "
-                       "e[x/X]it;  [q]uit session"))
-       (help-3 (concat "[u]ncapitalized insert into dictionary.  "
-                       "Type 'C-h d ispell-help' for more help")))
-    (save-window-excursion
-      (if ispell-help-in-bufferp
-         (progn
-           (ispell-overlay-window 4)
-           (switch-to-buffer (get-buffer-create "*Ispell Help*"))
-           (insert (concat help-1 "\n" help-2 "\n" help-3))
-           (sit-for 5)
-           (kill-buffer "*Ispell Help*"))
-       (select-window (minibuffer-window))
-       ;;(enlarge-window 2)
-       (erase-buffer)
-       (cond ((string-match "Lucid" emacs-version)
-              (message help-3)
-              (enlarge-window 1)
-              (message help-2)
-              (enlarge-window 1)
-              (message help-1)
-              (goto-char (point-min)))
-             (t
-              (if (string-lessp "19" emacs-version)
-                  (message nil))
-              (enlarge-window 2)
-              (insert (concat help-1 "\n" help-2 "\n" help-3))))
-       (sit-for 5)
-       (erase-buffer)))))
+  (if (equal ispell-help-in-bufferp 'electric)
+      (progn
+       (require 'ehelp)
+       (with-electric-help
+        (function (lambda ()
+                    ;;This shouldn't be necessary: with-electric-help needs
+                    ;; an optional argument telling it about the smallest
+                    ;; acceptable window-height of the help buffer.
+                    (if (< (window-height) 15)
+                        (enlarge-window (- 15 (window-height))))
+                    (princ "Selections are:
+
+DIGIT: Replace the word with a digit offered in the *Choices* buffer.
+SPC:   Accept word this time.
+`i':   Accept word and insert into private dictionary.
+`a':   Accept word for this session.
+`A':   Accept word and place in `buffer-local dictionary'.
+`r':   Replace word with typed-in value.  Rechecked.
+`R':   Replace word with typed-in value. Query-replaced in buffer. Rechecked.
+`?':   Show these commands.
+`x':   Exit spelling buffer.  Move cursor to original point.
+`X':   Exit spelling buffer.  Leaves cursor at the current point, and permits
+        the aborted check to be completed later.
+`q':   Quit spelling session (Kills ispell process).
+`l':   Look up typed-in replacement in alternate dictionary.  Wildcards okay.
+`u':   Like `i', but the word is lower-cased first.
+`m':   Place typed-in value in personal dictionary, then recheck current word.
+`C-l':  redraws screen
+`C-r':  recursive edit
+`C-z':  suspend emacs or iconify frame")
+                    nil        ;undocumented requirement of with-electric-help
+                    ))))
+
+
+    (let ((help-1 (concat "[r/R]eplace word; [a/A]ccept for this session; "
+                         "[i]nsert into private dictionary"))
+         (help-2 (concat "[l]ook a word up in alternate dictionary;  "
+                         "e[x/X]it;  [q]uit session"))
+         (help-3 (concat "[u]ncapitalized insert into dict.  "
+                         "Type 'x C-h d ispell-help' for more help")))
+      (save-window-excursion
+       (if ispell-help-in-bufferp
+           (progn
+             (ispell-overlay-window
+              (+ 4 (ispell-mode-line-window-height-fudge)))
+             (switch-to-buffer (get-buffer-create "*Ispell Help*"))
+             (insert (concat help-1 "\n" help-2 "\n" help-3))
+             (sit-for 5)
+             (kill-buffer "*Ispell Help*"))
+         (select-window (minibuffer-window))
+         (erase-buffer)
+         (if (not version18p) (message nil))
+         ;;(set-minibuffer-window (selected-window))
+         (enlarge-window 2)
+         (insert (concat help-1 "\n" help-2 "\n" help-3))
+         (sit-for 5)
+         (erase-buffer))))))
 
 
 (defun lookup-words (word &optional lookup-dict)
-  "Look up word in word-list dictionary.
+  "Look up WORD in optional word-list dictionary LOOKUP-DICT.
 A `*' serves as a wild card.  If no wild cards, `look' is used if it exists.
 Otherwise the variable `ispell-grep-command' contains the command used to
 search for the words (usually egrep).
@@ -1291,7 +1935,8 @@ Optional second argument contains the dictionary to use; the default is
            (while (not (bobp))
              (setq loc (point))
              (forward-line -1)
-             (setq results (cons (buffer-substring (point) (1- loc))
+             (setq results (cons (buffer-substring-no-properties (point)
+                                                                 (1- loc))
                                  results)))))
       ;; protected
       (kill-buffer ispell-grep-buffer)
@@ -1307,7 +1952,7 @@ Optional second argument contains the dictionary to use; the default is
 ;;;   multiple lines.
 ;;; "ispell-filter-continue" is true when we have received only part of a
 ;;;   line as output from a generating function ("output" did not end with \n)
-;;; NOTE THAT THIS FUNCTION WILL FAIL IF THE PROCESS OUTPUT DOESNT END WITH \n!
+;;; THIS FUNCTION WILL FAIL IF THE PROCESS OUTPUT DOESN'T END WITH \n!
 ;;;   This is the case when a process dies or fails. The default behavior
 ;;;   in this case treats the next input received as fresh input.
 
@@ -1343,27 +1988,42 @@ Optional second argument contains the dictionary to use; the default is
 
 ;;; This function destroys the mark location if it is in the word being
 ;;; highlighted.
-(defun ispell-highlight-spelling-error-generic (start end &optional highlight)
+(defun ispell-highlight-spelling-error-generic (start end &optional highlight
+                                                     refresh)
   "Highlight the word from START to END with a kludge using `inverse-video'.
 When the optional third arg HIGHLIGHT is set, the word is highlighted;
-otherwise it is displayed normally."
+otherwise it is displayed normally.
+Uses block cursor to highlight one character.
+Optional REFRESH will unhighlighted then highlight, using block cursor
+ highlighting when REFRESH is equal to `block'."
+  (and (eq 'block ispell-highlight-p)
+       (or (eq 'block refresh)
+          (setq start (1+ start))))    ; On block non-refresh, inc start.
   (let ((modified (buffer-modified-p)) ; don't allow this fn to modify buffer
        (buffer-read-only nil)          ; Allow highlighting read-only buffers.
-       (text (buffer-substring start end)) ; Save highlight region
+       (text (buffer-substring-no-properties start end)) ; Save hilight region
        (inhibit-quit t)                ; inhibit interrupt processing here.
        (buffer-undo-list t))           ; don't clutter the undo list.
+    (goto-char end)
     (delete-region start end)
-    (insert-char ?  (- end start))     ; mimimize amount of redisplay
+    (insert-char ?  (- end start))     ; minimize amount of redisplay
     (sit-for 0)                                ; update display
     (if highlight (setq inverse-video (not inverse-video))) ; toggle video
     (delete-region start end)          ; delete whitespace
     (insert text)                      ; insert text in inverse video.
     (sit-for 0)                                ; update display showing inverse video.
-    (if highlight (setq inverse-video (not inverse-video))) ; toggle video
-    (set-buffer-modified-p modified))) ; don't modify if flag not set.
-
-
-(defun ispell-highlight-spelling-error-lucid (start end &optional highlight)
+    (if (not highlight)
+       (goto-char end)
+      (setq inverse-video (not inverse-video)) ; toggle video
+      (and (eq 'block ispell-highlight-p)
+          (goto-char (1- start))))     ; use block cursor to "highlight" char
+    (set-buffer-modified-p modified)   ; don't modify if flag not set.
+    (and refresh                       ; re-highlight
+        (ispell-highlight-spelling-error-generic
+         (if (eq 'block refresh) start (- start 2)) end t))))
+
+
+(defun ispell-highlight-spelling-error-xemacs (start end &optional highlight)
   "Highlight the word from START to END using `isearch-highlight'.
 When the optional third arg HIGHLIGHT is set, the word is highlighted,
 otherwise it is displayed normally."
@@ -1387,14 +2047,16 @@ The variable `ispell-highlight-face' selects the face to use for highlighting."
     (delete-overlay ispell-overlay)))
 
 
-(defun ispell-highlight-spelling-error (start end &optional highlight)
+(defun ispell-highlight-spelling-error (start end &optional highlight refresh)
   (cond
-   ((string-match "Lucid" emacs-version)
-    (ispell-highlight-spelling-error-lucid start end highlight))
-   ((and (string-lessp "19" emacs-version)
-        (featurep 'faces) window-system)
+   (xemacsp
+    (ispell-highlight-spelling-error-xemacs start end highlight))
+   ((and (not version18p)
+        (featurep 'faces)
+        (or (and (fboundp 'display-color-p) (display-color-p))
+            window-system))
     (ispell-highlight-spelling-error-overlay start end highlight))
-   (t (ispell-highlight-spelling-error-generic start end highlight))))
+   (t (ispell-highlight-spelling-error-generic start end highlight refresh))))
 
 
 (defun ispell-overlay-window (height)
@@ -1404,36 +2066,45 @@ scrolling the current window.  Leave the new window selected."
   (save-excursion
     (let ((oldot (save-excursion (forward-line -1) (point)))
          (top (save-excursion (move-to-window-line height) (point))))
-      ;; If line above old point (line starting at olddot) would be
+      ;; If line above old point (line starting at oldot) would be
       ;; hidden by new window, scroll it to just below new win
       ;; otherwise set top line of other win so it doesn't scroll.
       (if (< oldot top) (setq top oldot))
-      ;; NB: Lemacs 19.9 bug: If a window of size N (N includes the mode
-      ;; line) is demanded, the last line is not visible.
-      ;; At least this happens on AIX 3.2, lemacs w/ Motif, font 9x15.
-      ;; So we increment the height for this case.
-      (if (string-match "19\.9.*Lucid" (emacs-version))
-         (setq height (1+ height)))
-      (split-window nil height)
+      ;; if frame is unsplitable, temporarily disable that...
+      (if (cdr (assq 'unsplittable (frame-parameters (selected-frame))))
+         (let ((frame (selected-frame)))
+           (modify-frame-parameters frame '((unsplittable . nil)))
+           (split-window nil height)
+           (modify-frame-parameters frame '((unsplittable . t))))
+       (split-window nil height))
       (set-window-start (next-window) top))))
 
 
 ;;; Should we add a compound word match return value?
-(defun ispell-parse-output (output)
-  "Parse the OUTPUT string from Ispell and return:
+(defun ispell-parse-output (output &optional accept-list shift)
+  "Parse the OUTPUT string from Ispell process and return:
 1: t for an exact match.
-2: A string containing the root word for a match via suffix removal.
+2: A string containing the root word matched via suffix removal.
 3: A list of possible correct spellings of the format:
-   '(\"ORIGINAL-WORD\" OFFSET MISS-LIST GUESS-LIST)
+   (\"ORIGINAL-WORD\" OFFSET MISS-LIST GUESS-LIST)
    ORIGINAL-WORD is a string of the possibly misspelled word.
    OFFSET is an integer giving the line offset of the word.
-   MISS-LIST and GUESS-LIST are possibly null lists of guesses and misses."
+   MISS-LIST and GUESS-LIST are possibly null lists of guesses and misses.
+4: Nil when an error has occurred.
+
+Optinal second arg ACCEPT-LIST is list of words already accepted.
+Optional third arg SHIFT is an offset to apply based on previous corrections."
   (cond
    ((string= output "") t)             ; for startup with pipes...
    ((string= output "*") t)            ; exact match
-   ((string= output "-") t)             ; compound word match
-   ((string= (substring output 0 1) "+") ; found cuz of root word
+   ((string= output "-") t)            ; compound word match
+   ((string= (substring output 0 1) "+") ; found because of root word
     (substring output 2))              ; return root word
+   ((equal 0 (string-match "[\ra-zA-Z]" output))
+    (ding)                             ; error message from ispell!
+    (message (concat "Ispell error: " output))
+    (sit-for 5)
+    nil)
    (t                                  ; need to process &, ?, and #'s
     (let ((type (substring output 0 1))        ; &, ?, or #
          (original-word (substring output 2 (string-match " " output 2)))
@@ -1457,50 +2128,61 @@ scrolling the current window.  Leave the new window selected."
          (if (match-end 1)             ; True only when at end of line.
              (setq output nil)         ; no more misses or guesses
            (setq output (substring output (+ end 2))))))
-      (list original-word offset miss-list guess-list)))))
+      ;; return results.  Accept word if it was already accepted.
+      ;; adjust offset.
+      (if (member original-word accept-list)
+         t
+       (list original-word
+             (if (numberp shift) (+ shift offset) offset)
+             (nreverse miss-list) (nreverse guess-list)))))))
+
+
+(defun ispell-process-status ()
+  "Return the status of the Ispell process.
+When asynchronous processes are not supported, `run' is always returned."
+  (if ispell-async-processp
+      (process-status ispell-process)
+    (and ispell-process 'run)))
+
+
+(defun ispell-start-process ()
+  "Start the ispell process, with support for no asynchronous processes.
+Keeps argument list for future ispell invocations for no async support."
+  (let (args)
+    ;; Local dictionary becomes the global dictionary in use.
+    (if ispell-local-dictionary
+       (setq ispell-dictionary ispell-local-dictionary))
+    (setq args (ispell-get-ispell-args))
+    (if ispell-dictionary              ; use specified dictionary
+       (setq args
+             (append (list "-d" ispell-dictionary) args)))
+    (if ispell-personal-dictionary     ; use specified pers dict
+       (setq args
+             (append args
+                     (list "-p"
+                           (expand-file-name ispell-personal-dictionary)))))
+    (setq args (append args ispell-extra-args))
+
+    (if ispell-async-processp
+       (let ((process-connection-type ispell-use-ptys-p))
+         (apply 'start-process
+                "ispell" nil ispell-program-name
+                "-a"                   ; accept single input lines
+                "-m"                   ; make root/affix combos not in dict
+                args))
+      (setq ispell-cmd-args args
+           ispell-output-buffer (generate-new-buffer " *ispell-output*")
+           ispell-session-buffer (generate-new-buffer " *ispell-session*"))
+      (ispell-send-string "\032\n")    ; so Ispell prints version and exits
+      t)))
 
 
-(defun check-ispell-version ()
-  ;; This is a little wasteful as we actually launch ispell twice: once
-  ;; to make sure it's the right version, and once for real.  But people
-  ;; get confused by version mismatches *all* the time (and I've got the
-  ;; email to prove it) so I think this is worthwhile.  And the -v[ersion]
-  ;; option is the only way I can think of to do this that works with
-  ;; all versions, since versions earlier than 3.0.09 didn't identify
-  ;; themselves on startup.
-  (save-excursion
-    (let ((case-fold-search t)
-         ;; avoid bugs when syntax of `.' changes in various default modes
-         (default-major-mode 'fundamental-mode)
-         status)
-      (set-buffer (get-buffer-create " *ispell-tmp*"))
-      (erase-buffer)
-      (setq status (call-process ispell-program-name nil t nil "-v"))
-      (goto-char (point-min))
-      (if (not (memq status '(0 nil)))
-         (error "%s exited with %s %s" ispell-program-name
-                (if (stringp status) "signal" "code") status))
-      (if (not (re-search-forward
-               (concat "\\b\\("
-                       (regexp-quote (car ispell-required-version))
-                       "\\)\\([0-9]*\\)\\b")
-               nil t))
-         (error
-          "%s version %s* is required: try renaming ispell4.el to ispell.el"
-          ispell-program-name (car ispell-required-version))
-       ;; check that it is the correct version.
-       (if (< (car (read-from-string (buffer-substring
-                                      (match-beginning 2) (match-end 2))))
-              (car (cdr ispell-required-version)))
-           (setq ispell-offset 0)))
-      (kill-buffer (current-buffer)))))
-
 
 (defun ispell-init-process ()
   "Check status of Ispell process and start if necessary."
   (if (and ispell-process
-          (eq (process-status ispell-process) 'run)
-          ;; If we're using a personal dictionary, assure
+          (eq (ispell-process-status) 'run)
+          ;; If we're using a personal dictionary, ensure
           ;; we're in the same default directory!
           (or (not ispell-personal-dictionary)
               (equal ispell-process-directory default-directory)))
@@ -1510,40 +2192,34 @@ scrolling the current window.  Leave the new window selected."
     (message "Starting new Ispell process...")
     (sit-for 0)
     (check-ispell-version)
-    (setq ispell-process
-         (let ((process-connection-type ispell-use-ptys-p))
-           (apply 'start-process
-                  "ispell" nil ispell-program-name
-                  "-a"                 ; accept single input lines
-                  "-m"                 ; make root/affix combos not in dict
-                  (let (args)
-                    ;; Local dictionary becomes the global dictionary in use.
-                    (if ispell-local-dictionary
-                        (setq ispell-dictionary ispell-local-dictionary))
-                    (setq args (ispell-get-ispell-args))
-                    (if ispell-dictionary ; use specified dictionary
-                        (setq args
-                              (append (list "-d" ispell-dictionary) args)))
-                    (if ispell-personal-dictionary ; use specified pers dict
-                        (setq args
-                              (append args
-                                      (list "-p"
-                                            (expand-file-name
-                                             ispell-personal-dictionary)))))
-                    (setq args (append args ispell-extra-args))
-                    args)))
+    (setq ispell-process-directory default-directory
+         ispell-process (ispell-start-process)
          ispell-filter nil
-         ispell-filter-continue nil
-         ispell-process-directory default-directory)
-    (set-process-filter ispell-process 'ispell-filter)
-    (accept-process-output ispell-process) ; Get version ID line
+         ispell-filter-continue nil)
+    (if ispell-async-processp
+       (set-process-filter ispell-process 'ispell-filter))
+    ;; protect against bogus binding of `enable-multibyte-characters' in XEmacs
+    (if (and (or xemacsp
+                (and (boundp 'enable-multibyte-characters)
+                     enable-multibyte-characters))
+            (fboundp 'set-process-coding-system))
+       (set-process-coding-system ispell-process (ispell-get-coding-system)
+                                  (ispell-get-coding-system)))
+    ;; Get version ID line
+    (if (not version18p)
+       (ispell-accept-output 3)
+      (ispell-accept-output))
+    ;; get more output if filter empty?
+    (if (null ispell-filter) (ispell-accept-output 3))
     (cond ((null ispell-filter)
           (error "%s did not output version line" ispell-program-name))
          ((and
            (stringp (car ispell-filter))
            (if (string-match "warning: " (car ispell-filter))
                (progn
-                 (accept-process-output ispell-process 5) ; 1st was warn msg.
+                 (if (not version18p)
+                     (ispell-accept-output 3) ; was warn msg.
+                   (ispell-accept-output))
                  (stringp (car ispell-filter)))
              (null (cdr ispell-filter)))
            (string-match "^@(#) " (car ispell-filter)))
@@ -1555,14 +2231,14 @@ scrolling the current window.  Leave the new window selected."
           ;; But first wait to see if some more output is going to arrive.
           ;; Otherwise we get cool errors like "Can't open ".
           (sleep-for 1)
-          (accept-process-output)
+          (ispell-accept-output 3)
           (error "%s" (mapconcat 'identity ispell-filter "\n"))))
     (setq ispell-filter nil)           ; Discard version ID line
     (let ((extended-char-mode (ispell-get-extended-character-mode)))
-      (if extended-char-mode
-         (process-send-string ispell-process
-                              (concat extended-char-mode "\n"))))
-    (process-kill-without-query ispell-process)))
+      (if extended-char-mode           ; ~ extended character mode
+         (ispell-send-string (concat extended-char-mode "\n"))))
+    (if ispell-async-processp
+       (process-kill-without-query ispell-process))))
 
 ;;;###autoload
 (defun ispell-kill-ispell (&optional no-error)
@@ -1570,10 +2246,26 @@ scrolling the current window.  Leave the new window selected."
 With NO-ERROR, just return non-nil if there was no Ispell running."
   (interactive)
   (if (not (and ispell-process
-               (eq (process-status ispell-process) 'run)))
+               (eq (ispell-process-status) 'run)))
       (or no-error
          (error "There is no ispell process running!"))
-    (kill-process ispell-process)
+    (if ispell-async-processp
+       (progn
+         (process-send-eof ispell-process)
+         (if (eq (ispell-process-status) 'run)
+             (ispell-accept-output 1))
+         (if (eq (ispell-process-status) 'run)
+             (kill-process ispell-process))
+         (while (not (or (eq (ispell-process-status) 'exit)
+                         (eq (ispell-process-status) 'signal)))
+           (if (or xemacsp version20p) (sleep-for 0.25)
+             (sleep-for 0 250))))
+      ;; synchronous processes
+      (ispell-send-string "\n")                ; make sure side effects occurred.
+      (kill-buffer ispell-output-buffer)
+      (kill-buffer ispell-session-buffer)
+      (setq ispell-output-buffer nil
+           ispell-session-buffer nil))
     (setq ispell-process nil)
     (message "Ispell process killed")
     nil))
@@ -1584,7 +2276,7 @@ With NO-ERROR, just return non-nil if there was no Ispell running."
 
 ;;;###autoload
 (defun ispell-change-dictionary (dict &optional arg)
-  "Change `ispell-dictionary' (q.v.) and kill old Ispell process.
+  "Change `ispell-dictionary' (q.v.) to DICT and kill old Ispell process.
 A new one will be started as soon as necessary.
 
 By just answering RET you can find out what the current dictionary is.
@@ -1613,7 +2305,7 @@ With prefix argument, set the default directory."
                   (setq ispell-dictionary dict))
               (if (null arg)           ; set local dictionary
                   (setq ispell-local-dictionary dict)))
-          (error "Illegal dictionary: %s" dict))
+          (error "Undefined dictionary: %s" dict))
         (ispell-kill-ispell t)
         (message "(Next %sIspell command will use %s dictionary)"
                  (cond ((equal ispell-local-dictionary ispell-dictionary)
@@ -1630,10 +2322,13 @@ With prefix argument, set the default directory."
 ;;; Spelling of comments are checked when ispell-check-comments is non-nil.
 
 ;;;###autoload
-(defun ispell-region (reg-start reg-end)
-  "Interactively check a region for spelling errors."
+(defun ispell-region (reg-start reg-end &optional recheckp shift)
+  "Interactively check a region for spelling errors.
+Return nil if spell session is quit,
+ otherwise returns shift offset amount for last line processed."
   (interactive "r")                    ; Don't flag errors on read-only bufs.
-  (ispell-accept-buffer-local-defs)    ; set up dictionary, local words, etc.
+  (if (not recheckp)
+      (ispell-accept-buffer-local-defs)) ; set up dictionary, local words, etc.
   (unwind-protect
       (save-excursion
        (message "Spell checking %s using %s dictionary..."
@@ -1643,238 +2338,420 @@ With prefix argument, set the default directory."
        ;; Returns cursor to original location.
        (save-window-excursion
          (goto-char reg-start)
-         (let ((transient-mark-mode nil)
-               ref-type)
-           (while (and (not ispell-quit) (< (point) reg-end))
-             (let ((start (point))
-                   (offset-change 0)
-                   (end (save-excursion (end-of-line) (min (point) reg-end)))
-                   (ispell-casechars (ispell-get-casechars))
-                   string)
-               (cond                   ; LOOK AT THIS LINE AND SKIP OR PROCESS
-                ((eolp)                ; END OF LINE, just go to next line.
-                 (forward-char 1))
-                ((and (null ispell-check-comments) ; SKIPPING COMMENTS
-                      comment-start    ; skip comments that start on the line.
-                      (search-forward comment-start end t)) ; or found here.
-                 (if (= (- (point) start) (length comment-start))
-                     ;; comment starts the line.  Skip entire line or region
-                     (if (string= "" comment-end) ; skip to next line
-                         (beginning-of-line 2) ; or jump to comment end.
-                       (search-forward comment-end reg-end 'limit))
-                   ;; Comment later in line.  Check spelling before comment.
-                   (let ((limit (- (point) (length comment-start))))
-                     (goto-char (1- limit))
-                     (if (looking-at "\\\\") ; "quoted" comment, don't skip
-                         ;; quoted comment.  Skip over comment-start
-                         (if (= start (1- limit))
-                             (setq limit (+ limit (length comment-start)))
-                           (setq limit (1- limit))))
-                     (goto-char start)
-                     ;; Only check when "casechars" or math before comment
-                     (if (or (re-search-forward ispell-casechars limit t)
-                             (re-search-forward "[][()$]" limit t))
-                         (setq string
-                               (concat "^" (buffer-substring start limit)
-                                       "\n")
-                               offset-change (- offset-change ispell-offset)))
-                     (goto-char limit))))
-                ((looking-at "[---#@*+!%~^]") ; SKIP SPECIAL ISPELL CHARACTERS
-                 (forward-char 1))
-                ((or (and ispell-skip-tib ; SKIP TIB REFERENCES OR SGML MARKUP
-                          (re-search-forward ispell-tib-ref-beginning end t)
-                          (setq ref-type 'tib))
-                     (and ispell-skip-sgml
-                          (search-forward "[<&]" end t)
-                          (setq ref-type 'sgml)))
-                 (if (or (and (eq 'tib ref-type) ; tib tag is 2 chars.
-                              (= (- (point) 2) start))
-                         (and (eq 'sgml ref-type) ; sgml skips 1 char.
-                              (= (- (point) 1) start)))
-                     ;; Skip to end of reference, not necessarily on this line
-                     ;; Return an error if tib/sgml reference not found
-                     (if (or
-                          (and
-                           (eq 'tib ref-type)
-                           (not
-                            (re-search-forward ispell-tib-ref-end reg-end t)))
-                          (and (eq 'sgml ref-type)
-                               (not (search-forward "[>;]" reg-end t))))
-                         (progn
-                           (ispell-pdict-save ispell-silently-savep)
-                           (ding)
-                           (message
-                            (concat
-                             "Open tib or SGML command.  Fix buffer or set "
-                             (if (eq 'tib ref-type)
-                                 "ispell-skip-tib"
-                               "ispell-skip-sgml")
-                             " to nil"))
-                           ;; keep cursor at error location
-                           (setq ispell-quit (- (point) 2))))
-                   ;; Check spelling between reference and start of the line.
-                   (let ((limit (- (point) (if (eq 'tib ref-type) 2 1))))
-                     (goto-char start)
-                     (if (or (re-search-forward ispell-casechars limit t)
-                             (re-search-forward "[][()$]" limit t))
-                         (setq string
-                               (concat "^" (buffer-substring start limit)
-                                       "\n")
-                               offset-change (- offset-change ispell-offset)))
-                     (goto-char limit))))
-                ((or (re-search-forward ispell-casechars end t) ; TEXT EXISTS
-                     (re-search-forward "[][()$]" end t)) ; or MATH COMMANDS
-                 (setq string (concat "^" (buffer-substring start end) "\n")
-                       offset-change (- offset-change ispell-offset))
-                 (goto-char end))
-                (t (beginning-of-line 2))) ; EMPTY LINE, skip it.
-
-               (setq end (point))      ; "end" tracks end of region to check.
-
-               (if string              ; there is something to spell!
-                   (let (poss)
-                     ;; send string to spell process and get input.
-                     (process-send-string ispell-process string)
-                     (while (progn
-                              (accept-process-output ispell-process)
-                              ;; Last item of output contains a blank line.
-                              (not (string= "" (car ispell-filter)))))
-                     ;; parse all inputs from the stream one word at a time.
-                     ;; Place in FIFO order and remove the blank item.
-                     (setq ispell-filter (nreverse (cdr ispell-filter)))
-                     (while (and (not ispell-quit) ispell-filter)
-                       (setq poss (ispell-parse-output (car ispell-filter)))
-                       (if (listp poss) ; spelling error occurred.
-                           (let* ((word-start (+ start offset-change
-                                                 (car (cdr poss))))
-                                  (word-end (+ word-start
-                                               (length (car poss))))
-                                  replace)
-                             (goto-char word-start)
-                             ;; Adjust the horizontal scroll & point
-                             (ispell-horiz-scroll)
-                             (goto-char word-end)
-                             (ispell-horiz-scroll)
-                             (goto-char word-start)
-                             (ispell-horiz-scroll)
-                             (if (/= word-end
-                                     (progn
-                                       (search-forward (car poss) word-end t)
-                                       (point)))
-                                 ;; This occurs due to filter pipe problems
-                                 (error
-                                  (concat "Ispell misalignment: word "
-                                          "`%s' point %d; please retry")
-                                  (car poss) word-start))
-                              (if (not (pos-visible-in-window-p))
-                                  (sit-for 0))
-                             (if ispell-keep-choices-win
-                                 (setq replace
-                                       (ispell-command-loop
-                                        (car (cdr (cdr poss)))
-                                        (car (cdr (cdr (cdr poss))))
-                                        (car poss) word-start word-end))
-                               (save-window-excursion
-                                 (setq replace
-                                       (ispell-command-loop
-                                        (car (cdr (cdr poss)))
-                                        (car (cdr (cdr (cdr poss))))
-                                        (car poss) word-start word-end))))
-                             (cond
-                              ((and replace (listp replace))
-                               ;; REPLACEMENT WORD entered.  Recheck line
-                               ;; starting with the replacement word.
-                               (setq ispell-filter nil
-                                     string (buffer-substring word-start
-                                                              word-end))
-                               (let ((change (- (length (car replace))
-                                                (length (car poss)))))
-                                 ;; adjust regions
-                                 (setq reg-end (+ reg-end change)
-                                       offset-change (+ offset-change
-                                                        change)))
-                               (if (not (equal (car replace) (car poss)))
-                                   (progn
-                                     (delete-region word-start word-end)
-                                     (insert (car replace))))
-                               ;; I only need to recheck typed-in replacements
-                               (if (not (eq 'query-replace
-                                            (car (cdr replace))))
-                                   (backward-char (length (car replace))))
-                               (setq end (point)) ; reposition for recheck
-                               ;; when second arg exists, query-replace, saving regions
-                               (if (car (cdr replace))
-                                   (unwind-protect
-                                       (save-window-excursion
-                                         (set-marker
-                                          ispell-query-replace-marker reg-end)
-                                         ;; Assume case-replace &
-                                         ;; case-fold-search correct?
-                                         (query-replace string (car replace)
-                                                        t))
-                                     (setq reg-end
-                                           (marker-position
-                                            ispell-query-replace-marker))
-                                     (set-marker ispell-query-replace-marker
-                                                 nil))))
-                              ((or (null replace)
-                                   (equal 0 replace)) ; ACCEPT/INSERT
-                               (if (equal 0 replace) ; BUFFER-LOCAL DICT ADD
-                                   (setq reg-end
-                                         (ispell-add-per-file-word-list
-                                          (car poss) reg-end)))
-                               ;; This avoids pointing out the word that was
-                               ;; just accepted (via 'i' or 'a') if it follows
-                               ;; on the same line.
-                               ;; Redo check following the accepted word.
-                               (if (and ispell-pdict-modified-p
-                                        (listp ispell-pdict-modified-p))
-                                   ;; Word accepted.  Recheck line.
-                                   (setq ispell-pdict-modified-p ; update flag
-                                         (car ispell-pdict-modified-p)
-                                         ispell-filter nil ; discontinue check
-                                         end word-start))) ; reposition loc.
-                              (replace ; STRING REPLACEMENT for this word.
-                               (delete-region word-start word-end)
-                               (insert replace)
-                               (let ((change (- (length replace)
-                                                (length (car poss)))))
-                                 (setq reg-end (+ reg-end change)
-                                       offset-change (+ offset-change change)
-                                       end (+ end change)))))
-                             (if (not ispell-quit)
-                                 (message
-                                  (concat "Continuing spelling check using "
-                                          (or ispell-dictionary "default")
-                                          " dictionary...")))
-                             (sit-for 0)))
-                       ;; finished with line!
-                       (setq ispell-filter (cdr ispell-filter)))))
+         (let ((transient-mark-mode)
+               (case-fold-search case-fold-search)
+               (skip-region-start (make-marker))
+               (skip-regexp (ispell-begin-skip-region-regexp))
+               (skip-alist ispell-skip-region-alist)
+               key)
+           (if (eq ispell-parser 'tex)
+               (setq case-fold-search nil
+                     skip-alist
+                     (append (car ispell-tex-skip-alists)
+                             (car (cdr ispell-tex-skip-alists))
+                             skip-alist)))
+           (let (message-log-max)
+             (message "searching for regions to skip"))
+           (if (re-search-forward skip-regexp reg-end t)
+               (progn
+                 (setq key (buffer-substring-no-properties
+                            (match-beginning 0) (match-end 0)))
+                 (set-marker skip-region-start (- (point) (length key)))
+                 (goto-char reg-start)))
+           (let (message-log-max)
+             (message "Continuing spelling check using %s dictionary..."
+                      (or ispell-dictionary "default")))
+           (set-marker ispell-region-end reg-end)
+           (while (and (not ispell-quit)
+                       (< (point) ispell-region-end))
+             ;; spell-check region with skipping
+             (if (and (marker-position skip-region-start)
+                      (<= skip-region-start (point)))
+                 (progn
+                   (ispell-skip-region key skip-alist) ; moves pt past region.
+                   (setq reg-start (point))
+                   (if (and (< reg-start ispell-region-end)
+                            (re-search-forward skip-regexp
+                                               ispell-region-end t))
+                       (progn
+                         (setq key (buffer-substring-no-properties
+                                    (car (match-data))
+                                    (car (cdr (match-data)))))
+                         (set-marker skip-region-start
+                                     (- (point) (length key)))
+                         (goto-char reg-start))
+                     (set-marker skip-region-start nil))))
+             (setq reg-end (if (marker-position skip-region-start)
+                               (min skip-region-start ispell-region-end)
+                             (marker-position ispell-region-end)))
+             (let* ((start (point))
+                    (end (save-excursion (end-of-line) (min (point) reg-end)))
+                    (string (ispell-get-line start end reg-end)))
+               (setq end (point))      ; "end" tracks region retrieved.
+               (if string              ; there is something to spell check!
+                   ;; (special start end)
+                   (setq shift (ispell-process-line string
+                                                    (and recheckp shift))))
                (goto-char end)))))
-       (not ispell-quit))
+       (if ispell-quit
+           nil
+         (or shift 0)))
     ;; protected
-    (if (get-buffer ispell-choices-buffer)
+    (if (and (not (and recheckp ispell-keep-choices-win))
+            (get-buffer ispell-choices-buffer))
        (kill-buffer ispell-choices-buffer))
     (if ispell-quit
        (progn
          ;; preserve or clear the region for ispell-continue.
          (if (not (numberp ispell-quit))
              (set-marker ispell-region-end nil)
-           ;; Enable ispell-continue.
-           (set-marker ispell-region-end reg-end)
+           ;; Ispell-continue enabled - ispell-region-end is set.
            (goto-char ispell-quit))
          ;; Check for aborting
          (if (and ispell-checking-message (numberp ispell-quit))
              (progn
                (setq ispell-quit nil)
-               (error "Message send aborted.")))
-         (setq ispell-quit nil))
-      (set-marker ispell-region-end nil)
+               (error "Message send aborted")))
+         (if (not recheckp) (setq ispell-quit nil)))
+      (if (not recheckp) (set-marker ispell-region-end nil))
       ;; Only save if successful exit.
       (ispell-pdict-save ispell-silently-savep)
       (message "Spell-checking done"))))
 
 
+;;; Creates the regexp for skipping a region.
+;;; Makes the skip-regexp local for tex buffers adding in the
+;;; tex expressions to skip as well.
+;;; Call AFTER ispell-buffer-local-parsing.
+(defun ispell-begin-skip-region-regexp ()
+  (let ((skip-regexp (ispell-begin-skip-region)))
+    (if (and (null ispell-check-comments) comment-start)
+       (setq skip-regexp (concat (regexp-quote comment-start) "\\|"
+                                 skip-regexp)))
+    (if (and (eq 'exclusive ispell-check-comments) comment-start)
+       (setq skip-regexp (concat (if (string= "" comment-end) "^"
+                                   (regexp-quote comment-end))
+                                 "\\|" skip-regexp)))
+    (if ispell-skip-tib
+       (setq skip-regexp (concat ispell-tib-ref-beginning "\\|" skip-regexp)))
+    (if ispell-skip-html
+       (setq skip-regexp (concat "<[cC][oO][dD][eE]\\>[^>]*>" "\\|"
+                                 "<[sS][cC][rR][iI][pP][tT]\\>[^>]*>" "\\|"
+                                 "<[aA][pP][pP][lL][eE][tT]\\>[^>]*>" "\\|"
+                                 "<[vV][eE][rR][bB]\\>[^>]*>" "\\|"
+                                 ;; "<[tT][tT]\\>[^>]*>" "\\|"
+                                 "<[tT][tT]/" "\\|"
+                                 "<" "\\|"
+                                 "&" "\\|"
+                                 skip-regexp)))
+    (if (eq ispell-parser 'tex)
+       (setq skip-regexp (concat (ispell-begin-tex-skip-regexp) "\\|"
+                                 skip-regexp)))
+    skip-regexp))
+
+
+(defun ispell-begin-tex-skip-regexp ()
+  "Regular expression of tex commands to skip.
+Generated from `ispell-tex-skip-alists'."
+  (concat
+   (mapconcat (function (lambda (lst) (car lst)))
+             (car ispell-tex-skip-alists)
+             "\\|")
+   "\\|"
+   (mapconcat (function (lambda (lst)
+                         (concat "\\\\begin[ \t\n]*{[ \t\n]*"
+                                 (car lst)
+                                 "[ \t\n]*}")))
+             (car (cdr ispell-tex-skip-alists))
+             "\\|")))
+
+
+(defun ispell-begin-skip-region ()
+  "Regular expression of regions to skip for all buffers.
+Each selection should be a key of `ispell-skip-region-alist';
+otherwise, the current line is skipped."
+  (mapconcat (function (lambda (lst) (if (stringp (car lst)) (car lst)
+                                       (eval (car lst)))))
+            ispell-skip-region-alist
+            "\\|"))
+
+
+(defun ispell-tex-arg-end (&optional arg)
+  (condition-case nil
+      (progn
+       (while (looking-at "[ \t\n]*\\[") (forward-sexp))
+       (forward-sexp (or arg 1)))
+    (error
+     (message "error skipping s-expressions at point %d." (point))
+     (beep)
+     (sit-for 2))))
+
+
+;;; Skips to region-end from point, or a single line.
+;;; Places point at end of region skipped.
+(defun ispell-skip-region (key alist)
+  ;; move over key to begin checking.
+  (forward-char (length key))
+  (let ((start (point))
+       alist-key null-skip)
+    (cond
+     ;; what about quoted comment, or comment inside strings?
+     ((and (null ispell-check-comments) comment-start
+          (string= key comment-start))
+      (if (string= "" comment-end)
+         (forward-line)
+       (search-forward comment-end ispell-region-end t)))
+     ((and (eq 'exclusive ispell-check-comments) comment-start
+          (string= key comment-end))
+      (search-forward comment-start ispell-region-end :end))
+     ((and ispell-skip-tib (string-match ispell-tib-ref-beginning key))
+      (re-search-forward ispell-tib-ref-end ispell-region-end t))
+     ((and ispell-skip-html (string-match "</" key))
+      (search-forward ">" ispell-region-end t))
+     ((and ispell-skip-html (string-match "<[cC][oO][dD][eE]\\>[^>]*>" key))
+      (search-forward-regexp "</[cC][oO][dD][eE]>" ispell-region-end t))
+     ((and ispell-skip-html
+          (string-match "<[sS][cC][rR][iI][pP][tT]\\>[^>]*>" key))
+      (search-forward-regexp "</[sS][cC][rR][iI][pP][tT]>" ispell-region-end t))
+     ((and ispell-skip-html
+          (string-match "<[aA][pP][pP][lL][eE][tT]\\>[^>]*>" key))
+      (search-forward-regexp "</[aA][pP][pP][lL][eE][tT]>" ispell-region-end t))
+     ((and ispell-skip-html (string-match "<[vV][eE][rR][bB]\\>[^>]*>" key))
+      (search-forward-regexp "</[vV][eE][rR][bB]>" ispell-region-end t))
+     ;;((and ispell-skip-html (string-match "<[tT][tT]\\>[^>]*>" key))
+     ;; (search-forward-regexp "</[tT][tT]>" ispell-region-end t))
+     ((and ispell-skip-html (string-match "<[tT][tT]/" key))
+      (search-forward "/" ispell-region-end t))
+     ((and ispell-skip-html (string-match "<" key))
+      (search-forward ">" ispell-region-end t))
+     ((and ispell-skip-html (string-match "&" key))
+      (search-forward-regexp "[; \t\n]" ispell-region-end t))
+     ;; markings from alist
+     (t
+      (while alist
+       (setq alist-key (eval (car (car alist))))
+       (if (string-match alist-key key)
+           (progn
+             (setq alist (cdr (car alist)))
+             (cond
+              ((null alist) (setq null-skip t)) ; done!  Just skip key.
+              ((not (consp alist))
+               ;; Search past end of spell region to find this region end.
+               (re-search-forward (eval alist) (point-max) t))
+              ((consp alist)
+               (if (stringp alist)
+                   (re-search-forward alist (point-max) t)
+                 (setq null-skip t)    ; error handling in functions!
+                 (if (consp (cdr alist))
+                     (apply (car alist) (cdr alist))
+                   (funcall (car alist))))))
+             (setq alist nil))
+         (setq alist (cdr alist))))))
+    (if (and (= start (point)) (null null-skip))
+       (progn
+         (message "Matching region end for `%s' point %d not found"
+                  key (point))
+         (beep)
+         (sit-for 2)))))
+
+
+;;; Grab the next line of data.
+;;; Returns a string with the line data
+(defun ispell-get-line (start end reg-end)
+  (let ((ispell-casechars (ispell-get-casechars))
+       string)
+    (cond                              ; LOOK AT THIS LINE AND SKIP OR PROCESS
+     ((eolp)                           ; END OF LINE, just go to next line.
+      (forward-line))
+     ;;((looking-at "[-#@*+!%~^]")     ; SKIP SPECIAL ISPELL CHARACTERS
+     ;; (forward-char 1))              ; not needed as quoted below.
+     ((or (re-search-forward ispell-casechars end t) ; TEXT EXISTS
+         (re-search-forward "[][()${}]" end t)) ; or MATH COMMANDS
+      (setq string (concat "^" (buffer-substring-no-properties start end)
+                          "\n"))
+      (goto-char end))
+     (t (goto-char end)))              ; EMPTY LINE, skip it.
+    string))
+
+
+;;; Avoid error messages when compiling for these dynamic variables.
+(eval-when-compile
+  (defvar start)
+  (defvar end))
+
+(defun ispell-process-line (string shift)
+  "Sends a LINE of text to ispell and processes the result.
+This will modify the buffer for spelling errors.
+Requires variables START and END to be defined in its lexical scope.
+Returns the sum shift due to changes in word replacements."
+  ;;(declare special start end)
+  (let (poss accept-list)
+    (if (not (numberp shift))
+       (setq shift 0))
+    ;; send string to spell process and get input.
+    (ispell-send-string string)
+    (while (progn
+            (ispell-accept-output)
+            ;; Last item of output contains a blank line.
+            (not (string= "" (car ispell-filter)))))
+    ;; parse all inputs from the stream one word at a time.
+    ;; Place in FIFO order and remove the blank item.
+    (setq ispell-filter (nreverse (cdr ispell-filter)))
+    (while (and (not ispell-quit) ispell-filter)
+      ;; get next word, accounting for accepted words and start shifts
+      (setq poss (ispell-parse-output (car ispell-filter)
+                                     accept-list shift))
+      (if (and poss (listp poss))      ; spelling error occurred.
+         ;; Whenever we have misspellings, we can change
+         ;; the buffer.  Keep boundaries as markers.
+         ;; Markers can move with highlighting!  This destroys
+         ;; end of region markers line-end and ispell-region-end
+         (let ((word-start
+                (copy-marker (+ start ispell-offset (car (cdr poss)))))
+               (word-len (length (car poss)))
+               (line-end (copy-marker end))
+               (line-start (copy-marker start))
+               recheck-region replace)
+           (goto-char word-start)
+           ;; Adjust the horizontal scroll & point
+           (ispell-horiz-scroll)
+           (goto-char (+ word-len word-start))
+           (ispell-horiz-scroll)
+           (goto-char word-start)
+           (ispell-horiz-scroll)
+
+           ;; Alignment cannot be tracked and this error will occur when
+           ;; `query-replace' makes multiple corrections on the starting line.
+           (if (/= (+ word-len (point))
+                   (progn
+                     ;; NB: Search can fail with Mule character sets that don't
+                     ;;  display properly.  Ignore the error in this case?
+                     (search-forward (car poss) (+ word-len (point)) t)
+                     (point)))
+               ;; This occurs due to filter pipe problems
+               (error (concat "Ispell misalignment: word "
+                              "`%s' point %d; probably incompatible versions")
+                      (car poss) (marker-position word-start)))
+           ;; ispell-cmd-loop can go recursive & change buffer
+           (if ispell-keep-choices-win
+               (setq replace (ispell-command-loop
+                              (car (cdr (cdr poss)))
+                              (car (cdr (cdr (cdr poss))))
+                              (car poss) (marker-position word-start)
+                              (+ word-len (marker-position word-start))))
+             (save-window-excursion
+               (setq replace (ispell-command-loop
+                              (car (cdr (cdr poss)))
+                              (car (cdr (cdr (cdr poss))))
+                              (car poss) (marker-position word-start)
+                              (+ word-len (marker-position word-start))))))
+
+           (goto-char word-start)
+           ;; Recheck when query replace edit changes misspelled word.
+           ;; Error in tex mode when a potential math mode change exists.
+           (if (and replace (listp replace) (= 2 (length replace)))
+               (if (and (eq ispell-parser 'tex)
+                        (string-match "[\\\\][]()[]\\|\\\\begin\\|\\$"
+                                      (regexp-quote string)))
+                   (error
+                    "Don't start query replace on a line with math characters"
+                    )
+                 (set-marker line-end (point))
+                 (setq ispell-filter nil
+                       recheck-region t)))
+
+           ;; insert correction if needed
+           (cond
+            ((or (null replace)
+                 (equal 0 replace))    ; ACCEPT/INSERT
+             (if (equal 0 replace)     ; BUFFER-LOCAL DICT ADD
+                 (ispell-add-per-file-word-list (car poss)))
+             ;; do not recheck accepted word on this line
+             (setq accept-list (cons (car poss) accept-list)))
+            (t                         ; replacement word selected or entered
+             (delete-region (point) (+ word-len (point)))
+             (if (not (listp replace))
+                 (progn
+                   (insert replace)    ; insert dictionary word
+                   (setq accept-list (cons replace accept-list)))
+               (let ((replace-word (car replace)))
+                 ;; Recheck hand entered replacement word
+                 (insert replace-word)
+                 (if (car (cdr replace))
+                     (save-window-excursion
+                       (delete-other-windows) ; to correctly show help.
+                       ;; Assume case-replace &
+                       ;; case-fold-search correct?
+                       (query-replace (car poss) (car replace) t)))
+                 (goto-char word-start)
+                 ;; do not recheck if already accepted
+                 (if (member replace-word accept-list)
+                     (setq accept-list (cons replace-word accept-list)
+                           replace replace-word)
+                   (let ((region-end (copy-marker ispell-region-end)))
+                     (setq recheck-region ispell-filter
+                           ispell-filter nil ; save filter
+                           shift 0     ; already accounted
+                           shift (ispell-region
+                                 word-start
+                                 (+ word-start (length replace-word))
+                                 t shift))
+                     (if (null shift)  ; quitting check.
+                         (setq shift 0))
+                     (set-marker ispell-region-end region-end)
+                     (set-marker region-end nil)
+                     (setq ispell-filter recheck-region
+                           recheck-region nil
+                           replace replace-word)))))
+
+             (setq shift (+ shift (- (length replace) word-len)))
+
+             ;; Move line-start across word...
+             ;; new shift function does this now...
+             ;;(set-marker line-start (+ line-start
+             ;;                        (- (length replace)
+             ;;                           (length (car poss)))))
+             ))
+           (if (not ispell-quit)
+               (let (message-log-max)
+                 (message "Continuing spelling check using %s dictionary..."
+                          (or ispell-dictionary "default"))))
+           (sit-for 0)
+           (setq start (marker-position line-start)
+                 end (marker-position line-end))
+           ;; Adjust markers when end of region lost from highlighting.
+           (if (and (not recheck-region) (< end (+ word-start word-len)))
+               (setq end (+ word-start word-len)))
+           (if (= word-start ispell-region-end)
+               (set-marker ispell-region-end (+ word-start word-len)))
+           ;; going out of scope - unneeded
+           (set-marker line-start nil)
+           (set-marker word-start nil)
+           (set-marker line-end nil)))
+      ;; finished with misspelling!
+      (setq ispell-filter (cdr ispell-filter)))
+    shift))
+
+
+;;;###autoload
+(defun ispell-comments-and-strings ()
+  "Check comments and strings in the current buffer for spelling errors."
+  (interactive)
+  (goto-char (point-min))
+  (let (state done)
+    (while (not done)
+      (setq done t)
+      (setq state (parse-partial-sexp (point) (point-max)
+                                     nil nil state 'syntax-table))
+      (if (or (nth 3 state) (nth 4 state))
+         (let ((start (point)))
+           (setq state (parse-partial-sexp start (point-max)
+                                           nil nil state 'syntax-table))
+           (if (or (nth 3 state) (nth 4 state))
+               (error "Unterminated string or comment"))
+           (save-excursion
+             (setq done (not (ispell-region start (point))))))))))
+
 
 ;;;###autoload
 (defun ispell-buffer ()
@@ -1885,14 +2762,17 @@ With prefix argument, set the default directory."
 
 ;;;###autoload
 (defun ispell-continue ()
+  "Continue a halted spelling session beginning with the current word."
   (interactive)
-  "Continue a spelling session after making some changes."
   (if (not (marker-position ispell-region-end))
       (message "No session to continue.  Use 'X' command when checking!")
     (if (not (equal (marker-buffer ispell-region-end) (current-buffer)))
        (message "Must continue ispell from buffer %s"
                 (buffer-name (marker-buffer ispell-region-end)))
-      (ispell-region (point) (marker-position ispell-region-end)))))
+      (ispell-region
+       ;; find beginning of current word:
+       (car (cdr (ispell-get-word t)))
+       (marker-position ispell-region-end)))))
 
 
 ;;; Horizontal scrolling
@@ -1912,14 +2792,14 @@ With prefix argument, set the default directory."
 
 ;;;###autoload
 (defun ispell-complete-word (&optional interior-frag)
-  "Look up word before or under point in dictionary (see lookup-words command)
-and try to complete it.  If optional INTERIOR-FRAG is non-nil then the word
-may be a character sequence inside of a word.
+  "Try to complete the word before or under point (see `lookup-words')
+If optional INTERIOR-FRAG is non-nil then the word may be a character
+sequence inside of a word.
 
 Standard ispell choices are then available."
   (interactive "P")
   (let ((cursor-location (point))
-       case-fold-search
+       (case-fold-search-val case-fold-search)
        (word (ispell-get-word nil "\\*")) ; force "previous-word" processing.
        start end possibilities replacement)
     (setq start (car (cdr word))
@@ -1927,20 +2807,29 @@ Standard ispell choices are then available."
          word (car word)
          possibilities
          (or (string= word "")         ; Will give you every word
-             (lookup-words (concat (if interior-frag "*") word "*")
+             (lookup-words (concat (and interior-frag "*") word
+                                   (if (or interior-frag (null ispell-look-p))
+                                       "*"))
                            ispell-complete-word-dict)))
     (cond ((eq possibilities t)
           (message "No word to complete"))
          ((null possibilities)
           (message "No match for \"%s\"" word))
          (t                            ; There is a modification...
-          (cond                        ; Try and respect case of word.
-           ((string-match "^[^A-Z]+$" word)
-            (setq possibilities (mapcar 'downcase possibilities)))
-           ((string-match "^[^a-z]+$" word)
+          (setq case-fold-search nil)  ; Try and respect case of word.
+          (cond
+           ((string-equal (upcase word) word)
             (setq possibilities (mapcar 'upcase possibilities)))
-           ((string-match "^[A-Z]" word)
-            (setq possibilities (mapcar 'capitalize possibilities))))
+           ((string-equal (upcase (substring word 0 1)) (substring word 0 1))
+             (setq possibilities (mapcar (function
+                                          (lambda (pos)
+                                            (if (string-equal
+                                                (substring word 0 1)
+                                                (substring pos 0 1))
+                                               pos
+                                              (capitalize pos))))
+                                         possibilities))))
+          (setq case-fold-search case-fold-search-val)
           (save-window-excursion
             (setq replacement
                   (ispell-command-loop possibilities nil word start end)))
@@ -1970,6 +2859,22 @@ Standard ispell choices are then available."
   (ispell-complete-word t))
 
 
+;;;###autoload
+(defun ispell ()
+  "Interactively check a region or buffer for spelling errors.
+If `transient-mark-mode' is on, and a region is active, spell-check
+that region.  Otherwise spell-check the buffer.
+
+Ispell dictionaries are not distributed with Emacs.  If you are
+looking for a dictionary, please see the distribution of the GNU ispell
+program, or do an Internet search; there are various dictionaries
+available on the net."
+  (interactive)
+  (if (and transient-mark-mode mark-active)
+      (ispell-region (region-beginning) (region-end))
+    (ispell-buffer)))
+
+
 ;;; **********************************************************************
 ;;;                    Ispell Minor Mode
 ;;; **********************************************************************
@@ -2000,24 +2905,31 @@ Standard ispell choices are then available."
 (defun ispell-minor-mode (&optional arg)
   "Toggle Ispell minor mode.
 With prefix arg, turn Ispell minor mode on iff arg is positive.
+
 In Ispell minor mode, pressing SPC or RET
-warns you if the previous word is incorrectly spelled."
+warns you if the previous word is incorrectly spelled.
+
+All the buffer-local variables and dictionaries are ignored -- to read
+them into the running ispell process, type \\[ispell-word] SPC."
   (interactive "P")
   (setq ispell-minor-mode
        (not (or (and (null arg) ispell-minor-mode)
                 (<= (prefix-numeric-value arg) 0))))
   (force-mode-line-update))
+
 (defun ispell-minor-check ()
-  ;; Check previous word then continue with the normal binding of this key.
+  "Check previous word then continue with the normal binding of this key.
+Don't check previous word when character before point is a space or newline.
+Don't read buffer-local settings or word lists."
   (interactive "*")
   (let ((ispell-minor-mode nil)
-       (ispell-check-only t))
-    (save-restriction
-      (narrow-to-region (point-min) (point))
-      (ispell-word nil t))
-    (call-interactively (key-binding (this-command-keys)))))
+       (ispell-check-only t)
+       (last-char (char-after (1- (point)))))
+    (command-execute (key-binding (this-command-keys)))
+    (if (not (or (eq last-char ?\ ) (eq last-char ?\n)
+                (and ispell-skip-html (eq last-char ?>))
+                (and ispell-skip-html (eq last-char ?\;))))
+       (ispell-word nil t))))
 
 
 ;;; **********************************************************************
@@ -2029,48 +2941,28 @@ warns you if the previous word is incorrectly spelled."
 (defvar ispell-message-text-end
   (mapconcat (function identity)
             '(
+              ;; Don't spell check signatures
+              "^-- $"
               ;; Matches postscript files.
               "^%!PS-Adobe-[123].0"
               ;; Matches uuencoded text
               "^begin [0-9][0-9][0-9] .*\nM.*\nM.*\nM"
-              ;; Matches shell files (esp. auto-decoding)
+              ;; Matches shell files (especially auto-decoding)
               "^#! /bin/[ck]?sh"
               ;; Matches context difference listing
-              "\\(diff -c .*\\)?\n\\*\\*\\* .*\n--- .*\n\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*"
-              ;; Matches "----------------- cut here"
-              ;; and "------- Start of forwarded message"
-              "^[-=_]+\\s ?\\(cut here\\|Start of forwarded message\\)")
+              "\\(\\(^cd .*\n\\)?diff -c .*\\)?\n\\*\\*\\* .*\n--- .*\n\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*"
+              ;; Matches unidiff difference listing
+              "\\(diff -u .*\\)?\n--- .*\n\\+\\+\\+ .*\n@@ [-+][0-9]+,[0-9]+ [-+][0-9]+,[0-9]+ @@\n"
+              ;; Matches reporter.el bug report
+              "^current state:\n==============\n"
+              ;; Matches commonly used "cut" boundaries
+              "^\\(- \\)?[-=_]+\\s ?\\(cut here\\|Environment Follows\\)")
             "\\|")
-  "*End of text which will be checked in ispell-message.
+  "*End of text which will be checked in `ispell-message'.
 If it is a string, limit at first occurrence of that regular expression.
 Otherwise, it must be a function which is called to get the limit.")
 
 
-(defvar ispell-message-start-skip
-  (mapconcat (function identity)
-            '(
-              ;; Matches forwarded messages
-              "^---* Forwarded Message"
-              ;; Matches PGP Public Key block
-              "^---*BEGIN PGP [A-Z ]*--*"
-              )
-            "\\|")
-  "Spelling is skipped inside these start/end groups by ispell-message.
-Assumed that blocks are not mutually inclusive.")
-
-
-(defvar ispell-message-end-skip
-  (mapconcat (function identity)
-            '(
-              ;; Matches forwarded messages
-              "^--- End of Forwarded Message"
-              ;; Matches PGP Public Key block
-              "^---*END PGP [A-Z ]*--*"
-              )
-            "\\|")
-  "Spelling is skipped inside these start/end groups by ispell-message.
-Assumed that blocks are not mutually inclusive.")
-
 
 ;;;###autoload
 (defun ispell-message ()
@@ -2078,13 +2970,14 @@ Assumed that blocks are not mutually inclusive.")
 Don't check spelling of message headers except the Subject field.
 Don't check included messages.
 
-To abort spell checking of a message REGION and send the message anyway,
-use the `x' or `q' command.  (Any subsequent regions will be checked.)
+To abort spell checking of a message region and send the message anyway,
+use the `x' command.  (Any subsequent regions will be checked.)
 The `X' command aborts the message send so that you can edit the buffer.
 
 To spell-check whenever a message is sent, include the appropriate lines
 in your .emacs file:
-   (add-hook 'news-inews-hook 'ispell-message)
+   (add-hook 'message-send-hook 'ispell-message)  ;; GNUS 5
+   (add-hook 'news-inews-hook 'ispell-message)    ;; GNUS 4
    (add-hook 'mail-send-hook  'ispell-message)
    (add-hook 'mh-before-send-letter-hook 'ispell-message)
 
@@ -2094,13 +2987,18 @@ You can bind this to the key C-c i in GNUS or mail by adding to
   (interactive)
   (save-excursion
     (goto-char (point-min))
-    (let* ((internal-messagep (save-excursion
-                               (re-search-forward
-                                (concat "^"
-                                        (regexp-quote mail-header-separator)
-                                        "$")
-                                nil t)))
-          (limit (copy-marker
+    (let* (
+          ;; Nil when message came from outside (eg calling emacs as editor)
+          ;; Non-nil marker of end of headers.
+          (internal-messagep
+           (re-search-forward
+            (concat "^" (regexp-quote mail-header-separator) "$") nil t))
+          (end-of-headers              ; Start of body.
+           (copy-marker
+            (or internal-messagep
+                (re-search-forward "^$" nil t)
+                (point-min))))
+          (limit (copy-marker          ; End of region we will spell check.
                   (cond
                    ((not ispell-message-text-end) (point-max))
                    ((char-or-string-p ispell-message-text-end)
@@ -2108,89 +3006,77 @@ You can bind this to the key C-c i in GNUS or mail by adding to
                         (match-beginning 0)
                       (point-max)))
                    (t (min (point-max) (funcall ispell-message-text-end))))))
-          (cite-regexp                 ;Prefix of inserted text
+          (default-prefix   ; Vanilla cite prefix (just used for cite-regexp)
+            (if (and (boundp 'mail-yank-prefix) mail-yank-prefix)
+                (ispell-non-empty-string mail-yank-prefix)
+              "   \\|\t"))
+          (cite-regexp                 ;Prefix of quoted text
            (cond
-            ((featurep 'supercite)     ; sc 3.0
+            ((functionp 'sc-cite-regexp) ; sc 3.0
              (concat "\\(" (sc-cite-regexp) "\\)" "\\|"
                      (ispell-non-empty-string sc-reference-tag-string)))
-            ((featurep 'sc)            ; sc 2.3
+            ((boundp 'sc-cite-regexp)  ; sc 2.3
              (concat "\\(" sc-cite-regexp "\\)" "\\|"
                      (ispell-non-empty-string sc-reference-tag-string)))
-            ((equal major-mode 'news-reply-mode) ;GNUS
+            ((or (equal major-mode 'news-reply-mode) ;GNUS 4 & below
+                 (equal major-mode 'message-mode))   ;GNUS 5
              (concat "In article <" "\\|"
-                     (if mail-yank-prefix
-                         (ispell-non-empty-string mail-yank-prefix)
-                       "^   \\|^\t")))
+                     "[^,;&+=\n]+ <[^,;&+=]+> writes:" "\\|"
+                     message-yank-prefix "\\|"
+                     default-prefix))
             ((equal major-mode 'mh-letter-mode) ; mh mail message
-             (ispell-non-empty-string mh-ins-buf-prefix))
-            ((not internal-messagep)   ; Assume n sent us this message.
+             (concat "[^,;&+=\n]+ writes:" "\\|"
+                     (ispell-non-empty-string mh-ins-buf-prefix)))
+            ((not internal-messagep)   ; Assume nn sent us this message.
              (concat "In [a-zA-Z.]+ you write:" "\\|"
                      "In <[^,;&+=]+> [^,;&+=]+ writes:" "\\|"
                      " *> *"))
             ((boundp 'vm-included-text-prefix) ; VM mail message
-             (concat "[^,;&+=]+ writes:" "\\|"
+             (concat "[^,;&+=\n]+ writes:" "\\|"
                      (ispell-non-empty-string vm-included-text-prefix)))
-            (mail-yank-prefix          ; vanilla mail message.
-             (ispell-non-empty-string mail-yank-prefix))
-            (t "^   \\|^\t")))
-          (cite-regexp-start (concat "^[ \t]*$\\|" cite-regexp))
-          (cite-regexp-end   (concat "^\\(" cite-regexp "\\)"))
+            (t default-prefix)))
+          (ispell-skip-region-alist
+           (cons (list (concat "^\\(" cite-regexp "\\)")
+                       (function forward-line))
+                 ispell-skip-region-alist))
           (old-case-fold-search case-fold-search)
-          (case-fold-search t)
+          (dictionary-alist ispell-message-dictionary-alist)
           (ispell-checking-message t))
-      (goto-char (point-min))
-      ;; Skip header fields except Subject: without Re:'s
-      ;;(search-forward mail-header-separator nil t)
-      (while (if internal-messagep
-                (< (point) internal-messagep)
-              (and (looking-at "[a-zA-Z---]+:\\|\t\\| ")
-                   (not (eobp))))
-       (if (looking-at "Subject: *")   ; Spell check new subject fields
-           (progn
-             (goto-char (match-end 0))
-             (if (and (not (looking-at ".*Re\\>"))
-                      (not (looking-at "\\[")))
-                 (let ((case-fold-search old-case-fold-search))
-                   (ispell-region (point)
-                                  (progn
-                                    (end-of-line)
-                                    (while (looking-at "\n[ \t]")
-                                      (end-of-line 2))
-                                    (point)))))))
-       (forward-line 1))
-      (setq case-fold-search nil)
-      ;; Skip mail header, particularly for non-english languages.
-      (if (looking-at (concat (regexp-quote mail-header-separator) "$"))
-         (forward-line 1))
-      (while (< (point) limit)
-       ;; Skip across text cited from other messages.
-       (while (and (looking-at cite-regexp-start)
-                   (< (point) limit)
-                   (zerop (forward-line 1))))
-
-       (if (< (point) limit)
-           (let* ((start (point))
-                  ;; Check the next batch of lines that *aren't* cited.
-                  (end-c (and (re-search-forward cite-regexp-end limit 'end)
-                              (match-beginning 0)))
-                  ;; Skip a block of included text.
-                  (end-fwd (and (goto-char start)
-                                (re-search-forward ispell-message-start-skip
-                                                   limit 'end)
-                                (progn (beginning-of-line)
-                                       (point))))
-                  (end (or (and end-c end-fwd (min end-c end-fwd))
-                           end-c end-fwd
-                           ;; defalut to limit of text.
-                           (marker-position limit))))
-             (goto-char start)
-             (ispell-region start end)
-             (if (and end-fwd (= end end-fwd))
-                 (progn
-                   (goto-char end)
-                   (re-search-forward ispell-message-end-skip limit 'end))
-               (goto-char end)))))
-      (set-marker limit nil))))
+
+      ;; Select dictionary for message
+      (or (local-variable-p 'ispell-local-dictionary (current-buffer))
+         (while dictionary-alist
+           (goto-char (point-min))
+           (if (re-search-forward (car (car dictionary-alist))
+                                  end-of-headers t)
+               (setq ispell-local-dictionary (cdr (car dictionary-alist))
+                     dictionary-alist nil)
+             (setq dictionary-alist (cdr dictionary-alist)))))
+
+      (unwind-protect
+         (progn
+           ;; Spell check any original Subject:
+           (goto-char (point-min))
+           (setq case-fold-search t)
+           (if (re-search-forward "^Subject: *" end-of-headers t)
+               (progn
+                 (goto-char (match-end 0))
+                 (if (and (not (looking-at ".*Re\\>"))
+                          (not (looking-at "\\[")))
+                     (progn
+                       (setq case-fold-search old-case-fold-search)
+                       (ispell-region (point)
+                                      (progn ;Tab-initiated continuation lns.
+                                        (end-of-line)
+                                        (while (looking-at "\n[ \t]")
+                                          (end-of-line 2))
+                                        (point)))))))
+           (setq case-fold-search old-case-fold-search)
+           (goto-char end-of-headers)
+           (forward-line 1)
+           (ispell-region (point) limit))
+       (set-marker end-of-headers nil)
+       (set-marker limit nil)))))
 
 
 (defun ispell-non-empty-string (string)
@@ -2205,7 +3091,7 @@ You can bind this to the key C-c i in GNUS or mail by adding to
 
 
 (defun ispell-accept-buffer-local-defs ()
-  "Load all buffer-local information, restarting ispell when necessary."
+  "Load all buffer-local information, restarting Ispell when necessary."
   (ispell-buffer-local-dict)           ; May kill ispell-process.
   (ispell-buffer-local-words)          ; Will initialize ispell-process.
   (ispell-buffer-local-parsing))
@@ -2214,46 +3100,57 @@ You can bind this to the key C-c i in GNUS or mail by adding to
 (defun ispell-buffer-local-parsing ()
   "Place Ispell into parsing mode for this buffer.
 Overrides the default parsing mode.
-Includes latex/nroff modes and extended character mode."
+Includes Latex/Nroff modes and extended character mode."
   ;; (ispell-init-process) must already be called.
-  (process-send-string ispell-process "!\n") ; Put process in terse mode.
+  (ispell-send-string "!\n")           ; Put process in terse mode.
   ;; We assume all major modes with "tex-mode" in them should use latex parsing
-  (if (or (and (eq ispell-parser 'use-mode-name)
-              (string-match "[Tt][Ee][Xx]-mode" (symbol-name major-mode)))
-         (eq ispell-parser 'tex))
-      (process-send-string ispell-process "+\n") ; set ispell mode to tex
-    (process-send-string ispell-process "-\n"))        ; set mode to normal (nroff)
-  ;; Hard-wire test for SGML & HTML mode.
-  (setq ispell-skip-sgml (memq major-mode '(sgml-mode html-mode)))
+  ;; When exclusively checking comments, set to raw text mode (nroff).
+  (if (and (not (eq 'exclusive ispell-check-comments))
+          (or (and (eq ispell-parser 'use-mode-name)
+                   (string-match "[Tt][Ee][Xx]-mode"
+                                 (symbol-name major-mode)))
+              (eq ispell-parser 'tex)))
+      (progn
+       (ispell-send-string "+\n")      ; set ispell mode to tex
+       (if (not (eq ispell-parser 'tex))
+           (set (make-local-variable 'ispell-parser) 'tex)))
+    (ispell-send-string "-\n"))                ; set mode to normal (nroff)
+  ;; If needed, test for SGML & HTML modes and set a buffer local nil/t value.
+  (if (and ispell-skip-html (not (eq ispell-skip-html t)))
+      (set (make-local-variable 'ispell-skip-html)
+          (not (null (string-match "sgml\\|html"
+                                   (downcase (symbol-name major-mode)))))))
   ;; Set default extended character mode for given buffer, if any.
   (let ((extended-char-mode (ispell-get-extended-character-mode)))
     (if extended-char-mode
-       (process-send-string ispell-process (concat extended-char-mode "\n"))))
+       (ispell-send-string (concat extended-char-mode "\n"))))
   ;; Set buffer-local parsing mode and extended character mode, if specified.
   (save-excursion
-    (goto-char (point-min))
-    ;; Uses last valid definition
-    (while (search-forward ispell-parsing-keyword nil t)
-      (let ((end (save-excursion (end-of-line) (point)))
-           (case-fold-search t)
-           string)
-       (while (re-search-forward " *\\([^ \"]+\\)" end t)
-         ;; space separated definitions.
-         (setq string (buffer-substring (match-beginning 1) (match-end 1)))
-         (cond ((string-match "latex-mode" string)
-                (process-send-string ispell-process "+\n~tex\n"))
-               ((string-match "nroff-mode" string)
-                (process-send-string ispell-process "-\n~nroff"))
-               ((string-match "~" string) ; Set extended character mode.
-                (process-send-string ispell-process (concat string "\n")))
-               (t (message "Illegal Ispell Parsing argument!")
-                  (sit-for 2))))))))
+    (goto-char (point-max))
+    ;; Uses last occurrence of ispell-parsing-keyword
+    (if (search-backward ispell-parsing-keyword nil t)
+       (let ((end (save-excursion (end-of-line) (point)))
+             string)
+         (search-forward ispell-parsing-keyword)
+         (while (re-search-forward " *\\([^ \"]+\\)" end t)
+           ;; space separated definitions.
+           (setq string (downcase (buffer-substring-no-properties
+                                   (match-beginning 1) (match-end 1))))
+           (cond ((and (string-match "latex-mode" string)
+                       (not (eq 'exclusive ispell-check-comments)))
+                  (ispell-send-string "+\n~tex\n"))
+                 ((string-match "nroff-mode" string)
+                  (ispell-send-string "-\n~nroff\n"))
+                 ((string-match "~" string) ; Set extended character mode.
+                  (ispell-send-string (concat string "\n")))
+                 (t (message "Invalid Ispell Parsing argument!")
+                    (sit-for 2))))))))
 
 
 ;;; Can kill the current ispell process
 
 (defun ispell-buffer-local-dict ()
-  "Initializes local dictionary.
+  "Initializes local dictionary and local personal dictionary.
 When a dictionary is defined in the buffer (see variable
 `ispell-dictionary-keyword'), it will override the local setting
 from \\[ispell-change-dictionary].
@@ -2262,18 +3159,25 @@ Both should not be used to define a buffer-local dictionary."
     (goto-char (point-min))
     (let (end)
       ;; Override the local variable definition.
-      ;; Uses last valid definition.
-      (while (search-forward ispell-dictionary-keyword nil t)
-       (setq end (save-excursion (end-of-line) (point)))
-       (if (re-search-forward " *\\([^ \"]+\\)" end t)
-           (setq ispell-local-dictionary
-                 (buffer-substring (match-beginning 1) (match-end 1)))))
-      (goto-char (point-min))
-      (while (search-forward ispell-pdict-keyword nil t)
-       (setq end (save-excursion (end-of-line) (point)))
-       (if (re-search-forward " *\\([^ \"]+\\)" end t)
-           (setq ispell-local-pdict
-                 (buffer-substring (match-beginning 1) (match-end 1)))))))
+      ;; Uses last occurrence of ispell-dictionary-keyword.
+      (goto-char (point-max))
+      (if (search-backward ispell-dictionary-keyword nil t)
+         (progn
+           (search-forward ispell-dictionary-keyword)
+           (setq end (save-excursion (end-of-line) (point)))
+           (if (re-search-forward " *\\([^ \"]+\\)" end t)
+               (setq ispell-local-dictionary
+                     (buffer-substring-no-properties (match-beginning 1)
+                                                     (match-end 1))))))
+      (goto-char (point-max))
+      (if (search-backward ispell-pdict-keyword nil t)
+         (progn
+           (search-forward ispell-pdict-keyword)
+           (setq end (save-excursion (end-of-line) (point)))
+           (if (re-search-forward " *\\([^ \"]+\\)" end t)
+               (setq ispell-local-pdict
+                     (buffer-substring-no-properties (match-beginning 1)
+                                                     (match-end 1))))))))
   ;; Reload if new personal dictionary defined.
   (if (and ispell-local-pdict
           (not (equal ispell-local-pdict ispell-personal-dictionary)))
@@ -2300,30 +3204,38 @@ Both should not be used to define a buffer-local dictionary."
       (or ispell-buffer-local-name
          (setq ispell-buffer-local-name (buffer-name)))
       (let ((end (save-excursion (end-of-line) (point)))
+           (ispell-casechars (ispell-get-casechars))
            string)
        ;; buffer-local words separated by a space, and can contain
-       ;; any character other than a space.
+       ;; any character other than a space.  Not rigorous enough.
        (while (re-search-forward " *\\([^ ]+\\)" end t)
-         (setq string (buffer-substring (match-beginning 1) (match-end 1)))
-         (process-send-string ispell-process (concat "@" string "\n")))))))
+         (setq string (buffer-substring-no-properties (match-beginning 1)
+                                                      (match-end 1)))
+         ;; This can fail when string contains a word with illegal chars.
+         ;; Error handling needs to be added between ispell and emacs.
+         (if (and (< 1 (length string))
+                  (equal 0 (string-match ispell-casechars string)))
+             (ispell-send-string (concat "@" string "\n"))))))))
 
 
 ;;; returns optionally adjusted region-end-point.
 
-(defun ispell-add-per-file-word-list (word &optional reg-end)
-  "Adds new word to the per-file word list."
+(defun ispell-add-per-file-word-list (word)
+  "Add WORD to the per-file word list."
   (or ispell-buffer-local-name
       (setq ispell-buffer-local-name (buffer-name)))
-  (if (null reg-end)
-      (setq reg-end 0))
   (save-excursion
     (goto-char (point-min))
-    (let (case-fold-search line-okay search done string)
+    (let ((old-case-fold-search case-fold-search)
+         line-okay search done found)
       (while (not done)
-       (setq search (search-forward ispell-words-keyword nil 'move)
+       (setq case-fold-search nil
+             search (search-forward ispell-words-keyword nil 'move)
+             found (or found search)
              line-okay (< (+ (length word) 1 ; 1 for space after word..
                              (progn (end-of-line) (current-column)))
-                          80))
+                          80)
+             case-fold-search old-case-fold-search)
        (if (or (and search line-okay)
                (null search))
            (progn
@@ -2331,24 +3243,15 @@ Both should not be used to define a buffer-local dictionary."
              (if (null search)
                  (progn
                    (open-line 1)
-                   (setq string (concat comment-start " "
-                                        ispell-words-keyword))
-                   ;; in case the keyword is in the middle of the file....
-                   (if (> reg-end (point))
-                       (setq reg-end (+ reg-end (length string))))
-                   (insert string)
-                   (if (and comment-end (not (equal "" comment-end)))
+                   (unless found (newline))
+                   (insert (concat comment-start " " ispell-words-keyword))
+                   (if (> (length comment-end) 0)
                        (save-excursion
-                         (open-line 1)
-                         (forward-line 1)
+                         (newline)
                          (insert comment-end)))))
-             (if (> reg-end (point))
-                 (setq reg-end (+ 1 reg-end (length word))))
-             (insert (concat " " word)))))))
-  reg-end)
+             (insert (concat " " word))))))))
 
-
-(defconst ispell-version "2.37 -- Tue Jun 13 12:05:28 EDT 1995")
+(add-to-list 'debug-ignored-errors "^No word found to check!$")
 
 (provide 'ispell)
 
@@ -2360,12 +3263,14 @@ Both should not be used to define a buffer-local dictionary."
 ;;; eval: expression
 ;;; local-variable: value
 
-;;; The following sets the buffer local dictionary to english!
+;;; The following sets the buffer local dictionary to `american' English
+;;; and spell checks only comments.
 
 ;;; Local Variables:
 ;;; mode: emacs-lisp
 ;;; comment-column: 40
-;;; ispell-local-dictionary: "english"
+;;; ispell-check-comments: exclusive
+;;; ispell-local-dictionary: "american"
 ;;; End:
 
 
@@ -2373,13 +3278,16 @@ Both should not be used to define a buffer-local dictionary."
 
 ;;; The following places this file in nroff parsing and extended char modes.
 ;;; Local IspellParsing: nroff-mode ~nroff
-;;; Change IspellDict to IspellDict: to enable the following line.
-;;; Local IspellDict english
 ;;; Change IspellPersDict to IspellPersDict: to enable the following line.
 ;;; Local IspellPersDict ~/.ispell_lisp
 ;;; The following were automatically generated by ispell using the 'A' command:
-; LocalWords:  ispell ispell-highlight-p ispell-check-comments query-replace
-; LocalWords:  ispell-query-replace-choices ispell-skip-tib non-nil tib
-; LocalWords:  regexps ispell-tib-ref-beginning ispell-tib-ref-end
-
-;; ispell.el ends here
+; LocalWords:  settable alist inews mh frag pdict Wildcards iconify arg tex kss
+; LocalWords:  alists minibuffer bufferp autoload loaddefs aff Dansk KOI SPC op
+; LocalWords:  Francais Nederlands charset autoloaded popup nonmenu regexp num
+; LocalWords:  AMStex hspace includeonly nocite epsfig displaymath eqnarray reg
+; LocalWords:  minipage modeline pers dict unhighlight buf grep sync prev inc
+; LocalWords:  fn hilight oldot NB AIX msg init read's bufs pt cmd Quinlan eg
+; LocalWords:  uuencoded unidiff sc nn VM SGML eval IspellPersDict unsplitable
+; LocalWords:  lns XEmacs HTML casechars Multibyte
+
+;;; ispell.el ends here