]> git.eshelyaron.com Git - emacs.git/commitdiff
Version 3.5
authorStefan Monnier <monnier@iro.umontreal.ca>
Tue, 13 May 2003 22:52:20 +0000 (22:52 +0000)
committerStefan Monnier <monnier@iro.umontreal.ca>
Tue, 13 May 2003 22:52:20 +0000 (22:52 +0000)
lisp/textmodes/ispell.el

index 9788eb18ec830652e3374d3276f1f571672226de..cc22053f80d80df8343e072d21dec09e44f29edc 100644 (file)
@@ -1,11 +1,11 @@
 ;;; ispell.el --- Interface to International Ispell Versions 3.1 and 3.2
 
-;; Copyright (C) 1994, 1995, 1997, 1998, 1999, 2000, 2003 Free Software Foundation, Inc.
+;; Copyright (C) 1994, 1995, 1997, 1998, 1999, 2000, 2001, 2003 Free Software Foundation, Inc.
 
-;; Author: Ken Stevens <k.stevens@ieee.org>
-;; Maintainer: <k.stevens@ieee.org>
-;; Stevens Mod Date: Fri Aug  4 09:41:50 PDT 2000
-;; Stevens Revision: 3.4
+;; Author:          Ken Stevens <k.stevens@ieee.org>
+;; Maintainer:      Ken Stevens <k.stevens@ieee.org>
+;; Stevens Mod Date: Wed Jul 11 18:43:57 PDT 2001
+;; Stevens Revision: 3.5
 ;; 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
 
 ;; Modifications made in latest versions:
 
+;; 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.
 ;; 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)
