Commits

Anonymous committed 709cd85

Upgrade to version 3.6.

  • Participants
  • Parent commits fce3d63

Comments (0)

Files changed (2)

+2003-12-20  Jerry James  <james@xemacs.org>
+
+	* ispell.el: Upgrade to version 3.6 from Ken Stevens
+	<k.stevens@ieee.org>.
+
 2003-11-02  Norbert Koch  <viteno@xemacs.org>
 
 	* Makefile (VERSION): XEmacs package 1.26 released.
 ;;; ispell.el --- Interface to International Ispell Versions 3.1 and 3.2
 
-;; Copyright (C) 1994, 1995, 1997, 1998, 1999 Free Software Foundation, Inc.
-
-;; Authors         : Ken Stevens <k.stevens@ieee.org>
-;; Stevens Mod Date: Mon 29 Nov 11:38:34 PST 1999
-;; Stevens Revision: 3.3
+;; Copyright (C) 1994, 1995, 1997, 1998, 1999, 2000, 2001, 2003 Free Software Foundation, Inc.
+
+;; Author:	     Ken Stevens <k.stevens@ieee.org>
+;; Maintainer:	     Ken Stevens <k.stevens@ieee.org>
+;; Stevens Mod Date: Mon Jan  7 12:32:44 PST 2003
+;; Stevens Revision: 3.6
 ;; 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.
 
 
 ;; Modifications made in latest versions:
 
+;; Revision 3.6 2003/01/07 12:32:44	kss
+;; Removed extra -d LIB in dictionary defs. (Pavel Janik)
+;; Filtered process calls with duplicate dictionary entries.
+;; Fixed bug where message-text-end is inside a mime skipped region.
+;; Minor fixes to get ispell menus right in XEmacs
+;; Fixed skip regexp so it doesn't match stuff like `/.\w'.
+;; Detecting dictionary change not working.  Fixed.  kss
+;; function `ispell-change-dictionary' now only completes valid dicts.
+
+;; Revision 3.5 2001/7/11 18:43:57	kss
+;; Added fix for aspell to work in XEmacs (check-ispell-version).
+;; Added Portuguese dictionary definition.
+;; New feature: MIME mail message support, Fcc support.
+;; Bug fix: retain comment syntax on lines with region skipping. (TeX $ bug...)
+;; Improved allocation for graphic mode lines.  (Miles Bader)
+;; Support -v flag for old versions of aspell.  (Eli Zaretskii)
+;; Clear minibuffer on ^G from ispell-help (Tak Ota)
+
+;; 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.
 	"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 (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)
 (defconst version20p (string-match "20\\.[0-9]+\\.[0-9]+" emacs-version)
   "Non nil if using emacs version 20.")
 
+(and (not version18p)
+     (not (boundp 'epoch::version))
+     (defalias 'ispell-check-version 'check-ispell-version))
+
 
 ;;; **********************************************************************
 ;;; The following variables should be set according to personal preference
   :type 'boolean
   :group 'ispell)
 
-(defcustom ispell-choices-win-default-height (if xemacsp 3 2)
+(defcustom ispell-choices-win-default-height 2
   "*The default size of the `*Choices*' window, including mode line.
-Must be greater than 1.
-XEmacs modeline is thicker than a line of text, so it partially covers the
-last line of text in the buffer.  Include an extra line in XEmacs to see
-all of the choices clearly."
+Must be greater than 1."
   :type 'integer
   :group 'ispell)
 
 	((file-exists-p "/usr/dict/words") "/usr/dict/words")
 	((file-exists-p "/usr/lib/dict/words") "/usr/lib/dict/words")
 	((file-exists-p "/usr/share/dict/words") "/usr/share/dict/words")
+	((file-exists-p "/usr/share/lib/dict/words")
+	 "/usr/share/lib/dict/words")
 	((file-exists-p "/sys/dict") "/sys/dict")
 	(t "/usr/dict/words"))
   "*Alternate dictionary for spelling help."
   :group 'ispell)
 
 
+(defcustom ispell-message-fcc-skip 50000
+  "*Query before saving Fcc message copy if attachment larger than this value.
+Nil always stores Fcc copy of message."
+  :type '(choice integer (const :tag "off" nil))
+  :group 'ispell)
+
+
 (defcustom ispell-grep-command "egrep"
   "Name of the grep command for search processes."
   :type 'string
              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. 
+  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)
   :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.
+
+This is a local variable.  To change the default value use `set-default'."
+  :type '(choice (const :tag "always" t) (const :tag "never" nil)
+		 (const :tag "use-mode-name" use-mode-name))
+  :group 'ispell)
+
+(make-variable-buffer-local 'ispell-skip-html)
+
+
 ;;; Define definitions here only for personal dictionaries.
 ;;;###autoload
 (defcustom ispell-local-dictionary-alist nil
   "*Contains local or customized dictionary definitions.
-See `ispell-dictionary-alist'."
+
+These will override the values in `ispell-dictionary-alist'.
+
+Customization changes made to `ispell-dictionary-alist' will not operate
+over emacs sessions.  To make permanent changes to your dictionary
+definitions, you will need to make your changes in this variable, save,
+and then re-start emacs."
   :type '(repeat (list (choice :tag "Dictionary"
 			       (string :tag "Dictionary name")
 			       (const :tag "default" nil))
 		       (choice :tag "Extended character mode"
 			       (const "~tex") (const "~plaintex")
 			       (const "~nroff") (const "~list")
-			       (const "~latin1") (const "~latin3") 
+			       (const "~latin1") (const "~latin3")
  			       (const :tag "default" nil))
-		       (choice :tag "Character set"
+		       (choice :tag "Coding system"
 			       (const iso-8859-1)
 			       (const iso-8859-2)
 			       (const koi8-r))))
     "[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)
-   ("brasiliano"			; Brazilian mode
+   ("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)
+    "[']" nil nil nil iso-8859-1)
    ("british"				; British version
-    "[A-Za-z]" "[^A-Za-z]" "[']" nil ("-B" "-d" "british") nil iso-8859-1)
+    "[A-Za-z]" "[^A-Za-z]" "[']" nil ("-B") 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)
+    "[-]" nil ("-B") "~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)))
+    "[-]" nil ("-B" "-d" "castellano") "~latin1" iso-8859-1)))
 
 
 ;;; Second part of dictionary, shortened for loaddefs.el
  '(("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)
