Replace still more end-of-line etc with line-end-position, etc.
[bpt/emacs.git] / lisp / textmodes / ispell.el
index 2f7b2e6..9a49489 100644 (file)
@@ -1,7 +1,8 @@
 ;;; ispell.el --- interface to International Ispell Versions 3.1 and 3.2
 
 ;; Copyright (C) 1994, 1995, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
-;;   2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
+;;   2004, 2005, 2006, 2007, 2008, 2009, 2010
+;;   Free Software Foundation, Inc.
 
 ;; Author:           Ken Stevens <k.stevens@ieee.org>
 ;; Maintainer:       Ken Stevens <k.stevens@ieee.org>
 ;; Improved message reference matching in `ispell-message'.
 ;; Fixed bug in returning to nroff mode from tex mode.
 
-;;; Compatibility code for xemacs and (not too) older emacsen:
+;;; Compatibility code for XEmacs and (not too) older emacsen:
 
-(eval-and-compile ;; Protect against declare-function undefined in xemacs
+(eval-and-compile ;; Protect against declare-function undefined in XEmacs
   (unless (fboundp 'declare-function) (defmacro declare-function (&rest r))))
 
 (declare-function ispell-check-minver "ispell" (v1 v2))
+(declare-function ispell-looking-back "ispell"
+                 (regexp &optional limit &rest ignored))
 
 (if (fboundp 'version<=)
     (defalias 'ispell-check-minver 'version<=)
@@ -219,10 +222,10 @@ compatibility function in case `version<=' is not available."
        (let (ver mver)
          (if (string-match "[0-9]+" version start-ver)
              (setq start-ver (match-end 0)
-                   ver (string-to-number (substring version (match-beginning 0) (match-end 0)))))
+                   ver (string-to-number (match-string 0 version))))
          (if (string-match "[0-9]+" minver start-mver)
              (setq start-mver (match-end 0)
-                   mver (string-to-number (substring minver (match-beginning 0) (match-end 0)))))
+                   mver (string-to-number (match-string 0 minver))))
 
          (if (or ver mver)
              (progn
@@ -238,6 +241,21 @@ compatibility function in case `version<=' is not available."
            (setq pending nil))))
       return)))
 
+;; XEmacs does not have looking-back
+(if (fboundp 'looking-back)
+    (defalias 'ispell-looking-back 'looking-back)
+  (defun ispell-looking-back (regexp &optional limit &rest ignored)
+    "Return non-nil if text before point matches regular expression REGEXP.
+Like `looking-at' except matches before point, and is slower.
+LIMIT if non-nil speeds up the search by specifying a minimum
+starting position, to avoid checking matches that would start
+before LIMIT.
+
+This is a stripped down compatibility function for use when
+full featured `looking-back' function is missing."
+    (save-excursion
+      (re-search-backward (concat "\\(?:" regexp "\\)\\=") limit t))))
+
 ;;; Code:
 
 (defvar mail-yank-prefix)
@@ -293,7 +311,9 @@ Warning!  Not checking comments, when a comment start is embedded in strings,
 may produce undesired results."
   :type '(choice (const exclusive) (const :tag "off" nil) (const :tag "on" t))
   :group 'ispell)
-;;;###autoload(put 'ispell-check-comments 'safe-local-variable (lambda (a) (memq a '(nil t exclusive))))
+;;;###autoload
+(put 'ispell-check-comments 'safe-local-variable
+     (lambda (a) (memq a '(nil t exclusive))))
 
 (defcustom ispell-query-replace-choices nil
   "*Corrections made throughout region when non-nil.
@@ -340,21 +360,21 @@ Must be greater than 1."
   :group 'ispell)
 
 (defcustom ispell-alternate-dictionary
-  (cond ((file-exists-p "/usr/dict/web2") "/usr/dict/web2")
-       ((file-exists-p "/usr/share/dict/web2") "/usr/share/dict/web2")
-       ((file-exists-p "/usr/dict/words") "/usr/dict/words")
-       ((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")
+  (cond ((file-readable-p "/usr/dict/web2") "/usr/dict/web2")
+       ((file-readable-p "/usr/share/dict/web2") "/usr/share/dict/web2")
+       ((file-readable-p "/usr/dict/words") "/usr/dict/words")
+       ((file-readable-p "/usr/lib/dict/words") "/usr/lib/dict/words")
+       ((file-readable-p "/usr/share/dict/words") "/usr/share/dict/words")
+       ((file-readable-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."
+       ((file-readable-p "/sys/dict") "/sys/dict"))
+  "*Alternate plain word-list dictionary for spelling help."
   :type '(choice file (const :tag "None" nil))
   :group 'ispell)
 
-(defcustom ispell-complete-word-dict ispell-alternate-dictionary
-  "*Dictionary used for word completion."
+(defcustom ispell-complete-word-dict nil
+  "*Plain word-list dictionary used for word completion if
+different from `ispell-alternate-dictionary'."
   :type '(choice file (const :tag "None" nil))
   :group 'ispell)
 
@@ -390,7 +410,7 @@ Always stores Fcc copy of message when nil."
   (if (memq system-type '(windows-nt ms-dos)) "-Ei" "-i")
   "String of options to use when running the program in `ispell-grep-command'.
 Should probably be \"-i\" or \"-e\".
-Some machines (like the NeXT) don't support \"-i\""
+Some machines (like the NeXT) don't support \"-i\"."
   :type 'string
   :group 'ispell)
 
@@ -497,7 +517,8 @@ is automatically set when defined in the file with either
   :type '(choice string
                 (const :tag "default" nil))
   :group 'ispell)
-;;;###autoload(put 'ispell-local-dictionary 'safe-local-variable 'string-or-null-p)
+;;;###autoload
+(put 'ispell-local-dictionary 'safe-local-variable 'string-or-null-p)
 
 (make-variable-buffer-local 'ispell-local-dictionary)
 
@@ -599,6 +620,10 @@ re-start Emacs."
     ("esperanto-tex"
      "[A-Za-z^\\]" "[^A-Za-z^\\]"
      "[-'`\"]" t ("-C" "-d" "esperanto") "~tex" iso-8859-3)
+    ("finnish"
+     "[A-Za-z\345\344\366\305\304\326]"
+     "[^A-Za-z\345\344\366\305\304\326]"
+     "[:]" nil ("-C") "~list" iso-8859-1)
     ("francais7"
      "[A-Za-z]" "[^A-Za-z]" "[`'^-]" t nil nil iso-8859-1)
     ("francais"                                ; Francais.aff
@@ -639,8 +664,8 @@ re-start Emacs."
      "[^A-Za-z\241\243\246\254\257\261\263\266\274\277\306\312\321\323\346\352\361\363]"
      "[.]" 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]"
+     "[a-zA-Z\301\302\307\311\323\340\341\342\351\352\355\363\343\347\372]"
+     "[^a-zA-Z\301\302\307\311\323\340\341\342\351\352\355\363\343\347\372]"
      "[']" t ("-C") "~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]"
@@ -708,7 +733,7 @@ can be encoded as \\\"a, a\\\", \"a, ...)  Defaults are ~tex and ~nroff
 in English.  This has the same effect as the command-line `-T' option.
 The buffer Major Mode controls Ispell's parsing in tex or nroff mode,
 but the dictionary can control the extended character mode.
-Both defaults can be overruled in a buffer-local fashion. See
+Both defaults can be overruled in a buffer-local fashion.  See
 `ispell-parsing-keyword' for details on this.
 
 CHARACTER-SET used for languages with multibyte characters.
@@ -717,13 +742,13 @@ Note that the CASECHARS and OTHERCHARS slots of the alist should
 contain the same character set as casechars and otherchars in the
 LANGUAGE.aff file \(e.g., english.aff\).")
 
-(defvar ispell-really-aspell nil)   ; Non-nil if aspell extensions should be used
-(defvar ispell-really-hunspell nil) ; Non-nil if hunspell extensions should be used
+(defvar ispell-really-aspell nil)   ; Non-nil if we can use aspell extensions.
+(defvar ispell-really-hunspell nil) ; Non-nil if we can use hunspell extensions.
 (defvar ispell-encoding8-command nil
   "Command line option prefix to select UTF-8 if supported, nil otherwise.
 If UTF-8 if supported by spellchecker and is selectable from the command line
 this variable will contain \"--encoding=\" for aspell and \"-i \" for hunspell,
-so UTF-8 or other mime charsets can be selected. That will be set for hunspell
+so UTF-8 or other mime charsets can be selected.  That will be set for hunspell
 >=1.1.6 or aspell >= 0.60 in `ispell-check-version'.
 
 For aspell non-nil means to try to automatically find aspell dictionaries.
@@ -731,9 +756,9 @@ Earlier aspell versions do not consistently support UTF-8.  Handling
 this would require some extra guessing in `ispell-aspell-find-dictionary'.")
 
 (defvar ispell-aspell-supports-utf8 nil
-  "Non nil if aspell has consistent command line UTF-8 support. Obsolete.
+  "Non nil if aspell has consistent command line UTF-8 support.  Obsolete.
 ispell.el and flyspell.el will use for this purpose the more generic
-variable `ispell-encoding8-command' for both aspell and hunspell. Is left
+variable `ispell-encoding8-command' for both aspell and hunspell.  Is left
 here just for backwards compatibility.")
 
 (make-obsolete-variable 'ispell-aspell-supports-utf8
@@ -746,8 +771,8 @@ here just for backwards compatibility.")
 
 
 
-;;; The version must be 3.1 or greater for this version of ispell.el
-;;; There is an incompatibility between version 3.1.12 and lower versions.
+;; The version must be 3.1 or greater for this version of ispell.el
+;; There is an incompatibility between version 3.1.12 and lower versions.
 (defconst ispell-required-version '(3 1 12)
   "Ispell versions with which this version of ispell.el is known to work.")
 (defvar ispell-offset -1
@@ -768,9 +793,7 @@ Otherwise returns the library directory name, if that is defined."
   ;; all versions, since versions earlier than 3.0.09 didn't identify
   ;; themselves on startup.
   (interactive "p")
-  (let (;; avoid bugs when syntax of `.' changes in various default modes
-       (default-major-mode 'fundamental-mode)
-       (default-directory (or (and (boundp 'temporary-file-directory)
+  (let ((default-directory (or (and (boundp 'temporary-file-directory)
                                    temporary-file-directory)
                               default-directory))
        result status ispell-program-version)
@@ -797,7 +820,7 @@ Otherwise returns the library directory name, if that is defined."
            (message "%s" result))
        ;; return library directory.
        (if (re-search-forward "LIBDIR = \\\"\\([^ \t\n]*\\)\\\"" nil t)
-           (setq result (buffer-substring (match-beginning 1) (match-end 1)))))
+           (setq result (match-string 1))))
       (goto-char (point-min))
       (if (not (memq status '(0 nil)))
          (error "%s exited with %s %s" ispell-program-name
@@ -822,7 +845,8 @@ Otherwise returns the library directory name, if that is defined."
                       (match-string 1)))
            (setq ispell-really-hunspell
                  (and (search-forward-regexp
-                       "(but really Hunspell \\([0-9]+\\.[0-9\\.-]+\\)?)" nil t)
+                       "(but really Hunspell \\([0-9]+\\.[0-9\\.-]+\\)?)"
+                        nil t)
                       (match-string 1)))))
 
       (let ((aspell-minver    "0.50")
@@ -870,10 +894,9 @@ Otherwise returns the library directory name, if that is defined."
 
 
 
-;;; 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 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.")
@@ -923,7 +946,7 @@ Internal use.")
 (defun ispell-find-aspell-dictionaries ()
   "Find Aspell's dictionaries, and record in `ispell-dictionary-alist'."
   (unless (and ispell-really-aspell ispell-encoding8-command)
-    (error "This function only works with aspell >= 0.60."))
+    (error "This function only works with aspell >= 0.60"))
   (let* ((dictionaries
          (split-string
           (with-temp-buffer
@@ -943,7 +966,8 @@ Internal use.")
        (setq found (nconc found (list dict)))))
     (setq ispell-aspell-dictionary-alist found)
     ;; Add a default entry
-    (let ((default-dict '(nil "[[:alpha:]]" "[^[:alpha:]]" "[']" nil ("-B") nil utf-8)))
+    (let ((default-dict
+           '(nil "[[:alpha:]]" "[^[:alpha:]]" "[']" nil ("-B") nil utf-8)))
       (push default-dict ispell-aspell-dictionary-alist))))
 
 (defvar ispell-aspell-data-dir nil
@@ -960,19 +984,32 @@ Assumes that value contains no whitespace."
     (car (split-string (buffer-string)))))
 
 (defun ispell-aspell-find-dictionary (dict-name)
-  ;; This returns nil if the data file does not exist.
-  ;; Can someone please explain the return value format when the
-  ;; file does exist -- rms?
-  (let* ((lang ;; Strip out region, variant, etc.
-         (and (string-match "^[[:alpha:]]+" dict-name)
-              (match-string 0 dict-name)))
+  "For aspell dictionary DICT-NAME, return a list of parameters if an
+  associated data file is found or nil otherwise.  List format is
+  that of `ispell-dictionary-base-alist' elements."
+  ;; Make sure `ispell-aspell-data-dir' is defined
+  (or ispell-aspell-data-dir
+      (setq ispell-aspell-data-dir
+           (ispell-get-aspell-config-value "data-dir")))
+  ;; Try finding associated datafile
+  (let* ((datafile1
+         (concat ispell-aspell-data-dir "/"
+                 ;; Strip out variant, country code, etc.
+                 (and (string-match "^[[:alpha:]]+" dict-name)
+                      (match-string 0 dict-name)) ".dat"))
+        (datafile2
+         (concat ispell-aspell-data-dir "/"
+                 ;; Strip out anything but xx_YY.
+                 (and (string-match "^[[:alpha:]_]+" dict-name)
+                      (match-string 0 dict-name)) ".dat"))
         (data-file
-         (concat (or ispell-aspell-data-dir
-                     (setq ispell-aspell-data-dir
-                           (ispell-get-aspell-config-value "data-dir")))
-                 "/" lang ".dat"))
+         (if (file-readable-p datafile1)
+             datafile1
+           (if (file-readable-p datafile2)
+               datafile2)))
         otherchars)
-    (condition-case ()
+
+    (if data-file
        (with-temp-buffer
          (insert-file-contents data-file)
          ;; There is zero or one line with special characters declarations.
@@ -1000,14 +1037,13 @@ Assumes that value contains no whitespace."
                ;; Here we specify the encoding to use while communicating with
                ;; aspell.  This doesn't apply to command line arguments, so
                ;; just don't pass words to spellcheck as arguments...
-               'utf-8))
-      (file-error
-       nil))))
+               'utf-8)))))
 
 (defun ispell-aspell-add-aliases (alist)
   "Find aspell's dictionary aliases and add them to dictionary ALIST.
 Return the new dictionary alist."
-  (let ((aliases (file-expand-wildcards
+  (let ((aliases
+         (file-expand-wildcards
                  (concat (or ispell-aspell-dict-dir
                              (setq ispell-aspell-dict-dir
                                    (ispell-get-aspell-config-value "dict-dir")))
@@ -1029,7 +1065,7 @@ Return the new dictionary alist."
 ;; Set params according to the selected spellchecker
 
 (defvar ispell-last-program-name nil
-  "Last value of `ispell-program-name'. Internal use.")
+  "Last value of `ispell-program-name'.  Internal use.")
 
 (defvar ispell-initialize-spellchecker-hook nil
   "Normal hook run on spellchecker initialization.
@@ -1082,7 +1118,7 @@ aspell is used along with Emacs).")
 
 
 (defun ispell-valid-dictionary-list ()
-  "Returns a list of valid dictionaries.
+  "Return a list of valid dictionaries.
 The variable `ispell-library-directory' defines the library location."
   ;; Initialize variables and dictionaries alists for desired spellchecker.
   ;; Make sure ispell.el is loaded to avoid some autoload loops in XEmacs
@@ -1092,26 +1128,24 @@ The variable `ispell-library-directory' defines the library location."
 
   (let ((dicts (append ispell-local-dictionary-alist ispell-dictionary-alist))
        (dict-list (cons "default" nil))
-       name load-dict)
+       name dict-bname)
     (dolist (dict dicts)
       (setq name (car dict)
-           load-dict (car (cdr (member "-d" (nth 5 dict)))))
+           dict-bname (or (car (cdr (member "-d" (nth 5 dict))))
+                          name))
       ;; Include if the dictionary is in the library, or dir not defined.
       (if (and
           name
-          ;; include all dictionaries if lib directory not known.
           ;; For Aspell, we already know which dictionaries exist.
           (or ispell-really-aspell
+              ;; Include all dictionaries if lib directory not known.
+              ;; Same for Hunspell, where ispell-library-directory is nil.
               (not ispell-library-directory)
               (file-exists-p (concat ispell-library-directory
-                                     "/" name ".hash"))
-              (file-exists-p (concat ispell-library-directory "/" name ".has"))
-              (and load-dict
-                   (or (file-exists-p (concat ispell-library-directory
-                                              "/" load-dict ".hash"))
-                       (file-exists-p (concat ispell-library-directory
-                                              "/" load-dict ".has"))))))
-         (setq dict-list (cons name dict-list))))
+                                     "/" dict-bname ".hash"))
+              (file-exists-p (concat ispell-library-directory
+                                     "/" dict-bname ".has"))))
+         (push name dict-list)))
     dict-list))
 
 ;;; define commands in menu in opposite order you want them to appear.
@@ -1120,69 +1154,71 @@ The variable `ispell-library-directory' defines the library location."
     (progn
       (setq ispell-menu-map (make-sparse-keymap "Spell"))
       (define-key ispell-menu-map [ispell-change-dictionary]
-       '(menu-item "Change Dictionary..." ispell-change-dictionary
-                   :help "Supply explicit dictionary file name"))
+       `(menu-item ,(purecopy "Change Dictionary...") ispell-change-dictionary
+                   :help ,(purecopy "Supply explicit dictionary file name")))
       (define-key ispell-menu-map [ispell-kill-ispell]
-       '(menu-item "Kill Process" ispell-kill-ispell
+       `(menu-item ,(purecopy "Kill Process") ispell-kill-ispell
                    :enable (and (boundp 'ispell-process) ispell-process
                                 (eq (ispell-process-status) 'run))
-                   :help "Terminate Ispell subprocess"))
+                   :help ,(purecopy "Terminate Ispell subprocess")))
       (define-key ispell-menu-map [ispell-pdict-save]
-       '(menu-item "Save Dictionary"
+       `(menu-item ,(purecopy "Save Dictionary")
                    (lambda () (interactive) (ispell-pdict-save t t))
-                   :help "Save personal dictionary"))
+                   :help ,(purecopy "Save personal dictionary")))
       (define-key ispell-menu-map [ispell-customize]
-       '(menu-item "Customize..."
+       `(menu-item ,(purecopy "Customize...")
                    (lambda () (interactive) (customize-group 'ispell))
-                   :help "Customize spell checking options"))
+                   :help ,(purecopy "Customize spell checking options")))
       (define-key ispell-menu-map [ispell-help]
        ;; use (x-popup-menu last-nonmenu-event(list "" ispell-help-list)) ?
-       '(menu-item "Help"
+       `(menu-item ,(purecopy "Help")
                    (lambda () (interactive) (describe-function 'ispell-help))
-                   :help "Show standard Ispell keybindings and commands"))
+                   :help ,(purecopy "Show standard Ispell keybindings and commands")))
       (define-key ispell-menu-map [flyspell-mode]
-       '(menu-item "Automatic spell checking (Flyspell)"
+       `(menu-item ,(purecopy "Automatic spell checking (Flyspell)")
                    flyspell-mode
-                   :help "Check spelling while you edit the text"
+                   :help ,(purecopy "Check spelling while you edit the text")
                    :button (:toggle . (bound-and-true-p flyspell-mode))))
       (define-key ispell-menu-map [ispell-complete-word]
-       '(menu-item "Complete Word" ispell-complete-word
-                   :help "Complete word at cursor using dictionary"))
+       `(menu-item ,(purecopy "Complete Word") ispell-complete-word
+                   :help ,(purecopy "Complete word at cursor using dictionary")))
       (define-key ispell-menu-map [ispell-complete-word-interior-frag]
-       '(menu-item "Complete Word Fragment" ispell-complete-word-interior-frag
-                   :help "Complete word fragment at cursor"))))
+       `(menu-item ,(purecopy "Complete Word Fragment")
+                    ispell-complete-word-interior-frag
+                   :help ,(purecopy "Complete word fragment at cursor")))))
 
 ;;;###autoload
 (if ispell-menu-map-needed
     (progn
       (define-key ispell-menu-map [ispell-continue]
-       '(menu-item "Continue Spell-Checking" ispell-continue
+       `(menu-item ,(purecopy "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"))
+                   :help ,(purecopy "Continue spell checking last region")))
       (define-key ispell-menu-map [ispell-word]
-       '(menu-item "Spell-Check Word" ispell-word
-                   :help "Spell-check word at cursor"))
+       `(menu-item ,(purecopy "Spell-Check Word") ispell-word
+                   :help ,(purecopy "Spell-check word at cursor")))
       (define-key ispell-menu-map [ispell-comments-and-strings]
-       '(menu-item "Spell-Check Comments" ispell-comments-and-strings
-                   :help "Spell-check only comments and strings"))))
+       `(menu-item ,(purecopy "Spell-Check Comments")
+                    ispell-comments-and-strings
+                   :help ,(purecopy "Spell-check only comments and strings")))))
 
 ;;;###autoload
 (if ispell-menu-map-needed
     (progn
       (define-key ispell-menu-map [ispell-region]
-       '(menu-item "Spell-Check Region" ispell-region
+       `(menu-item ,(purecopy "Spell-Check Region") ispell-region
                    :enable mark-active
-                   :help "Spell-check text in marked region"))
+                   :help ,(purecopy "Spell-check text in marked region")))
       (define-key ispell-menu-map [ispell-message]
-       '(menu-item "Spell-Check Message" ispell-message
+       `(menu-item ,(purecopy "Spell-Check Message") ispell-message
                    :visible (eq major-mode 'mail-mode)
-                   :help "Skip headers and included message text"))
+                   :help ,(purecopy "Skip headers and included message text")))
       (define-key ispell-menu-map [ispell-buffer]
-       '(menu-item "Spell-Check Buffer" ispell-buffer
-                   :help "Check spelling of selected buffer"))
+       `(menu-item ,(purecopy "Spell-Check Buffer") ispell-buffer
+                   :help ,(purecopy "Check spelling of selected buffer")))
       ;;(put 'ispell-region 'menu-enable 'mark-active)
       (fset 'ispell-menu-map (symbol-value 'ispell-menu-map))))
 
@@ -1245,9 +1281,6 @@ The variable `ispell-library-directory' defines the library location."
 
 ;;; **********************************************************************
 
-
-;;; This variable contains the current dictionary being used if the ispell
-;;; process is running.
 (defvar ispell-current-dictionary nil
   "The name of the current dictionary, or nil for the default.
 This is passed to the ispell process using the `-d' switch and is
@@ -1272,9 +1305,11 @@ Protects against bogus binding of `enable-multibyte-characters' in XEmacs."
 
 ;; Return a string decoded from Nth element of the current dictionary.
 (defun ispell-get-decoded-string (n)
+  "Get the decoded string in slot N of the descriptor of the current dict."
   (let* ((slot (or
                (assoc ispell-current-dictionary ispell-local-dictionary-alist)
-               (assoc ispell-current-dictionary ispell-dictionary-alist)))
+               (assoc ispell-current-dictionary ispell-dictionary-alist)
+               (error "No match for the current dictionary")))
         (str (nth n slot)))
     (when (and (> (length str) 0)
               (not (multibyte-string-p str)))
@@ -1373,20 +1408,21 @@ The last occurring definition in the buffer will be used.")
 
 ;;;###autoload
 (defvar ispell-skip-region-alist
-  '((ispell-words-keyword         forward-line)
+  `((ispell-words-keyword         forward-line)
     (ispell-dictionary-keyword    forward-line)
     (ispell-pdict-keyword         forward-line)
     (ispell-parsing-keyword       forward-line)
-    ("^---*BEGIN PGP [A-Z ]*--*" . "^---*END PGP [A-Z ]*--*")
+    (,(purecopy "^---*BEGIN PGP [A-Z ]*--*")
+     . ,(purecopy "^---*END PGP [A-Z ]*--*"))
     ;; 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")
+    (,(purecopy "^begin [0-9][0-9][0-9] [^ \t]+$") . ,(purecopy "\nend\n"))
+    (,(purecopy "^%!PS-Adobe-[123].0")  . ,(purecopy "\n%%EOF\n"))
+    (,(purecopy "^---* \\(Start of \\)?[Ff]orwarded [Mm]essage")
+     . ,(purecopy "^---* End of [Ff]orwarded [Mm]essage"))
     ;; Matches e-mail addresses, file names, http addresses, etc.  The
     ;; `-+' `_+' patterns are necessary for performance reasons when
     ;; `-' or `_' part of word syntax.
-    ("\\(--+\\|_+\\|\\(/\\w\\|\\(\\(\\w\\|[-_]\\)+[.:@]\\)\\)\\(\\w\\|[-_]\\)*\\([.:/@]+\\(\\w\\|[-_~=?&]\\)+\\)+\\)")
+    (,(purecopy "\\(--+\\|_+\\|\\(/\\w\\|\\(\\(\\w\\|[-_]\\)+[.:@]\\)\\)\\(\\w\\|[-_]\\)*\\([.:/@]+\\(\\w\\|[-_~=?&]\\)+\\)+\\)"))
     ;; above checks /.\w sequences
     ;;("\\(--+\\|\\(/\\|\\(\\(\\w\\|[-_]\\)+[.:@]\\)\\)\\(\\w\\|[-_]\\)*\\([.:/@]+\\(\\w\\|[-_~=?&]\\)+\\)+\\)")
     ;; This is a pretty complex regexp.  It can be simplified to the following:
@@ -1409,6 +1445,7 @@ Valid forms include:
 
 ;;;###autoload
 (defvar ispell-tex-skip-alists
+  (purecopy
   '((;;("%\\[" . "%\\]") ; AMStex block comment...
      ;; All the standard LaTeX keywords from L. Lamport's guide:
      ;; \cite, \hspace, \hspace*, \hyphenation, \include, \includeonly, \input,
@@ -1427,7 +1464,7 @@ Valid forms include:
      ("\\(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]*}")))
+     ("verbatim\\*?"   . "\\\\end[ \t\n]*{[ \t\n]*verbatim\\*?[ \t\n]*}"))))
   "*Lists of regions to be skipped in TeX mode.
 First list is used raw.
 Second list has key placed inside \\begin{}.
@@ -1438,7 +1475,7 @@ for skipping in latex mode.")
 
 
 ;;;###autoload
-(defvar ispell-html-skip-alists
+(defconst 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]>")
@@ -1507,13 +1544,11 @@ pass it the output of the last ispell invocation."
            ispell-output)
        (if (not (bufferp buf))
            (setq ispell-filter nil)
-         (save-excursion
-           (set-buffer buf)
+         (with-current-buffer buf
            (setq ispell-output (buffer-substring-no-properties
                                 (point-min) (point-max))))
          (ispell-filter t ispell-output)
-         (save-excursion
-           (set-buffer buf)
+         (with-current-buffer buf
            (erase-buffer)))))))
 
 (defun ispell-send-replacement (misspelled replacement)
@@ -1536,14 +1571,12 @@ This allows it to improve the suggestion list based on actual misspellings."
          ;; 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)
+      (with-current-buffer session-buf
        (setq prev-pos (point))
        (setq default-directory defdir)
        (insert string)
@@ -1858,12 +1891,12 @@ Global `ispell-quit' set to start location to continue spell session."
        char num result textwin dedicated-win)
 
     ;; setup the *Choices* buffer with valid data.
-    (save-excursion
-      (set-buffer (get-buffer-create ispell-choices-buffer))
+    (with-current-buffer (get-buffer-create ispell-choices-buffer)
       (setq mode-line-format
-           (concat "--  %b  --  word: " word
-                   "  --  dict: " (or ispell-current-dictionary "default")
-                   "  --  prog: " (file-name-nondirectory ispell-program-name)))
+           (concat
+             "--  %b  --  word: " word
+             "  --  dict: " (or ispell-current-dictionary "default")
+             "  --  prog: " (file-name-nondirectory ispell-program-name)))
       ;; XEmacs: no need for horizontal scrollbar in choices window
       (with-no-warnings
        (and (fboundp 'set-specifier)
@@ -1940,9 +1973,9 @@ Global `ispell-quit' set to start location to continue spell session."
                    ;; If the user types C-g, or generates some other
                    ;; non-character event (such as a frame switch
                    ;; event), stop ispell.  As a special exception,
-                   ;; ignore mouse events occuring in the same frame.
+                   ;; ignore mouse events occurring in the same frame.
                    (while (and input-valid (not (characterp char)))
-                     (setq char (read-event))
+                     (setq char (read-key))
                      (setq input-valid
                            (or (characterp char)
                                (and (mouse-event-p char)
@@ -2025,16 +2058,16 @@ Global `ispell-quit' set to start location to continue spell session."
                                     word)))
                      (if new-word
                          (progn
-                           (save-excursion
-                             (set-buffer (get-buffer-create
-                                          ispell-choices-buffer))
+                           (with-current-buffer (get-buffer-create
+                                                  ispell-choices-buffer)
                              (erase-buffer)
                              (setq count ?0
                                    skipped 0
-                                   mode-line-format
+                                   mode-line-format ;; setup the *Choices* buffer with valid data.
                                    (concat "--  %b  --  word: " new-word
-                                           "  --  dict: "
-                                           ispell-alternate-dictionary)
+                                           "  --  word-list: "
+                                           (or ispell-complete-word-dict
+                                               ispell-alternate-dictionary))
                                    miss (lookup-words new-word)
                                    choices miss
                                    line ispell-choices-win-default-height)
@@ -2128,7 +2161,7 @@ Global `ispell-quit' set to start location to continue spell session."
   (if (and ispell-use-framepop-p (fboundp 'framepop-display-buffer))
       (progn
        (framepop-display-buffer (get-buffer ispell-choices-buffer))
-;;;    (get-buffer-window ispell-choices-buffer t)
+        ;; (get-buffer-window ispell-choices-buffer t)
        (select-window (previous-window))) ; *Choices* window
     ;; standard selection by splitting a small buffer out of this window.
     (let ((choices-window (get-buffer-window ispell-choices-buffer)))
@@ -2169,7 +2202,7 @@ SPC:   Accept word this time.
 `a':   Accept word for this session.
 `A':   Accept word and place in `buffer-local dictionary'.
 `r':   Replace word with typed-in value.  Rechecked.
-`R':   Replace word with typed-in value. Query-replaced in buffer. Rechecked.
+`R':   Replace word with typed-in value.  Query-replaced in buffer.  Rechecked.
 `?':   Show these commands.
 `x':   Exit spelling buffer.  Move cursor to original point.
 `X':   Exit spelling buffer.  Leaves cursor at the current point, and permits
@@ -2201,7 +2234,7 @@ SPC:   Accept word this time.
 `a':   Accept word for this session.
 `A':   Accept word and place in `buffer-local dictionary'.
 `r':   Replace word with typed-in value.  Rechecked.
-`R':   Replace word with typed-in value. Query-replaced in buffer. Rechecked.
+`R':   Replace word with typed-in value.  Query-replaced in buffer.  Rechecked.
 `?':   Show these commands.
 `x':   Exit spelling buffer.  Move cursor to original point.
 `X':   Exit spelling buffer.  Leaves cursor at the current point, and permits
@@ -2249,11 +2282,21 @@ Otherwise the variable `ispell-grep-command' contains the command used to
 search for the words (usually egrep).
 
 Optional second argument contains the dictionary to use; the default is
-`ispell-alternate-dictionary'."
+`ispell-alternate-dictionary', overriden by `ispell-complete-word-dict'
+if defined."
   ;; We don't use the filter for this function, rather the result is written
   ;; into a buffer.  Hence there is no need to save the filter values.
   (if (null lookup-dict)
-      (setq lookup-dict ispell-alternate-dictionary))
+      (setq lookup-dict (or ispell-complete-word-dict
+                           ispell-alternate-dictionary)))
+
+  (if lookup-dict
+      (unless (file-readable-p lookup-dict)
+       (error "lookup-words error: Unreadable or missing plain word-list %s."
+              lookup-dict))
+    (error (concat "lookup-words error: No plain word-list found at system"
+                   "default locations.  "
+                   "Customize `ispell-alternate-dictionary' to set yours.")))
 
   (let* ((process-connection-type ispell-use-ptys-p)
         (wild-p (string-match "\\*" word))
@@ -2304,16 +2347,16 @@ Optional second argument contains the dictionary to use; the default is
     results))
 
 
-;;; "ispell-filter" is a list of output lines from the generating function.
-;;;   Each full line (ending with \n) is a separate item on the list.
-;;; "output" can contain multiple lines, part of a line, or both.
-;;; "start" and "end" are used to keep bounds on lines when "output" contains
-;;;   multiple lines.
-;;; "ispell-filter-continue" is true when we have received only part of a
-;;;   line as output from a generating function ("output" did not end with \n)
-;;; THIS FUNCTION WILL FAIL IF THE PROCESS OUTPUT DOESN'T END WITH \n!
-;;;   This is the case when a process dies or fails. The default behavior
-;;;   in this case treats the next input received as fresh input.
+;; "ispell-filter" is a list of output lines from the generating function.
+;;   Each full line (ending with \n) is a separate item on the list.
+;; "output" can contain multiple lines, part of a line, or both.
+;; "start" and "end" are used to keep bounds on lines when "output" contains
+;;   multiple lines.
+;; "ispell-filter-continue" is true when we have received only part of a
+;;   line as output from a generating function ("output" did not end with \n)
+;; THIS FUNCTION WILL FAIL IF THE PROCESS OUTPUT DOESN'T END WITH \n!
+;;   This is the case when a process dies or fails. The default behavior
+;;   in this case treats the next input received as fresh input.
 
 (defun ispell-filter (process output)
   "Output filter function for ispell, grep, and look."
@@ -2513,18 +2556,18 @@ Optional third arg SHIFT is an offset to apply based on previous corrections."
        (setq count (string-to-number output) ; get number of misses.
              output (substring output (1+ (string-match " " output 1)))))
       (setq offset (string-to-number output))
-      (if (eq type ?#)                 ; No miss or guess list.
-         (setq output nil)
-       (setq output (substring output (1+ (string-match " " output 1)))))
+      (setq output (if (eq type ?#)     ; No miss or guess list.
+                       nil
+                     (substring output (1+ (string-match " " output 1)))))
       (while output
        (let ((end (string-match ", \\|\\($\\)" output))) ; end of miss/guess.
          (setq cur-count (1+ cur-count))
          (if (> cur-count count)
-             (setq guess-list (cons (substring output 0 end) guess-list))
-           (setq miss-list (cons (substring output 0 end) miss-list)))
-         (if (match-end 1)             ; True only when at end of line.
-             (setq output nil)         ; no more misses or guesses
-           (setq output (substring output (+ end 2))))))
+             (push (substring output 0 end) guess-list)
+           (push (substring output 0 end) miss-list))
+         (setq output (if (match-end 1) ; True only when at end of line.
+                           nil           ; No more misses or guesses.
+                         (substring output (+ end 2))))))
       ;; return results.  Accept word if it was already accepted.
       ;; adjust offset.
       (if (member original-word accept-list)
@@ -2545,37 +2588,35 @@ When asynchronous processes are not supported, `run' is always returned."
 (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 ((default-directory default-directory)
-       args)
-    (unless (and (file-directory-p default-directory)
-                (file-readable-p default-directory))
-      ;; Defend against bad `default-directory'.
-      (setq default-directory (expand-file-name "~/")))
-    ;; Local dictionary becomes the global dictionary in use.
-    (setq ispell-current-dictionary
-         (or ispell-local-dictionary ispell-dictionary))
-    (setq ispell-current-personal-dictionary
-         (or ispell-local-pdict ispell-personal-dictionary))
-    (setq args (ispell-get-ispell-args))
-    (if (and ispell-current-dictionary ; use specified dictionary
-            (not (member "-d" args)))  ; only define if not overridden
-       (setq args
-             (append (list "-d" ispell-current-dictionary) args)))
-    (if ispell-current-personal-dictionary     ; use specified pers dict
-       (setq args
-             (append args
-                     (list "-p"
-                           (expand-file-name ispell-current-personal-dictionary)))))
-
-    ;; If we are using recent aspell or hunspell, make sure we use the right encoding
-    ;; for communication. ispell or older aspell/hunspell does not support this
-    (if ispell-encoding8-command
-       (setq args
-             (append args
-                     (list
-                      (concat ispell-encoding8-command
-                              (symbol-name (ispell-get-coding-system)))))))
-    (setq args (append args ispell-extra-args))
+  ;; Local dictionary becomes the global dictionary in use.
+  (setq ispell-current-dictionary
+        (or ispell-local-dictionary ispell-dictionary))
+  (setq ispell-current-personal-dictionary
+        (or ispell-local-pdict ispell-personal-dictionary))
+  (let* ((default-directory
+           (if (and (file-directory-p default-directory)
+                    (file-readable-p default-directory))
+               default-directory
+             ;; Defend against bad `default-directory'.
+             (expand-file-name "~/")))
+        (orig-args (ispell-get-ispell-args))
+         (args
+          (append
+           (if (and ispell-current-dictionary      ; Not for default dict (nil)
+                    (not (member "-d" orig-args))) ; Only define if not overridden.
+               (list "-d" ispell-current-dictionary))
+           orig-args
+           (if ispell-current-personal-dictionary ; Use specified pers dict.
+               (list "-p"
+                     (expand-file-name ispell-current-personal-dictionary)))
+           ;; If we are using recent aspell or hunspell, make sure we use the
+           ;; right encoding for communication. ispell or older aspell/hunspell
+           ;; does not support this.
+           (if ispell-encoding8-command
+               (list
+                (concat ispell-encoding8-command
+                        (symbol-name (ispell-get-coding-system)))))
+           ispell-extra-args)))
 
     ;; Initially we don't know any buffer's local words.
     (setq ispell-buffer-local-name nil)
@@ -2584,9 +2625,11 @@ Keeps argument list for future ispell invocations for no async support."
        (let ((process-connection-type ispell-use-ptys-p))
          (apply 'start-process
                 "ispell" nil ispell-program-name
-                "-a"                                ; accept single input lines
-                (if ispell-really-hunspell "" "-m") ; make root/affix combos not in dict
-                args))                              ; hunspell -m option means different
+                "-a"                   ; Accept single input lines.
+                 ;; Make root/affix combos not in dict.
+                 ;; hunspell -m option means different.
+                (if ispell-really-hunspell "" "-m")
+                args))
       (setq ispell-cmd-args args
            ispell-output-buffer (generate-new-buffer " *ispell-output*")
            ispell-session-buffer (generate-new-buffer " *ispell-session*"))
@@ -2594,65 +2637,114 @@ Keeps argument list for future ispell invocations for no async support."
       t)))
 
 
-
 (defun ispell-init-process ()
   "Check status of Ispell process and start if necessary."
-  (if (and ispell-process
-          (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)))
-      (setq ispell-filter nil ispell-filter-continue nil)
-    ;; may need to restart to select new personal dictionary.
-    (ispell-kill-ispell t)
-    (message "Starting new Ispell process [%s] ..."
-            (or ispell-local-dictionary ispell-dictionary "default"))
-    (sit-for 0)
-    (setq ispell-library-directory (ispell-check-version)
-         ispell-process-directory default-directory
-         ispell-process (ispell-start-process)
-         ispell-filter nil
-         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 (featurep 'xemacs)
-                (and (boundp 'enable-multibyte-characters)
-                     enable-multibyte-characters))
-            (fboundp 'set-process-coding-system))
-       (set-process-coding-system ispell-process (ispell-get-coding-system)
-                                  (ispell-get-coding-system)))
-    ;; Get version ID line
-    (ispell-accept-output 3)
-    ;; get more output if filter empty?
-    (if (null ispell-filter) (ispell-accept-output 3))
-    (cond ((null ispell-filter)
-          (error "%s did not output version line" ispell-program-name))
-         ((and
-           (stringp (car ispell-filter))
-           (if (string-match "warning: " (car ispell-filter))
-               (progn
-                 (ispell-accept-output 3) ; was warn msg.
-                 (stringp (car ispell-filter)))
-             (null (cdr ispell-filter)))
-           (string-match "^@(#) " (car ispell-filter)))
-          ;; got the version line as expected (we already know it's the right
-          ;; version, so don't bother checking again.)
-          nil)
-         (t
-          ;; Otherwise, it must be an error message.  Show the user.
-          ;; 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)
-          (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
-         (ispell-send-string (concat extended-char-mode "\n"))))
-    (if ispell-async-processp
-       (set-process-query-on-exit-flag ispell-process nil))))
+  (let* (;; Basename of dictionary used by the spell-checker
+        (dict-bname (or (car (cdr (member "-d" (ispell-get-ispell-args))))
+                        ispell-current-dictionary))
+        ;; Use "~/" as default-directory unless using Ispell with per-dir
+        ;; personal dictionaries and not in a minibuffer under XEmacs
+        (default-directory
+          (if (or ispell-really-aspell
+                  ispell-really-hunspell
+                  ;; Protect against bad default-directory
+                  (not (and (file-directory-p default-directory)
+                            (file-readable-p default-directory)))
+                  ;; Ispell and per-dir personal dicts available
+                  (not (or (file-readable-p (concat default-directory
+                                                    ".ispell_words"))
+                           (file-readable-p (concat default-directory
+                                                    ".ispell_"
+                                                    (or dict-bname
+                                                        "default")))))
+                  ;; Ispell, in a minibuffer, and XEmacs
+                  (and (window-minibuffer-p)
+                       (not (fboundp 'minibuffer-selected-window))))
+              (expand-file-name "~/")
+            (expand-file-name default-directory))))
+    ;; Check if process needs restart
+    (if (and ispell-process
+            (eq (ispell-process-status) 'run)
+            ;; Unless we are using an explicit personal dictionary, ensure
+            ;; we're in the same default directory!  Restart check for
+            ;; personal dictionary is done in
+            ;; `ispell-internal-change-dictionary', called from
+            ;; `ispell-buffer-local-dict'
+            (or (or ispell-local-pdict ispell-personal-dictionary)
+                (equal ispell-process-directory default-directory)))
+       (setq ispell-filter nil ispell-filter-continue nil)
+      ;; may need to restart to select new personal dictionary.
+      (ispell-kill-ispell t)
+      (message "Starting new Ispell process [%s] ..."
+              (or ispell-local-dictionary ispell-dictionary "default"))
+      (sit-for 0)
+      (setq ispell-library-directory (ispell-check-version)
+           ispell-process (ispell-start-process)
+           ispell-filter nil
+           ispell-filter-continue nil
+           ispell-process-directory default-directory)
+
+      (unless (equal ispell-process-directory (expand-file-name "~/"))
+       ;; At this point, `ispell-process-directory' will be "~/" unless using
+       ;; Ispell with directory-specific dicts and not in XEmacs minibuffer.
+       ;; If not, kill ispell process when killing buffer.  It may be in a
+       ;; removable device that would otherwise become un-mountable.
+       (with-current-buffer
+           (if (and (window-minibuffer-p)                  ;; In minibuffer
+                    (fboundp 'minibuffer-selected-window)) ;; Not XEmacs.
+               ;; In this case kill ispell only when parent buffer is killed
+               ;; to avoid over and over ispell kill.
+               (window-buffer (minibuffer-selected-window))
+             (current-buffer))
+         ;; 'local does not automatically make hook buffer-local in XEmacs.
+         (if (featurep 'xemacs)
+             (make-local-hook 'kill-buffer-hook))
+         (add-hook 'kill-buffer-hook
+                   (lambda () (ispell-kill-ispell t)) nil 'local)))
+
+      (if ispell-async-processp
+         (set-process-filter ispell-process 'ispell-filter))
+      ;; Protect against XEmacs bogus binding of `enable-multibyte-characters'.
+      (if (and (or (featurep 'xemacs)
+                  (and (boundp 'enable-multibyte-characters)
+                       enable-multibyte-characters))
+              (fboundp 'set-process-coding-system))
+         (set-process-coding-system ispell-process (ispell-get-coding-system)
+                                    (ispell-get-coding-system)))
+      ;; Get version ID line
+      (ispell-accept-output 3)
+      ;; get more output if filter empty?
+      (if (null ispell-filter) (ispell-accept-output 3))
+      (cond ((null ispell-filter)
+            (error "%s did not output version line" ispell-program-name))
+           ((and
+             (stringp (car ispell-filter))
+             (if (string-match "warning: " (car ispell-filter))
+                 (progn
+                   (ispell-accept-output 3) ; was warn msg.
+                   (stringp (car ispell-filter)))
+               (null (cdr ispell-filter)))
+             (string-match "^@(#) " (car ispell-filter)))
+            ;; got the version line as expected (we already know it's the right
+            ;; version, so don't bother checking again.)
+            nil)
+           (t
+            ;; Otherwise, it must be an error message.  Show the user.
+            ;; 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)
+            (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
+           (ispell-send-string (concat extended-char-mode "\n"))))
+      (if ispell-async-processp
+         (if (featurep 'emacs)
+             (set-process-query-on-exit-flag ispell-process nil)
+           (if (fboundp 'set-process-query-on-exit-flag)
+               (set-process-query-on-exit-flag ispell-process nil)
+             (process-kill-without-query ispell-process)))))))
 
 ;;;###autoload
 (defun ispell-kill-ispell (&optional no-error)
@@ -2678,7 +2770,6 @@ With NO-ERROR, just return non-nil if there was no Ispell running."
     (message "Ispell process killed")
     nil))
 
-
 ;;; ispell-change-dictionary is set in some people's hooks.  Maybe this should
 ;;;  call ispell-init-process rather than wait for a spell checking command?
 
@@ -2739,7 +2830,11 @@ a new one will be started when needed."
       (setq ispell-current-dictionary dict
            ispell-current-personal-dictionary pdict))))
 
-;;; Spelling of comments are checked when ispell-check-comments is non-nil.
+;; Avoid error messages when compiling for these dynamic variables.
+(defvar ispell-start)
+(defvar ispell-end)
+
+;; Spelling of comments are checked when ispell-check-comments is non-nil.
 
 ;;;###autoload
 (defun ispell-region (reg-start reg-end &optional recheckp shift)
@@ -2770,14 +2865,14 @@ Return nil if spell session is quit,
              (message "searching for regions to skip"))
            (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)))
+                 (setq key (match-string-no-properties 0))
                  (set-marker skip-region-start (- (point) (length key)))
                  (goto-char reg-start)))
            (let (message-log-max)
-             (message "Continuing spelling check using %s with %s dictionary..."
-                      (file-name-nondirectory ispell-program-name)
-                      (or ispell-current-dictionary "default")))
+             (message
+               "Continuing spelling check using %s with %s dictionary..."
+               (file-name-nondirectory ispell-program-name)
+               (or ispell-current-dictionary "default")))
            (set-marker rstart reg-start)
            (set-marker ispell-region-end reg-end)
            (while (and (not ispell-quit)
@@ -2816,18 +2911,19 @@ Return nil if spell session is quit,
                                 (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 in-comment)))
+             (let* ((ispell-start (point))
+                    (ispell-end (min (point-at-eol) reg-end))
+                    (string (ispell-get-line
+                              ispell-start ispell-end in-comment)))
                (if in-comment          ; account for comment chars added
-                   (setq start (- start (length in-comment))
+                   (setq ispell-start (- ispell-start (length in-comment))
                          in-comment nil))
-               (setq end (point))      ; "end" tracks region retrieved.
+               (setq ispell-end (point)) ; "end" tracks region retrieved.
                (if string              ; there is something to spell check!
                    ;; (special start end)
                    (setq shift (ispell-process-line string
                                                     (and recheckp shift))))
-               (goto-char end)))))
+               (goto-char ispell-end)))))
        (if ispell-quit
            nil
          (or shift 0)))
@@ -2864,42 +2960,30 @@ Return nil if spell session is quit,
   "Return 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
-                          (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 (lambda (lst) (car lst))
-                                     ispell-checking-message
-                                     "\\|")
-                          "\\|"
-                          skip-regexp)))
-
-    ;; return new regexp
-    skip-regexp))
+  (mapconcat
+   'identity
+   (delq nil
+         (list
+          ;; messages
+          (if (and ispell-checking-message
+                   (not (eq t ispell-checking-message)))
+              (mapconcat #'car ispell-checking-message "\\|"))
+          ;; tex
+          (if (eq ispell-parser 'tex)
+              (ispell-begin-tex-skip-regexp))
+          ;; html stuff
+          (if ispell-skip-html
+              (ispell-begin-skip-region ispell-html-skip-alists))
+          ;; tib
+          (if ispell-skip-tib ispell-tib-ref-beginning)
+          ;; Comments
+          (if (and (eq 'exclusive ispell-check-comments) comment-start)
+              ;; search from end of current comment to start of next comment.
+              (if (string= "" comment-end) "^" (regexp-quote comment-end)))
+          (if (and (null ispell-check-comments) comment-start)
+              (regexp-quote comment-start))
+          (ispell-begin-skip-region ispell-skip-region-alist)))
+   "\\|"))
 
 
 (defun ispell-begin-skip-region (skip-alist)
@@ -3042,9 +3126,9 @@ Point is placed at end of skipped region."
          (sit-for 2)))))
 
 
-;;; Grab the next line of data.
-;;; Returns a string with the line data
 (defun ispell-get-line (start end in-comment)
+  "Grab the next line of data.
+Returns a string with the line data."
   (let ((ispell-casechars (ispell-get-casechars))
        string)
     (cond                              ; LOOK AT THIS LINE AND SKIP OR PROCESS
@@ -3071,16 +3155,13 @@ Point is placed at end of skipped region."
                                       (point) (+ (point) len))
                                      coding)))))
 
-;;; Avoid error messages when compiling for these dynamic variables.
-(defvar start)
-(defvar end)
-
 (defun ispell-process-line (string shift)
   "Send STRING, a line of text, to ispell and processes the result.
 This will modify the buffer for spelling errors.
-Requires variables START and END to be defined in its lexical scope.
+Requires variables ISPELL-START and ISPELL-END to be defined in its
+dynamic scope.
 Returns the sum SHIFT due to changes in word replacements."
-  ;;(declare special start end)
+  ;;(declare special ispell-start ispell-end)
   (let (poss accept-list)
     (if (not (numberp shift))
        (setq shift 0))
@@ -3103,10 +3184,10 @@ Returns the sum SHIFT due to changes in word replacements."
          ;; Markers can move with highlighting!  This destroys
          ;; end of region markers line-end and ispell-region-end
          (let ((word-start
-                (copy-marker (+ start ispell-offset (car (cdr poss)))))
+                (copy-marker (+ ispell-start ispell-offset (car (cdr poss)))))
                (word-len (length (car poss)))
-               (line-end (copy-marker end))
-               (line-start (copy-marker start))
+               (line-end (copy-marker ispell-end))
+               (line-start (copy-marker ispell-start))
                recheck-region replace)
            (goto-char word-start)
            ;; Adjust the horizontal scroll & point
@@ -3206,16 +3287,19 @@ Returns the sum SHIFT due to changes in word replacements."
              ;;                           (length (car poss)))))
              ))
            (if (not ispell-quit)
+                ;; FIXME: remove redundancy with identical code above.
                (let (message-log-max)
-                 (message "Continuing spelling check using %s with %s dictionary..."
-                          (file-name-nondirectory ispell-program-name)
-                          (or ispell-current-dictionary "default"))))
+                 (message
+                   "Continuing spelling check using %s with %s dictionary..."
+                   (file-name-nondirectory ispell-program-name)
+                   (or ispell-current-dictionary "default"))))
            (sit-for 0)
-           (setq start (marker-position line-start)
-                 end (marker-position line-end))
+           (setq ispell-start (marker-position line-start)
+                 ispell-end (marker-position line-end))
            ;; Adjust markers when end of region lost from highlighting.
-           (if (and (not recheck-region) (< end (+ word-start word-len)))
-               (setq end (+ word-start word-len)))
+           (if (and (not recheck-region)
+                     (< ispell-end (+ word-start word-len)))
+               (setq ispell-end (+ word-start word-len)))
            (if (= word-start ispell-region-end)
                (set-marker ispell-region-end (+ word-start word-len)))
            ;; going out of scope - unneeded
@@ -3282,7 +3366,7 @@ Returns the sum SHIFT due to changes in word replacements."
 
 
 ;;; Interactive word completion.
-;;; Forces "previous-word" processing.  Do we want to make this selectable?
+;; Forces "previous-word" processing.  Do we want to make this selectable?
 
 ;;;###autoload
 (defun ispell-complete-word (&optional interior-frag)
@@ -3304,7 +3388,8 @@ Standard ispell choices are then available."
              (lookup-words (concat (and interior-frag "*") word
                                    (if (or interior-frag (null ispell-look-p))
                                        "*"))
-                           ispell-complete-word-dict)))
+                           (or ispell-complete-word-dict
+                               ispell-alternate-dictionary))))
     (cond ((eq possibilities t)
           (message "No word to complete"))
          ((null possibilities)
@@ -3372,15 +3457,6 @@ available on the net."
 ;;;                    Ispell Minor Mode
 ;;; **********************************************************************
 
-(defvar ispell-minor-mode nil
-  "Non-nil if Ispell minor mode is enabled.")
-;; Variable indicating that ispell minor mode is active.
-(make-variable-buffer-local 'ispell-minor-mode)
-
-(or (assq 'ispell-minor-mode minor-mode-alist)
-    (setq minor-mode-alist
-          (cons '(ispell-minor-mode " Spell") minor-mode-alist)))
-
 (defvar ispell-minor-keymap
   (let ((map (make-sparse-keymap)))
     (define-key map " " 'ispell-minor-check)
@@ -3388,14 +3464,8 @@ available on the net."
     map)
   "Keymap used for Ispell minor mode.")
 
-(or (not (boundp 'minor-mode-map-alist))
-    (assoc 'ispell-minor-mode minor-mode-map-alist)
-    (setq minor-mode-map-alist
-          (cons (cons 'ispell-minor-mode ispell-minor-keymap)
-                minor-mode-map-alist)))
-
 ;;;###autoload
-(defun ispell-minor-mode (&optional arg)
+(define-minor-mode ispell-minor-mode
   "Toggle Ispell minor mode.
 With prefix argument ARG, turn Ispell minor mode on if ARG is positive,
 otherwise turn it off.
@@ -3405,11 +3475,7 @@ warns you if the previous word is incorrectly spelled.
 
 All the buffer-local variables and dictionaries are ignored -- to read
 them into the running ispell process, type \\[ispell-word] SPC."
-  (interactive "P")
-  (setq ispell-minor-mode
-       (not (or (and (null arg) ispell-minor-mode)
-                (<= (prefix-numeric-value arg) 0))))
-  (force-mode-line-update))
+  nil " Spell" ispell-minor-keymap)
 
 (defun ispell-minor-check ()
   "Check previous word then continue with the normal binding of this key.
@@ -3444,7 +3510,7 @@ Don't read buffer-local settings or word lists."
               ;; Matches context difference listing
               "\\(\\(^cd .*\n\\)?diff -c .*\\)?\n\\*\\*\\* .*\n--- .*\n\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*"
               ;; Matches unidiff difference listing
-              "\\(diff -u .*\\)?\n--- .*\n\\+\\+\\+ .*\n@@ [-+][0-9]+,[0-9]+ [-+][0-9]+,[0-9]+ @@\n"
+              "\\(diff -u .*\\)?\n--- .*\n\\+\\+\\+ .*\n@@ [-+][0-9]+,[0-9]+ [-+][0-9]+,[0-9]+ @@"
               ;; Matches reporter.el bug report
               "^current state:\n==============\n"
               ;; Matches commonly used "cut" boundaries
@@ -3675,15 +3741,14 @@ You can bind this to the key C-c i in GNUS or mail by adding to
            (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'
+           (when (and mimep (not boundary))
                  (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)))))
+                  (set-marker limit (point))))
            (goto-char (max end-of-headers (point)))
            (forward-line 1)
            (setq case-fold-search old-case-fold-search)
@@ -3749,7 +3814,7 @@ Includes Latex/Nroff modes and extended character mode."
     (goto-char (point-max))
     ;; Uses last occurrence of ispell-parsing-keyword
     (if (search-backward ispell-parsing-keyword nil t)
-       (let ((end (save-excursion (end-of-line) (point)))
+       (let ((end (point-at-eol))
              string)
          (search-forward ispell-parsing-keyword)
          (while (re-search-forward " *\\([^ \"]+\\)" end t)
@@ -3766,7 +3831,7 @@ Includes Latex/Nroff modes and extended character mode."
                     (sit-for 2))))))))
 
 
-;;; Can kill the current ispell process
+;; Can kill the current ispell process
 
 (defun ispell-buffer-local-dict (&optional no-reload)
   "Initializes local dictionary and local personal dictionary.
@@ -3785,7 +3850,7 @@ Both should not be used to define a buffer-local dictionary."
        (if (search-backward ispell-dictionary-keyword nil t)
            (progn
              (search-forward ispell-dictionary-keyword)
-             (setq end (save-excursion (end-of-line) (point)))
+             (setq end (point-at-eol))
              (if (re-search-forward " *\\([^ \"]+\\)" end t)
                  (setq ispell-local-dictionary
                        (match-string-no-properties 1))))))
@@ -3793,7 +3858,7 @@ Both should not be used to define a buffer-local dictionary."
       (if (search-backward ispell-pdict-keyword nil t)
          (progn
            (search-forward ispell-pdict-keyword)
-           (setq end (save-excursion (end-of-line) (point)))
+           (setq end (point-at-eol))
            (if (re-search-forward " *\\([^ \"]+\\)" end t)
                (setq ispell-local-pdict
                      (match-string-no-properties 1)))))))
@@ -3817,7 +3882,7 @@ Both should not be used to define a buffer-local dictionary."
     (while (search-forward ispell-words-keyword nil t)
       (or ispell-buffer-local-name
          (setq ispell-buffer-local-name (buffer-name)))
-      (let ((end (save-excursion (end-of-line) (point)))
+      (let ((end (point-at-eol))
            (ispell-casechars (ispell-get-casechars))
            string)
        ;; buffer-local words separated by a space, and can contain
@@ -3833,22 +3898,23 @@ Both should not be used to define a buffer-local dictionary."
 
 ;;; returns optionally adjusted region-end-point.
 
+;; If comment-padright is defined, newcomment must be loaded.
+(declare-function comment-add "newcomment" (arg))
+
 (defun ispell-add-per-file-word-list (word)
   "Add WORD to the per-file word list."
   (or ispell-buffer-local-name
       (setq ispell-buffer-local-name (buffer-name)))
   (save-excursion
     (goto-char (point-min))
-    (let ((old-case-fold-search case-fold-search)
-         line-okay search done found)
+    (let (line-okay search done found)
       (while (not done)
-       (setq case-fold-search nil
-             search (search-forward ispell-words-keyword nil 'move)
+        (let ((case-fold-search nil))
+          (setq 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)
-             case-fold-search old-case-fold-search)
+                             fill-column)))
        (if (or (and search line-okay)
                (null search))
            (progn
@@ -3857,7 +3923,13 @@ Both should not be used to define a buffer-local dictionary."
                  (progn
                    (open-line 1)
                    (unless found (newline))
-                   (insert (concat comment-start " " ispell-words-keyword))
+                   (insert (if (fboundp 'comment-padright)
+                                ;; Try and use the proper comment marker,
+                                ;; e.g. ";;" rather than ";".
+                                (comment-padright comment-start
+                                                  (comment-add nil))
+                              comment-start)
+                            " " ispell-words-keyword)
                    (if (> (length comment-end) 0)
                        (save-excursion
                          (newline)
@@ -3903,5 +3975,4 @@ Both should not be used to define a buffer-local dictionary."
 ; LocalWords:  uuencoded unidiff sc nn VM SGML eval IspellPersDict unsplitable
 ; LocalWords:  lns XEmacs HTML casechars Multibyte
 
-;; arch-tag: 4941b9f9-3b7c-4a76-a4ed-5fa8b6010ef5
 ;;; ispell.el ends here