+;; 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
 
 (and (not version18p)
      (not (boundp 'epoch::version))
-     (defalias 'ispell 'ispell-buffer)
      (defalias 'ispell-check-version 'check-ispell-version))
 
 
@@ -279,12 +287,9 @@ This minimizes redisplay thrashing."
   :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)
 
@@ -322,6 +327,13 @@ E.g. you may use the following value:
   :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
@@ -455,11 +467,15 @@ for language-specific arguments."
   "*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."
+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
@@ -586,10 +602,14 @@ See `ispell-dictionary-alist'."
    ("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 ( "-d" "polish") 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" "-d" "portugues") "~latin1" iso-8859-1)))
 
 
 ;;; Sixth part of dictionary, shortened for loaddefs.el
@@ -602,13 +622,16 @@ See `ispell-dictionary-alist'."
     "[\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)
+   ("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" "-d" "slovak") 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
@@ -704,9 +727,10 @@ LANGUAGE.aff file \(e.g., english.aff\)."
 (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")
+(defconst ispell-version "ispell.el 3.5 - 07/11/01")
 
 
+;;;###autoload
 (defun check-ispell-version (&optional interactivep)
   "Ensure that `ispell-program-name' is valid and the correct version.
 Returns version number if called interactively.
@@ -726,20 +750,25 @@ Otherwise returns the library path if defined."
     (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
@@ -800,12 +829,27 @@ and added as a submenu of the \"Edit\" menu.")
        (not xemacsp)
        'reload))
 
-(defvar ispell-library-path (check-ispell-version)
+(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
+(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)))
          ;; `ispell-library-path' intentionally not defined in autoload
          (path (and (boundp 'ispell-library-path) ispell-library-path))
@@ -837,14 +881,16 @@ and added as a submenu of the \"Edit\" menu.")
 
 ;;; 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]
        '(menu-item "Change Dictionary..." ispell-change-dictionary
                    :help "Supply explicit path to dictionary"))
       (define-key ispell-menu-map [ispell-kill-ispell]
        '(menu-item "Kill Process" ispell-kill-ispell
-                   :enable (and ispell-process
+                   :enable (and (boundp 'ispell-process) ispell-process
                                 (eq (ispell-process-status) 'run))
                    :help "Terminate Ispell subprocess"))
       (define-key ispell-menu-map [ispell-pdict-save]
@@ -864,7 +910,9 @@ and added as a submenu of the \"Edit\" menu.")
                    :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]
        '(menu-item "Continue Spell-Checking" ispell-continue
@@ -880,9 +928,10 @@ and added as a submenu of the \"Edit\" menu.")
        '(menu-item "Spell-Check Comments" ispell-comments-and-strings
                    :help "Spell-check only comments and strings"))))
 
-
 ;;;###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-region]
        '(menu-item "Spell-Check Region" ispell-region
@@ -897,7 +946,7 @@ and added as a submenu of the \"Edit\" menu.")
       ;;(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)
@@ -946,10 +995,16 @@ and added as a submenu of the \"Edit\" menu.")
       (setq ispell-menu-xemacs menu)
       (if current-menubar
          (progn
-           (delete-menu-item '("Edit" "Spell")) ; in case already defined
-           (add-menu '("Edit") "Spell" ispell-menu-xemacs)))))
-
-;;; Allow incrementing characters as integers in XEmacs 20
+           (if (car (find-menu-item current-menubar '("Cmds")))
+               (progn
+                 ;; XEmacs 21.2
+                 (delete-menu-item '("Cmds" "Spell-Check"))
+                 (add-menu '("Cmds") "Spell-Check" ispell-menu-xemacs))
+             ;; previous
+             (delete-menu-item '("Edit" "Spell")) ; in case already defined
+             (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)
@@ -999,17 +1054,6 @@ Protects against bogus binding of `enable-multibyte-characters' in XEmacs."
 (defun ispell-get-coding-system ()
   (nth 7 (assoc ispell-dictionary ispell-dictionary-alist)))
 
-(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.")
 
 (defvar ispell-pdict-modified-p nil
   "Non-nil means personal dictionary has modifications to be saved.")
@@ -1044,7 +1088,8 @@ Protects against bogus binding of `enable-multibyte-characters' in XEmacs."
   "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*")
 
@@ -1084,10 +1129,14 @@ The last occurring definition in the buffer will be used.")
     (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")
+    ;; 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\\|[-_]\\)+[.:@]\\)\\)\\(\\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
@@ -1118,13 +1167,13 @@ Valid forms include:
      ;;("\\\\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.
@@ -1135,6 +1184,22 @@ Delete or add any regions you want to be automatically selected
 for skipping in latex mode.")
 
 
+;;;###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.
 If non-nil, the value must be a string, which is a file name.
@@ -1167,6 +1232,20 @@ You can set this variable in hooks in your init file -- eg:
 (defvar ispell-check-only nil
   "If non-nil, `ispell-word' does not try to correct the word.")
 
+(defconst ispell-graphic-p
+  (if (fboundp 'display-graphic-p)
+      (display-graphic-p)
+    xemacsp)
+  "True if running on a `graphics capable' display.
+These displays have thicker mode lines that can partially cover text.")
+
+(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)))
+
 
 ;;; **********************************************************************
 ;;; **********************************************************************
@@ -1210,7 +1289,7 @@ pass it the output of the last ispell invocation."
     ;; 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.
+         ;; we have a *real* reason to invoke it.
          (cmds-to-defer '(?* ?@ ?~ ?+ ?- ?! ?%))
          (default-major-mode 'fundamental-mode)
          (session-buf ispell-session-buffer)
@@ -1225,9 +1304,8 @@ pass it the output of the last ispell invocation."
        (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))
+             (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)
@@ -1297,7 +1375,7 @@ This will check or reload the dictionary.  Use \\[ispell-change-dictionary]
 or \\[ispell-region] to update the Ispell process.
 
 return values:
-nil           word is correct or spelling is accpeted.
+nil           word is correct or spelling is accepted.
 0             word is inserted into buffer-local definitions.
 \"word\"        word corrected from word list.
 \(\"word\" arg\)  word is hand entered.
@@ -1339,16 +1417,26 @@ quit          spell session exited."
        (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
@@ -1454,7 +1542,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
+;;;###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."
@@ -1470,6 +1558,14 @@ If so, ask if it needs to be saved."
   (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.
@@ -1485,11 +1581,11 @@ indicates whether the dictionary has been modified when option `a' or `i' is
 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)
@@ -1499,9 +1595,11 @@ Global `ispell-quit' set to start location to continue spell session."
     (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)
+          (boundp 'horizontal-scrollbar-visible-p)
+           (set-specifier horizontal-scrollbar-visible-p nil
+                         (cons (current-buffer) nil)))
       (erase-buffer)
       (if guess
          (progn
@@ -1660,7 +1758,7 @@ Global `ispell-quit' set to start location to continue spell session."
                                                      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))
@@ -1762,19 +1860,19 @@ Global `ispell-quit' set to start location to continue spell session."
            ;; 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)))))
 
@@ -1847,19 +1945,22 @@ SPC:   Accept word this time.
       (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)
@@ -2037,7 +2138,7 @@ The variable `ispell-highlight-face' selects the face to use for highlighting."
 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
@@ -2065,7 +2166,7 @@ scrolling the current window.  Leave the new window selected."
    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 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...
@@ -2164,8 +2265,8 @@ Keeps argument list for future ispell invocations for no async support."
     (ispell-kill-ispell t)
     (message "Starting new Ispell process...")
     (sit-for 0)
-    (check-ispell-version)
-    (setq ispell-process-directory default-directory
+    (setq ispell-library-path (check-ispell-version)
+         ispell-process-directory default-directory
          ispell-process (ispell-start-process)
          ispell-filter nil
          ispell-filter-continue nil)
@@ -2254,7 +2355,7 @@ A new one will be started as soon as necessary.
 
 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): "
@@ -2302,6 +2403,8 @@ Return nil if spell session is quit,
   (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..."
@@ -2313,19 +2416,12 @@ Return nil if spell session is quit,
          (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)))
@@ -2334,6 +2430,7 @@ Return nil if spell session is quit,
            (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))
@@ -2341,25 +2438,44 @@ Return nil if spell session is quit,
              (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)
@@ -2373,6 +2489,8 @@ Return nil if spell session is quit,
     (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.
@@ -2389,48 +2507,74 @@ Return nil if spell session is quit,
       (if (not recheckp) (set-marker ispell-region-end nil))
       ;; Only save if successful exit.
       (ispell-pdict-save ispell-silently-savep)
-      (message "Spell-checking done"))))
+      (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)))
+  "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)))
+    ;; html stuff
     (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)))