+    "" nil ("-B") 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)
    ("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)))
+    "[-']" t nil "~list" iso-8859-1)))
 
 
 ;;; Fourth part of dictionary, shortened for loaddefs.el
  '(("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)
+    "[-'^`\"]" 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") "~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]"
  '(("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)
+    "[\"]" nil nil "~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 
+   ("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)))
+    "" nil nil nil iso-8859-2)
+   ("portugues"				; Portuguese mode
+    "[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") "~latin1" iso-8859-1)))
 
 
 ;;; Sixth part of dictionary, shortened for loaddefs.el
 ;;;###autoload
 (setq
  ispell-dictionary-alist-6
- ;; include Russian iso character set too?
+ ;; include Russian iso coding system 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)
+    "" nil nil nil koi8-r)
+   ("slovak"				; Slovakian
+    "[A-Za-z\301\304\311\315\323\332\324\300\305\245\335\256\251\310\317\253\322\341\344\351\355\363\372\364\340\345\265\375\276\271\350\357\273\362]"
+    "[^A-Za-z\301\304\311\315\323\332\324\300\305\245\335\256\251\310\317\253\322\341\344\351\355\363\372\364\340\345\265\375\276\271\350\357\273\362]"
+    "" nil ("-B") nil iso-8859-2)
    ("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)))
 
 
-
 ;;;###autoload
 (defcustom ispell-dictionary-alist
   (append ispell-local-dictionary-alist	; dictionary customizations
 		       (choice :tag "Extended character mode"
 			       (const "~tex") (const "~plaintex")
 			       (const "~nroff") (const "~list")
-			       (const "~latin1") (const "~latin3") 
+			       (const "~latin1") (const "~latin3")
  			       (const :tag "default" nil))
-		       (choice :tag "Character set"
+		       (choice :tag "Coding System"
 			       (const iso-8859-1)
 			       (const iso-8859-2)
 			       (const koi8-r))))
 	      ispell-dictionary-alist-3 ispell-dictionary-alist-4
 	      ispell-dictionary-alist-5 ispell-dictionary-alist-6))
 
-;;; 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.
+
+
+
+;;; **********************************************************************
+;;; 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
 (defvar ispell-offset -1
   "Offset that maps protocol differences between ispell 3.1 versions.")
 
