(sgml-parse-tag-backward): Fix to work at beginning of buffer.
[bpt/emacs.git] / lisp / mail / mh-e.el
index 5acb71d..386a9ac 100644 (file)
@@ -1,13 +1,9 @@
 ;;; mh-e.el --- GNU Emacs interface to the MH mail system
 
-;;; Copyright (C) 1985,86,87,88,90,92,93,94,95 Free Software Foundation, Inc.
+;; Copyright (C) 1985,86,87,88,90,92,93,94,95,97,2000  Free Software Foundation, Inc.
 
-(defconst mh-e-time-stamp "Time-stamp: <95/03/05 23:39:29 gildea>")
-(defconst mh-e-version "5.0"
-  "Version numbers of this version of mh-e.")
-
-;; Maintainer: Stephen Gildea <gildea@lcs.mit.edu>
-;; Version: 5.0
+;; Maintainer: Bill Wohler <wohler@newt.com>
+;; Version: 5.0.2
 ;; Keywords: mail
 ;; Bug-reports: include `M-x mh-version' output in any correspondence
 
 ;; GNU General Public License for more details.
 
 ;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs; see the file COPYING.  If not, write to
-;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+;; along with GNU Emacs; see the file COPYING.  If not, write to the
+;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
 
 ;;; Commentary:
 
-;;; HOW TO USE:
-;;; M-x mh-rmail to read mail.  Type C-h m there for a list of commands.
-;;; C-u M-x mh-rmail to visit any folder.
-;;; M-x mh-smail to send mail.  From within the mail reader, "m" works, too.
+;; HOW TO USE:
+;; M-x mh-rmail to read mail.  Type C-h m there for a list of commands.
+;; C-u M-x mh-rmail to visit any folder.
+;; M-x mh-smail to send mail.  From within the mail reader, "m" works, too.
 
-;;; MH (Message Handler) is a powerful mail reader.  The MH newsgroup
-;;; is comp.mail.mh; the mailing list is mh-users@ics.uci.edu (send to
-;;; mh-users-request to be added).  See the monthly Frequently Asked
-;;; Questions posting there for information on getting MH and mh-e.
+;; MH (Message Handler) is a powerful mail reader.  The MH newsgroup
+;; is comp.mail.mh; the mailing list is mh-users@ics.uci.edu (send to
+;; mh-users-request to be added).  See the monthly Frequently Asked
+;; Questions posting there for information on getting MH and mh-e.
 
-;;; mh-e is an Emacs interface to the MH mail system.
-;;; The mailing list mh-e@x.org is for discussion of mh-e and
-;;; announcements of new versions.  Send a "subscribe" message to
-;;; mh-e-request@x.org to be added.  Do not report bugs here; mail
-;;; them directly to the author (see top of mh-e.el source).
-;;; Include the output of M-x mh-version in any bug report.
+;; mh-e is an Emacs interface to the MH mail system.
+;; There is a mailing list for discussion of mh-e and
+;; announcements of new versions.  Send a "subscribe" message to
+;; mh-e-request@gnu.org to be added.  Do not report bugs here; mail
+;; them directly to the maintainer (see top of mh-e.el source).
+;; Include the output of M-x mh-version in any bug report.
 
-;;; mh-e works with GNU Emacs 18 or 19, and MH 6.
+;; mh-e works with GNU Emacs 18 or 19, and MH 6.
 
-;;; NB.  MH must have been compiled with the MHE compiler flag or several
-;;; features necessary for mh-e will be missing from MH commands, specifically
-;;; the -build switch to repl and forw.
+;; NB.  MH must have been compiled with the MHE compiler flag or several
+;; features necessary for mh-e will be missing from MH commands, specifically
+;; the -build switch to repl and forw.
 
-;;; Your .emacs might benefit from these bindings:
-;;; (global-set-key "\C-cr" 'mh-rmail)
-;;; (global-set-key "\C-xm" 'mh-smail)
-;;; (global-set-key "\C-x4m" 'mh-smail-other-window)
+;; Your .emacs might benefit from these bindings:
+;; (global-set-key "\C-cr" 'mh-rmail)
+;; (global-set-key "\C-xm" 'mh-smail)
+;; (global-set-key "\C-x4m" 'mh-smail-other-window)
 
 ;;; Change Log:
 
-;;; Original version for Gosling emacs by Brian Reid, Stanford, 1982.
-;;; Modified by James Larus, BBN, July 1984 and UCB, 1984 & 1985.
-;;; Rewritten for GNU Emacs, James Larus 1985.  larus@ginger.berkeley.edu
-;;; Modified by Stephen Gildea 1988.  gildea@lcs.mit.edu
-(defconst mh-e-RCS-id "$Id: mh-e.el,v 1.5 1995/04/09 22:28:32 kwzh Exp kwzh $")
+;; Original version for Gosling emacs by Brian Reid, Stanford, 1982.
+;; Modified by James Larus, BBN, July 1984 and UCB, 1984 & 1985.
+;; Rewritten for GNU Emacs, James Larus 1985.  larus@ginger.berkeley.edu
+;; Modified by Stephen Gildea 1988.  gildea@stop.mail-abuse.org
+(defconst mh-e-RCS-id "$Id: mh-e.el,v 1.30 2001/09/23 17:38:22 eliz Exp $")
 
 ;;; Code:
 
 \f
 ;;; Hooks:
 
-(defvar mh-folder-mode-hook nil
-  "Invoked in MH-Folder mode on a new folder.")
+(defgroup mh nil
+  "Emacs interface to the MH mail system"
+  :group 'mail)
+
+(defgroup mh-hook nil
+  "Hooks to mh-e mode"
+  :prefix "mh-"
+  :group 'mh)
+
 
-(defvar mh-inc-folder-hook nil
-  "Invoked by \\<mh-folder-mode-map>`\\[mh-inc-folder]' after incorporating mail into a folder.")
+(defcustom mh-folder-mode-hook nil
+  "Invoked in MH-Folder mode on a new folder."
+  :type 'hook
+  :group 'mh-hook)
 
-(defvar mh-show-hook nil
-  "Invoked after \\<mh-folder-mode-map>`\\[mh-show]' shows a message.")
+(defcustom mh-inc-folder-hook nil
+  "Invoked by \\<mh-folder-mode-map>`\\[mh-inc-folder]' after incorporating mail into a folder."
+  :type 'hook
+  :group 'mh-hook)
 
-(defvar mh-show-mode-hook nil
-  "Invoked in MH-Show mode on each message.")
+(defcustom mh-show-hook nil
+  "Invoked after \\<mh-folder-mode-map>`\\[mh-show]' shows a message."
+  :type 'hook
+  :group 'mh-hook)
 
-(defvar mh-delete-msg-hook nil
-  "Invoked after marking each message for deletion.")
+(defcustom mh-show-mode-hook nil
+  "Invoked in MH-Show mode on each message."
+  :type 'hook
+  :group 'mh-hook)
 
-(defvar mh-refile-msg-hook nil
-  "Invoked after marking each message for refiling.")
+(defcustom mh-delete-msg-hook nil
+  "Invoked after marking each message for deletion."
+  :type 'hook
+  :group 'mh-hook)
 
-(defvar mh-before-quit-hook nil
-  "Invoked by \\<mh-folder-mode-map>`\\[mh-quit]' before quitting mh-e.  See also  mh-quit-hook.")
+(defcustom mh-refile-msg-hook nil
+  "Invoked after marking each message for refiling."
+  :type 'hook
+  :group 'mh-hook)
 
-(defvar mh-quit-hook nil
-  "Invoked after \\<mh-folder-mode-map>`\\[mh-quit]' quits mh-e.  See also  mh-before-quit-hook.")
+(defcustom mh-before-quit-hook nil
+  "Invoked by \\<mh-folder-mode-map>`\\[mh-quit]' before quitting mh-e.  See also  mh-quit-hook."
+  :type 'hook
+  :group 'mh-hook)
+
+(defcustom mh-quit-hook nil
+  "Invoked after \\<mh-folder-mode-map>`\\[mh-quit]' quits mh-e.  See also  mh-before-quit-hook."
+  :type 'hook
+  :group 'mh-hook)
 
 
 
 ;;; Personal preferences:
 
-(defvar mh-lpr-command-format "lpr -J '%s'"
+(defcustom mh-lpr-command-format "lpr -J '%s'"
   "*Format for Unix command that prints a message.
 The string should be a Unix command line, with the string '%s' where
 the job's name (folder and message number) should appear.  The formatted
-message text is piped to this command when you type \\<mh-folder-mode-map>`\\[mh-print-msg]'.")
+message text is piped to this command when you type \\<mh-folder-mode-map>`\\[mh-print-msg]'."
+  :type 'string
+  :group 'mh)
 
-(defvar mh-scan-prog "scan"
+(defcustom mh-scan-prog "scan"
   "*Program to run to generate one-line-per-message listing of a folder.
 Normally \"scan\" or a file name linked to scan.  This file is searched
 for relative to the mh-progs directory unless it is an absolute pathname.
-Automatically becomes buffer-local when set in any fashion.")
+Automatically becomes buffer-local when set in any fashion."
+  :type 'string
+  :group 'mh)
 (make-variable-buffer-local 'mh-scan-prog)
 
-(defvar mh-inc-prog "inc"
+(defcustom mh-inc-prog "inc"
   "*Program to run to incorporate new mail into a folder.
 Normally \"inc\".  This file is searched for relative to
-the mh-progs directory unless it is an absolute pathname.")
+the mh-progs directory unless it is an absolute pathname."
+  :type 'string
+  :group 'mh)
 
-(defvar mh-print-background nil
+(defcustom mh-print-background nil
   "*Print messages in the background if non-nil.
 WARNING: do not delete the messages until printing is finished;
-otherwise, your output may be truncated.")
+otherwise, your output may be truncated."
+  :type 'boolean
+  :group 'mh)
 
-(defvar mh-recenter-summary-p nil
-  "*Recenter summary window when the show window is toggled off if non-nil.")
+(defcustom mh-recenter-summary-p nil
+  "*Recenter summary window when the show window is toggled off if non-nil."
+  :type 'boolean
+  :group 'mh)
 
-(defvar mh-do-not-confirm nil
+(defcustom mh-do-not-confirm nil
   "*Non-nil means do not prompt for confirmation before some mh-e commands.
-Affects non-recoverable commands such as mh-kill-folder and mh-undo-folder.")
+Affects non-recoverable commands such as `mh-kill-folder' and `mh-undo-folder'."
+  :type 'boolean
+  :group 'mh)
 
-(defvar mh-store-default-directory nil
+(defcustom mh-store-default-directory nil
   "*Last directory used by \\[mh-store-msg]; default for next store.
-A directory name string, or nil to use current directory.")
+A directory name string, or nil to use current directory."
+  :type '(choice (const :tag "Current" nil)
+                directory)
+  :group 'mh)
 
 ;;; Parameterize mh-e to work with different scan formats.  The defaults work
 ;;; with the standard MH scan listings, in which the first 4 characters on
 ;;; the line are the message number, followed by two places for notations.
 
 (defvar mh-good-msg-regexp  "^....[^D^]"
-  "Regexp specifiying the scan lines that are 'good' messages.")
+  "Regexp specifying the scan lines that are 'good' messages.")
 
 (defvar mh-deleted-msg-regexp "^....D"
   "Regexp matching scan lines of deleted messages.")
@@ -165,7 +203,7 @@ A directory name string, or nil to use current directory.")
 
 (defvar mh-partial-folder-mode-line-annotation "select"
   "Annotation when displaying part of a folder.
-The string is displayed after the folder's name.  NIL for no annotation.")
+The string is displayed after the folder's name.  nil for no annotation.")
 
 
 ;;; Internal variables:
@@ -390,7 +428,6 @@ previous refile or write command."
         (message "Destination: %s" (cdr mh-last-destination))))
   (mh-next-msg))
 
-
 (defun mh-quit ()
   "Quit the current mh-e folder.
 Start by running mh-before-quit-hook.  Restore the previous window
@@ -522,7 +559,7 @@ provided, then prompt for the message sequence."
   (mh-find-progs)
   (set-buffer (get-buffer-create mh-temp-buffer))
   (erase-buffer)
-  (insert "  mh-e info:\n\nversion: " mh-e-version "\n" mh-e-time-stamp
+  (insert "  mh-e info:\n\nversion: " mh-e-RCS-id
          "\nEmacs: " emacs-version " on " (symbol-name system-type) " ")
   (condition-case ()
       (call-process "uname" nil t nil "-a")
@@ -592,7 +629,7 @@ Flush mh-e's state out to MH.  The message at the cursor becomes current."
   (save-excursion
     (mh-goto-msg msg nil t)
     (if (looking-at mh-refiled-msg-regexp)
-       (error "Message %d is refiled.  Undo refile before deleting." msg))
+       (error "Message %d is refiled.  Undo refile before deleting" msg))
     (if (looking-at mh-deleted-msg-regexp)
        nil
        (mh-set-folder-modified-p t)
@@ -606,7 +643,7 @@ Flush mh-e's state out to MH.  The message at the cursor becomes current."
   (save-excursion
     (mh-goto-msg msg nil t)
     (cond ((looking-at mh-deleted-msg-regexp)
-          (error "Message %d is deleted.  Undo delete before moving." msg))
+          (error "Message %d is deleted.  Undo delete before moving" msg))
          ((looking-at mh-refiled-msg-regexp)
           (if (y-or-n-p
                (format "Message %d already refiled.  Copy to %s as well? "
@@ -637,7 +674,7 @@ Flush mh-e's state out to MH.  The message at the cursor becomes current."
   (if (get-buffer mh-show-buffer)
       (delete-windows-on mh-show-buffer))
   (setq mh-showing nil)
-  (set-buffer-modified-p (buffer-modified-p)) ;force mode line update
+  (force-mode-line-update)
   (if mh-recenter-summary-p
       (mh-recenter nil)))
 
@@ -720,50 +757,50 @@ Here are all the commands with their current binding, listed in key order:
 
 Variables controlling mh-e operation are (defaults in parentheses):
 
mh-recursive-folders (nil)
`mh-recursive-folders' (nil)
     Non-nil means commands which operate on folders do so recursively.
 
mh-bury-show-buffer (t)
`mh-bury-show-buffer' (t)
     Non-nil means that the buffer used to display message is buried.
     It will never be offered as the default other buffer.
 
mh-clean-message-header (nil)
`mh-clean-message-header' (nil)
     Non-nil means remove header lines matching the regular expression
     specified in mh-invisible-headers from messages.
 
mh-visible-headers (nil)
`mh-visible-headers' (nil)
     If non-nil, it contains a regexp specifying the headers that are shown in
     a message if mh-clean-message-header is non-nil.  Setting this variable
-    overrides mh-invisible-headers.
+    overrides `mh-invisible-headers'.
 
mh-do-not-confirm (nil)
`mh-do-not-confirm' (nil)
     Non-nil means do not prompt for confirmation before executing some
-    non-recoverable commands such as mh-kill-folder and mh-undo-folder.
+    non-recoverable commands such as `mh-kill-folder' and `mh-undo-folder'.
 
mhl-formfile (nil)
`mhl-formfile' (nil)
     Name of format file to be used by mhl to show messages.
-    A value of T means use the default format file.
-    Nil means don't use mhl to format messages.
+    A value of t means use the default format file.
+    nil means don't use mhl to format messages.
 
mh-lpr-command-format (\"lpr -p -J '%s'\")
`mh-lpr-command-format' (\"lpr -p -J '%s'\")
     Format for command used to print a message on a system printer.
 
mh-scan-prog (\"scan\")
`mh-scan-prog' (\"scan\")
     Program to run to generate one-line-per-message listing of a folder.
     Normally \"scan\" or a file name linked to scan.  This file is searched
     for relative to the mh-progs directory unless it is an absolute pathname.
     Automatically becomes buffer-local when set in any fashion.
 
mh-print-background (nil)
`mh-print-background' (nil)
     Print messages in the background if non-nil.
     WARNING: do not delete the messages until printing is finished;
     otherwise, your output may be truncated.
 
mh-recenter-summary-p (nil)
`mh-recenter-summary-p' (nil)
     If non-nil, then the scan listing is recentered when the window displaying
     a messages is toggled off.
 
mh-summary-height (4)
`mh-summary-height' (4)
     Number of lines in the summary window including the mode line.
 
 The value of mh-folder-mode-hook is called when a new folder is set up."
@@ -786,6 +823,7 @@ The value of mh-folder-mode-hook is called when a new folder is set up."
    'mh-narrowed-to-seq nil             ; Sequence display is narrowed to
    'mh-first-msg-num nil               ; Number of first msg in buffer
    'mh-last-msg-num nil                        ; Number of last msg in buffer
+   'mh-msg-count nil                   ; Number of msgs in buffer
    'mh-mode-line-annotation nil                ; Indiction this is not the full folder
    'mh-previous-window-config nil)     ; Previous window configuration
   (setq truncate-lines t)
@@ -807,8 +845,7 @@ The value of mh-folder-mode-hook is called when a new folder is set up."
   ;; Take VARIABLE-VALUE pairs and make local variables initialized to the
   ;; value.
   (while pairs
-    (make-variable-buffer-local (car pairs))
-    (set (car pairs) (car (cdr pairs)))
+    (set (make-local-variable (car pairs)) (car (cdr pairs)))
     (setq pairs (cdr (cdr pairs)))))
 
 
@@ -867,9 +904,9 @@ The value of mh-folder-mode-hook is called when a new folder is set up."
        (folder mh-current-folder)
        (new-mail-p nil))
     (with-mh-folder-updating (t)
-      (message (if maildrop-name
-                  (format "inc %s -file %s..." folder maildrop-name)
-                  (format "inc %s..." folder)))
+      (if maildrop-name
+         (message "inc %s -file %s..." folder maildrop-name)
+       (message "inc %s..." folder))
       (setq mh-next-direction 'forward)
       (goto-char (point-max))
       (let ((start-of-inc (point)))
@@ -882,10 +919,9 @@ The value of mh-folder-mode-hook is called when a new folder is set up."
                                "-truncate")
            (mh-exec-cmd-output mh-inc-prog nil
                                "-width" (window-width)))
-       (message
-        (if maildrop-name
-            (format "inc %s -file %s...done" folder maildrop-name)
-            (format "inc %s...done" folder)))
+       (if maildrop-name
+           (message "inc %s -file %s...done" folder maildrop-name)
+         (message "inc %s...done" folder))
        (goto-char start-of-inc)
        (cond ((save-excursion
                 (re-search-forward "^inc: no mail" nil t))
@@ -901,8 +937,8 @@ The value of mh-folder-mode-hook is called when a new folder is set up."
        (mh-notate-user-sequences)
        (if new-mail-p
            (progn
-             (mh-goto-cur-msg)
-             (mh-make-folder-mode-line))
+             (mh-make-folder-mode-line)
+             (mh-goto-cur-msg))
            (goto-char point-before-inc))))))
 
 
@@ -916,19 +952,19 @@ The value of mh-folder-mode-hook is called when a new folder is set up."
     (setq mh-first-msg-num (mh-get-msg-num nil))
     (mh-last-msg)
     (setq mh-last-msg-num (mh-get-msg-num nil))
-    (let ((lines (count-lines (point-min) (point-max))))
-      (setq mode-line-buffer-identification
-           (list (format "{%%b%s} %d msg%s"
-                         (if mh-mode-line-annotation
-                             (format "/%s" mh-mode-line-annotation)
-                           "")
-                         lines
-                         (if (zerop lines)
-                             "s"
-                             (if (> lines 1)
-                                 (format "s (%d-%d)" mh-first-msg-num
-                                         mh-last-msg-num)
-                                 (format " (%d)" mh-first-msg-num)))))))))
+    (setq mh-msg-count (count-lines (point-min) (point-max)))
+    (setq mode-line-buffer-identification
+         (list (format "{%%b%s} %d msg%s"
+                       (if mh-mode-line-annotation
+                           (format "/%s" mh-mode-line-annotation)
+                         "")
+                       mh-msg-count
+                       (if (zerop mh-msg-count)
+                           "s"
+                         (if (> mh-msg-count 1)
+                             (format "s (%d-%d)" mh-first-msg-num
+                                     mh-last-msg-num)
+                           (format " (%d)" mh-first-msg-num))))))))
 
 
 (defun mh-unmark-all-headers (remove-all-flags)
@@ -944,16 +980,16 @@ The value of mh-folder-mode-hook is called when a new folder is set up."
        (forward-char mh-cmd-note)
        (setq char (following-char))
        (if (or (and remove-all-flags
-                    (or (eql char (aref mh-note-deleted 0))
-                        (eql char (aref mh-note-refiled 0))))
-               (eql char (aref mh-note-cur 0)))
+                    (or (= char (aref mh-note-deleted 0))
+                        (= char (aref mh-note-refiled 0))))
+               (= char (aref mh-note-cur 0)))
            (progn
              (delete-char 1)
              (insert " ")))
        (if remove-all-flags
            (progn
              (forward-char 1)
-             (if (eql (following-char) (aref mh-note-seq 0))
+             (if (= (following-char) (aref mh-note-seq 0))
                  (progn
                    (delete-char 1)
                    (insert " ")))))
@@ -1044,7 +1080,7 @@ The value of mh-folder-mode-hook is called when a new folder is set up."
 
 (defun mh-update-unseen ()
   ;; Flush updates to the Unseen sequence out to MH.
-  ;; Return non-NIL iff set the MH folder.
+  ;; Return non-nil iff set the MH folder.
   (if mh-seen-list
       (let* ((unseen-seq (mh-find-seq mh-unseen-seq))
             (unseen-msgs (mh-seq-msgs unseen-seq)))
@@ -1061,16 +1097,11 @@ The value of mh-folder-mode-hook is called when a new folder is set up."
 
 (defun mh-delete-scan-msgs (msgs)
   ;; Delete the scan listing lines for each of the msgs in the LIST.
-  ;; Optimized for speed (i.e., no regular expressions).
-  (setq msgs (sort msgs '<))   ;okay to clobber msgs
   (save-excursion
-    (mh-first-msg)
-    (while (and msgs (< (point) (point-max)))
-      (cond ((equal (mh-get-msg-num nil) (car msgs))
-            (delete-region (point) (save-excursion (forward-line) (point)))
-            (setq msgs (cdr msgs)))
-           (t
-            (forward-line))))))
+    (while msgs
+      (if (mh-goto-msg (car msgs) t t)
+         (mh-delete-line 1))
+      (setq msgs (cdr msgs)))))
 
 
 (defun mh-outstanding-commands-p ()
@@ -1090,9 +1121,9 @@ The value of mh-folder-mode-hook is called when a new folder is set up."
     (while prev
       (if range-high
          (if (or (not (numberp prev))
-                 (not (eql (car msgs) (1- prev))))
+                 (not (equal (car msgs) (1- prev))))
              (progn                    ;non-sequential, flush old range
-               (if (eql prev range-high)
+               (if (eq prev range-high)
                    (setq ranges (cons range-high ranges))
                  (setq ranges (cons (format "%s-%s" prev range-high) ranges)))
                (setq range-high nil))))
@@ -1180,7 +1211,7 @@ The value of mh-folder-mode-hook is called when a new folder is set up."
 
 
 (defun mh-internal-seq (name)
-  ;; Return non-NIL if NAME is the name of an internal mh-e sequence.
+  ;; Return non-nil if NAME is the name of an internal mh-e sequence.
   (or (memq name '(answered cur deleted forwarded printed))
       (eq name mh-unseen-seq)
       (eq name mh-previous-seq)
@@ -1329,6 +1360,8 @@ inform MH of the change."
 (define-key mh-folder-mode-map ">" 'mh-write-msg-to-file)
 (define-key mh-folder-mode-map "!" 'mh-refile-or-write-again)
 
+;; "C-c /" prefix is used in mh-folder-mode by pgp.el and mailcrypt
+
 \f
 
 ;;;autoload the other mh-e parts
@@ -1487,4 +1520,8 @@ If optional prefix argument provided, then prompt for the message sequence." t)
 (autoload 'mh-rename-seq "mh-seq"
   "Rename SEQUENCE to have NEW-NAME." t)
 
+(dolist (mess '("^Cursor not pointing to message$"
+               "^There is no other window$"))
+  (add-to-list 'debug-ignored-errors mess))
+
 ;;; mh-e.el ends here