+       (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)
@@ -2439,17 +2583,30 @@ Generated from `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-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))
@@ -2460,12 +2617,42 @@ otherwise, the current line is skipped."
      (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 quesitons 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?
@@ -2479,26 +2666,6 @@ otherwise, the current line is skipped."
       (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
@@ -2511,13 +2678,14 @@ otherwise, the current line is skipped."
               ((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))
@@ -2530,7 +2698,7 @@ otherwise, the current line is skipped."
 
 ;;; 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
@@ -2540,7 +2708,8 @@ otherwise, the current line is skipped."
      ;; (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.
@@ -2592,6 +2761,7 @@ Returns the sum shift due to changes in word replacements."
            (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))
@@ -2681,8 +2851,8 @@ Returns the sum shift due to changes in word replacements."
              ;; 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)
@@ -2831,6 +3001,23 @@ 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 (boundp 'transient-mark-mode) transient-mark-mode
+          (boundp 'mark-active) mark-active)
+      (ispell-region (region-beginning) (region-end))
+    (ispell-buffer)))
+
+
 ;;; **********************************************************************
 ;;;                    Ispell Minor Mode
 ;;; **********************************************************************
@@ -2891,7 +3078,6 @@ Don't read buffer-local settings or word lists."
 ;;; **********************************************************************
 ;;;                    Ispell Message
 ;;; **********************************************************************
-;;; Original from D. Quinlan, E. Bradford, A. Albert, and M. Ernst
 
 
 (defvar ispell-message-text-end
@@ -2900,9 +3086,9 @@ Don't read buffer-local settings or word lists."
               ;; 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
@@ -2919,6 +3105,97 @@ 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.")
 
 
+(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 ()
@@ -2943,7 +3220,8 @@ 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* (
+    (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
@@ -3013,7 +3291,9 @@ You can bind this to the key C-c i in GNUS or mail by adding to
          (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))
@@ -3027,12 +3307,38 @@ You can bind this to the key C-c i in GNUS or mail by adding to
                                         (while (looking-at "\n[ \t]")
                                           (end-of-line 2))
                                         (point)))))))
-           (setq case-fold-search old-case-fold-search)
-           (goto-char end-of-headers)
+           (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)))
+           (goto-char (max end-of-headers (point)))
            (forward-line 1)
+           (setq case-fold-search old-case-fold-search)
+           ;; 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)
@@ -3073,9 +3379,9 @@ Includes Latex/Nroff modes and extended character mode."
     (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)))))))
+      (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
@@ -3242,8 +3548,9 @@ Both should not be used to define a buffer-local dictionary."
 ; 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