-(defconst ispell-version "ispell.el 3.3 -- Mon 29 Nov 11:38:34 PST 1999")
-
-
+(defconst ispell-version "ispell.el 3.6 - 01/07/2003")
+
+
+;;;###autoload
 (defun check-ispell-version (&optional interactivep)
   "Ensure that `ispell-program-name' is valid and the correct version.
 Returns version number if called interactively.
     (save-excursion
       (set-buffer (get-buffer-create " *ispell-tmp*"))
       (erase-buffer)
-      (setq status (call-process ispell-program-name nil t nil "-vv"))
+      (setq status (call-process
+		    ispell-program-name nil t nil
+		    ;; aspell doesn't accept the -vv switch.
+		    (let ((case-fold-search
+			   (memq system-type '(ms-dos windows-nt)))
+			  (speller
+			   (file-name-nondirectory ispell-program-name)))
+		      ;; Assume anything that isn't `aspell' is Ispell.
+		      (if (string-match "\\`aspell" speller) "-v" "-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.
-	(re-search-forward "LIBDIR = \\\"\\([^ \t\n]*\\)\\\"" nil t)
-	(if (match-beginning 0)
-	    (setq result (buffer-substring (match-beginning 1) (match-end 1)))))
+      (if (not interactivep)
+	  ;; return library path.
+	  (if (re-search-forward "LIBDIR = \\\"\\([^ \t\n]*\\)\\\"" nil t)
+	      (setq result (buffer-substring (match-beginning 1)
+					     (match-end 1))))
+	;; write message string to minibuffer
+	(end-of-line)
+	(message (concat (buffer-substring-no-properties (point-min) (point))
+			 ", " ispell-version)))
       (goto-char (point-min))
       (if (not (memq status '(0 nil)))
 	  (error "%s exited with %s %s" ispell-program-name
     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.
+
+
 ;;;###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)
 
 ;;;###autoload
-(defvar ispell-menu-xemacs
-  '(:filter
-    (lambda (menu)
-      (let ((dicts (cons (cons "default" nil) ispell-dictionary-alist))
-	    name load-dict)
-	(while dicts
-	  (setq name (car (car dicts))
-		load-dict (car (cdr (member "-d" (nth 5 (car dicts)))))
-		dicts (cdr dicts))
-	  ;; 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)))))))
-      menu)
-    ["Help"		(describe-function 'ispell-help) t]
-    ;;["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]
-    ["Complete Word Frag"ispell-complete-word-interior-frag t]
-    ["Complete Word"	ispell-complete-word		t]
-    ["Kill Process"	ispell-kill-ispell		t]
-    "-"
-    ["Save Personal Dict"(ispell-pdict-save t t)	t]
-    ["Change Dictionary" ispell-change-dictionary	t]
-    ["Select Default"  (ispell-change-dictionary "default") t])
+(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.")
        (not xemacsp)
        'reload))
 
-(defvar ispell-library-path nil
+(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.")
+
+(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 ispell-menu-map-needed
-    (let ((dicts (reverse (cons (cons "default" nil) ispell-dictionary-alist)))
-	  ;; `ispell-library-path' intentionally not defined in autoload
-	  (path (and (boundp 'ispell-library-path) ispell-library-path))
-	  name load-dict)
+(defun valid-dictionary-list ()
+  "Returns a list of valid dictionaries.
+The variable `ispell-library-path' defines the library location."
+  (let ((dicts ispell-dictionary-alist)
+	;; `ispell-library-path' intentionally not defined in autoload
+	(path (and (boundp 'ispell-library-path) ispell-library-path))
+	(dict-list (cons "default" nil))
+	name load-dict)
+    (while dicts
+      (setq name (car (car dicts))
+	    load-dict (car (cdr (member "-d" (nth 5 (car dicts)))))
+	    dicts (cdr dicts))
+	;; Include if the dictionary is in the library, or path not defined.
+	(if (and (stringp name)
+		 (or (not path)
+		     (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"))))))
+	    (setq dict-list (cons name dict-list))))
+    dict-list))
+
+
+
+;;;###autoload
+(if (and ispell-menu-map-needed
+	 (or (not (fboundp 'byte-compiling-files-p))
+	     (not (byte-compiling-files-p))))
+    (let ((dicts (valid-dictionary-list)))
       (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))
-	(cond ((not (stringp name))
-	       (define-key ispell-menu-map (vector 'default)
-		 (cons "Select Default Dict"
-		       (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))
-		       (list 'lambda () '(interactive)
-			     (list 'ispell-change-dictionary name)))))))))
+	(if (string-equal "default" (car dicts))
+	    (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")))))
+	  (define-key ispell-menu-map (vector (intern (car dicts)))
+	    (cons (concat "Select " (capitalize (car dicts)) " Dict")
+		  (list 'lambda () '(interactive)
+			(list 'ispell-change-dictionary (car dicts))))))
+	(setq dicts (cdr dicts)))))
 
 
 ;;; define commands in menu in opposite order you want them to appear.
 ;;;###autoload
-(if ispell-menu-map-needed
+(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"))))
 
 ;;;###autoload
-(if ispell-menu-map-needed
+(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))
+	'(menu-item "Spell-Check Word" ispell-word
+		    :help "Spell-check word at cursor"))
       (define-key ispell-menu-map [ispell-comments-and-strings]
-	'("Check Comments" . ispell-comments-and-strings))
+	'(menu-item "Spell-Check Comments" ispell-comments-and-strings
+		    :help "Spell-check only comments and strings"))))
+
+;;;###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]
-	'("Check Region" . 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]
+	'(menu-item "Spell-Check Message" ispell-message
+		    :help "Skip headers and included message text"))
       (define-key ispell-menu-map [ispell-buffer]
-	'("Check Buffer" . ispell-buffer))))
-
-;;;###autoload
-(if ispell-menu-map-needed
-    (progn
-      (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 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 versions 19 & 20
-
-
+;;; XEmacs versions 19+
 (if (and xemacsp
 	 (not version18p)
 	 (featurep 'menubar)
+	 ;;(null ispell-menu-xemacs)
 	 (not (and (boundp 'infodock-version) infodock-version)))
-    (let ((current-menubar (or current-menubar default-menubar)))
+    (let ((dicts (valid-dictionary-list))
+	  (current-menubar (or current-menubar default-menubar))
+	  (menu
+	   '(["Help"		(describe-function 'ispell-help) t]
+	     ;;["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]
+	     ["Complete Word Frag"ispell-complete-word-interior-frag t]
+	     ["Complete Word"	ispell-complete-word		t]
+	     ["Kill Process"	ispell-kill-ispell		t]
+	     "-"
+	     ["Save Personal Dict"(ispell-pdict-save t t)	t]
+	     ["Change Dictionary" ispell-change-dictionary	t]
+	     ["Select Default"  (ispell-change-dictionary "default") t])))
+      (while dicts
+	(setq menu (append menu
+			   (list
+			     (vector
+			      (concat "Select " (capitalize (car dicts)))
+			      (list 'ispell-change-dictionary (car dicts))
+			      t)))
+	      dicts (cdr dicts)))
+      (setq ispell-menu-xemacs menu)
       (if current-menubar
 	  (progn
 	    (if (car (find-menu-item current-menubar '("Cmds")))
 		  (delete-menu-item '("Cmds" "Spell-Check"))
 		  (add-menu '("Cmds") "Spell-Check" ispell-menu-xemacs))
 	      ;; previous
-	      (delete-menu-item '("Edit" "Spell"))
+	      (delete-menu-item '("Edit" "Spell")) ; in case already defined
 	      (add-menu '("Edit") "Spell" ispell-menu-xemacs))))))
 
-;;; Allow incrementing characters as integers in XEmacs 20
+;;; Allow incrementing characters as integers in XEmacs 20+
 (if (and xemacsp
 	 (fboundp 'int-char))
     (fset 'ispell-int-char 'int-char)
 
 
 ;;; **********************************************************************
-;;; The following are used by ispell, and should not be changed.
-;;; **********************************************************************
 
 
 ;;; This variable contains the current dictionary being used if the ispell
 (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.")
 ;;; 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.")
   "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.
+Used to hold MIME boundaries.")
 
 (defconst ispell-choices-buffer "*Choices*")
 
     (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.
-    ("\\(/\\|\\(\\(\\w\\|-\\)+[.:@]\\)\\)\\(\\w\\|-\\)*\\([.:/@]+\\(\\w\\|-\\|~\\)+\\)+")
+    ;; assume multiline uuencoded file? "\nM.*$"?
+    ("^begin [0-9][0-9][0-9] [^ \t]+$" . "\nend\n")
+    ("^%!PS-Adobe-[123].0"	 . "\n%%EOF\n")
+    ("^---* \\(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\\|[-_]\\)*\\([.:/@]+\\(\\w\\|[-_~=?&]\\)+\\)+\\)")
+    ;; above checks /.\w sequences
+    ;;("\\(--+\\|\\(/\\|\\(\\(\\w\\|[-_]\\)+[.:@]\\)\\)\\(\\w\\|[-_]\\)*\\([.:/@]+\\(\\w\\|[-_~=?&]\\)+\\)+\\)")
     ;; This is a pretty complex regexp.  It can be simplified to the following:
-    ;; "\\(\\w\\|-\\)*\\([.:/@]+\\(\\w\\|-\\|~\\)+\\)+"
+    ;; "\\(\\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\\|-\\|~\\)+\\)+"
+    ;; "\\(\\w\\|[-_]\\)+[.:@]\\(\\w\\|[-_]\\)*\\([.:/@]+\\(\\w\\|[-_]\\|~\\)+\\)+"
     )
   "Alist expressing beginning and end of regions not to spell check.
 The alist key must be a regular expression.
      ;;("\\\\author"			 ispell-tex-arg-end)
      ("\\\\bibliographystyle"		 ispell-tex-arg-end)
      ("\\\\makebox"			 ispell-tex-arg-end 0)
-     ;;("\\\\epsfig"		ispell-tex-arg-end)
+     ("\\\\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)
+     ("\\(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.
 for skipping in latex mode.")
 
 
-(defcustom ispell-skip-sgml '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)
+;;;###autoload
+(defvar ispell-html-skip-alists
+  '(("<[cC][oO][dD][eE]\\>[^>]*>"	  "</[cC][oO][dD][eE]*>")
+    ("<[sS][cC][rR][iI][pP][tT]\\>[^>]*>" "</[sS][cC][rR][iI][pP][tT]>")
+    ("<[aA][pP][pP][lL][eE][tT]\\>[^>]*>" "</[aA][pP][pP][lL][eE][tT]>")
+    ("<[vV][eE][rR][bB]\\>[^>]*>"         "<[vV][eE][rR][bB]\\>[^>]*>")
+    ;;("<[tT][tT]\\>[^>]*>"		  "<[tT][tT]\\>[^>]*>")
+    ("<[tT][tT]/"			  "/")
+    ("<[^ \t\n>]"			  ">")
+    ("&[^ \t\n;]"			  "[; \t\n]"))
+  "*Lists of start and end keys to skip in HTML buffers.
+Same format as `ispell-skip-region-alist'
+Note - substrings of other matches must come last
+ (e.g. \"<[tT][tT]/\" and \"<[^ \t\n>]\").")
+
 
 (defvar ispell-local-pdict ispell-personal-dictionary
   "A buffer local variable containing the current personal dictionary.
 (defvar ispell-check-only nil
   "If non-nil, `ispell-word' does not try to correct the word.")
 
+(defun ispell-mode-line-window-height-fudge ()
+  "Return 1 if using a wide mode-line that covers text, otherwise 0."
+  (if (and xemacsp window-system) 1 0))
+
 
 ;;; **********************************************************************
 ;;; **********************************************************************
 
 
 
-(and (not version18p)
-     (not (boundp 'epoch::version))
-     (defalias 'ispell 'ispell-buffer)
-     (defalias 'ispell-check-version 'check-ispell-version))
-
-
-(if (not (fboundp 'buffer-substring-no-properties))
-    (defun buffer-substring-no-properties (start end)
-      (buffer-substring start end)))
-
 ;;;###autoload
 (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 *real* 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 (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)
   "Check spelling of word under or before the cursor.
 	(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"))
+	(ispell-send-string "%\n")	; put in verbose mode
+	(ispell-send-string (concat "^" word "\n"))
 	;; wait until ispell has processed word
 	(while (progn
-		 (accept-process-output ispell-process)
+		 (ispell-accept-output)
 		 (not (string= "" (car ispell-filter)))))
-	;;(process-send-string ispell-process "!\n") ;back to terse mode.
+	;;(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)
 	(cond ((eq poss t)
 	       (or quietly
 		   (message "%s is correct"
-			    (funcall ispell-format-word word))))
+			    (funcall ispell-format-word word)))
+	       (and (fboundp 'extent-at)
+		    (extent-at start)
+		    (delete-extent (extent-at start))))
 	      ((stringp poss)
 	       (or quietly
 		   (message "%s is correct because of root %s"
 			    (funcall ispell-format-word word)
-			    (funcall ispell-format-word poss))))
+			    (funcall ispell-format-word poss)))
+	       (and (fboundp 'extent-at)
+		    (extent-at start)
+		    (delete-extent (extent-at start))))
 	      ((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)))
+	       (if (fboundp 'make-extent)
+		   (let ((ext (make-extent start end)))
+		     (set-extent-property ext 'face ispell-highlight-face)
+		     (set-extent-property ext 'priority 2000))
+		 (beep)
+		 (message "%s is incorrect"(funcall ispell-format-word word))))
 	      (t			; prompt for correct word.
 	       (save-window-excursion
 		 (setq replace (ispell-command-loop
 ;;; a value or a list, whose value is the state of whether the
 ;;; dictionary needs to be saved.
 
-;;; ###autoload
+;;;###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."
   (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") ; save dictionary
+	    (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.
 used.
 Global `ispell-quit' set to start location to continue spell session."
   (let ((count ?0)
-	(line ispell-choices-win-default-height)
+	(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)
     (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)))
       ;; XEmacs: no need for horizontal scrollbar in choices window
       (and (fboundp 'set-specifier)
-           (featurep 'scrollbar)
-           (set-specifier horizontal-scrollbar-visible-p nil (cons (current-buffer) nil)))
+	   (boundp 'horizontal-scrollbar-visible-p)
+           (set-specifier horizontal-scrollbar-visible-p nil
+			  (cons (current-buffer) nil)))
       (erase-buffer)
       (if guess
 	  (progn
     ;; ensure word is visible
     (if (not (pos-visible-in-window-p end))
 	(sit-for 0))
-    
+
     ;; allow temporary split of dedicated windows...
     (if dedicated
 	(progn
 		  (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)))
 						      new-word)
 				    miss (lookup-words new-word)
 				    choices miss
-				    line ispell-choices-win-default-height)
+				    line (ispell-choices-win-default-height))
 			      (while (and choices ; adjust choices window.
 					  (< (if (> (+ 7 (current-column)
 						       (length (car choices))
 							  'block))
 		    t)			; reselect from new choices
 		   ((= char ?u)		; insert lowercase into dictionary
-		    (process-send-string ispell-process
-					 (concat "*" (downcase word) "\n"))
+		    (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))
 	    ;; 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)
+	      (if (< line (ispell-choices-win-default-height))
 		  (setq window-line (+ window-line
-				       (- ispell-choices-win-default-height
+				       (- (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
+	      (select-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)))
+	(ispell-overlay-window (max line (ispell-choices-win-default-height))))
       (switch-to-buffer ispell-choices-buffer)
       (goto-char (point-min)))))
 
   (if (equal ispell-help-in-bufferp 'electric)
       (progn
 	(require 'ehelp)
-	(with-electric-help 
+	(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))))
+		     ;(if (< (window-height) 15)
+		     ;	 (enlarge-window (- 15 (window-height))))
 		     (princ "Selections are:
 
 DIGIT: Replace the word with a digit offered in the *Choices* buffer.
       (save-window-excursion
 	(if ispell-help-in-bufferp
 	    (progn
-	      (ispell-overlay-window (if xemacsp 5 4))
+	      (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))))))
+	  (unwind-protect
+	      (progn
+		(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)
    (xemacsp
     (ispell-highlight-spelling-error-xemacs start end highlight))
    ((and (not version18p)
-	 (featurep 'faces) window-system)
+	 (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 refresh))))
 
 Ensure that the line above point is still visible but otherwise avoid
 scrolling the current window.  Leave the new window selected."
   (save-excursion
-    (let ((oldot (save-excursion (forward-line -1) (point)))
+    (let ((oldot (save-excursion (vertical-motion -1) (point)))
 	  (top (save-excursion (move-to-window-line height) (point))))
       ;; If line above old point (line starting at oldot) would be
       ;; hidden by new window, scroll it to just below new win
 	      (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 (and ispell-dictionary		; use specified dictionary
+	     (not (member "-d" args)))	; only define if not overriden
+	(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 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)))
     (ispell-kill-ispell t)
     (message "Starting new Ispell process...")
     (sit-for 0)
-    (setq ispell-library-path (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-library-path (check-ispell-version)
+	  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)
+	  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)
 				   (ispell-get-coding-system)))
     ;; Get version ID line
     (if (not version18p)
-	(accept-process-output ispell-process 3)
-      (accept-process-output ispell-process))
+	(ispell-accept-output 3)
+      (ispell-accept-output))
     ;; get more output if filter empty?
-    (if (null ispell-filter) (accept-process-output ispell-process 3))
+    (if (null ispell-filter) (ispell-accept-output 3))
     (cond ((null ispell-filter)
 	   (error "%s did not output version line" ispell-program-name))
 	  ((and
 	    (if (string-match "warning: " (car ispell-filter))
 		(progn
 		  (if (not version18p)
-		      (accept-process-output ispell-process 3) ; was warn msg.
-		    (accept-process-output ispell-process))
+		      (ispell-accept-output 3) ; was warn msg.
+		    (ispell-accept-output))
 		  (stringp (car ispell-filter)))
 	      (null (cdr ispell-filter)))
 	    (string-match "^@(#) " (car ispell-filter)))
 	   ;; 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-process 3)
+	   (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		; ~ extended character mode
-	  (process-send-string ispell-process
-			       (concat extended-char-mode "\n"))))
-    (process-kill-without-query ispell-process)))
+	  (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)
 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!"))
-    (process-send-eof ispell-process)
-    (if (eq (process-status ispell-process) 'run)
-	(accept-process-output ispell-process 1))
-    (if (eq (process-status ispell-process) 'run)
-	(kill-process ispell-process))
-    (while (not (or (eq (process-status ispell-process) 'exit)
-		    (eq (process-status ispell-process) 'signal)))
-      (if (or xemacsp version20p) (sleep-for 0.25)
-	(sleep-for 0 250)))
+    (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))
 
 By just answering RET you can find out what the current dictionary is.
 
-With prefix argument, set the default directory."
+With prefix argument, set the default dictionary."
   (interactive
    (list (completing-read
 	  "Use new dictionary (RET for current, SPC to complete): "
-	  (cons (cons "default" nil) ispell-dictionary-alist) nil t)
+	  (mapcar (function (lambda (dict) (setf dict (cons dict nil))))
+		  (valid-dictionary-list))
+	  nil t)
 	 current-prefix-arg))
   (if (equal dict "default") (setq dict nil))
   ;; This relies on completing-read's bug of returning "" for no match
   (interactive "r")			; Don't flag errors on read-only bufs.
   (if (not recheckp)
       (ispell-accept-buffer-local-defs)) ; set up dictionary, local words, etc.
+  (let ((skip-region-start (make-marker))
+	(rstart (make-marker)))
   (unwind-protect
       (save-excursion
 	(message "Spell checking %s using %s dictionary..."
 	  (goto-char reg-start)
 	  (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)
+		(query-fcc t)
+		in-comment
 		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)
+	    (if (re-search-forward (ispell-begin-skip-region-regexp) reg-end t)
 		(progn
 		  (setq key (buffer-substring-no-properties
 			     (match-beginning 0) (match-end 0)))
 	    (let (message-log-max)
 	      (message "Continuing spelling check using %s dictionary..."
 		       (or ispell-dictionary "default")))
+	    (set-marker rstart reg-start)
 	    (set-marker ispell-region-end reg-end)
 	    (while (and (not ispell-quit)
 			(< (point) ispell-region-end))
 	      (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))
+		    ;; If region inside line comment, must keep comment start.
+		    (setq in-comment (point)
+			  in-comment
+			  (and comment-start
+			       (or (null comment-end) (string= "" comment-end))
+			       (save-excursion
+				 (beginning-of-line)
+				 (re-search-forward comment-start in-comment t))
+			       comment-start))
+		    ;; Can change skip-regexps (in ispell-message)
+		    (ispell-skip-region key) ; moves pt past region.
+		    (set-marker rstart (point))
+		    ;; check for saving large attachments...
+		    (setq query-fcc (and query-fcc
+					 (ispell-ignore-fcc skip-region-start
+							    rstart)))
+		    (if (and (< rstart ispell-region-end)
+			     (re-search-forward
+			      (ispell-begin-skip-region-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))
+			  (goto-char rstart))
 		      (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)))
+	      (setq reg-end (max (point)
+				 (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)))
+		     (string (ispell-get-line start end reg-end in-comment)))
+		(if in-comment		; account for comment chars added
+		    (setq start (- start (length in-comment))
+			  in-comment nil))
 		(setq end (point))	; "end" tracks region retrieved.
 		(if string		; there is something to spell check!
 		    ;; (special start end)
     (if (and (not (and recheckp ispell-keep-choices-win))
 	     (get-buffer ispell-choices-buffer))
 	(kill-buffer ispell-choices-buffer))
+    (set-marker skip-region-start nil)
+    (set-marker rstart nil)
     (if ispell-quit
 	(progn
 	  ;; preserve or clear the region for ispell-continue.
       (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.
+      (message "Spell-checking done")))))
+
+
 (defun ispell-begin-skip-region-regexp ()
-  (let ((skip-regexp (ispell-begin-skip-region)))
+  "Returns a regexp of the search keys for region skipping.
+Includes `ispell-skip-region-alist' plus tex, tib, html, and comment keys.
+Must call after ispell-buffer-local-parsing due to dependence on mode."
+  ;; start with regions generic to all buffers
+  (let ((skip-regexp (ispell-begin-skip-region ispell-skip-region-alist)))
+    ;; Comments
     (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)
+	;; search from end of current comment to start of next comment.
 	(setq skip-regexp (concat (if (string= "" comment-end) "^"
 				    (regexp-quote comment-end))
 				  "\\|" skip-regexp)))
+    ;; tib
     (if ispell-skip-tib
 	(setq skip-regexp (concat ispell-tib-ref-beginning "\\|" skip-regexp)))
-    (if ispell-skip-sgml
-	(setq skip-regexp (concat "<author>" "\\|"
-				  "<[cC][oO][dD][eE]>" "\\|" 
-				  "<[vV][eE][rR][bB]>" "\\|" 
-				  ;; "<[tT][tT]>" "\\|"
-				  "<[tT][tT]/" "\\|"
-				  "</" "\\|"
-				  "<" "\\|"
-				  "&" "\\|"
-				  skip-regexp)))
+    ;; html stuff
+    (if ispell-skip-html
+	(setq skip-regexp (concat
+			   (ispell-begin-skip-region ispell-html-skip-alists)
+			   "\\|"
+			   skip-regexp)))
+    ;; tex
     (if (eq ispell-parser 'tex)
 	(setq skip-regexp (concat (ispell-begin-tex-skip-regexp) "\\|"
 				  skip-regexp)))
+    ;; messages
+    (if (and ispell-checking-message
+	     (not (eq t ispell-checking-message)))
+	(setq skip-regexp (concat
+			   (mapconcat
+			    (function (lambda (lst) (car lst)))
+			    ispell-checking-message
+			    "\\|")
+			   "\\|"
+			   skip-regexp)))
+
+    ;; return new regexp
     skip-regexp))
 
 
+(defun ispell-begin-skip-region (skip-alist)
+  "Regular expression for start of regions to skip generated from SKIP-ALIST.
+Each selection should be a key of SKIP-ALIST;
+otherwise, the current line is skipped."
+  (mapconcat (function (lambda (lst)
+			 (if (stringp (car lst))
+			     (car lst)
+			   (eval (car lst)))))
+	     skip-alist
+	     "\\|"))
+
+
 (defun ispell-begin-tex-skip-regexp ()
   "Regular expression of tex commands to skip.
 Generated from `ispell-tex-skip-alists'."
   (concat
+   ;; raw tex keys
    (mapconcat (function (lambda (lst) (car lst)))
 	      (car ispell-tex-skip-alists)
 	      "\\|")
    "\\|"
+   ;; keys wrapped in begin{}
    (mapconcat (function (lambda (lst)
 			  (concat "\\\\begin[ \t\n]*{[ \t\n]*"
 				  (car lst)
 	      "\\|")))
 
 
-(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-skip-region-list ()
+  "Returns a list describing key and body regions to skip for this buffer.
+Includes regions defined by `ispell-skip-region-alist', tex mode,
+`ispell-html-skip-alists', and `ispell-checking-message'.
+Manual checking must include comments and tib references.
+The list is of the form described by variable `ispell-skip-region-alist'.
+Must call after `ispell-buffer-local-parsing' due to dependence on mode."
+  (let ((skip-alist ispell-skip-region-alist))
+    ;; only additional explicit region definition is tex.
+    (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)))
+    (if ispell-skip-html
+	(setq skip-alist (append ispell-html-skip-alists skip-alist)))
+    (if (and ispell-checking-message
+	     (not (eq t ispell-checking-message)))
+	(setq skip-alist (append ispell-checking-message skip-alist)))
+    skip-alist))
 
 
 (defun ispell-tex-arg-end (&optional arg)
+  "Skip across ARG number of braces."
   (condition-case nil
       (progn
 	(while (looking-at "[ \t\n]*\\[") (forward-sexp))
      (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)
+(defun ispell-ignore-fcc (start end)
+  "Deletes the Fcc: message header when large attachments are included.
+Return value `nil' if file with large attachments are saved.
+This can be used to avoid multiple questions for multiple large attachments.
+Returns point to starting location afterwards."
+  (let ((result t))
+    (if (and ispell-checking-message ispell-message-fcc-skip)
+	(if (< ispell-message-fcc-skip (- end start))
+	    (let (case-fold-search head-end)
+	      (goto-char (point-min))
+	      (setq head-end
+		    (or (re-search-forward
+			 (concat "^" (regexp-quote mail-header-separator) "$")
+			 nil t)
+			(re-search-forward "^$" nil t)
+			(point-min)))
+	      (goto-char (point-min))
+	      (if (re-search-forward "^Fcc:" head-end t)
+		  (if (y-or-n-p
+		       "Save copy of this message with large attachments? ")
+		      (setq result nil)
+		    (beginning-of-line)
+		    (kill-line 1)))
+	      (goto-char end))))
+    result))
+
+
+(defun ispell-skip-region (key)
+  "Skips across KEY and then to end of region.
+Key lookup determines region to skip.
+Point is placed at end of skipped region."
   ;; move over key to begin checking.
   (forward-char (length key))
   (let ((start (point))
+	;; Regenerate each call... This function can change region definition.
+	(alist (ispell-skip-region-list))
 	alist-key null-skip)
     (cond
      ;; what about quoted comment, or comment inside strings?
       (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-sgml (string-match "<author>" key))
-      (search-forward-regexp ".$" ispell-region-end t))
-     ((and ispell-skip-sgml (string-match "</" key))
-      (search-forward ">" ispell-region-end t))
-     ((and ispell-skip-sgml (string-match "<[cC][oO][dD][eE]>" key))  
-      (search-forward-regexp "</[cC][oO][dD][eE]>" ispell-region-end t))
-     ((and ispell-skip-sgml (string-match "<[vV][eE][rR][bB]>" key))  
-      (search-forward-regexp "</[vV][eE][rR][bB]>" ispell-region-end t))
-     ;;((and ispell-skip-sgml (string-match "<[tT][tT]>" key))  
-     ;; (search-forward-regexp "</[tT][tT]>" ispell-region-end t))     
-     ((and ispell-skip-sgml (string-match "<[tT][tT]/" key))  
-      (search-forward "/" ispell-region-end t))      
-     ((and ispell-skip-sgml (string-match "<" key))
-      (search-forward-regexp "[/>]" ispell-region-end t))
-     ((and ispell-skip-sgml (string-match "&" key))
-      (search-forward ";" ispell-region-end t))
      ;; markings from alist
      (t
       (while alist
 	       ((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))))))
+	       ((and (= 1 (length alist))
+		     (stringp (car alist)))
+		(re-search-forward (car alist) (point-max) t))
+	       (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))
 
 ;;; Grab the next line of data.
 ;;; Returns a string with the line data
-(defun ispell-get-line (start end reg-end)
+(defun ispell-get-line (start end reg-end in-comment)
   (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
+     ;;((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)
+      (setq string (concat "^" in-comment
+			   (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.
     (if (not (numberp shift))
 	(setq shift 0))
     ;; send string to spell process and get input.
-    (process-send-string ispell-process string)
+    (ispell-send-string string)
     (while (progn
-	     (accept-process-output ispell-process)
+	     (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.
 	    ;; `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
+		      ;; NB: Search can fail with Mule coding systems that don't
 		      ;;  display properly.  Ignore the error in this case?
 		      (search-forward (car poss) (+ word-len (point)) t)
 		      (point)))
 		  (set-marker line-end (point))
 		  (setq ispell-filter nil
 			recheck-region t)))
-				       
+
 	    ;; insert correction if needed
 	    (cond
 	     ((or (null replace)
 		    (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))
 	      ;; Move line-start across word...
 	      ;; new shift function does this now...
 	      ;;(set-marker line-start (+ line-start
-		;;			(- (length replace)
-		;;			   (length (car poss)))))
+	      ;;			(- (length replace)
+	      ;;			   (length (car poss)))))
 	      ))
 	    (if (not ispell-quit)
 		(let (message-log-max)
   (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 (boundp 'transient-mark-mode) transient-mark-mode
+	   (boundp 'mark-active) mark-active)
+      (ispell-region (region-beginning) (region-end))
+    (ispell-buffer)))
+
+
 ;;; **********************************************************************
 ;;; 			Ispell Minor Mode
 ;;; **********************************************************************
 (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.
 
 	(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.
 Don't check previous word when character before point is a space or newline.
 	(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-sgml (eq last-char ?>))
-		 (and ispell-skip-sgml (eq last-char ?\;))))
+		 (and ispell-skip-html (eq last-char ?>))
+		 (and ispell-skip-html (eq last-char ?\;))))
 	(ispell-word nil t))))
 
 
 ;;; **********************************************************************
 ;;; 			Ispell Message
 ;;; **********************************************************************
-;;; Original from D. Quinlan, E. Bradford, A. Albert, and M. Ernst
-
 
 (defvar ispell-message-text-end
   (mapconcat (function identity)
 	       ;; Don't spell check signatures
 	       "^-- $"
 	       ;; Matches postscript files.
-	       "^%!PS-Adobe-[123].0"
+	       ;;"^%!PS-Adobe-[123].0"
 	       ;; Matches uuencoded text
-	       "^begin [0-9][0-9][0-9] .*\nM.*\nM.*\nM"
+	       ;;"^begin [0-9][0-9][0-9] .*\nM.*\nM.*\nM"
 	       ;; Matches shell files (especially auto-decoding)
 	       "^#! /bin/[ck]?sh"
 	       ;; Matches context difference listing
 Otherwise, it must be a function which is called to get the limit.")
 
 
+(defun ispell-mime-multipartp (&optional limit)
+  "Return multipart message start boundary or nil if none."
+  ;; caller must ensure `case-fold-search' is set to `t'
+  (and
+   (re-search-forward
+    "Content-Type: *multipart/\\([^ \t\n]*;[ \t]*[\n]?[ \t]*\\)+boundary="
+    limit t)
+   (let (boundary)
+     (if (looking-at "\"")
+	 (let (start)
+	   (forward-char)
+	   (setq start (point))
+	   (while (not (looking-at "\""))
+	     (forward-char 1))
+	   (setq boundary (buffer-substring-no-properties start (point))))
+       (let ((start (point)))
+	 (while (looking-at "[-0-9a-zA-Z'()+_,./:=?]")
+	   (forward-char))
+	 (setq boundary (buffer-substring-no-properties start (point)))))
+     (if (< (length boundary) 1)
+	 (setq boundary nil)
+       (concat "--" boundary)))))
+
+
+(defun ispell-mime-skip-part (boundary)
+  "Moves point across header, or entire MIME part if message is encoded.
+All specified types except `7bit' `8bit' and `quoted-printable' are considered
+encoded and therefore skipped.  See rfc 1521, 2183, ...
+If no boundary is given, then entire message is skipped.
+
+This starts one line ABOVE the MIME content messages, on the boundary marker,
+for operation with the generic region-skipping code.
+This places new MIME boundaries into variable `ispell-checking-message'."
+  (forward-line)			; skip over boundary to headers
+  (let ((save-case-fold-search case-fold-search)
+	(continuep t)
+	textp)
+    (setq case-fold-search t
+	  ispell-skip-html nil)
+    (while continuep
+      (setq continuep nil)
+      (if (looking-at "Content-Type: *text/")
+	  (progn
+	    (goto-char (match-end 0))
+	    (if (looking-at "html")
+		(setq ispell-skip-html t))
+	    (setq textp t
+		  continuep t)
+	    (re-search-forward "\\(.*;[ \t]*[\n]\\)*.*$" nil t)
+	    (forward-line)))
+      (if (looking-at "Content-Transfer-Encoding: *\\([^ \t\n]*\\)")
+	  (let ((match (buffer-substring (match-beginning 1) (match-end 1))))
+	    (setq textp (member (upcase match)
+				;; only spell check the following encodings:
+				'("7BIT" "8BIT" "QUOTED-PRINTABLE" "BINARY"))
+		  continuep t)
+	    (goto-char (match-end 0))
+	    (re-search-forward "\\(.*;[ \t]*[\n]\\)*.*$" nil t)
+	    (forward-line)))
+      ;; hierarchical boundary definition
+      (if (looking-at "Content-Type: *multipart/")
+	  (let ((new-boundary (ispell-mime-multipartp)))
+	    (if (string-match new-boundary boundary)
+		(setq continuep t)
+	      ;; first pass redefine skip function to include new boundary
+	      ;;(re-search-backward boundary nil t)
+	      (forward-line)
+	      (setq ispell-checking-message
+		    (cons
+		     (list new-boundary 'ispell-mime-skip-part new-boundary)
+		     (if (eq t ispell-checking-message) nil
+		       ispell-checking-message))
+		    textp t
+		    continuep t)))
+	;; Skip all MIME headers that don't affect spelling
+	(if (looking-at "Content-[^ \t]*: *\\(.*;[ \t]*[\n]\\)*.*$")
+	    (progn
+	      (setq continuep t)
+	      (goto-char (match-end 0))
+	      (forward-line)))))
+
+    (setq case-fold-search save-case-fold-search)
+    (if textp
+	(point)
+      ;; encoded message.  Skip to boundary, or entire message.
+      (if (not boundary)
+	  (goto-char (point-max))
+	(re-search-forward boundary nil t)
+	(beginning-of-line)
+	(point)))))
+
 
 ;;;###autoload
 (defun ispell-message ()
   (interactive)
   (save-excursion
     (goto-char (point-min))
-    (let* (
+    (let* (boundary mimep
+	   (ispell-skip-region-alist-save ispell-skip-region-alist)
 	   ;; Nil when message came from outside (eg calling emacs as editor)
 	   ;; Non-nil marker of end of headers.
 	   (internal-messagep
 	  (progn
 	    ;; Spell check any original Subject:
 	    (goto-char (point-min))
-	    (setq case-fold-search t)
+	    (setq case-fold-search t
+		  mimep (re-search-forward "MIME-Version:" end-of-headers t))
+	    (goto-char (point-min))
 	    (if (re-search-forward "^Subject: *" end-of-headers t)
 		(progn
 		  (goto-char (match-end 0))
 					 (while (looking-at "\n[ \t]")
 					   (end-of-line 2))
 					 (point)))))))
+	    (if mimep
+		(progn
+		  (goto-char (point-min))
+		  (setq boundary (ispell-mime-multipartp end-of-headers))))
+	    ;; Adjust message limit to MIME message if necessary.
+	    (and boundary
+		 (re-search-forward (concat boundary "--") nil t)
+		 (re-search-backward boundary nil t)
+		 (< (point) (marker-position limit))
+		 (set-marker limit (point)))
+	    (goto-char (point-min))
+	    ;; Select type or skip checking if this is a non-multipart message
+	    ;; Point moved to end of buffer if region is encoded.
+	    (if (and mimep (not boundary))
+		(let (skip-regexp)	; protect from `ispell-mime-skip-part'
+		  (goto-char (point-min))
+		  (re-search-forward "Content-[^ \t]*:" end-of-headers t)
+		  (forward-line -1)	; following fn starts one line above
+		  (ispell-mime-skip-part nil)
+		  ;; if message-text-end region, limit may be less than point.
+		  (if (> (point) limit)
+		      (set-marker limit (point)))))
+	    (goto-char (max end-of-headers (point)))
+	    (forward-line 1)
 	    (setq case-fold-search old-case-fold-search)
-	    (goto-char end-of-headers)
-	    (forward-line 1)
+	    ;; Define MIME regions to skip.
+	    (if boundary
+		(setq ispell-checking-message
+		      (list (list boundary 'ispell-mime-skip-part boundary))))
 	    (ispell-region (point) limit))
 	(set-marker end-of-headers nil)
-	(set-marker limit nil)))))
+	(set-marker limit nil)
+	(setq ispell-skip-region-alist ispell-skip-region-alist-save
+	      ispell-skip-html nil
+	      case-fold-search old-case-fold-search)))))
 
 
 (defun ispell-non-empty-string (string)
 Overrides the default parsing 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
   ;; When exclusively checking comments, set to raw text mode (nroff).
   (if (and (not (eq 'exclusive ispell-check-comments))
 				  (symbol-name major-mode)))
 	       (eq ispell-parser 'tex)))
       (progn
-	(process-send-string ispell-process "+\n") ; set ispell mode to tex
+	(ispell-send-string "+\n")	; set ispell mode to tex
 	(if (not (eq ispell-parser 'tex))
 	    (set (make-local-variable 'ispell-parser) 'tex)))
-    (process-send-string ispell-process "-\n"))	; set mode to normal (nroff)
+    (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-sgml (not (eq ispell-skip-sgml t)))
-      (set (make-local-variable 'ispell-skip-sgml)
-	   (not (null (string-match "sgml\\|html"
-				    (downcase (symbol-name major-mode)))))))
+  (if (and ispell-skip-html (not (eq ispell-skip-html t)))
+      (setq 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-max))
 				    (match-beginning 1) (match-end 1))))
 	    (cond ((and (string-match "latex-mode" string)
 			(not (eq 'exclusive ispell-check-comments)))
-		   (process-send-string ispell-process "+\n~tex\n"))
+		   (ispell-send-string "+\n~tex\n"))
 		  ((string-match "nroff-mode" string)
-		   (process-send-string ispell-process "-\n~nroff\n"))
+		   (ispell-send-string "-\n~nroff\n"))
 		  ((string-match "~" string) ; Set extended character mode.
-		   (process-send-string ispell-process (concat string "\n")))
+		   (ispell-send-string (concat string "\n")))
 		  (t (message "Invalid Ispell Parsing argument!")
 		     (sit-for 2))))))))
 
 	(ispell-kill-ispell t)
 	(setq ispell-personal-dictionary ispell-local-pdict)))
   ;; Reload if new dictionary defined.
-  (if (and ispell-local-dictionary
-	   (not (equal ispell-local-dictionary ispell-dictionary)))
+  (if (not (equal ispell-local-dictionary ispell-dictionary))
       (ispell-change-dictionary ispell-local-dictionary)))
 
 
 	  ;; Error handling needs to be added between ispell and emacs.
 	  (if (and (< 1 (length string))
 		   (equal 0 (string-match ispell-casechars string)))
-	      (process-send-string ispell-process
-				   (concat "@" string "\n"))))))))
+	      (ispell-send-string (concat "@" string "\n"))))))))
 
 
 ;;; returns optionally adjusted region-end-point.
   (save-excursion
     (goto-char (point-min))
     (let ((old-case-fold-search case-fold-search)
-	  line-okay search done string)
+	  line-okay search done found)
       (while (not done)
 	(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)
 	      (if (null search)
 		  (progn
 		    (open-line 1)
-		    (setq string (concat comment-start " "
-					 ispell-words-keyword))
-		    (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)))))
 	      (insert (concat " " word))))))))
 
+(add-to-list 'debug-ignored-errors "^No word found to check!$")
 
 (provide 'ispell)
 
 ; 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:  fn hilight oldot NB AIX msg init read's bufs pt cmd eg multibyte
 ; LocalWords:  uuencoded unidiff sc nn VM SGML eval IspellPersDict unsplitable
-; LocalWords:  lns XEmacs HTML casechars Multibyte
+; LocalWords:  lns XEmacs html casechars Multibyte Aug unix wp iso multiline
+; LocalWords:  multipart aspell Fcc regexps tib russian latin Slovakian
 
 ;;; ispell.el ends here