diff options
author | Bill Wohler <wohler@newt.com> | 2004-08-15 22:00:06 +0000 |
---|---|---|
committer | Bill Wohler <wohler@newt.com> | 2004-08-15 22:00:06 +0000 |
commit | f0d73c14e2c9b9329a86ed8092f9329823598638 (patch) | |
tree | 673d530ab2247283cf5af3b570fa6a78450f144c /lisp | |
parent | 6dad1714dbc14c28b3ea54bd32744b6ff0ac2d76 (diff) | |
download | emacs-f0d73c14e2c9b9329a86ed8092f9329823598638.tar.gz |
Upgraded to MH-E version 7.4.80.
See etc/MH-E-NEWS and lisp/mh-e/ChangeLog for details.
Diffstat (limited to 'lisp')
-rw-r--r-- | lisp/mh-e/ChangeLog | 2008 | ||||
-rw-r--r-- | lisp/mh-e/mh-alias.el | 207 | ||||
-rw-r--r-- | lisp/mh-e/mh-comp.el | 450 | ||||
-rw-r--r-- | lisp/mh-e/mh-customize.el | 2662 | ||||
-rw-r--r-- | lisp/mh-e/mh-e.el | 524 | ||||
-rw-r--r-- | lisp/mh-e/mh-funcs.el | 97 | ||||
-rw-r--r-- | lisp/mh-e/mh-gnus.el | 36 | ||||
-rw-r--r-- | lisp/mh-e/mh-identity.el | 296 | ||||
-rw-r--r-- | lisp/mh-e/mh-inc.el | 8 | ||||
-rw-r--r-- | lisp/mh-e/mh-index.el | 225 | ||||
-rw-r--r-- | lisp/mh-e/mh-junk.el | 454 | ||||
-rw-r--r-- | lisp/mh-e/mh-loaddefs.el | 513 | ||||
-rw-r--r-- | lisp/mh-e/mh-mime.el | 288 | ||||
-rw-r--r-- | lisp/mh-e/mh-pick.el | 39 | ||||
-rw-r--r-- | lisp/mh-e/mh-seq.el | 212 | ||||
-rw-r--r-- | lisp/mh-e/mh-speed.el | 33 | ||||
-rw-r--r-- | lisp/mh-e/mh-utils.el | 743 |
17 files changed, 5730 insertions, 3065 deletions
diff --git a/lisp/mh-e/ChangeLog b/lisp/mh-e/ChangeLog index 3d19028b099..645e9a1bd6d 100644 --- a/lisp/mh-e/ChangeLog +++ b/lisp/mh-e/ChangeLog @@ -1,3 +1,239 @@ +2004-08-15 Bill Wohler <wohler@newt.com> + + * Released MH-E version 7.4.80. + + * MH-E-NEWS, README: Updated for release 7.4.80. + + * mh-e.el (Version, mh-version): Updated for release 7.4.80. + +2004-08-15 Bill Wohler <wohler@newt.com> + + * mh-funcs.el, mh-gnus.el, mh-inc.el, mh-init.el, mh-junk.el, + mh-pick.el, mh-print.el, mh-xemacs.el: Added 2004 to Copyright. + + * mh-acros.el, mh-alias.el: Checkdoc fixes. + +2004-08-12 Satyaki Das <satyaki@theforce.stanford.edu> + + * mh-acros.el (cl): Load cl in this file. That is all right, since + this file is only used at compile time, and so cl doesn't get + loaded at run time. This avoids problems with stale *.elc files + present in the Emacs source tree during compilation. + (mh-defstruct): Modify it to make it more CL like and in the + process simplify it a bit. This makes the argument list of the + constructor compatible with the previous version, thereby avoiding + a compilation error when an old version of mh-seq.elc is present. + + * mh-seq.el (mh-thread-id-container, mh-thread-get-message) + (mh-thread-get-message-container): Revert back to the CL style + of using keyword arguments, since the mh-defstruct now produces + code compatible to such usage. + +2004-08-11 Satyaki Das <satyaki@theforce.stanford.edu> + + * mh-acros.el (mh-defstruct, mh-require-cl): Checkdoc fixes. + + * mh-utils.el (message-tokenize-header, message-fetch-field): Add + autoloads. + (mh-folder-completing-read): Make the folder completion look + better with CVS Emacs. + + * mh-init.el (mh-variant-set): Remove dead code. + +2004-08-11 Bill Wohler <wohler@newt.com> + + * *.el: Use the following at the top of each file which seems to + do a good job of suppressing compilation warnings in 21.3 and CVS + Emacs (21.4). This replaces (require 'cl) or (require + 'utils) (mh-require-cl) calls: + + (eval-when-compile (require 'mh-acros)) + (mh-require-cl) + +2004-08-10 Bill Wohler <wohler@newt.com> + + * release-utils (DESCRIPTION): Added one. + (FILES, SEE ALSO, VERSION): Deleted empty and incorrect sections. + + * mh-e.el (mh-colors-available-p): Call x-display-color-cells with + mh-funcall-if-exists since it no longer seems to be defined in + GNU Emacs 21.4. + +2004-08-10 Satyaki Das <satyaki@theforce.stanford.edu> + + * mh-speed.el (mh-process-kill-without-query, mh-speed-flists): + Avoid a compiler warning in versions of Emacs where + process-kill-without-query is a deprecated function. + + * mh-seq.el (mh-thread-message, mh-thread-container): Use + mh-defstruct instead of defstruct. + (mh-thread-id-container, mh-thread-get-message-container) + (mh-thread-get-message): Use the slightly different structure + constructor function. + + * mh-acros.el (mh-defstruct): New macro which is a partial + replacement of the defstruct in CL. + (no-byte-compile): Don't compile the file since it isn't loaded at + run time, so efficiency isn't an issue. + + * mh-utils.el (mh-buffer-data): Use mh-defstruct instead of + defstruct. + +2004-08-09 Satyaki Das <satyaki@theforce.stanford.edu> + + * mh-funcs.el, mh-junk.el, mh-print.el: Use mh-require-cl to avoid + compilation warnings in Emacs-21.3. + + * mh-acros.el (mh-require-cl): Add autoloads of CL functions used. + +2004-08-09 Bill Wohler <wohler@newt.com> + + * mh-customize.el (mh-show-use-xface-flag): Mention that `fetch' and + `curl' are supported as well. + +2004-08-08 Bill Wohler <wohler@newt.com> + + * mh-xemacs.el (mh-xemacs-has-toolbar-flag): Checkdoc fixes. + + * mh-mime.el (mh-display-with-external-viewer): Checkdoc fixes. + + * mh-identity.el: (mh-identity-attribution-verb-end): Stripped + trailing space; checkdoc fixes. + + * mh-e.el (mh-restore-desktop-buffer): Checkdoc fixes. + + * mh-customize.el: (mh-inc-spool-list, + mh-compose-forward-as-mime-flag, defcustom): Stripped trailing + space; checkdoc fixes. + + * mh-comp.el (mh-reply): Stripped trailing space. + + * mh-unit.el (mh-unit-files): Added mh-acros.el and mh-gnus.el. + (mh-unit): Don't lm-verify pre-21.4. Save buffers before killing + since we might have done some editing. + + * import-emacs: Deleted. Functionality subsumed by release-utils. + + * release-utils: New script. Performs import-emacs functionality + and displays new and deleted options. + + * Makefile (import-emacs): Call release-utils instead of + import-emacs. + + * mh-funcs.el (mh-undo-folder): Removed deprecated `ignore' + argument. + + * mh-e.el (mh-scan-date-regexp): Deleted as Peter claims it is + obsolete. + (mh-folder-font-lock-keywords): Removed reference to deleted + variable `mh-scan-date-regexp'. + + * mh-customize.el (mh-auto-fields-prompt-flag): Made reference to + `mh-auto-fileds-lists'. + (mh-forward-hook): Fixed docstring typo. + +2004-08-07 Bill Wohler <wohler@newt.com> + + * mh-acros.el: New file. Currently holds macros needed by + mh-customize.el but is planned to hold all macros to avoid + dependency problems when compiling. + + * mh-utils.el (mh-xemacs-flag): Defined in mh-customize.el now. + (mh-require-cl, mh-do-in-gnu-emacs, mh-do-in-xemacs) + (mh-funcall-if-exists, mh-make-local-hook, mh-mark-active-p): + Moved to new file mh-acros.el. + + * mh-customize.el: Require mh-acros and cl only when compiling and + mh-loaddefs at runtime instead of mh-utils. + (mh-xemacs-flag): Define it here instead of mh-utils.el. + + * Makefile (MH-E-SRC): Added mh-acros.el. + + * mh-gnus.el (default-enable-multibyte-characters): Don't define + any more. It doesn't seem to be needed. + + * mh-customize.el (mh-junk-background): New variable. If on, spam + programs are run in background. Running in foreground can be slow. + Defaults to nil to spare machines with little memory. + + * mh-junk.el (mh-spamassassin-blacklist, mh-bogofilter-blacklist) + (mh-bogofilter-whitelist, mh-spamprobe-blacklist) + (mh-spamprobe-whitelist): Use new option mh-junk-background. + +2004-07-25 Satyaki Das <satyaki@theforce.stanford.edu> + + * mh-utils.el (mh-folder-completing-read): In recent CVS Emacs, + the first letter of the possible choices in the completion buffer + is highlighted. The change is needed for this feature to work + during folder name completion. This is not entirely sufficient, + since the leading "+" in folder names is still mishandled. A patch + is required in Emacs itself to address that. + +2004-07-22 Mark D. Baushke <mdb@gnu.org> + + * mh-e.el (recursive-load-depth-limit): Move + recursive-load-depth-limit code to ... + * mh-utils.el (recursive-load-depth-limit): ... here to avoid + problems compiling mh-utils.el and mh-alias.el with gnus-5.10.6 + under emacs-21.1. Use eval-and-compile instead of eval-when. + +2004-07-20 Bill Wohler <wohler@newt.com> + + * mh-customize.el (mh-invisible-header-fields-internal): Added + header fields emitted by T-Mobile picture phones (X-Mms-*, and + commented out X-Operator field saying it's like X-Mailer). + +2004-07-12 Bill Wohler <wohler@newt.com> + + * mh-gnus.el: Set local variables indent-tabs-mode and + sentence-end-double-space to nil. + + * mh-customize.el: Checkpoint from option docstring updates and + manual synchronization from last summer. For the options listed + below, docstring was usually completely rewritten. Use "on" + instead of "t" in docstring to match what is seen in customization + buffer. Use headline capitalization. Standardize on "Auto-detect" + text when option has that capibility. + (mh): Since we work on more than one type of Emacs, use Emacs + instead of GNU Emacs. Prefer GNU mailutils over GNU Mailutils. + (mh-variant): s/Autodetect at startup/Auto-detect/. + (mh-alias-insertion-location): s/Sorted + alphabetically/Alphabetical/. s/At the top of file/Top/. s/At the + bottom of file/Bottom/. + (mh-alias-local-users-prefix): s/Use login instead of real + name/Use Login/. + (mh-identity-list): Sorted values by fields, attribution, + signature, GPG key. + (mh-auto-fields-list): Missing quote. + (mh-compose-insertion): s/Use Gnus/Gnus/. s/Use mhn/mhn/. + (mh-compose-space-does-completion-flag): s/SPACE/<SPC>/. + (mh-extract-from-attribution-verb): Since we have French, added + German too ;-). + (mh-letter-complete-function): Mention default in docstring. + (mh-invisible-header-fields-internal): Added X-ELNK-Trace from + Earthlink. + (mh-alias-flash-on-comma, mh-alias-insert-file) + (mh-alias-passwd-gecos-comma-separator-flag) + (mh-recenter-summary-flag, mh-default-folder-for-message-function) + (mh-default-folder-must-exist-flag, mh-index-program) + (mh-index-ticked-messages-folders, mh-ins-buf-prefix) + (mh-delete-yanked-msg-window-flag, mh-identity-default): See + summary above. + + * mh-init.el (mh-variant-set, mh-sys-path, mh-variant-mu-mh-info): + Prefer GNU mailutils over GNU Mailutils MH. + + * mh-comp.el (sc-cite-original, mh-smail, mh-smail-batch) + (mh-edit-again, mh-extract-rejected-mail, mh-forward) + (mh-smail-other-window, mh-reply, mh-send, mh-send-other-window): + Use `mh-send' instead of \\[mh-send]] since links in the docstring + are more useful than a key sequence in these cases. Use "See also" + instead of "See also documentation for". + + * Merged in 7.4.4 changes, described below. + + * mh-e.el (Version, mh-version): Set to 7.4.4+cvs. + 2004-07-10 Bill Wohler <wohler@newt.com> * Released MH-E version 7.4.4. @@ -66,7 +302,7 @@ require that the cl package not be required at runtime. However, the cl package in versions of Emacs prior to 21.4 left cl routines in their macro expansions. Use mh-require-cl to provide the cl - routines in the best way possible. + routines in the best way possible (closes SF #930012). (require 'mouse): To shush compiler. * Use new function mh-require-cl throughout. @@ -87,6 +323,128 @@ 4. Run xbmtopbm < file.xbm > file.pbm. Thanks to jan.h.d@swipnet.se for the help. +2004-07-07 Stephen Gildea + + * mh-customize.el (mh-invisible-header-fields-internal): + Add X-Greylist, X-Source*, and X-WebTV-Signature. + Replace specific X-Spam-* headers with general pattern. + +2004-06-15 Bill Wohler <wohler@newt.com> + + * README: Vladimir Ivanovic reports that mh-rmail works with + XEmacs 21.5.17, so updated requirements text accordingly (closes + SF #644321). + +2004-05-12 Satyaki Das <satyaki@theforce.stanford.edu> + + * mh-utils.el (mh-mail-header-end): Replace call to + rfc822-goto-eoh with something that allows From_ lines in the mail + header. + +2004-04-14 Bill Wohler <wohler@newt.com> + + * mh-utils.el (mh-show-mouse): s/EVENT/event/. Thanks to John Paul + Wallington <jpw@gnu.org> for pointing this out. + +2004-04-12 Satyaki Das <satyaki@theforce.stanford.edu> + + * mh-e.el (mh-folder-size-flist): Add -showzero option so that the + parsing code doesn't get confused by the presence of -noshowzero + in the user's .mh_profile (closes SF #933954). + +2004-04-07 Satyaki Das <satyaki@theforce.stanford.edu> + + * mh-mime.el (mh-insert-mime-button) + (mh-insert-mime-security-button): Add evaporate property to + overlays used in MIME part buttons. This avoids problems with + CVS Emacs. + +2004-03-16 Satyaki Das <satyaki@theforce.stanford.edu> + + * mh-e.el (mh-folder-from-address): Go to the end of buffer if the + re-search-forward fails (closes SF #917096). + +2004-02-02 Satyaki Das <satyaki@theforce.stanford.edu> + + * mh-customize.el (mh-compose-forward-as-mime-flag): New user + customizable variable that controls whether messages are forwarded + as MIME attachments (closes SF #827203). + + * mh-comp.el (mh-forward): Call forw with -mime option only if + mh-compose-forward-as-mime-flag is non-nil. + +2003-12-26 Jeffrey C Honig <jch@honig.net> + + * mh-junk.el (mh-junk-blacklist, mh-junk-whitelist) + (mh-spamassassin-blacklist, mh-spamassassin-blacklist) + (mh-spamassassin-blacklist, mh-spamassassin-whitelist) + (mh-spamassassin-whitelist, mh-bogofilter-blacklist) + (mh-spamprobe-blacklist): Add progress messages. Change "Couldn't" + to "Unable" in error messages. Run bogofilter and spamprobe in + the foreground to prevent a large number of processes from + swamping the system. + +2003-12-25 Satyaki Das <satyaki@theforce.stanford.edu> + + * mh-e.el (mh-prompt-for-refile-folder): Marking the whole folder + and then refiling all messages throws an error, since this + function expects point to be on a valid scan line. The change + relaxes this requirement, thereby avoiding the above problem. + +2003-12-14 Satyaki Das <satyaki@theforce.stanford.edu> + + * mh-comp.el (mh-ascii-buffer-p): New function that checks if a + buffer is entirely composed of ASCII. + (mh-send-letter): Encode the draft if it contains non-ASCII + characters. + +2003-12-12 Satyaki Das <satyaki@theforce.stanford.edu> + + * mh-customize.el (mh-invisible-headers): Keep only unique fields + in list of header fields to hide. This avoids problems in XEmacs. + +2003-12-10 Satyaki Das <satyaki@theforce.stanford.edu> + + * mh-seq.el (mh-thread-print-scan-lines): The imenu index was not + getting created for threaded index buffers. The change fixes this. + + * mh-index.el (mh-index-insert-folder-headers): Always create the + imenu index. + (mh-index-create-imenu-index): Set which-func-mode to t. If + which-function-mode is turned on after the folder buffer has been + prepared, display of the folder info was being inhibited. The + change fixes that. + +2003-12-09 Satyaki Das <satyakid@stanford.edu> + + * mh-comp.el (mh-letter-mode): Setup mh-mail-header-separator + based on draft contents. + (mh-letter-mode, mh-letter-mail-header-end-marker) + (mh-letter-header-end): Remove use of the variable + mh-letter-mail-header-end-marker. Instead use + mh-mail-header-separator. This avoids problems in font locking + draft buffers (closes SF #855479). + +2003-12-09 Satyaki Das <satyakid@stanford.edu> + + * mh-index.el (mh-index-insert-folder-headers): Modified so that + imenu--index-alist is updated. + (mh-index-create-imenu-index): New function that generates an + index usable by imenu. This adds which-func-mode support to index + folders (closes SF #855520). + + * mh-e.el (which-func, which-func-modes): Tell which-func that + mh-folder-mode supports it. + (mh-folder-mode): Add support for imenu. + +2003-11-22 Peter S Galbraith <psg@debian.org> + + * Makefile: renamed mh-startup.el to mh-e-autoloads.el + + * README: renamed mh-startup.el to mh-e-autoloads.el + + * .cvsignore: Added mh-e-autoloads.el + 2003-11-18 Bill Wohler <wohler@newt.com> * Released MH-E version 7.4.3. @@ -95,6 +453,8 @@ * mh-e.el (Version, mh-version): Updated for release 7.4.3. + * This patch release contains the following two patches: + * mh-identity.el (mh-identity-make-menu): Removed condition on mh-auto-fields-list. Use it to enable or disable menu item instead. @@ -103,6 +463,810 @@ defcustom before mh-auto-fields-list so that defvar wouldn't clobber user's customization settings. +2003-11-17 Jeffrey C Honig <jch@honig.net> + + * mh-print.el (mh-print-msg): Do not print a message on deprecated + usage, the bindings have been removed. + + * mh-e.el (mh-folder-mode-map): Remove "l" binding for + mh-print-msg. + + * mh-utils.el (mh-show-mode-map): Remove "l" binding for + mh-print-msg. + +2003-11-16 Satyaki Das <satyakid@stanford.edu> + + * mh-comp.el (mh-beginning-of-word): Use the function + mh-mail-abbrev-make-syntax-table instead of the function + mail-abbrev-make-syntax-table. + + * mh-gnus.el (mh-mail-abbrev-make-syntax-table): Add a wrapper + function that calls mail-abbrev-make-syntax-table if available. + This is needed so that MH-E built with CVS Emacs will work with + released versions of Emacs21 and vice versa. + +2003-11-14 Peter S Galbraith <psg@debian.org> + + * mh-customize.el (mh-invisible-header-fields-internal): Add + "X-NAI-Spam-" and "X-Spam-Report:". + +2003-11-14 Mark D. Baushke <mdb@gnu.org> + + * mh-customize.el (mh-invisible-header-fields-internal): + Add X-AntiAbuse and X-MailScanner. + (Patch from Stephen Gildea.) + +2003-11-13 Peter S Galbraith <psg@debian.org> + + * mh-identity.el (mh-identity-handler-attribution-verb): New + function. A new Identity handler for the attribution verb (e.g. + "wrote:") to allow for different identities to use different + languages. + (mh-identity-insert-attribution-verb): New function. Insert the + attribution verb, placing special markers so it can be deleted and + replaced later. + (mh-identity-attribution-verb-start): New variable. Holds the + marker for the start of the attribution verb. + (mh-identity-attribution-verb-end): New variable. Holds the + marker for the end of the attribution verb. + + * mh-customize.el (mh-identity-handlers): Add new + ":attribution-verb" tag for the attribution-verb handler. + (mh-identity-list): Idem. + + * mh-comp.el (mh-yank-cur-msg): Insert attribution verb using + mh-identity-insert-attribution-verb. + (mh-extract-from-attribution): Extract only the name from the From + line, without appending `mh-extract-from-attribution-verb' since + markers need to be inserted around that now. + +2003-11-12 Bill Wohler <wohler@newt.com> + + * mh-e.el (mh-rmail, mh-nmail): Well, actually, we run in both GNU + Emacs and XEmacs, so removed the "GNU" in the docstrings unless + one is strictly talking about GNU Emacs. + + * mh-comp.el (mh-smail, mh-smail-batch, mh-smail-other-window): + Ditto. + +2003-11-11 Bill Wohler <wohler@newt.com> + + * mh-customize.el (mh-customize): Minor docstring change. + (mh, mh-e): The short description for MH-E is: The GNU Emacs + Interface to the MH Mail System. Therefore, updated docstrings + accordingly. + + * mh-comp.el (mh-smail, mh-smail-batch, mh-smail-other-window): + Ditto. + + * mh-e.el (mh-rmail, mh-nmail): Ditto. + +2003-11-10 Satyaki Das <satyakid@stanford.edu> + + * mh-mime.el (mh-mml-to-mime): In case errors happen in + mml-to-mime, restore contents of the draft buffer (closes SF + #839303). + +2003-11-07 Bill Wohler <wohler@newt.com> + + * mh-customize.el (mh-letter-mode-hook): Moved to mh-sending-mail + group (where it is now documented in the manual). + (mh-pick-mode-hook): Moved to mh-index group (where it is now + documented in the manual). + + * mh-loaddefs.el: Deleted per our discussion on mh-e-devel. No + more conflicts! No more check-ins! Anyone pulling CVS MH-E is + expected to compile. This file shall be added to the tarball so + that users of the distribution are not. + +2003-11-07 Mark D. Baushke <mdb@gnu.org> + + * mh-loaddefs.el: Regenerated. + + * mh-customize.el (mh-forward-hook): Define new hook. + * mh-comp.el (mh-forward): Use it. + +2003-11-07 Satyaki Das <satyakid@stanford.edu> + + * mh-loaddefs.el: Regenerated. + + * mh-utils.el (mh-show-toggle-mime-buttons) + (mh-show-display-with-external-viewer): New interactive functions + callable from the show buffer. + (mh-show-mime-map): Add bindings for "K t" and "K e". + (mh-show-msg): Propagate change to + mh-display-buttons-for-inline-parts-flag to the show buffer. + + * mh-mime.el (mh-display-with-external-viewer): New interactive + function to display MIME parts with external viewer (closes SF + #839318). + + * mh-e.el (mh-folder-mode): Make the variable + mh-display-buttons-for-inline-parts-flag buffer-local so that + display of MIME buttons can be toggled. + (mh-toggle-mime-buttons): New interactive function to toggle + display of MIME buttons. + (mh-mime-map): Modified to add bindings for "K t" and "K e". + +2003-11-04 Steve Youngs <sryoungs@bigpond.net.au> + + * Makefile (XEMACS_LOADDEFS_FILE): New. Used to generate mh-loaddefs.el + in XEmacs. + (XEMACS_LOADDEFS_COOKIE): Ditto. + (XEMACS_LOADDEFS_PKG_NAME): Ditto. + (xemacs): Add target to build mh-loaddefs.el in XEmacs + (clean-xemacs): Remove `mh-loaddefs.el*' + (loaddefs-xemacs): New rule to build mh-loaddefs.el in XEmacs. + +2003-11-02 Peter S Galbraith <psg@debian.org> + + * mh-init.el (mh-variant-set-variant): Reset `mh-x-mailer-string' + when we select an MH variant. + +2003-11-02 Jeffrey C Honig <jch@honig.net> + + * mh-loaddefs.el: Regenerated. + + * mh-funcs.el (mh-print-msg): Move to mh-print.el. + + * mh-e.el (mh-folder-mode-map): Add mh-print-msg ("l") back, it + will print a message that this usage is deprecated. + + * mh-print.el (require, mh-ps-print-msg, mh-ps-print-msg-file): + Require mh-funcs for mh-note-printed. PS print functions were not + setting the printed notation. Move mh-print-msg here for + consistency. Print message if mh-print-msg invoked via deprecated + key binding. + +2003-11-01 Peter S Galbraith <psg@debian.org> + + * Makefile: Add target to make `mh-startup.el', a file containg + usual entry commands into MH-E to be used for users installing + MH-E separately from Emacs. + + * README: Document the above for users. + +2003-10-29 Jeffrey C Honig <jch@honig.net> + + * mh-utils.el (mh-show-ps-print-map): Add "?" and "l" to + mh-show-ps-print-map. + + * mh-e.el (mh-ps-print-map, mh-help-messages): Add "?" and "l" to + mh-ps-print-map. Add "l" to help message. + +2003-10-27 Bill Wohler <wohler@newt.com> + + * Makefile (MH-E-SRC): Moved mh-xemacs.el to new variable + MH-E-XEMACS-SRC. + (MH-E-XEMACS-SRC): New variable to hold XEmacs source files. + (MH-E-XEMACS-OBJ): New variable to hold XEmacs object files. + (clean): Moved XEmacs-specific code to clean-xemacs. + (xemacs): Added clean-xemacs prerequisite. Moved down to + XEmacs section of file. + (clean-xemacs): New target to remove XEmacs-specific files. + (compile-xemacs): Added $(MH-E-XEMACS-SRC) prerequisite. + (dist): Added $(MH-E-XEMACS-SRC) to tarball. + +2003-10-27 Satyaki Das <satyakid@stanford.edu> + + * mh-loaddefs.el: Regenerated. + + * mh-index.el (mh-indexer-choices): Remove option for the non-free + glimpse indexer (closes SF #831276). + (mh-glimpse-binary, mh-glimpse-directory) + (mh-glimpse-execute-search, mh-glimpse-next-result): Functions + and variables to implement glimpse support are removed. + + * mh-customize.el (mh-index-program): Remove option for glimpse. + +2003-10-24 Satyaki Das <satyakid@stanford.edu> + + * mh-customize.el: Remove top-level test for toolbar enabled + XEmacs since it is not needed. + (mh-tool-bar-define): Add test for XEmacs toolbar in the functions + mh-toolbar-init, mh-tool-bar-letter-buttons-set and + mh-tool-bar-folder-buttons-set. This enables proper compilation + irrespective of whether the XEmacs was built with toolbar support + or not. + + * mh-comp.el (mh-letter-mode): Remove conditional since it is not + needed. + + * mh-e.el (mh-folder-mode): Same as above. + + * mh-utils.el (mh-show-mode): Same as above. + + * mh-xemacs.el (mh-xemacs-icon-map): Remove condition on toolbar + presence since we want the build to work if XEmacs without + toolbars is used during compilation. + +2003-10-23 Bill Wohler <wohler@newt.com> + + * mh-customize.el: The Great Reorganization. Sorted groups + alphabetically. Aligned variables in customization groups with + manual sections. Group docstrings changed to match manual chapter + titles. + +2003-10-22 Satyaki Das <satyakid@stanford.edu> + + * mh-speed.el (timer): Avoid compiler warning in XEmacs. + +2003-10-22 Steve Youngs <sryoungs@bigpond.net.au> + + * Makefile (XEMACS_OPTIONS): Add '-no-autoloads' to give a cleaner + build environment. + (AUTO_PRELOADS): Removed, in favour of 'AUTOLOAD_PACKAGE_NAME' and + 'AUTOLOAD_FILE'. + (AUTOLOAD_PACKAGE_NAME): New. + (AUTOLOAD_FILE): New. + (all): Don't set $EMACS_HOME if building with XEmacs. + (xemacs): Use 'compile-xemacs' instead of 'compile'. + (auto-autoloads.elc): Use new $AUTOLOAD_* vars and allow for + '-no-autoloads'. + (custom-load.elc): Allow for '-no-autoloads'. + (compile-xemacs): New. It allows for the '-no-autoloads' option + and byte-compiles all the source files with a single instance of + XEmacs. + + * mh-e.el (mh-folder-mode): Only load the toolbar in XEmacs if + toolbar support is available. + + * mh-comp.el (mh-letter-mode): Only load the toolbar in XEmacs if + toolbar support is available. + + * mh-customize.el: Require 'mh-xemacs' at toplevel when + 'mh-xemacs-flag' is non-nil. + Wrap all the toolbar code in a test that is true if using + GNU/Emacs or a toolbar-enabled XEmacs. + + * mh-print.el (mh-ps-spool-a-msg): Comment out + `clean-message-header-flag' because it isn't used anywhere. + + * mh-utils.el (mh-show-mode): Only load the toolbar in XEmacs if + toolbar support is available. + + * mh-xemacs.el: Autoload `regexp-opt', `customize-group', + `view-mode', `with-electric-help', `pp', `sort-numeric-fields', + `reverse-region', and `goto-address' at compile time. + (mh-xemacs-has-toolbar-flag): New. This is non-nil when XEmacs + has toolbar support. + (mh-xemacs-toolbar-*-icon): Use it. + +2003-10-21 Mark D. Baushke <mdb@gnu.org> + + * mh-identity.el (mh-identity-field-handler): Fields that begin + with ":" must have an mh-identity-handler defined or the user + gets an error. + +2003-10-17 Peter S Galbraith <psg@debian.org> + + * mh-customize.el (mh-identity-list): This change affects users! + The keyword "signature" becomes ":signature". The recently added + keyword "pgg-default-user-id" becomes ":pgg-default-user-id". + (mh-auto-fields-list): The keyword "Identity" becomes ":identity". + (mh-identity-handlers): Idem for signature and pgg-default-user-id. + + * mh-comp.el (mh-insert-auto-fields): Idem for Identity. + +2003-10-17 Peter S Galbraith <psg@debian.org> + + * mh-xemacs.el: Add eval-and-compile call to (load "toolbar" t t) to + make sure `toolbar-make-button-list' is defined. We can't use + require because Emacs doesn't have this library. + +2003-10-16 Bill Wohler <wohler@newt.com> + + * mh-customize.el (mh-signature-file-name) + (mh-letter-insert-signature-hook): Merge docstring with manual. + + * mh-comp.el (mh-file-is-vcard-p): Checkdoc fix. + (mh-insert-signature): Merge docstring with manual. + + * mh-customize.el (mh-junk): Changed manual link in defgroup from + Customizing mh-e to Junk. + (mh-junk-function-alist): Moved SpamAssassin to first in list on + the hunch that it is the most popular and should be chosen if + other anti-spam programs exist. + (mh-junk-mail-folder): Since the variable can accept values other + than folder names, renamed to mh-junk-disposition to more + accurately reflect the content. Merge docstring with manual. + (mh-junk-program): Moved SpamAssassin to the top of the menu for + the same reason presented in mh-junk-function-alist. Also, fixed + case of spam programs to match official usage. Merge docstring + with manual. + + * mh-junk.el (mh-junk-blacklist): + s/mh-junk-mail-folder/mh-junk-disposition/. Merge docstring with + manual. + (mh-junk-whitelist): Merge docstring with manual. + (mh-bogofilter-blacklist): No longer suggest using automatic + classification so use -s instead of -Ns. + (mh-bogofilter-whitelist): No longer suggest using automatic + classification so use -n instead of -Sn. + (mh-spamassassin-blacklist, mh-spamassassin-whitelist): Merge + docstring with manual. Moved spamassassin functions to top of file + so functions appear in same order that they are presented in menu. + +2003-10-09 Peter S Galbraith <psg@debian.org> + + * mh-customize.el (mail-citation-hook): Moved from mh-comp.el and + made into a defcustom. + +2003-10-09 Satyaki Das <satyakid@stanford.edu> + + * mh-loaddefs.el: Regenerated. + + * mh-comp.el (mh-get-header-field): Add autoload cookie. + + * mh-utils.el (mh-show-ps-print-toggle-mime) + (mh-show-ps-print-toggle-color, mh-show-ps-print-toggle-faces) + (mh-show-ps-print-msg-file, mh-show-ps-print-msg) + (mh-show-ps-print-msg-show): New interactive functions callable + from the show buffer. + (mh-show-ps-print-map): New key map for printing. + + * mh-e.el (mh-folder-mode-map): Remove key binding for + mh-print-msg. + (mh-ps-print-map): Add new key map for printing. + + * Makefile (MH-E-SRC): Add mh-print.el. + +2003-10-07 Satyaki Das <satyakid@stanford.edu> + + * mh-utils.el (mh-x-image-url-fetch-image): In XEmacs, + make-temp-file is not present. So to avoid security problems, use + a temporary file in the user's home directory. This avoids issues + in creating files in a world-writable directory. + + * mh-mime.el (mh-signature-highlight): In Emacs, arrange for the + overlay to be freed when it is no longer needed. Also, implement + signature highlighting in XEmacs. + +2003-10-05 Satyaki Das <satyakid@stanford.edu> + + * mh-mime.el (mh-mime-display, mh-mm-inline-message): Respect the + value of `mm-verify-option' and `mm-decrypt-option'. + (mh-mime-display-security): Rearrange code a bit to avoid too many + new lines being inserted when message verification/decryption is + carried out while the message is being read. Also use the + point-m{in|ax}-marker functions to make the function easier to read. + (mh-mime-security-press-button): Extend the function so that the + user can verify/decrypt messages while reading them. + + * mh-gnus.el (mm-possibly-verify-or-decrypt): Added to avoid + compiler warning with old Gnus. + + * mh-utils.el (mh-x-image-url-sane-p): New function which checks + if the URL in X-Image-URL is something we can handle. + (mh-x-image-url-display): Don't display image if the URL looks + malformed. + +2003-10-04 Mark D Baushke <mdb@gnu.org> + + * mh-comp.el (mh-letter-menu): Simplify menu heading. + +2003-10-03 Mark D Baushke <mdb@gnu.org> + + * mh-mime.el (mh-mml-query-cryptographic-method): Avoid + revisionist history and still provide a good default. + + * mh-comp.el (mh-letter-menu): Remove the Disable Security + parenthetical comment. + + * mh-loaddefs.el: Regenerated. + + * mh-customize.el (mh-mml-method-default): What method should be + used in secure directives. + + * mh-mime.el (mh-secure-message): New function used to generate + the mml security tags. + (mh-mml-unsecure-message): New wrapper function around + mml-unsecure-messages. + (mh-mml-secure-message-sign-pgpmime): Remove function. + (mh-mml-secure-message-encrypt-pgpmime): Ditto. + (mh-mml-cryptographic-method-history): New variable. + (mh-mml-query-cryptographic-method): New function. + (mh-mml-secure-message-encrypt): Ditto. + (mh-mml-secure-message-signencrypt): Ditto. + (mh-mml-secure-message-sign): Ditto. + + * mh-comp.el (mh-letter-menu, mh-letter-mode-help-messages, + (mh-letter-mode-map): Update to use new functions. + +2003-09-26 Satyaki Das <satyakid@stanford.edu> + + * mh-seq.el (mh-interactive-range): The function has been + extended so that it now takes a default result to return if no + interactive prefix arg is given and no region is active. + + * mh-e.el (mh-add-sequence-notation): If transient-mark-mode is + on, then the active region is deactivated based on whether a user + sequence or a internal sequence is being notated. The change + removes this inconsistency. + (mh-catchup, mh-folder-map): A new interactive function to mark + messages as read has been added and bound to "F c" in the folder + mode. + + * mh-utils.el (mh-show-catchup, mh-show-folder-map): New + interactive function callable from show mode buffers has been + bound to "F c". + +2003-09-24 Bill Wohler <wohler@newt.com> + + * mh-customize.el (mh-clean-message-header-flag) + (mh-invisible-header-fields-default, mh-invisible-header-fields): + Merge docstring with manual. + +2003-09-24 Mark D. Baushke <mdb@gnu.org> + + * mh-junk.el (mh-junk-blacklist): Junked messages should be put + into the mh-seen-list to avoid propagating the unseen sequence + into the spam folder. + + * mh-loaddefs.el: Regenerated. + + * mh-mime.el (mh-mml-secure-message-sign-pgpmime): Add an optional + dontsign argument to remove an existing secure message directive. + Update the docstring -- this fuction does not allow for + encrypt/sign, just sign directives. + + * mh-mime.el (mh-mml-secure-message-sign-pgpmime): Use + mml-insert-tag directly to provide a sender if + mh-identity-pgg-default-user-id is set. + (mh-mml-secure-message-encrypt-pgpmime): Use mml-insert-tag + directly to provide a sender if this message is to be both signed + and encrypted and mh-identity-pgg-default-user-id is set. + +2003-09-23 Bill Wohler <wohler@newt.com> + + * mh-alias.el (Commentary): Removed as it is now in the manual. + (mh-alias-system-aliases): Moved here from mh-customize.el. By + definition, "system" definitions are not user-visible, and user + filenames are in the the Aliasfile: profile component, so this + variable really shouldn't be a defcustom + (mh-alias-tstamp, mh-alias-filenames, mh-alias-reload) + (mh-alias-add-alias, mh-alias-grab-from-field) + (mh-alias-add-address-under-point, mh-alias-apropos): Merge + docstring with manual. + (mh-alias-reload-maybe): Minor comment update. + (mh-alias-insert-file): Merge docstring with manual. Removed + "[press TAB]" from prompt since users should know about completion + and space can be used as well. + (mh-alias-for-from-p): No longer returns a surprising result (t if + there was **not** an alias for the From field) if the From header + field is missing. This function now returns what you would expect + a function of this name to return. Renamed from + mh-alias-from-has-no-alias-p since negatives in the function name + make logic harder to follow. + (mh-alias-add-alias-to-file): Merge docstring with manual. + Improved verbiage of prompt. Aliases are now inserted "[b]efore" + or "[a]fter" the existing alias instead of "[i]nsert" or + "[a]ppend." Note how the new usage flows better. + + * mh-customize.el (mh-alias): Changed manual link in defgroup from + Customizing mh-e to Aliases. + (mh-alias-grab-from-field button): mh-alias-from-has-no-alias-p + renamed to mh-alias-for-from-p and no longer returns surprising + value if there isn't a From field. Therefore, enable button if + there is a From header field and mh-alias-for-from-p returns nil. + (mh-letter-complete-function) + (mh-alias-completion-ignore-case-flag, mh-alias-flash-on-comma) + (mh-alias-insert-file, mh-alias-insertion-location) + (mh-alias-local-users, mh-alias-local-users-prefix) + (mh-alias-passwd-gecos-comma-separator-flag): Merge docstring with + manual. + (mh-alias-system-aliases): Moved to mh-alias.el. + + * mh-comp.el (mh-letter-complete-function-alist): Removed comment + about making this customizable since I didn't think it seemed + appropriate in the manual. + (mh-letter-complete): Merge docstring with manual. + +2003-09-23 Satyaki Das <satyakid@stanford.edu> + + * mh-speed.el (mh-speed-flists): When exiting emacs, don't ask if + the flists process should be killed. + + * mh-e.el (mh-folder-message-menu): Enable undo menu entry only + if something can be undone. + + * mh-customize.el (undo): Enable undo button only if something + can be undone. + +2003-09-22 Peter S Galbraith <psg@debian.org> + + * mh-customize.el (mh-identity-handlers): New defcustom. Alist of + Handler functions for mh-identity (downcased) fields. + (mh-identity-list): Add support for pgg-default-user-id. + + * mh-identity.el (mh-insert-identity): Modified to use + `mh-identity-handlers', adding hacking flexibility for those who + might need it. + (mh-identity-field-handler): New function. Return the handler for + a FIELD or nil if none set. The field name is downcased. + (mh-identity-handler-gpg-identity): New function; handler for pgg + pgp identities. It sets a buffer-local value for + `mh-pgg-default-user-id' which must be handled by mh-send-letter. + (mh-identity-pgg-default-user-id): New buffer-local variable to + hold the requested key ID. + (mh-identity-handler-signature): New function; handler t insert + and remove signature files. + (mh-identity-handler-default): New function; the default handler + to insert or remove generic field. + (mh-identity-handler-top): Insert a field at the top of the + header. + (mh-identity-handler-bottom): Insert a field at the bottom of the + header. + (mh-header-field-delete): Make more robust wrt the field having a + trailing colon or not. + (mh-identity-make-menu): Add a "Customize Identities" menu entry. + + * mh-loaddefs.el: Regenerated. + +2003-09-21 Peter S Galbraith <psg@debian.org> + + * mh-init.el (mh-variant-set): Bug fix for mh-variant long names + with version numbers. + + * mh-e.el (mh-scan-format): patch from Sergey Poznyakoff. + GNU mailutils now supports the %(decode) format + +2003-09-20 Satyaki Das <satyakid@stanford.edu> + + * mh-gnus.el (mh-mm-text-html-renderer): New function to query + which HTML renderer is being used by Gnus. + + * mh-mime.el (mh-signature-highlight): Renderers used to display + HTML parts garble the signature separator in various ways. The + function has been modified to take that into account. + (mh-mime-display-single, mh-mm-display-part): Pass the new + optional argument to `mh-signature-highlight'. + +2003-09-19 Mark D. Baushke <mdb@gnu.org> + + * mh-mime.el (mh-have-file-command, mh-file-mime-type): Made an + mh-autoload as they are used in mh-comp.el. + + * mh-loaddefs.el: Regenerated. + +2003-09-18 Peter S Galbraith <psg@debian.org> + + * mh-comp.el (mh-insert-fields): Make sure field has a colon. + +2003-09-18 Satyaki Das <satyakid@stanford.edu> + + * mh-seq.el (mh-toggle-tick): Don't hardcode the name of the tick + sequencence in the function. This would have caused improper + highlighting of the tick sequence if the user had changed its + name. + +2003-09-15 Satyaki Das <satyakid@stanford.edu> + + * mh-e.el (mh-folder-message-menu): Fix a little bug which shows + up as a problem during compilation (closes SF #806577). + +2003-09-15 Mark D. Baushke <mdb@gnu.org> + + * mh-customize.el (mh-invisible-header-fields-internal): Added + a new field for GNU mailutils per Sergey Poznyakoff. + +2003-09-09 Satyaki Das <satyakid@stanford.edu> + + * mh-utils.el (vcard): Unconditionally load vcard.el, if + available, so that vcards are always inlined. + +2003-09-09 Peter S Galbraith <psg@debian.org> + + * mh-mime.el (mh-file-mime-type-substitutions): Add entry to + convert text/plain .vcf files to text/x-vcard. + (mh-mime-content-types): Add text/x-vcard. + +2003-09-09 Bill Wohler <wohler@newt.com> + + * mh-comp.el (mh-rejected-letter-start): Added strings for qmail + and exim (addresses SF #404965). + +2003-09-09 Satyaki Das <satyakid@stanford.edu> + + * mh-gnus.el (mm-inline-text-vcard): Make vcard display work with + Gnus-5.9. The extra file vcard.el is still needed. + + * mh-mime.el (mh-signature-highlight): New function that + highlights message signatures. + (mh-mm-display-part, mh-mime-display-single): Highlight signatures + using `mh-signature-highlight' (closes SF #802722). More work is + needed for XEmacs. + (mh-mime-display): Highlight signature in non-MIME email too. + + * mh-customize.el (mh-show-signature-face): New face used to + display message signature. + +2003-09-08 Peter S Galbraith <psg@debian.org> + + * mh-e.el (mh-version): Do something sensible when + mh-variant-in-use is undefined. + * mh-junk.el (mh-spamassassin-blacklist) + (mh-spamassassin-whitelist): Change options to be compatoble with + old version of spamassassin (V2.20). + +2003-09-07 Mark D. Baushke <mdb@gnu.org> + + * mh-mime.el (mh-access-types): Per RFC 2049, the "afs" + access-type for message/external-body has been removed. + Update the comments to reference the current MIME RFCs + 2045, 2046 and 2049 rather than the obsolete RFC 1521. + +2003-09-05 Peter S Galbraith <psg@debian.org> + + * mh-e.el (mh-version): Bumped version number to 7.4.2+cvs. + +2003-09-04 Satyaki Das <satyakid@stanford.edu> + + * mh-utils.el (mh-picon-directory-list, mh-picon-directory): The + mh-picon-directory-list variable supersedes mh-picon-directory. + (mh-picon-existing-directory-list): New variable that contains + the list of picon directories that actually exist. + (mh-picon-set-directory-list): New function to update + mh-picon-existing-directory-list from mh-picon-directory-list. + (mh-picon-get-image): The function has been modified to search a + list of possible picon source directories. The regexp to extract + the username from the email address has been made smarter so that + it can recognize email addresses of the form user+random@foo.net + and extract "user" from there. + (mh-picon-file-contents): The file type recognition code has been + moved from mh-picon-get-image into this function. + (mh-picon-generate-path): The function has been generalized so + that searching multiple paths is now feasible. + + * mh-pick.el, mh-e.el: Checkdoc fixes. + +2003-09-02 Satyaki Das <satyakid@stanford.edu> + + * mh-identity.el (eval-when): It seems that the mh-comp-loaded + code isn't required any more. + +2003-08-30 Satyaki Das <satyakid@stanford.edu> + + * mh-init.el (mh-variant-set): Replace `error' with `message' so + that Emacs CVS will compile without errors if no MH variant is + present. + +2003-08-29 Satyaki Das <satyakid@stanford.edu> + + * mh-init.el (mh-variant-set): Add interactive spec to the + function. + + * mh-mime.el (mh-mhn-compose-external-type): Optional arguments + are prompted for only if prefix arg is given. + +2003-08-29 Mark D. Baushke <mdb@gnu.org> + + * mh-mime.el (mh-mhn-compose-external-type): Modified to be + interactive and prompts for many of the fields. Made an + mh-autoload. + (mh-access-types): New table derived from RFC2017, RFC1521 and + RFC1738, used in a completing-read in + mh-mhn-compose-external-type. + + * mh-loaddefs.el: Regenerated. + +2003-08-26 Satyaki Das <satyakid@stanford.edu> + + * mh-utils.el (mh-picon-image-types, mh-picon-get-image): Avoid + compiler warnings. + (mh-sub-folders-actual): Parsing of the output from folders has + been modified, so that it also works for MH (closes SF #792300). + + * mh-junk.el (mh-spamassassin-whitelist): Avoid calling + ietf-drums-parse-address if it isn't present. + (mh-spamassassin-identify-spammers): Avoid use of puthash so that + Emacs20 doesn't complain. + + * mh-e.el (mh-colors-available-p): Wrap call to + display-color-cells in a mh-funcall-if-exists to avoid compiler + warning in Emacs20. + +2003-08-25 Satyaki Das <satyakid@stanford.edu> + + * mh-e.el (mh-colors-available-flag, mh-folder-mode): New + variable to track if colors are available and it is set + appropriately in mh-folder-mode. + (mh-colors-available-p, mh-colors-in-use-p): Two functions to + check whether colors are available and if they are actually being + used. + (mh-add-sequence-notation): Just changing a scan line doesn't + make font-lock refontify the line in Emacs20. So explicitly + refontify the scan line in such a situation. + (mh-internal-seq): If colors aren't being used then treat the + tick sequence like a normal user sequence. + + * mh-seq.el (mh-put-msg-in-seq): Do font-lock highlighting after + the messages have been added to the sequence. + (mh-toggle-tick): Modified so that highlighting of the ticked + messages will be properly done. If font-lock isn't being used or + if colors aren't supported by the Emacs where MH-E is running, + then the `%' character is used to annotate ticked messages. + + * mh-utils.el (mh-picon-image-types): Since Emacs20 doesn't have + image-type-available-p, wrap calls to that function in + ignore-errors. + (mh-add-msgs-to-seq): Do the font-lock highlighting after the + messages have been added. + +2003-08-24 Bill Wohler <wohler@newt.com> + + * Makefile (MH-E-SRC): Replaced mh-xemacs-compat.el and + mh-xemacs-icons.el with mh-xemacs.el. + + * mh-e.el: Don't require mh-xemacs-compat which no longer exists. + The XEmacs stuff gets required by mh-customize.el which is + required by mh-utils.el which is required by mh-e.el. This all + happens before mh-xemacs-compat was required, so all should be + well. + + * mh-unit.el (mh-unit-files): Replaced mh-xemacs-compat.el and + mh-xemacs-icons.el with mh-xemacs.el. + + * mh-xemacs.el: New file from concatenation of mh-xemacs-compat.el + and mh-xemacs-icons.el which were removed since their names + exceeded DOS 8+3 limits. + + * mh-customize.el (mh-compose-skipped-header-fields): Use + uppercase for field names. + +2003-08-21 Bill Wohler <wohler@newt.com> + + * mh-customize.el (mh-sequences): Introduced new customization + group for sequences. + (mh-refile-preserves-sequences-flag, mh-tick-seq) + (mh-update-sequences-after-mh-show-flag): Moved option from + mh-folder to mh-sequences group. Synced docstring with manual. + (mh-index-ticked-messages-folders): Since mh-tick-seq is + customizable, use it instead of tick in the docstring. + + * mh-index.el (mh-index-ticked-messages): Since mh-tick-seq is + customizable, use it instead of tick in the docstring. + + * mh-seq.el (mh-msg-is-in-seq): Can now specify an alternate + message number with a prefix argument. + (mh-narrow-to-tick): Since mh-tick-seq is customizable, use it + instead of tick in the docstring. Also, use mh-tick-seq instead of + tick in warning message. + +2003-08-20 Peter S Galbraith <psg@debian.org> + + * mh-customize.el: setq mh-variant to 'none when byte-compiling, + since we don't care what MH variant (if any) is on the system at + that point. + + * mh-init.el (mh-variant-set): Don't probe for MH variant when + mh-variant is set to'none (during byte-compilation). + +2003-08-19 Peter S Galbraith <psg@debian.org> + + * mh-pick.el (mh-pick-single-dash): New defconst. Search + components that are supported by single-dash option in + pick. + (mh-pick-regexp-builder): Use `mh-pick-single-dash' and adapt + patch from Sergey Poznyakoff. + + * mh-comp.el (mh-reply): mu-mh supports `repl -group', thanks to + Sergey Poznyakof. + + * mh-init.el: checkdoc fixes. + +2003-08-19 Bill Wohler <wohler@newt.com> + + * mh-seq.el: (mh-edit-pick-expr): Renamed from mh-read-pick-regexp + since the new name is more indicative of what the function does. + Prompt now says "Pick expression" instead of "Pick regexp". + (mh-narrow-to-subject): Rewrote function to behave like other + similar functions. + (mh-narrow-to-header-field, mh-narrow-to-range) + (mh-narrow-to-tick): s/regexp/pick-expr/. + (mh-widen, mh-narrow-to-from, mh-narrow-to-cc, mh-narrow-to-to): + Synced docstrings with manual + 2003-08-19 Bill Wohler <wohler@newt.com> * Released MH-E version 7.4.2. @@ -131,6 +1295,848 @@ (patches from 1.307 and 1.309 and branched for 7.4.2, closes SF #791021). +2003-08-18 Bill Wohler <wohler@newt.com> + + * mh-index.el (mh-index-sequenced-messages) + (mh-index-new-messages, mh-index-ticked-messages): Updated + docstrings from manual (closes SF #718833). + + * mh-customize.el (mh-variant): Checkdoc fix. + (mh-index-new-messages-folders): Don't mention defvar in + docstring, use `+inbox' instead. + (mh-index-ticked-messages-folders): Don't mention defvar in + docstring, use `tick' instead. + + * mh-comp.el (mh-repl-group-formfile): Checkdoc fix. + +2003-08-18 Peter S Galbraith <psg@debian.org> + + * mh-init.el (mh-variant-set, mh-sys-path, mh-variant-info): Add + support for GNU mailutils. + (mh-variant-mu-mh-info): New function to detect mu-mh and return + info about it for `mh-variants'. + + * mh-e.el (mh-regenerate-headers): mu-mh has different error + message for a invalid mesage list. + +2003-08-18 Peter S Galbraith <psg@debian.org> + + * mh-customize.el (mh-e): New defgroup. Sort of an alias for the + 'mh group that a user might be more likely to find. + +2003-08-18 Bill Wohler <wohler@newt.com> + + * mh-comp.el (mh-insert-auto-fields-done-local): Docstring tweak. + (mh-compose-and-send-mail): Do not call mh-insert-auto-fields. + This should be done only once in mh-send-letter. + +2003-08-18 Peter S Galbraith <psg@debian.org> + + * mh-comp.el (mh-letter-mode): Call `mh-find-path unconditionally, + like elsewehere in MH-E. + + * mh-utils.el (mh-find-path): Run setup code only if + `mh-find-path-run' is nil such that this is only done once. + Also remove the `setq' for `read-mail-command' and `mail-user-agent'. + +2003-08-18 Peter S Galbraith <psg@debian.org> + + * mh-e.el: require 'mh-utils first + + * mh-customize.el (mh-variant): defcustom moved here. + + * mh-init.el (mh-variants): Made an mh-autoload. + +2003-08-18 Peter S Galbraith <psg@debian.org> + + * Makefile (MH-E-SRC): Added mh-init.el to MH-E-SRC. + + * mh-utils.el (mh-find-progs): Deleted. Make obsolete by mh-init.el. + (mh-find-path): Call `mh-variants' instead of now obsolete + `mh-find-progs'. + (mh-path-search): Deleted. Was only used by `mh-find-progs'. + + * mh-e.el: require mh-init.el. + (mh-version): Use simpler `mh-variant-in-use'. + (mh-scan-format): Use (mh-variant-p 'nmh) instead of mh-nmh-flag. + + * mh-comp.el (mh-insert-x-mailer): Use simpler `mh-variant-in-use'. + + * mh-utils.el (mh-progs, mh-lib, mh-lib-progs) + (mh-flists-present-flag): Moved to mh-init.el. + (mh-nmh-flag): Deleted. Use (mh-variant-p 'nmh) instead. + + * mh-comp.el (mh-repl-group-formfile, mh-forward, mh-reply) + (mh-send-letter): Use (mh-variant-p 'nmh) instead of mh-nmh-flag. + + * mh-mime.el (mh-edit-mhn, mh-mime-save-parts): Use (mh-variant-p + 'nmh) instead of mh-nmh-flag. + +2003-08-16 Bill Wohler <wohler@newt.com> + + * mh-customize.el (mh-folder-selection): New group to hold + variables described in Folder Selection section in manual. + (mh-default-folder-list, mh-default-folder-must-exist-flag, + mh-default-folder-prefix): Moved to mh-folder-selection group. + Updated docstrings per manual update. + (mh-default-folder-for-message-function): New defcustom. Was a + defvar in mh-utils.el. Updated docstring per manual update. + + * mh-utils.el (mh-default-folder-for-message-function): Moved to + mh-customize.el. + + * mh-e.el (mh-folder-from-address, mh-prompt-for-refile-folder): + Updated docstrings per manual update. + + * mh-unit.el (mh-unit-files): Added mh-init.el. + +2003-08-16 Peter S Galbraith <psg@debian.org> + + * mh-init.el: New file. Code to initialize the MH-E back-end. + Highlights: + (mh-variant): New defcustom. Users may customize `mh-variant' to + switch between available variants. + (mh-variants): Available MH variants are described in this variable. + (mh-variant-in-use, mh-variant-p): Developers may check which + variant is currently in use with the variable `mh-variant-in-use' + or the function `mh-variant-p'. + +2003-08-15 Bill Wohler <wohler@newt.com> + + * mh-customize.el (mh-auto-fields-list): The manual uses Fcc + instead of fcc, so I've changed the user-visible text + accordingly. I've left the const alone for backwards + compatibility. + +2003-08-14 Bill Wohler <wohler@newt.com> + + * mh-identity.el (mh-insert-identity): Changed signature deletion + test to test for both markers, rather than testing to see if the + start marker is bound. Since the start marker is defined in this + file, it should always be bound. Suggestion by Satyaki. + + * mh-comp.el (mh-send-letter): Go to the top of the draft so that + the user can see which header fields have been inserted. I think + this is more important than leaving point alone or going to the + end to see the signature since Mail-Followup-To or Bcc or cc could + have some deleterious effects. + + * mh-customize.el (mh-auto-fields-prompt-flag): New variable. + Non-nil means to prompt before sending if fields inserted. + + * mh-comp.el (mh-insert-auto-fields): Now return t if fields + inserted; otherwise nil. + (mh-send-letter): Deleted obsolete documentation about adding + X-Mailer and X-Face. Prompt before sending if auto fields added + and mh-auto-fields-prompt-flag is t. + + * mh-customize.el (mh-identity-list): Allow signature to come from + mh-signature-file-name. In this case, the "signature" value is set + to nil. This might not be the best implementation. Suggestions + welcome. + + * mh-identity.el (mh-insert-identity): Now that the signature can + be a nil value, moved test higher up in cond so that the test for + a nil value would not be executed first prevening signature + handling. Handle nil signature value by calling + mh-insert-signature with no arguments which means to use + mh-signature-file-name. + + * mh-comp.el (mh-insert-signature): Changed text of message if no + signature inserted. + + * mh-customize.el (mh-identity-list): Changed "Signature" constant + back to "signature" so it *won't* be backwards-incompatible any + more. I discovered one could use the :tag keyword to get headline + captalization in the menu. + + * mh-identity.el (mh-insert-identity): Ditto. + + * mh-identity.el (mh-identity-make-menu): Always build menu. + Always create Insert Auto Fields menu item. Just don't enable it + if mh-auto-fields-list is nil. Enable radio buttons always. Make + None a radio button choice with the other identities. + + * mh-comp.el (mh-letter-menu): Removed cond on fboundp + 'easy-menu-define. We don't do this elsewhere. + +2003-08-13 Bill Wohler <wohler@newt.com> + + * mh-identity.el (mh-identity-make-menu, mh-insert-identity): Use + headline capitalization in menu items. Even the internal names are + exposed in the customize interface, so they need to be uppercase + too. + (mh-insert-identity): Rather than goto-char to + mh-identity-signature-start before deleting, simply pass it to + delete-region. When setting markers, use point-min-marker and + point-max-marker instead of moving point. Set marker type of + mh-identity-signature-start to t to fix a bug where changing + identity deleted user's text. + + * mh-customize.el (mh-identity-list, mh-auto-fields-list): + Reworked docstring. Use headline capitalization. Commented out + implementation details for later deletion or resurrection upon + popular demand. N.B. If your mh-identity-list contains "signature" + then you will need to either edit your .emacs file manually, or + delete your existing "signature" which will become a regular field + with this change and create a new signature. I figured I could get + away with this since 8.0 is a major release, and coinciding with + the manual update will be a MAJOR release. I apologize profusely + that I didn't catch this before it was released. + (mh-identity-default): Use headline capitalization in example. + +2003-08-12 Jeffrey C Honig <jch@honig.net> + + * mh-customize.el (mh-alias-reloaded-hook): Define + `mh-alias-reloaded-hook'. + + * mh-alias.el (mh-alias-reload): Run `mh-alias-reloaded-hook' + after reloading the aliases. + +2003-08-12 Mark D. Baushke <mdb@gnu.org> + + * mh-comp.el (mh-insert-signature): Use functionp to avoid + the possibility of doing a funcall on a void function. + +2003-08-12 Bill Wohler <wohler@newt.com> + + * mh-customize.el (mh-identity): Point group manual link to new + Identities section. + (mh-signature-separator-flag): New variable which can be used to + suppress the output of the signature separator. + + * mh-comp.el (mh-insert-signature): Use + mh-signature-separator-flag. + + * mh-identity.el (mh-insert-identity): If the identity's signature + file didn't exist, an fboundp error was thrown. This was fixed by + removing signature tests that were redundant and out of date with + the tests in mh-insert-signature. Removed second signature + condition as it is now handled in the first signature condition. + +2003-08-12 Peter S Galbraith <psg@debian.org> + + * mh-identity.el (mh-insert-identity): Don't insert new lines on + signatures anymore. + + * mh-comp.el (mh-insert-signature): Make sure signature file is + readable before trying to insert it. + +2003-08-11 Bill Wohler <wohler@newt.com> + + * mh-comp.el (mh-insert-signature): Unconditionally insert a + newline so that signatures are inserted consistently, and so that + there isn't any text after the cursor so that the user can start + typing his message immediately. Use new variable and function + mh-signature-separator and mh-signature-separator-p. + + * mh-customize.el (mh-delete-yanked-msg-window-flag): Checkdoc + fix. + (mh-signature-file-name): Updated docstring now that this variable + can be a function. Added cross-references to + mh-signature-separator, mh-signature-separator-regexp, and + mh-signature-separator-p which might be used in such functions. + + * mh-identity.el (mh-insert-identity): Don't include signature if + signature separator already present. Useful when running + mh-edit-again. + + * mh-mime.el (mh-inline-vcard-p): Use mh-signature-separator-p. + + * mh-utils.el (mh-signature-separator-regexp): New variable + containing "^-- $" which should be used when looking for the + signature separator. + (mh-signature-separator): New variable containing "-- \n" which + should be used when inserting the signature separator. + (mh-signature-separator-p): New function that returns non-nil if + mh-signature-separator-regexp is found in the buffer. + +2003-08-09 Satyaki Das <satyakid@stanford.edu> + + * mh-utils.el (mh-x-image-scaling-function): Variable that + contains function used to scale images. Possible choices are + mh-x-image-scale-with-convert and mh-x-image-scale-with-pnm. + (mh-convert-executable): Removed. + (mh-x-image-scale-with-pnm, mh-x-image-scale-with-convert): New + functions that scale images using pnm tools or ImageMagick. + (mh-x-image-scale-and-display, mh-x-image-url-display): Use + mh-x-image-scaling-function instead of mh-convert-executable. + +2003-08-08 Peter S Galbraith <psg@debian.org> + + * mh-comp.el (mh-insert-signature): Bug fix. Handle case of nil + `mh-signature-file-name' and hooks correctly. + + * mh-identity.el (mh-insert-identity): Refactor to use + mh-insert-signature + + * mh-comp.el (mh-signature-separator-p): Removed. + + * mh-comp.el (mh-insert-signature): Merge MIME awareness from + mh-insert-identity into this command. Allow + `mh-signature-file-name' to be a function to call. See if "-- " + needs to be inserted only after hooks have run. + +2003-08-07 Bill Wohler <wohler@newt.com> + + * mh-customize.el (mh-compose-skipped-header-fields): Added + X-Image-URL. + (mh-autoload): Removed cookies. They aren't necessary in + mh-e.el, mh-utils.el, or mh-customize.el. + + * mh-e.el (mh-autoload): Removed cookies. They aren't necessary in + mh-e.el, mh-utils.el, or mh-customize.el. + + * mh-identity.el (mh-insert-identity): Made regexp for signature + separator more explicit. Hmmm, maybe we should create + mh-signature-separator-regexp... + + * mh-index.el (mh-replace-string): Moved to mh-utils.el. + + * mh-utils.el (mh-replace-string): Moved here from mh-index.el. + (mh-autoload): Removed cookies. They aren't necessary in mh-e.el, + mh-utils.el, or mh-customize.el. + + * mh-comp.el (mh-insert-signature): Added file argument to insert + a file other than mh-signature-file-name. Insert signature + separator, unless file already contains one. + +2003-08-06 Satyaki Das <satyakid@stanford.edu> + + * mh-e.el (mh-folder-size, mh-folder-size-folder) + (mh-folder-size-flist): If flist is not present use folder to + find the number of messages in the folder. Also the .mh_sequences + file is read to find the number of unseen messages. + + * mh-utils.el (mh-flists-present-flag, mh-find-progs): Introduce + a new variable to test for the presence of the flists program and + set it in mh-find-progs. + +2003-08-06 Peter S Galbraith <psg@debian.org> + + * mh-customize.el: Change the order of `mh-identity-list' and + `mh-auto-fields-list' and remove byte-compilation defvar for + `mh-identity-list'. This fixes a customization bug for + `mh-identity-list', where it wasn't set correctly. + + * mh-identity.el (mh-identity-make-menu): mh-auto-fields-list may + not be bound yet when initially loaded. + +2003-08-06 Bill Wohler <wohler@newt.com> + + * mh-alias.el (mh-alias-add-address-under-point): Removed trailing + period from messages. The conventions say that errors should not + end with a period and that "Foo...done" messages should not end in + a period, but they aren't explicit about messages in general. + Given what the conventions *do* say, and because most of our + messages don't end with a period, let's just say that messages in + general don't end in a period, just like error messages. + + * mh-comp.el (mh-extract-rejected-mail, mh-letter-mode-message): + Ditto. + + * mh-e.el (mh-refile-a-msg): Ditto. + + * mh-funcs.el (mh-undo-folder): Ditto. + + * mh-mime.el (mh-mime-save-parts): Ditto. + + * mh-seq.el (mh-subject-to-sequence-unthreaded) + (mh-narrow-to-subject, mh-delete-subject): Ditto. + + * mh-index.el (mh-index-sequenced-messages) + (mh-index-new-messages, mh-index-ticked-messages): Discovered that + in general we should only use question marks in yes-or-no-p or + y-or-n-p prompts, but not in other prompts that use + completing-read and offer defaults. In these cases, use colons + instead (closes SF #730470). + + * mh-mime.el (mh-mime-save-parts): Ditto. + + * mh-utils.el (mh-prompt-for-folder): Ditto. + + * mh-alias.el (mh-alias-apropos): Multiple messages are usually + shown one at a time rather than appended. Send output to + mh-aliases-buffer instead of *Help*. + (mh-alias-local-users): Checkdoc fix. + + * mh-funcs.el (mh-undo-folder): Removed commented-out code since + its deadline had expired. + + * mh-utils.el (mh-aliases-buffer): New buffer name, used in + mh-aliases.el. + +2003-08-06 Satyaki Das <satyakid@stanford.edu> + + * mh-utils.el (mh-x-image-url-cache-canonicalize): Make this + function work for XEmacs too. + (mh-collect-folder-names): Use folders instead of flists. One + advantage is that folders is available on MH while flists is not. + Another is that if an explicit -sequence argument isn't given and + Unseen-Sequence profile is not present then flists croaks while + folders doesn't. + (mh-collect-folder-names-filter): Don't consider folder names that + start with a `.' character. This is needed since the folders + command doesn't filter them out like flists does. + + * mh-index.el (mh-replace-string): Add autoload for it. + +2003-08-05 Satyaki Das <satyakid@stanford.edu> + + * mh-mime.el (mh-compose-forward, mh-mhn-compose-forw) + (mh-mml-forward-message): The variable mh-sent-from-msg can be a + list. So check that the value is really a number before using it + like one. + + * mh-comp.el (mh-insert-letter): Same as above. + + * mh-utils.el (mh-picon-get-image): Make the code that finds the + address of the sender more robust. + (mh-face-display-function): Make it work with XEmacs. + (mh-picon-image-types): A new variable that stores what image + types can be used. + +2003-08-05 Satyaki Das <satyakid@stanford.edu> + + * mh-customize.el (mh-tool-bar-define): Make the save button such + that is activated only if the buffer needs to saved. + + * mh-utils.el (mh-face-display-function, mh-picon-get-image): Some + domains, for instance cs.cmu.edu, don't have xpm files. So we need + to search for all three files. The change does that. + (mh-picon-file-contents): A utility function to return the + contents of a file as a string. + (mh-picon-get-image): Write it as a loop to make it simpler. + (mh-x-image-set-download-state): Make the link simpler. + +2003-08-04 Satyaki Das <satyakid@stanford.edu> + + * mh-utils.el (mh-x-image-url-display): Don't bother to try to + download image if we don't have the necessary tools to display + it. + (mh-face-display-function): Add preliminary support for "domain" + picons. + (mh-picon-get-image, mh-picon-generate-path): Functions to find + best match for domain in the From header field. + + * mh-e.el (mh-previous-unread-msg): If some of the messages in the + unseen sequence are not present in the folder buffer then calling + this function gets stuck and can't skip over them. The change + fixes this. + (mh-next-unread-msg): Same as above. + +2003-08-04 Bill Wohler <wohler@newt.com> + + * mh-utils.el (mh-show-mode): Added cross reference to + mh-folder-mode in docstring (closes SF #728638). Added + \\{mh-show-mode-map} to show keymap. + + * mh-e.el (mh-folder-mode): Added information about ranges to + docstring (closes SF #728638). + + * mh-speed.el (mh-speed-refresh): New function that calls + mh-speed-flists and mh-invalidate-map. + (mh-folder-speedbar-key-map): Replaced keybindings for + mh-speed-invalidate-map and mh-speed-flists with a single binding + for mh-speed-refresh. + (mh-folder-speedbar-menu-items): Replaced menu items for Run + Flists and Invalidate Cached Folders with the single menu item + Refresh Speedbar in order to simplify the UI. + + * mh-customize.el (mh-fetch-x-image-url): Added DOS as another + reason not to set this to t. + +2003-08-04 Satyaki Das <satyakid@stanford.edu> + + * mh-e.el (mh-scan-folder): Handle ranges from user input properly. + +2003-08-03 Satyaki Das <satyakid@stanford.edu> + + * mh-utils.el (mh-find-msg-get-num): Removed. If threading isn't + present, the messages are sorted by index. So `mh-goto-msg' was + implemented as a binary search and this function was used in that + implementation. So this isn't needed any more. + (mh-msg-search-pat): Removed. Before the advent of message + threading, this function was used to generate a regexp used to + search for a particular message. It isn't used anymore. The + variable `mh-scan-msg-number-regexp' should be updated and used in + `mh-goto-msg' instead of hardcoding the regexp in the code. Then + we might be able to better support other scan line formats in the + future. + + * mh-seq.el (mh-map-to-seq-msgs, mh-notate-seq): Removed. These + functions were used to notate user sequences. But calling + `mh-goto-msg' inside of a loop is inefficient. So the sequence + notation code was rewritten thereby making these functions + redundant. + (mh-copy-line-to-point): Removed. This function was used in the + implementation the now removed function `mh-copy-seq-to-point'. + That function was problematic and was replaced by the less general + `mh-copy-seq-to-eob'. This makes `mh-copy-line-to-point' + redundant. + (mh-region-to-msg-list): Removed since this is a special case of + the more general `mh-range-to-msg-list'. + + * mh-loaddefs.el: Regenerated. + +2003-08-03 Jeffrey C Honig <jch@honig.net> + + * mh-customize.el (mh-invisible-header-fields-default): Added + several new fields to hide. Sorted the list with sort-lines. + +2003-08-03 Peter S Galbraith <psg@debian.org> + + * mh-customize.el (mh-invisible-headers): Variable renamed to + `mh-invisible-header-fields-compiled'. + (mh-invisible-headers): Implement above change. + + * mh-utils.el (mh-display-msg): Idem. + + * mh-mime.el (mh-mm-inline-message): Idem. + + * mh-comp.el (mh-insert-letter): Idem. + +2003-08-03 Bill Wohler <wohler@newt.com> + + * mh-speed.el (mh-folder-speedbar-menu-items): Added separator + between standard and MH-E menu items. Use headline capitalization + in menu items. + + * mh-utils.el (mh-temp-fetch-buffer): New constant to hold + buffer name for wget output. + (mh-x-image-url-fetch-image): Use mh-temp-fetch-buffer instead + of hard-coded buffer name. Use make-temp-file to avoid race + conditions and subsequent security issues raised in make-temp-name + docstring. + +2003-08-03 Satyaki Das <satyakid@stanford.edu> + + * mh-utils.el (mh-wget-executable, mh-wget-choice, mh-wget-option) + (mh-x-image-url-fetch-image): Support the use of `curl' and + `fetch' as alternatives to `wget'. + (mh-wget-choice): Change order of search. + (mh-x-image-url-fetch-image): Rename buffer. + +2003-08-03 Satyaki Das <satyakid@stanford.edu> + + * mh-utils.el (mh-x-image-set-download-state) + (mh-x-image-get-download-state): Specially named symbolic links + are used to keep track of whether a X-Image-URL header field needs + to downloaded the next time it is seen. These functions get and + set the symlinks appropriately. + (mh-x-image-url-fetch-image): Simplified since the query has been + moved to `mh-x-image-url-display'. Also if wget isn't present then + try again next time since the user might install wget before + trying once more. + (mh-x-image-scale-and-display): Handle absence of the `convert' + program better. If it isn't present then we will try to display + the image the next time it is encountered. Also use the -geometry + option to convert since the -resize option isn't present in older + versions. + (mh-x-image-url-display): Move all the code that decides whether + an X-Image-URL header field will be fetched in this function. Also + remember the user's decision so that if the image couldn't be + fetched the first time, we will try to fetch it later on without + asking again. + +2003-08-02 Peter S Galbraith <psg@debian.org> + + * mh-alias.el (mh-alias-local-users): Exclude all aliases already + in mh-alias-alist from `ali' (closes SF #772595). + +2003-08-01 Satyaki Das <satyakid@stanford.edu> + + * mh-utils.el (mh-x-image-display, mh-x-image-url-display): Avoid + a race. The X-Image-URL is displayed asynchronously. Suppose a + message with a image is shown with `mh-show'. If a different + message is displayed before the image can be fetched, then the new + message will have the image displayed. With this change the race + is less likely to happen. + +2003-08-01 Peter S Galbraith <psg@debian.org> + + * mh-inc.el (mh-inc-spool-map): Fix what `mh-inc-spool-map-help' + must look like as a fake `mh-help-messages' in order to work + correctly in mh-help. + +2003-07-31 Bill Wohler <wohler@newt.com> + + * mh-inc.el (mh-inc-spool-map): Use mh-help instead of + mh-ephem-message in order to display help in its own buffer + instead of minibuffer. + + * mh-utils.el (mh-help-buffer): New variable to hold the name of + the MH-E help buffer name. + + * mh-funcs.el (mh-help, mh-prefix-help): Use with-electric-help to + display help messages. I observed a friend with a vision + disability and the 5 seconds the help appeared on the screen was + not long enough for him to lock on it. I've therefore changed the + help function to display the help in its own buffer called *MH-E + Help* (closes SF #493740 and SF #656631). + + * mh-customize.el (mh-fetch-x-image-url): Changed default from nil + to 'ask. Updated docstring from manual. + (mh-invisible-header-fields-internal): Added X-Image-URL. + (mh-show-use-xface-flag): Updated docstring from manual. + (mh-x-face-file): Ditto. + + * mh-mime.el (mh-mhn-compose-external-type): Don't insert the + directory parameter if it's nil. The mhbuild man page indicates + that this parameter is optional, so this should be fine. + + * mh-comp.el (mh-letter-mode-map): Added keybindings for + mh-mhn-compose-anon-ftp and + mh-mhn-compose-external-compressed-tar. + (mh-letter-menu): Uncommented menu items for same. + +2003-07-30 Satyaki Das <satyakid@stanford.edu> + + * mh-loaddefs.el: Regenerated. + + * mh-pick.el (mh-do-pick-search): Removed since the function + `mh-pick-do-search' performs the same action as this function. + + * mh-index.el (mh-index-update-unseen): Removed since the + generalized sequence synchronization code that keeps sequences in + index folders in sync with the sequences in the source folders + makes this function redundant. + + * mh-e.el (mh-folder-unseen-seq-name, mh-folder-unseen-seq-list): + Removed. These two functions were used in the unseen sequence + highlighting before the sequence highlighting code was + generalized. In any event calls to the function + `mh-folder-unseen-seq-name' can be replaced by the variable + `mh-unseen-seq' and calls to `mh-folder-unseen-seq-list' can be + replaced with (cdr (assoc mh-unseen-seq mh-seq-list)). + (mh-unmark-all-headers): Removed since this function has been + superseded by mh-remove-all-notation. + (mh-map-over-seqs): Removed since we now have the generalized + iteration over message ranges (the `mh-iterate-on-range' macro) + that can be used instead. + (mh-notate-if-in-one-seq): Removed. This function was used for + changing the `%' notation for user sequences. It can't be used for + that purpose any more, since we have a different scheme now. + + * mh-unit.el (mh-unit-tests): Removed since it isn't needed any + more. + (mh-unit): Run all function that start with the string + "mh-unit-test-". + +2003-07-30 Bill Wohler <wohler@newt.com> + + * mh-customize.el (mh-invisible-header-fields): Checkdoc fix. + + * mh-utils.el (mh-x-image-url-cache-canonicalize): Shortened using + example in files.el:make-backup-file-name-1. + (mh-face-display-function): Added X-Image-URL to docstring. + + * mh-unit.el (mh-unit-x-image-url-cache-canonicalize): New + function to test mh-x-image-url-cache-canonicalize since it lent + itself well to unit testing. Had to start somewhere! + (mh-unit-equal): New function that throws an error if RESULT + doesn't equal EXPECTED. + (mh-unit): Call mh-unit-x-image-url-cache-canonicalize. + +2003-07-29 Satyaki Das <satyakid@stanford.edu> + + * mh-unit.el (mh-unit-update-call-graph): Make the function work + better with dotted lists, that is lists of the form (a b c . d) + where `d' isn't nil. With this we are able to avoid marking some + functions as unused even though they are actually used in alists. + +2003-07-28 Peter S Galbraith <psg@debian.org> + + * mh-comp.el (mh-insert-letter): Remove `mh-visible-headers' + operation. + + * mh-mime.el (mh-mm-inline-message): Same. + + * mh-utils.el (mh-display-msg): Same. + (mh-clean-msg-header): Make a note of above change. + + * mh-customize.el (mh-invisible-header-fields-internal): Renamed + from prior `mh-invisible-header-fields-default'. + (mh-invisible-header-fields-default): Renamed from prior + `mh-invisible-header-fields-default-override'. + (mh-invisible-header-fields): Renamed from prior + `mh-invisible-header-fields-user'. + (mh-visible-headers): Removed! We use invisible fields only now. + (mh-visible-header-fields): Removed! + +2003-07-28 Peter S Galbraith <psg@debian.org> + + * mh-customize.el (mh-invisible-header-fields-default): Added 3 + new fields to hide. + +2003-07-28 Satyaki Das <satyakid@stanford.edu> + + * mh-utils.el (mh-show): Add an extra argument to the function so + that interactive use will always force redisplay of the message. + + * mh-mime.el (mh-mime-display, mh-mm-inline-message): Bind the + variables `mm-verify-option' and `mm-decrypt-option' so that + verification and decryption of mail can happen without any + additional tinkering. + +2003-07-25 Peter S Galbraith <psg@debian.org> + + * mh-customize.el (mh-invisible-header-fields-default): New defvar + holding default fields to hide. This replaces the old + `mh-invisible-header-fields' defcustom. + (mh-invisible-header-fields-user): New defcustom. Users add + fields to suppress that we didn't include in + `mh-invisible-header-fields-default'. This could be named simply + `mh-invisible-header-fields' and it wouldn't really break anything + for users who have customized it to a long list now redundant with + `mh-invisible-header-fields-default'. + (mh-invisible-header-fields-default-override): New defcustom. + Users check off the fields they want displyed from what we + included in `mh-invisible-header-fields-default'. + (mh-invisible-headers): Function adapted to new variables. + +2003-07-25 Satyaki Das <satyakid@stanford.edu> + + * mh-e.el (mh-inc-folder): If the user is in a different folder + displaying a message and runs mh-inc-folder, then the folder + changes to +inbox but the show window continues to display the + message in the old folder. The change fixes this. + (mh-visit-folder): Make the handling of the show window similar to + that of mh-inc-folder. + +2003-07-24 Satyaki Das <satyakid@stanford.edu> + + * mh-e.el (mh-folder-message-menu, mh-folder-folder-menu): Use the + predicate mh-outstanding-commands-p instead of its exapansion. + Also use the same label in both menus. + (mh-outstanding-commands-p): Generalized so that it will work in + mh-show-mode buffers as well. + + * mh-customize.el (mh-tool-bar-define): Enable tool-bar button for + mh-execute-commands only if there are pending deletes or refiles. + +2003-07-19 Satyaki Das <satyakid@stanford.edu> + + * mh-utils.el (mh-show-msg): If a unseen message is first marked + for deletion/refiling and then displayed, the bold highlighting + does not go away. This change fixes that. + + * mh-seq.el (mh-msg-is-in-seq): Fix a bug in the function. If any + message was marked for refiling, then the function would have you + believe that every message in the folder is being refiled. + +2003-07-17 Bill Wohler <wohler@newt.com> + + * mh-e.el: Removed email address for Stephen Gildea's in Change + Log at his request (damn spammers). Removed other email addresses + while I was at it since the SourceForge URL should be sufficient + contact information. + + (mh-scan-format-*mh): Fixed typo in comment above these variables. + These variables are used if mh-scan-format-file is t, not nil. + Also mh-scan-format-file is no longer "above" (courtesy Stephen + Gildea). + +2003-07-17 Satyaki Das <satyakid@stanford.edu> + + * mh-mime.el (mh-mhn-quote-unescaped-sharp): New function that + quotes `#' characters in the first column that aren't part of a + MHN directive. + (mh-mhn-directive-present-p): Generalized to allow the function + to search for MHN directives in a part of the buffer. + (mh-edit-mhn): Quote unescaped `#' characters in the draft (closes + SF #762464). + +2003-07-16 Satyaki Das <satyakid@stanford.edu> + + * mh-alias.el (mh-alias-read-address-map): If + mh-alias-flash-on-comma is nil when mh-alias is loaded, then + setting mh-alias-flash-on-comma to t later on doesn't turn on + address completion display till Emacs is restarted. The change + fixes this. + +2003-07-15 Bill Wohler <wohler@newt.com> + + * mh-utils.el (mh-cmd-note): Cleaned up docstring (changed phrase + to sentences). Moved to Scan Line Formats section. + (mh-scan-msg-number-regexp) + (mh-scan-msg-overflow-regexp, mh-scan-msg-format-regexp) + (mh-scan-msg-format-string, mh-scan-msg-search-regexp): Cleaned up + docstrings (changed phrases to sentences). + (mh-note-seq): Cleaned up docstring (changed phrase to sentences). + Also, this variable is now a character and not a string. Moved to + Scan Line Formats section. + + * mh-funcs.el (mh-note-copied, mh-note-printed): Cleaned up + docstrings (changed phrases to sentences). Also, these variables + are now characters and not strings. + + * mh-e.el (mh-scan-format-mh, mh-scan-format-nmh): Filled. I was + hoping to quote the hint `t' but checkdoc wouldn't let me. + (mh-note-deleted, mh-note-refiled, mh-note-cur): Moved to Scan + Line Formats section. + (mh-scan-good-msg-regexp, mh-scan-deleted-msg-regexp) + (mh-scan-refiled-msg-regexp, mh-scan-valid-regexp) + (mh-scan-cur-msg-number-regexp, mh-scan-date-regexp) + (mh-scan-rcpt-regexp, mh-scan-body-regexp) + (mh-scan-subject-regexp, mh-scan-format-regexp): Cleaned + up docstrings (changed phrases to sentences). + (mh-scan-cur-msg-regexp): Marked this variable as obsolete; it + should be removed for 8.0. + + * mh-comp.el (mh-note-repl, mh-note-forw, mh-note-dist): Cleaned + up docstrings (changed phrases to sentences). Also, these + variables are now characters and not strings. + +2003-07-15 Satyaki Das <satyakid@stanford.edu> + + * mh-index.el (mh-index-update-single-msg) + (mh-index-create-sequences): Handle the situation where there are + copies of the exact same message correctly. + +2003-07-15 Satyaki Das <satyakid@stanford.edu> + + * mh-seq.el (mh-thread-update-scan-line-map): Modified since + notation is already a character. + + * mh-utils.el (mh-note-seq): Convert from string to character. + (mh-notate): Modified since characters are used to notate instead + of strings of length one. + + * mh-comp.el (mh-note-repl, mh-note-forw, mh-note-dist): Convert + from string to characters. + + * mh-e.el (mh-note-deleted, mh-note-refiled, mh-note-cur): Same as + above (closes SF #770772). + (mh-unmark-all-headers): Modified since mh-note-* variables are + now characters. + (mh-remove-sequence-notation): The mh-notate function remembers + the previous notation. Before the change to the mh-note-* + variables, mh-notate would only remember the change if a string + was used to notate the message. Now mh-notate is always called + with a character notation. So the deletion has to take place + explicitly. + +2003-06-28 Bill Wohler <wohler@newt.com> + + * mh-mime.el (mh-mhn-directive-present-p): If shell comments are + present that have a space after the # but no content, then this + function would throw an error. This has been fixed (closes SF + #762458). + +2003-06-27 Satyaki Das <satyakid@stanford.edu> + + * mh-index.el (mh-index-search): Use the new and improved + mh-index-new-folder. + (mh-index-new-folder): Improved so that redoing the same search + will reuse the old index folder. + (mh-index-folder-search-regexp): New function which that extracts + out the search expression that produced the index folder. + +2003-06-24 Bill Wohler <wohler@newt.com> + + * mh-e.el (Version, mh-version): Set to 7.4.1+cvs. + 2003-06-25 Bill Wohler <wohler@newt.com> * Released MH-E version 7.4.1. diff --git a/lisp/mh-e/mh-alias.el b/lisp/mh-e/mh-alias.el index bd20b9118b0..1356e2c8b95 100644 --- a/lisp/mh-e/mh-alias.el +++ b/lisp/mh-e/mh-alias.el @@ -27,75 +27,12 @@ ;;; Commentary: -;; [To be deleted when documented in MH-E manual.] -;; -;; This module provides mail alias completion when entering addresses. -;; -;; Use the TAB key to complete aliases (and optionally local usernames) when -;; initially composing a message in the To: and Cc: minibuffer prompts. You -;; may enter multiple addressees separated with a comma (but do *not* add any -;; space after the comma). -;; -;; In the header of a message draft, use "M-TAB (mh-letter-complete)" to -;; complete aliases. This is useful when you want to add an addressee as an -;; afterthought when creating a message, or when adding an additional -;; addressee to a reply. -;; -;; By default, completion is case-insensitive. This can be changed by -;; customizing the variable `mh-alias-completion-ignore-case-flag'. This is -;; useful, for example, to differentiate between people aliases in lowercase -;; such as: -;; -;; p.galbraith: Peter Galbraith <GalbraithP@dfo-mpo.gc.ca> -;; -;; and lists in uppercase such as: -;; -;; MH-E: MH-E mailing list <mh-e-devel@lists.sourceforge.net> -;; -;; Note that this variable affects minibuffer completion only. If you have an -;; alias for P.Galbraith and type in p.galbraith at the prompt, it will still -;; be expanded in the letter buffer because MH is case-insensitive. -;; -;; When you press ", (mh-alias-minibuffer-confirm-address)" after an alias in -;; the minibuffer, the expansion for the previous mail alias appears briefly. -;; To inhibit this, customize the variable `mh-alias-flash-on-comma'. -;; -;; The addresses and aliases entered in the minibuffer are added to the -;; message draft. To expand the aliases before they are added to the draft, -;; customize the variable `mh-alias-expand-aliases-flag'. -;; -;; Completion is also performed on usernames extracted from the /etc/passwd -;; file. This can be a handy tool on a machine where you and co-workers -;; exchange messages, but should probably be disabled on a system with -;; thousands of users you don't know. This is done by customizing the -;; variable `mh-alias-local-users'. This variable also takes a string which -;; is executed to generate the password file. For example, you'd use "ypcat -;; passwd" for NIS. -;; -;; Aliases are loaded the first time you send mail and get the "To:" prompt -;; and whenever a source of aliases changes. Sources of system aliases are -;; defined in the customization variable `mh-alias-system-aliases' and -;; include: -;; -;; /etc/nmh/MailAliases -;; /usr/lib/mh/MailAliases -;; /etc/passwd -;; -;; Sources of personal aliases are read from the files listed in your MH -;; profile component Aliasfile. Multiple files are separated by white space -;; and are relative to your mail directory. -;; -;; Alias Insertions -;; ~~~~~~~~~~~~~~~~ -;; There are commands to insert new aliases into your alias file(s) (defined -;; by the `Aliasfile' component in the .mh_profile file or by the variable -;; `mh-alias-insert-file'). In particular, there is a tool-bar icon to grab -;; an alias from the From line of the current message. - ;;; Change Log: ;;; Code: +(eval-when-compile (require 'mh-acros)) +(mh-require-cl) (require 'mh-e) (load "cmr" t t) ; Non-fatal dependency for ; completing-read-multiple. @@ -116,15 +53,23 @@ (defvar mh-alias-tstamp nil "Time aliases were last loaded.") (defvar mh-alias-read-address-map nil) -(if mh-alias-read-address-map - () +(unless mh-alias-read-address-map (setq mh-alias-read-address-map (copy-keymap minibuffer-local-completion-map)) - (if mh-alias-flash-on-comma - (define-key mh-alias-read-address-map - "," 'mh-alias-minibuffer-confirm-address)) + (define-key mh-alias-read-address-map + "," 'mh-alias-minibuffer-confirm-address) (define-key mh-alias-read-address-map " " 'self-insert-command)) +(defvar mh-alias-system-aliases + '("/etc/nmh/MailAliases" "/etc/mh/MailAliases" + "/usr/lib/mh/MailAliases" "/usr/share/mailutils/mh/MailAliases" + "/etc/passwd") + "*A list of system files which are a source of aliases. +If these files are modified, they are automatically reread. This list need +include only system aliases and the passwd file, since personal alias files +listed in your `Aliasfile:' MH profile component are automatically included. +You can update the alias list manually using \\[mh-alias-reload].") + ;;; Alias Loading @@ -138,7 +83,7 @@ This is a wrapper around `assoc-string' or `assoc-ignore-case'. Avoid (defun mh-alias-tstamp (arg) "Check whether alias files have been modified. -Return t if any file listed in the MH profile component Aliasfile has been +Return t if any file listed in the Aliasfile MH profile component has been modified since the timestamp. If ARG is non-nil, set timestamp with the current time." (if arg @@ -157,7 +102,7 @@ If ARG is non-nil, set timestamp with the current time." (defun mh-alias-filenames (arg) "Return list of filenames that contain aliases. -The filenames come from the MH profile component Aliasfile and are expanded. +The filenames come from the Aliasfile profile component and are expanded. If ARG is non-nil, filenames listed in `mh-alias-system-aliases' are appended." (or mh-progs (mh-find-path)) (save-excursion @@ -201,7 +146,8 @@ non-nil." res)) (defun mh-alias-local-users () - "Return an alist of local users from /etc/passwd." + "Return an alist of local users from /etc/passwd. +Exclude all aliases already in `mh-alias-alist' from `ali'" (let (passwd-alist) (save-excursion (set-buffer (get-buffer-create mh-temp-buffer)) @@ -222,23 +168,33 @@ non-nil." (gecos-name (match-string 3)) (realname (mh-alias-gecos-name gecos-name username - mh-alias-passwd-gecos-comma-separator-flag))) - (setq passwd-alist - (cons - (list (if mh-alias-local-users-prefix - (concat mh-alias-local-users-prefix - (mh-alias-suggest-alias realname t)) - username) - (if (string-equal username realname) - (concat "<" username ">") - (concat realname " <" username ">"))) - passwd-alist)))))) + mh-alias-passwd-gecos-comma-separator-flag)) + (alias-name (if mh-alias-local-users-prefix + (concat mh-alias-local-users-prefix + (mh-alias-suggest-alias realname t)) + username)) + (alias-translation + (if (string-equal username realname) + (concat "<" username ">") + (concat realname " <" username ">")))) + (when (not (mh-assoc-ignore-case alias-name mh-alias-alist)) + (setq passwd-alist (cons (list alias-name alias-translation) + passwd-alist))))))) (forward-line 1))) passwd-alist)) ;;;###mh-autoload (defun mh-alias-reload () - "Load MH aliases into `mh-alias-alist'." + "Reload MH aliases. + +Since aliases are updated frequently, MH-E will reload aliases automatically +whenever an alias lookup occurs if an alias source (a file listed in your +`Aliasfile:' profile component and your password file if variable +`mh-alias-local-users' is non-nil) has changed. However, you can reload your +aliases manually by calling this command directly. + +The value of `mh-alias-reloaded-hook' is a list of functions to be called, +with no arguments, after the aliases have been loaded." (interactive) (save-excursion (message "Loading MH aliases...") @@ -269,13 +225,14 @@ non-nil." (if (not (mh-assoc-ignore-case (car user) mh-alias-alist)) (setq mh-alias-alist (append mh-alias-alist (list user)))) (setq local-users (cdr local-users))))) + (run-hooks 'mh-alias-reloaded-hook) (message "Loading MH aliases...done")) ;;;###mh-autoload (defun mh-alias-reload-maybe () "Load new MH aliases." - (if (or (eq mh-alias-alist 'not-read) ; Doesn't exist, so create it. - (mh-alias-tstamp nil)) ; Out of date, so recreate it. + (if (or (eq mh-alias-alist 'not-read) ; Doesn't exist? + (mh-alias-tstamp nil)) ; Out of date? (mh-alias-reload))) @@ -461,21 +418,21 @@ is converted to lower case." found))) (defun mh-alias-insert-file (&optional alias) - "Return the alias file to write a new entry for ALIAS in. -Use variable `mh-alias-insert-file' if non-nil, else use AliasFile component -value. -If ALIAS is specified and it already exists, try to return the file that -contains it." + "Return filename which should be used to add ALIAS. +The value of the option `mh-alias-insert-file' is used if non-nil\; otherwise +the value of the `Aliasfile:' profile component is used. +If the alias already exists, try to return the name of the file that contains +it." (cond ((and mh-alias-insert-file (listp mh-alias-insert-file)) (if (not (elt mh-alias-insert-file 1)) ; Only one entry, use it (car mh-alias-insert-file) (if (or (not alias) (string-equal alias (mh-alias-ali alias))) ;alias doesn't exist - (completing-read "Alias file [press Tab]: " + (completing-read "Alias file: " (mapcar 'list mh-alias-insert-file) nil t) (or (mh-alias-which-file-has-alias alias mh-alias-insert-file) - (completing-read "Alias file [press Tab]: " + (completing-read "Alias file: " (mapcar 'list mh-alias-insert-file) nil t))))) ((and mh-alias-insert-file (stringp mh-alias-insert-file)) mh-alias-insert-file) @@ -490,16 +447,15 @@ contains it." (cond ((not autolist) (error "No writable alias file. -Set `mh-alias-insert-file' or set AliasFile in your .mh_profile file")) +Set `mh-alias-insert-file' or the Aliasfile profile component")) ((not (elt autolist 1)) ; Only one entry, use it (car autolist)) ((or (not alias) (string-equal alias (mh-alias-ali alias))) ;alias doesn't exist - (completing-read "Alias file [press Tab]: " - (mapcar 'list autolist) nil t)) + (completing-read "Alias file: " (mapcar 'list autolist) nil t)) (t (or (mh-alias-which-file-has-alias alias autolist) - (completing-read "Alias file [press Tab]: " + (completing-read "Alias file: " (mapcar 'list autolist) nil t)))))))) ;;;###mh-autoload @@ -520,10 +476,8 @@ Set `mh-alias-insert-file' or set AliasFile in your .mh_profile file")) (split-string aliases ", +"))))))) ;;;###mh-autoload -(defun mh-alias-from-has-no-alias-p () - "Return t is From has no current alias set. -In the exceptional situation where there isn't a From header in the message the -function returns nil." +(defun mh-alias-for-from-p () + "Return t if sender's address has a corresponding alias." (mh-alias-reload-maybe) (save-excursion (if (not (mh-folder-line-matches-show-buffer-p)) @@ -532,13 +486,16 @@ function returns nil." (set-buffer mh-show-buffer)) (let ((from-header (mh-extract-from-header-value))) (and from-header - (not (mh-alias-address-to-alias from-header))))))) + (mh-alias-address-to-alias from-header)))))) (defun mh-alias-add-alias-to-file (alias address &optional file) "Add ALIAS for ADDRESS in alias FILE without alias check or prompts. Prompt for alias file if not provided and there is more than one candidate. -If ALIAS matches exactly, prompt to [i]nsert before old value or [a]ppend -after it." + +If the alias exists already, you will have the choice of inserting the new +alias before or after the old alias. In the former case, this alias will be +used when sending mail to this alias. In the latter case, the alias serves as +an additional folder name hint when filing messages." (if (not file) (setq file (mh-alias-insert-file alias))) (save-excursion @@ -552,14 +509,15 @@ after it." ((re-search-forward (concat "^" (regexp-quote alias-search) " *\\(.*\\)") nil t) (let ((answer (read-string - (format "Exists for %s; [i]nsert, [a]ppend: " + (format (concat "Alias %s exists; insert new address " + "[b]efore or [a]fter: ") (match-string 1)))) (case-fold-search t)) - (cond ((string-match "^i" answer)) + (cond ((string-match "^b" answer)) ((string-match "^a" answer) (forward-line 1)) (t - (error "Quitting"))))) + (error "Unrecognized response"))))) ;; No, so sort-in at the right place ;; search for "^alias", then "^alia", etc. ((eq mh-alias-insertion-location 'sorted) @@ -587,8 +545,11 @@ after it." ;;;###mh-autoload (defun mh-alias-add-alias (alias address) "*Add ALIAS for ADDRESS in personal alias file. -Prompts for confirmation if the address already has an alias. -If the alias is already is use, `mh-alias-add-alias-to-file' will prompt." +This function prompts you for an alias and address. If the alias exists +already, you will have the choice of inserting the new alias before or after +the old alias. In the former case, this alias will be used when sending mail +to this alias. In the latter case, the alias serves as an additional folder +name hint when filing messages." (interactive "P\nP") (mh-alias-reload-maybe) (setq alias (completing-read "Alias: " mh-alias-alist nil nil alias)) @@ -614,9 +575,7 @@ If the alias is already is use, `mh-alias-add-alias-to-file' will prompt." ;;;###mh-autoload (defun mh-alias-grab-from-field () - "*Add ALIAS for ADDRESS in personal alias file. -Prompts for confirmation if the alias is already in use or if the address -already has an alias." + "*Add alias for the sender of the current message." (interactive) (mh-alias-reload-maybe) (save-excursion @@ -636,24 +595,26 @@ already has an alias." ;;;###mh-autoload (defun mh-alias-add-address-under-point () - "Insert an alias for email address under point." + "Insert an alias for address under point." (interactive) (let ((address (mh-goto-address-find-address-at-point))) (if address (mh-alias-add-alias nil address) - (message "No email address found under point.")))) + (message "No email address found under point")))) ;;;###mh-autoload (defun mh-alias-apropos (regexp) - "Show all aliases that match REGEXP either in name or content." + "Show all aliases or addresses that match REGEXP." (interactive "sAlias regexp: ") (if mh-alias-local-users (mh-alias-reload-maybe)) - (let ((matches "")(group-matches "")(passwd-matches)) + (let ((matches "") + (group-matches "") + (passwd-matches)) (save-excursion (message "Reading MH aliases...") (mh-exec-cmd-quiet t "ali" "-nolist" "-nouser") - (message "Reading MH aliases...done. Parsing...") + (message "Parsing MH aliases...") (while (re-search-forward regexp nil t) (beginning-of-line) (cond @@ -673,10 +634,9 @@ already has an alias." (concat matches (buffer-substring (point)(progn (end-of-line)(point))) "\n"))))) - (message "Reading MH aliases...done. Parsing...done.") + (message "Parsing MH aliases...done") (when mh-alias-local-users - (message - "Reading MH aliases...done. Parsing...done. Passwd aliases...") + (message "Making passwd aliases...") (setq passwd-matches (mapconcat '(lambda (elem) @@ -684,13 +644,12 @@ already has an alias." (string-match regexp (cadr elem))) (format "%s: %s\n" (car elem) (cadr elem)))) mh-alias-passwd-alist "")) - (message - "Reading MH aliases...done. Parsing...done. Passwd aliases...done."))) + (message "Making passwd aliases...done"))) (if (and (string-equal "" matches) (string-equal "" group-matches) (string-equal "" passwd-matches)) (message "No matches") - (with-output-to-temp-buffer "*Help*" + (with-output-to-temp-buffer mh-aliases-buffer (if (not (string-equal "" matches)) (princ matches)) (when (not (string-equal group-matches "")) diff --git a/lisp/mh-e/mh-comp.el b/lisp/mh-e/mh-comp.el index 489b6690bc7..cde52c65043 100644 --- a/lisp/mh-e/mh-comp.el +++ b/lisp/mh-e/mh-comp.el @@ -33,11 +33,12 @@ ;;; Code: +(eval-when-compile (require 'mh-acros)) +(mh-require-cl) (require 'mh-e) (require 'gnus-util) (require 'easymenu) -(require 'mh-utils) -(mh-require-cl) +(require 'mh-gnus) (eval-when (compile load eval) (ignore-errors (require 'mailabbrev))) @@ -48,6 +49,7 @@ (defvar sendmail-coding-system) (defvar mh-identity-list) (defvar mh-identity-default) +(defvar mh-mml-mode-default) (defvar mh-identity-menu) ;;; Autoloads @@ -58,7 +60,7 @@ (autoload 'sc-cite-original "sc" "Workhorse citing function which performs the initial citation. This is callable from the various mail and news readers' reply -function according to the agreed upon standard. See `\\[sc-describe]' +function according to the agreed upon standard. See `sc-describe' for more details. `sc-cite-original' does not do any yanking of the original message but it does require a few things: @@ -95,14 +97,16 @@ If MH will not allow you to redist a previously redist'd msg, set to nil.") This allows transaction log to be visible if -watch, -verbose or -snoop are used.") -(defvar mh-note-repl "-" - "String whose first character is used to notate replied to messages.") +;;; Scan Line Formats + +(defvar mh-note-repl ?- + "Messages that have been replied to are marked by this character.") -(defvar mh-note-forw "F" - "String whose first character is used to notate forwarded messages.") +(defvar mh-note-forw ?F + "Messages that have been forwarded are marked by this character.") -(defvar mh-note-dist "R" - "String whose first character is used to notate redistributed messages.") +(defvar mh-note-dist ?R + "Messages that have been redistributed are marked by this character.") (defvar mh-yank-hooks nil "Obsolete hook for modifying a citation just inserted in the mail buffer. @@ -113,23 +117,6 @@ text as modified. This is a normal hook, misnamed for historical reasons. It is semi-obsolete and is only used if `mail-citation-hook' is nil.") -(defvar mail-citation-hook nil - "*Hook for modifying a citation just inserted in the mail buffer. -Each hook function can find the citation between point and mark. -And each hook function should leave point and mark around the citation -text as modified. - -If this hook is entirely empty (nil), the text of the message is inserted -with `mh-ins-buf-prefix' prefixed to each line. - -See also the variable `mh-yank-from-start-of-msg', which controls how -much of the message passed to the hook. - -This hook was historically provided to set up supercite. You may now leave -this nil and set up supercite by setting the variable -`mh-yank-from-start-of-msg' to 'supercite or, for more automatic insertion, -to 'autosupercite.") - (defvar mh-comp-formfile "components" "Name of file to be used as a skeleton for composing messages. Default is \"components\". If not an absolute file name, the file @@ -145,7 +132,8 @@ system MH lib directory.") (defvar mh-repl-group-formfile "replgroupcomps" "Name of file to be used as a skeleton for replying to messages. This file is used to form replies to the sender and all recipients of a -message. Only used if `mh-nmh-flag' is non-nil. Default is \"replgroupcomps\". +message. Only used if `(mh-variant-p 'nmh)' is non-nil. +Default is \"replgroupcomps\". If not an absolute file name, the file is searched for first in the user's MH directory, then in the system MH lib directory.") @@ -153,6 +141,8 @@ directory, then in the system MH lib directory.") (format "^%s$" (regexp-opt '("Content-Type: message/rfc822" ;MIME MDN + "------ This is a copy of the message, including all the headers. ------";from exim + "--- Below this line is a copy of the message."; from qmail " ----- Unsent message follows -----" ;from sendmail V5 " --------Unsent Message below:" ; from sendmail at BU " ----- Original message follows -----" ;from sendmail V8 @@ -201,16 +191,16 @@ Used by the \\<mh-folder-mode-map>`\\[mh-edit-again]' and `\\[mh-extract-rejecte "Field name for message annotation.") (defvar mh-insert-auto-fields-done-local nil - "Buffer-local variable set when `mh-insert-auto-fields' successfully called.") + "Buffer-local variable set when `mh-insert-auto-fields' called successfully.") (make-variable-buffer-local 'mh-insert-auto-fields-done-local) ;;;###autoload (defun mh-smail () "Compose and send mail with the MH mail system. -This function is an entry point to MH-E, the Emacs front end -to the MH mail system. +This function is an entry point to MH-E, the Emacs interface to the MH mail +system. -See documentation of `\\[mh-send]' for more details on composing mail." +See `mh-send' for more details on composing mail." (interactive) (mh-find-path) (call-interactively 'mh-send)) @@ -220,11 +210,11 @@ See documentation of `\\[mh-send]' for more details on composing mail." ;;;###autoload (defun mh-smail-batch (&optional to subject other-headers &rest ignored) "Set up a mail composition draft with the MH mail system. -This function is an entry point to MH-E, the Emacs front end -to the MH mail system. This function does not prompt the user -for any header fields, and thus is suitable for use by programs -that want to create a mail buffer. -Users should use `\\[mh-smail]' to compose mail. +This function is an entry point to MH-E, the Emacs interface to the MH mail +system. This function does not prompt the user for any header fields, and thus +is suitable for use by programs that want to create a mail buffer. Users +should use `mh-smail' to compose mail. + Optional arguments for setting certain fields include TO, SUBJECT, and OTHER-HEADERS. Additional arguments are IGNORED." (mh-find-path) @@ -260,7 +250,8 @@ CONTINUE, SWITCH-FUNCTION, YANK-ACTION and SEND-ACTIONS are ignored." "Clean up a draft or a message MSG previously sent and make it resendable. Default is the current message. The variable `mh-new-draft-cleaned-headers' specifies the headers to remove. -See also documentation for `\\[mh-send]' function." + +See also `mh-send'." (interactive (list (mh-get-msg-num t))) (let* ((from-folder mh-current-folder) (config (current-window-configuration)) @@ -292,7 +283,8 @@ See also documentation for `\\[mh-send]' function." "Extract message MSG returned by the mail system and make it resendable. Default is the current message. The variable `mh-new-draft-cleaned-headers' gives the headers to clean out of the original message. -See also documentation for `\\[mh-send]' function." + +See also `mh-send'." (interactive (list (mh-get-msg-num t))) (let ((from-folder mh-current-folder) (config (current-window-configuration)) @@ -303,7 +295,7 @@ See also documentation for `\\[mh-send]' function." (delete-region (point-min) (point)) (mh-clean-msg-header (point-min) mh-new-draft-cleaned-headers nil)) (t - (message "Does not appear to be a rejected letter."))) + (message "Does not appear to be a rejected letter"))) (mh-insert-header-separator) (goto-char (point-min)) (save-buffer) @@ -323,7 +315,7 @@ Default is the displayed message. Check the documentation of `mh-interactive-range' to see how RANGE is read in interactive use. -See also documentation for `\\[mh-send]' function." +See also `mh-send'." (interactive (list (mh-interactive-read-address "To: ") (mh-interactive-read-address "Cc: ") (mh-interactive-range "Forward"))) @@ -335,7 +327,10 @@ See also documentation for `\\[mh-send]' function." (draft-name (expand-file-name "draft" mh-user-path)) (draft (cond ((or (not (file-exists-p draft-name)) (y-or-n-p "The file 'draft' exists. Discard it? ")) - (mh-exec-cmd "forw" "-build" (if mh-nmh-flag "-mime") + (mh-exec-cmd "forw" "-build" + (if (and (mh-variant-p 'nmh) + mh-compose-forward-as-mime-flag) + "-mime") mh-current-folder (mh-coalesce-msg-list msgs)) (prog1 @@ -388,7 +383,8 @@ See also documentation for `\\[mh-send]' function." mh-note-forw "Forwarded:" config) (mh-letter-mode-message) - (mh-letter-adjust-point))))) + (mh-letter-adjust-point) + (run-hooks 'mh-forward-hook))))) (defun mh-forwarded-letter-subject (from subject) "Return a Subject suitable for a forwarded message. @@ -406,10 +402,10 @@ Original message has headers FROM and SUBJECT." ;;;###autoload (defun mh-smail-other-window () "Compose and send mail in other window with the MH mail system. -This function is an entry point to MH-E, the Emacs front end -to the MH mail system. +This function is an entry point to MH-E, the Emacs interface to the MH mail +system. -See documentation of `\\[mh-send]' for more details on composing mail." +See `mh-send' for more details on composing mail." (interactive) (mh-find-path) (call-interactively 'mh-send-other-window)) @@ -496,13 +492,15 @@ to reply to: If optional prefix argument INCLUDEP provided, then include the message in the reply using filter `mhl.reply' in your MH directory. If the file named by `mh-repl-formfile' exists, it is used as a skeleton -for the reply. See also documentation for `\\[mh-send]' function." +for the reply. + +See also `mh-send'." (interactive (list (mh-get-msg-num t) (let ((minibuffer-help-form "from => Sender only\nto => Sender and primary recipients\ncc or all => Sender and all recipients")) (or mh-reply-default-reply-to - (completing-read "Reply to whom? (from, to, all) [from]: " + (completing-read "Reply to whom: [from] " '(("from") ("to") ("cc") ("all")) nil t))) @@ -511,7 +509,7 @@ for the reply. See also documentation for `\\[mh-send]' function." (show-buffer mh-show-buffer) (config (current-window-configuration)) (group-reply (or (equal reply-to "cc") (equal reply-to "all"))) - (form-file (cond ((and mh-nmh-flag group-reply + (form-file (cond ((and (mh-variant-p 'nmh 'mu-mh) group-reply (stringp mh-repl-group-formfile)) mh-repl-group-formfile) ((stringp mh-repl-formfile) mh-repl-formfile) @@ -525,7 +523,7 @@ for the reply. See also documentation for `\\[mh-send]' function." '("-nocc" "all")) ((equal reply-to "to") '("-cc" "to")) - (group-reply (if mh-nmh-flag + (group-reply (if (mh-variant-p 'nmh 'mu-mh) '("-group" "-nocc" "me") '("-cc" "all" "-nocc" "me")))) (cond ((or (eq mh-yank-from-start-of-msg 'autosupercite) @@ -562,7 +560,6 @@ for the reply. See also documentation for `\\[mh-send]' function." ;;;###mh-autoload (defun mh-send (to cc subject) "Compose and send a letter. - Do not call this function from outside MH-E; use \\[mh-smail] instead. The file named by `mh-comp-formfile' will be used as the form. @@ -581,7 +578,6 @@ passed three arguments: TO, CC, and SUBJECT." ;;;###mh-autoload (defun mh-send-other-window (to cc subject) "Compose and send a letter in another window. - Do not call this function from outside MH-E; use \\[mh-smail-other-window] instead. @@ -711,6 +707,8 @@ Do not insert any pairs whose value is the empty string." (while name-values (let ((field-name (car name-values)) (value (car (cdr name-values)))) + (if (not (string-match "^.*:$" field-name)) + (setq field-name (concat field-name ":"))) (cond ((equal value "") nil) ((mh-position-on-field field-name) @@ -730,6 +728,7 @@ The optional second arg is for pre-version 4 compatibility and is IGNORED." ((mh-goto-header-end 0) nil))) +;;;###mh-autoload (defun mh-get-header-field (field) "Find and return the body of FIELD in the mail header. Returns the empty string if the field is not in the header of the @@ -777,35 +776,53 @@ Returns t if found, nil if not." ;;; Menu extracted from mh-menubar.el V1.1 (31 July 2001) (eval-when-compile (defvar mh-letter-menu nil)) -(cond - ((fboundp 'easy-menu-define) - (easy-menu-define - mh-letter-menu mh-letter-mode-map "Menu for MH-E letter mode." - '("Letter" - ["Send This Draft" mh-send-letter t] - ["Split Current Line" mh-open-line t] - ["Check Recipient" mh-check-whom t] - ["Yank Current Message" mh-yank-cur-msg t] - ["Insert a Message..." mh-insert-letter t] - ["Insert Signature" mh-insert-signature t] - ["GPG Sign message" - mh-mml-secure-message-sign-pgpmime mh-gnus-pgp-support-flag] - ["GPG Encrypt message" - mh-mml-secure-message-encrypt-pgpmime mh-gnus-pgp-support-flag] - ["Compose Insertion (MIME)..." mh-compose-insertion t] - ;; ["Compose Compressed tar (MIME)..." - ;;mh-mhn-compose-external-compressed-tar t] - ;; ["Compose Anon FTP (MIME)..." mh-mhn-compose-anon-ftp t] - ["Compose Forward (MIME)..." mh-compose-forward t] - ;; The next two will have to be merged. But I also need to make sure the - ;; user can't mix directives of both types. - ["Pull in All Compositions (mhn)" - mh-edit-mhn (mh-mhn-directive-present-p)] - ["Pull in All Compositions (gnus)" - mh-mml-to-mime (mh-mml-directive-present-p)] - ["Revert to Non-MIME Edit (mhn)" - mh-revert-mhn-edit (equal mh-compose-insertion 'mhn)] - ["Kill This Draft" mh-fully-kill-draft t])))) +(easy-menu-define + mh-letter-menu mh-letter-mode-map "Menu for MH-E letter mode." + '("Letter" + ["Send This Draft" mh-send-letter t] + ["Split Current Line" mh-open-line t] + ["Check Recipient" mh-check-whom t] + ["Yank Current Message" mh-yank-cur-msg t] + ["Insert a Message..." mh-insert-letter t] + ["Insert Signature" mh-insert-signature t] + ("Encrypt/Sign Message" + ["Sign Message" + mh-mml-secure-message-sign mh-gnus-pgp-support-flag] + ["Encrypt Message" + mh-mml-secure-message-encrypt mh-gnus-pgp-support-flag] + ["Sign+Encrypt Message" + mh-mml-secure-message-signencrypt mh-gnus-pgp-support-flag] + ["Disable Security" + mh-mml-unsecure-message mh-gnus-pgp-support-flag] + "--" + "Security Method" + ["PGP (MIME)" (setq mh-mml-method-default "pgpmime") + :style radio + :selected (equal mh-mml-method-default "pgpmime")] + ["PGP" (setq mh-mml-method-default "pgp") + :style radio + :selected (equal mh-mml-method-default "pgp")] + ["S/MIME" (setq mh-mml-method-default "smime") + :style radio + :selected (equal mh-mml-method-default "smime")] + "--" + ["Save Method as Default" + (customize-save-variable 'mh-mml-method-default mh-mml-method-default) t] + ) + ["Compose Insertion (MIME)..." mh-compose-insertion t] + ["Compose Compressed tar (MIME)..." + mh-mhn-compose-external-compressed-tar t] + ["Compose Get File (MIME)..." mh-mhn-compose-anon-ftp t] + ["Compose Forward (MIME)..." mh-compose-forward t] + ;; The next two will have to be merged. But I also need to make sure the + ;; user can't mix directives of both types. + ["Pull in All Compositions (mhn)" + mh-edit-mhn (mh-mhn-directive-present-p)] + ["Pull in All Compositions (gnus)" + mh-mml-to-mime (mh-mml-directive-present-p)] + ["Revert to Non-MIME Edit (mhn)" + mh-revert-mhn-edit (equal mh-compose-insertion 'mhn)] + ["Kill This Draft" mh-fully-kill-draft t])) ;;; Help Messages ;;; Group messages logically, more or less. @@ -817,12 +834,15 @@ Returns t if found, nil if not." "\t\tInsert:\n" "Check recipients: \\[mh-check-whom]" "\t\t Current message: \\[mh-yank-cur-msg]\n" - "Encrypt message: \\[mh-mml-secure-message-encrypt-pgpmime]" - "\t\t Attachment: \\[mh-compose-insertion]\n" - "Sign message: \\[mh-mml-secure-message-sign-pgpmime]" - "\t\t Message to forward: \\[mh-compose-forward]\n" + "\t\t Attachment: \\[mh-compose-insertion]\n" + "\t\t Message to forward: \\[mh-compose-forward]\n" + " " + "Security:" + "\t\t Encrypt message: \\[mh-mml-secure-message-encrypt]" + "\t\t Sign+Encrypt message: \\[mh-mml-secure-message-signencrypt]" + "\t\t Sign message: \\[mh-mml-secure-message-sign]\n" " " - "\t\t Signature: \\[mh-insert-signature]")) + "\t\t Signature: \\[mh-insert-signature]")) "Key binding cheat sheet. This is an associative array which is used to show the most common commands. @@ -872,13 +892,19 @@ When a message is composed, the hooks `text-mode-hook' and `mh-letter-mode-hook' are run. \\{mh-letter-mode-map}" - (or mh-user-path (mh-find-path)) + (mh-find-path) (make-local-variable 'mh-send-args) (make-local-variable 'mh-annotate-char) (make-local-variable 'mh-annotate-field) (make-local-variable 'mh-previous-window-config) (make-local-variable 'mh-sent-from-folder) (make-local-variable 'mh-sent-from-msg) + ;; Set the local value of mh-mail-header-separator according to what is + ;; present in the buffer... + (set (make-local-variable 'mh-mail-header-separator) + (save-excursion + (goto-char (mh-mail-header-end)) + (buffer-substring-no-properties (point) (line-end-position)))) (make-local-variable 'mail-header-separator) (setq mail-header-separator mh-mail-header-separator) ;override sendmail.el (make-local-variable 'mh-help-messages) @@ -886,12 +912,6 @@ When a message is composed, the hooks `text-mode-hook' and (setq buffer-invisibility-spec '((vanish . t) t)) (set (make-local-variable 'line-move-ignore-invisible) t) - ;; Set mh-mail-header-end-marker to remember end of message header. - (set (make-local-variable 'mh-letter-mail-header-end-marker) - (set-marker (make-marker) (save-excursion - (goto-char (mh-mail-header-end)) - (line-beginning-position 2)))) - ;; From sendmail.el for proper paragraph fill ;; sendmail.el also sets a normal-auto-fill-function (not done here) (make-local-variable 'paragraph-separate) @@ -965,11 +985,15 @@ When a message is composed, the hooks `text-mode-hook' and t))) (defun mh-letter-header-end () - "Find the end of header from `mh-letter-mail-header-end-marker'." + "Find the end of the message header. +This function is to be used only for font locking. It works by searching for +`mh-mail-header-separator' in the buffer." (save-excursion - (goto-char (marker-position mh-letter-mail-header-end-marker)) - (forward-line -1) - (point))) + (goto-char (point-min)) + (cond ((equal mh-mail-header-separator "") (point-min)) + ((search-forward (format "\n%s\n" mh-mail-header-separator) nil t) + (line-beginning-position 0)) + (t (point-min))))) (defun mh-auto-fill-for-letter () "Perform auto-fill for message. @@ -1041,16 +1065,69 @@ Prompt for the field name with a completion list of the current folders." (substring folder 1) folder))))) +(defun mh-file-is-vcard-p (file) + "Return t if FILE is a .vcf vcard." + (let ((case-fold-search t)) + (and (stringp file) + (file-exists-p file) + (or (and (not (mh-have-file-command)) + (not (null (string-match "\.vcf$" file)))) + (and (mh-have-file-command) + (string-equal "text/x-vcard" (mh-file-mime-type file))))))) + ;;;###mh-autoload -(defun mh-insert-signature () - "Insert the file named by `mh-signature-file-name' at point. +(defun mh-insert-signature (&optional file) + "Insert the signature specified by `mh-signature-file-name' or FILE at point. +A signature separator (`-- ') will be added if the signature block does not +contain one and `mh-signature-separator-flag' is on. The value of `mh-letter-insert-signature-hook' is a list of functions to be -called, with no arguments, before the signature is actually inserted." - (interactive) - (let ((mh-signature-file-name mh-signature-file-name)) - (run-hooks 'mh-letter-insert-signature-hook) - (if mh-signature-file-name - (insert-file-contents mh-signature-file-name))) +called, with no arguments, after the signature is inserted. +The signature can also be inserted with `mh-identity-list'." +(interactive) + (save-excursion + (insert "\n") + (let ((mh-signature-file-name (or file mh-signature-file-name)) + (mh-mhn-p (mh-mhn-directive-present-p)) + (mh-mml-p (mh-mml-directive-present-p))) + (save-restriction + (narrow-to-region (point) (point)) + (cond + ((mh-file-is-vcard-p mh-signature-file-name) + (if (equal mh-compose-insertion 'gnus) + (insert "<#part type=\"text/x-vcard\" filename=\"" + mh-signature-file-name + "\" disposition=inline description=VCard>\n<#/part>") + (insert "#text/x-vcard; name=\"" + (file-name-nondirectory mh-signature-file-name) + "\" [VCard] " (expand-file-name mh-signature-file-name)))) + (t + (cond + (mh-mhn-p + (insert "#\n" "Content-Description: Signature\n")) + (mh-mml-p + (mml-insert-tag 'part 'type "text/plain" 'disposition "inline" + 'description "Signature"))) + (cond ((null mh-signature-file-name)) + ((and (stringp mh-signature-file-name) + (file-readable-p mh-signature-file-name)) + (insert-file-contents mh-signature-file-name)) + ((functionp mh-signature-file-name) + (funcall mh-signature-file-name))))) + (save-restriction + (widen) + (run-hooks 'mh-letter-insert-signature-hook)) + (goto-char (point-min)) + (when (and (not (mh-file-is-vcard-p mh-signature-file-name)) + mh-signature-separator-flag + (> (point-max) (point-min)) + (not (mh-signature-separator-p))) + (cond (mh-mhn-p + (forward-line 2)) + (mh-mml-p + (forward-line 1))) + (insert mh-signature-separator)) + (if (not (> (point-max) (point-min))) + (message "No signature found"))))) (force-mode-line-update)) ;;;###mh-autoload @@ -1100,33 +1177,18 @@ MH the first time a message is composed.") (defun mh-insert-x-mailer () "Append an X-Mailer field to the header. The versions of MH-E, Emacs, and MH are shown." - ;; Lazily initialize mh-x-mailer-string. (when (and mh-insert-x-mailer-flag (null mh-x-mailer-string)) - (save-window-excursion - ;; User would be confused if version info buffer disappeared magically, - ;; so don't delete buffer if it already existed. - (let ((info-buffer-exists-p (get-buffer mh-info-buffer))) - (mh-version) - (set-buffer mh-info-buffer) - (if mh-nmh-flag - (search-forward-regexp "^nmh-\\(\\S +\\)") - (search-forward-regexp "^MH \\(\\S +\\)" nil t)) - (let ((x-mailer-mh (buffer-substring (match-beginning 1) - (match-end 1)))) - (setq mh-x-mailer-string - (format "MH-E %s; %s %s; %sEmacs %s" - mh-version (if mh-nmh-flag "nmh" "MH") x-mailer-mh - (if mh-xemacs-flag "X" "GNU ") - (cond ((not mh-xemacs-flag) emacs-version) - ((string-match "[0-9.]*\\( +\([ a-z]+[0-9]+\)\\)?" - emacs-version) - (match-string 0 emacs-version)) - (t (format "%s.%s" - emacs-major-version - emacs-minor-version)))))) - (if (not info-buffer-exists-p) - (kill-buffer mh-info-buffer))))) + (setq mh-x-mailer-string + (format "MH-E %s; %s; %sEmacs %s" + mh-version mh-variant-in-use + (if mh-xemacs-flag "X" "GNU ") + (cond ((not mh-xemacs-flag) emacs-version) + ((string-match "[0-9.]*\\( +\([ a-z]+[0-9]+\)\\)?" + emacs-version) + (match-string 0 emacs-version)) + (t (format "%s.%s" emacs-major-version + emacs-minor-version)))))) ;; Insert X-Mailer, but only if it doesn't already exist. (save-excursion (when (and mh-insert-x-mailer-flag @@ -1155,25 +1217,31 @@ Sets buffer-local `mh-insert-auto-fields-done-local' when done and inserted something. If NON-INTERACTIVE is non-nil, do not be verbose and only attempt matches if `mh-insert-auto-fields-done-local' is nil. -An `identity' entry is skipped if one was already entered manually." +An `identity' entry is skipped if one was already entered manually. + +Return t if fields added; otherwise return nil." (interactive) - (when (or (not non-interactive) (not mh-insert-auto-fields-done-local)) + (when (or (not non-interactive) + (not mh-insert-auto-fields-done-local)) (save-excursion - (when (and (or (mh-goto-header-field "To:")(mh-goto-header-field "cc:"))) - (let ((list mh-auto-fields-list)) + (when (and (or (mh-goto-header-field "To:") + (mh-goto-header-field "cc:"))) + (let ((list mh-auto-fields-list) + (fields-inserted nil)) (while list (let ((regexp (nth 0 (car list))) (entries (nth 1 (car list)))) (when (mh-regexp-in-field-p regexp "To:" "cc:") (setq mh-insert-auto-fields-done-local t) + (setq fields-inserted t) (if (not non-interactive) - (message "Matched for regexp %s" regexp)) + (message "Fields for %s added" regexp)) (let ((entry-list entries)) (while entry-list (let ((field (caar entry-list)) (value (cdar entry-list))) (cond - ((equal "identity" field) + ((equal ":identity" field) (when (and (not mh-identity-local) (assoc value mh-identity-list)) (mh-insert-identity value))) @@ -1181,7 +1249,8 @@ An `identity' entry is skipped if one was already entered manually." (mh-modify-header-field field value (equal field "From"))))) (setq entry-list (cdr entry-list)))))) - (setq list (cdr list)))))))) + (setq list (cdr list))) + fields-inserted))))) (defun mh-modify-header-field (field value &optional overwrite-flag) "To header FIELD add VALUE. @@ -1201,8 +1270,6 @@ If OVERWRITE-FLAG is non-nil then the old value, if present, is discarded." (mh-goto-header-end 0) (insert field ": " value "\n")))) -(defvar mh-letter-mail-header-end-marker nil) - (defun mh-compose-and-send-mail (draft send-args sent-from-folder sent-from-msg to subject cc @@ -1221,22 +1288,19 @@ for `mh-annotate-msg'. CONFIG is the window configuration to restore after sending the letter." (pop-to-buffer draft) (mh-letter-mode) - (mh-insert-auto-fields t) - ;; mh-identity support + ;; Insert identity. (if (and (boundp 'mh-identity-default) mh-identity-default (not mh-identity-local)) (mh-insert-identity mh-identity-default)) - (when (and (boundp 'mh-identity-list) - mh-identity-list) - (mh-identity-make-menu) - (easy-menu-add mh-identity-menu)) + (mh-identity-make-menu) + (easy-menu-add mh-identity-menu) - ;; Extra fields + ;; Insert extra fields. (mh-insert-x-mailer) (mh-insert-x-face) - ;; Hide skipped fields + (mh-letter-hide-all-skipped-fields) (setq mh-sent-from-folder sent-from-folder) @@ -1264,7 +1328,16 @@ CONFIG is the window configuration to restore after sending the letter." This should be the last function called when composing the draft." (message "%s" (substitute-command-keys (concat "Type \\[mh-send-letter] to send message, " - "\\[mh-help] for help.")))) + "\\[mh-help] for help")))) + +(defun mh-ascii-buffer-p () + "Check if current buffer is entirely composed of ASCII. +The function doesn't work for XEmacs since `find-charset-region' doesn't exist +there." + (loop for charset in (mh-funcall-if-exists + find-charset-region (point-min) (point-max)) + unless (eq charset 'ascii) return nil + finally return t)) ;;;###mh-autoload (defun mh-send-letter (&optional arg) @@ -1273,15 +1346,17 @@ If optional prefix argument ARG is provided, monitor delivery. The value of `mh-before-send-letter-hook' is a list of functions to be called, with no arguments, before doing anything. Run `\\[mh-edit-mhn]' if mhn directives are present; otherwise -run `\\[mh-mml-to-mime]' if mml directives are present. -Insert X-Mailer field if variable `mh-insert-x-mailer-flag' is set. -Insert X-Face field if the file specified by `mh-x-face-file' exists." +run `\\[mh-mml-to-mime]' if mml directives are present." (interactive "P") (run-hooks 'mh-before-send-letter-hook) - (mh-insert-auto-fields t) + (if (and (mh-insert-auto-fields t) + mh-auto-fields-prompt-flag + (goto-char (point-min))) + (if (not (y-or-n-p "Auto fields inserted, send? ")) + (error "Send aborted"))) (cond ((mh-mhn-directive-present-p) (mh-edit-mhn)) - ((mh-mml-directive-present-p) + ((or (mh-mml-directive-present-p) (not (mh-ascii-buffer-p))) (mh-mml-to-mime))) (save-buffer) (message "Sending...") @@ -1302,7 +1377,7 @@ Insert X-Face field if the file specified by `mh-x-face-file' exists." 'iso-latin-1)))) ;; The default BCC encapsulation will make a MIME message unreadable. ;; With nmh use the -mime arg to prevent this. - (if (and mh-nmh-flag + (if (and (mh-variant-p 'nmh) (mh-goto-header-field "Bcc:") (mh-goto-header-field "Content-Type:")) (setq mh-send-args (format "-mime %s" mh-send-args))) @@ -1338,7 +1413,8 @@ Insert X-Face field if the file specified by `mh-x-face-file' exists." ;;;###mh-autoload (defun mh-insert-letter (folder message verbatim) "Insert a message into the current letter. -Removes the header fields according to the variable `mh-invisible-headers'. +Removes the header fields according to the variable +`mh-invisible-header-fields-compiled'. Prefixes each non-blank line with `mh-ins-buf-prefix', unless `mh-yank-from-start-of-msg' is set for supercite in which case supercite is used to format the message. @@ -1355,11 +1431,12 @@ and point after it." (save-restriction (narrow-to-region (point) (point)) (let ((start (point-min))) - (if (equal message "") (setq message (int-to-string mh-sent-from-msg))) + (if (and (equal message "") (numberp mh-sent-from-msg)) + (setq message (int-to-string mh-sent-from-msg))) (insert-file-contents (expand-file-name message (mh-expand-file-name folder))) (when (not verbatim) - (mh-clean-msg-header start mh-invisible-headers mh-visible-headers) + (mh-clean-msg-header start mh-invisible-header-fields-compiled nil) (goto-char (point-max)) ;Needed for sc-cite-original (push-mark) ;Needed for sc-cite-original (goto-char (point-min)) ;Needed for sc-cite-original @@ -1373,15 +1450,13 @@ and point after it." (skip-chars-forward " ") (cond ((looking-at "\"\\([^\"\n]+\\)\" \\(<.+>\\)") - (format "%s %s %s" (match-string 1)(match-string 2) - mh-extract-from-attribution-verb)) + (format "%s %s " (match-string 1)(match-string 2))) ((looking-at "\\([^<\n]+<.+>\\)$") - (format "%s %s" (match-string 1) mh-extract-from-attribution-verb)) + (format "%s " (match-string 1))) ((looking-at "\\([^ ]+@[^ ]+\\) +(\\(.+\\))$") - (format "%s <%s> %s" (match-string 2)(match-string 1) - mh-extract-from-attribution-verb)) + (format "%s <%s> " (match-string 2)(match-string 1))) ((looking-at " *\\(.+\\)$") - (format "%s %s" (match-string 1) mh-extract-from-attribution-verb)))))) + (format "%s " (match-string 1))))))) ;;;###mh-autoload (defun mh-yank-cur-msg () @@ -1444,9 +1519,11 @@ yanked message will be deleted." (push-mark) ;Needed for sc-cite-original (goto-char (point-min)) ;Needed for sc-cite-original (mh-insert-prefix-string mh-ins-buf-prefix) - (if (or (eq 'attribution mh-yank-from-start-of-msg) - (eq 'autoattrib mh-yank-from-start-of-msg)) - (insert from-attr "\n\n")) + (when (or (eq 'attribution mh-yank-from-start-of-msg) + (eq 'autoattrib mh-yank-from-start-of-msg)) + (insert from-attr) + (mh-identity-insert-attribution-verb nil) + (insert "\n\n")) ;; If the user has selected a region, he has already "edited" the ;; text, so leave the cursor at the end of the yanked text. In ;; either case, leave a mark at the opposite end of the included @@ -1572,7 +1649,7 @@ Any match found replaces the text from BEGIN to END." (let ((syntax-table (syntax-table))) (unwind-protect (save-excursion - (mh-funcall-if-exists mail-abbrev-make-syntax-table) + (mh-mail-abbrev-make-syntax-table) (set-syntax-table mail-abbrev-syntax-table) (backward-word n) (point)) @@ -1593,7 +1670,6 @@ Any match found replaces the text from BEGIN to END." (mh-folder-completion-function folder nil t)))) (mh-complete-word folder choices beg end))) -;; XXX: This should probably be customizable (defvar mh-letter-complete-function-alist '((cc . mh-alias-letter-expand-alias) (bcc . mh-alias-letter-expand-alias) @@ -1607,10 +1683,10 @@ Any match found replaces the text from BEGIN to END." (defun mh-letter-complete (arg) "Perform completion on header field or word preceding point. -Alias completion is done within the mail header on selected fields based on -the matches in `mh-letter-complete-function-alist'. Elsewhere the function -designated by `mh-letter-complete-function' is used and given the prefix ARG, -if present." +If the field contains addresses (for example, `To:' or `Cc:') or folders (for +example, `Fcc:') then this function will provide alias completion. Elsewhere, +this function runs `mh-letter-complete-function' instead and passes the prefix +ARG, if present." (interactive "P") (let ((func nil)) (cond ((not (mh-in-header-p)) @@ -1832,10 +1908,13 @@ Otherwise return the empty string." ;;; Build the letter-mode keymap: ;;; If this changes, modify mh-letter-mode-help-messages accordingly, above. (gnus-define-keys mh-letter-mode-map + " " mh-letter-complete-or-space + "," mh-letter-confirm-address "\C-c?" mh-help + "\C-c\C-\\" mh-fully-kill-draft ;if no C-q + "\C-c\C-^" mh-insert-signature ;if no C-s "\C-c\C-c" mh-send-letter "\C-c\C-d" mh-insert-identity - "\C-c\M-d" mh-insert-auto-fields "\C-c\C-e" mh-edit-mhn "\C-c\C-f\C-b" mh-to-field "\C-c\C-f\C-c" mh-to-field @@ -1852,31 +1931,38 @@ Otherwise return the empty string." "\C-c\C-fs" mh-to-field "\C-c\C-ft" mh-to-field "\C-c\C-i" mh-insert-letter - "\C-c\C-m\C-e" mh-mml-secure-message-encrypt-pgpmime + "\C-c\C-m\C-e" mh-mml-secure-message-encrypt "\C-c\C-m\C-f" mh-compose-forward + "\C-c\C-m\C-g" mh-mhn-compose-anon-ftp "\C-c\C-m\C-i" mh-compose-insertion "\C-c\C-m\C-m" mh-mml-to-mime - "\C-c\C-m\C-s" mh-mml-secure-message-sign-pgpmime + "\C-c\C-m\C-n" mh-mml-unsecure-message + "\C-c\C-m\C-s" mh-mml-secure-message-sign + "\C-c\C-m\C-t" mh-mhn-compose-external-compressed-tar "\C-c\C-m\C-u" mh-revert-mhn-edit - "\C-c\C-me" mh-mml-secure-message-encrypt-pgpmime + "\C-c\C-m\C-x" mh-mhn-compose-external-type + "\C-c\C-mee" mh-mml-secure-message-encrypt + "\C-c\C-mes" mh-mml-secure-message-signencrypt "\C-c\C-mf" mh-compose-forward + "\C-c\C-mg" mh-mhn-compose-anon-ftp "\C-c\C-mi" mh-compose-insertion "\C-c\C-mm" mh-mml-to-mime - "\C-c\C-ms" mh-mml-secure-message-sign-pgpmime + "\C-c\C-mn" mh-mml-unsecure-message + "\C-c\C-mse" mh-mml-secure-message-signencrypt + "\C-c\C-mss" mh-mml-secure-message-sign + "\C-c\C-mt" mh-mhn-compose-external-compressed-tar "\C-c\C-mu" mh-revert-mhn-edit + "\C-c\C-mx" mh-mhn-compose-external-type "\C-c\C-o" mh-open-line "\C-c\C-q" mh-fully-kill-draft - "\C-c\C-\\" mh-fully-kill-draft ;if no C-q "\C-c\C-s" mh-insert-signature - "\C-c\C-^" mh-insert-signature ;if no C-s + "\C-c\C-t" mh-letter-toggle-header-field-display "\C-c\C-w" mh-check-whom "\C-c\C-y" mh-yank-cur-msg - "\C-c\C-t" mh-letter-toggle-header-field-display - " " mh-letter-complete-or-space + "\C-c\M-d" mh-insert-auto-fields "\M-\t" mh-letter-complete "\t" mh-letter-next-header-field-or-indent - [backtab] mh-letter-previous-header-field - "," mh-letter-confirm-address) + [backtab] mh-letter-previous-header-field) ;; "C-c /" prefix is used in mh-letter-mode by pgp.el and mailcrypt.el. diff --git a/lisp/mh-e/mh-customize.el b/lisp/mh-e/mh-customize.el index 2ce36c88726..0cf35b3c0d9 100644 --- a/lisp/mh-e/mh-customize.el +++ b/lisp/mh-e/mh-customize.el @@ -34,814 +34,585 @@ ;; ;; 1. MH-E Customization Groups ;; -;; These are the customization group definitions. These are organized in a -;; logical order. High-level, windows and toolbar, folder, message, -;; composing and hooks. +;; These are the customization group definitions. Every group has a +;; associated manual node. The ordering is alphabetical, except for the +;; groups mh-faces and mh-hooks which are last . ;; ;; 2. MH-E Customization ;; -;; Here are the actual customization variables. There is a sub-section for -;; each group in the MH-E Customization Groups section. Within each -;; section, variables are sorted alphabetically. The manual section -;; dictates which group a variable should be placed. New variables should -;; be placed in the section where they would most likely be defined. +;; These are the actual customization variables. There is a sub-section for +;; each group in the MH-E Customization Groups section, in the same order, +;; separated by page breaks. Within each section, variables are sorted +;; alphabetically. ;; -;; All hooks should be placed in the 'mh-hook group; in addition, add the -;; group in which the hook is defined in the manual (or, if it is new, -;; where it would be defined). These two actions insures that the hooks -;; appear last in each group. +;; 3. Hooks +;; +;; All hooks must be placed in the mh-hook group; in addition, add the +;; group associated with the manual node in which the hook is described. +;; Since the mh-hook group appears near the end of this file, the hooks +;; will appear at the end of these other groups. +;; +;; 4. Faces +;; +;; Create a new face group if necessary; in this case, add the group +;; associated with the manual node in which the faces are described to the +;; faces' group definition. Since the face groups appear last, the face +;; groups will appear at the end of these other groups. ;; -;; 3. Faces - ;;; Change Log: ;;; Code: + (provide 'mh-customize) -(require 'mh-utils) + +(eval-when-compile (require 'mh-acros)) +(mh-require-cl) +(require 'mh-loaddefs) + +(autoload 'Info-goto-node "info") + +(eval-and-compile + (defvar mh-xemacs-flag (featurep 'xemacs) + "Non-nil means the current Emacs is XEmacs.")) (when mh-xemacs-flag (require 'mh-xemacs)) -;;;###mh-autoload (defun mh-customize (&optional delete-other-windows-flag) "Customize MH-E variables. -With optional argument DELETE-OTHER-WINDOWS-FLAG, other windows in the frame -are removed." +If optional argument DELETE-OTHER-WINDOWS-FLAG is non-nil, other windows in +the frame are removed." (interactive "P") (customize-group 'mh) (when delete-other-windows-flag (delete-other-windows))) + + +;;; For compiler warnings... +(defvar mh-show-buffer) +(defvar mh-show-folder-buffer) + ;;; MH-E Customization Groups (defgroup mh nil - "GNU Emacs interface to the MH mail system." + "Emacs interface to the MH mail system. +MH is the Rand Mail Handler. Other implementations include nmh and GNU +mailutils." :link '(custom-manual "(mh-e)Top") :group 'mail) -(defgroup mh-toolbar nil - "Toolbar configuration." - :prefix "mh-" +(defgroup mh-e '((mh custom-group)) ; Sort of an alias for 'mh group + "Emacs interface to the MH mail system. +MH is the Rand Mail Handler. Other implementations include nmh and GNU +mailutils." + :link '(custom-manual "(mh-e)Top")) + +(defgroup mh-alias nil + "Aliases." + :link '(custom-manual "(mh-e)Aliases") + :prefix "mh-alias-" :group 'mh) -(defgroup mh-speed nil - "Speedbar and folder configuration." +(defgroup mh-folder nil + "Organizing your mail with folders." :prefix "mh-" - :link '(custom-manual "(mh-e)Customizing Moving Mail") + :link '(custom-manual "(mh-e)Organizing") :group 'mh) -(defgroup mh-folder nil - "Options for controlling scan listing." +(defgroup mh-folder-selection nil + "Folder selection." :prefix "mh-" - :link '(custom-manual "(mh-e)Customizing Moving Mail") + :link '(custom-manual "(mh-e)Folder Selection") + :group 'mh) + +(defgroup mh-identity nil + "Identities." + :link '(custom-manual "(mh-e)Identities") + :prefix "mh-identity-" + :group 'mh) + +(defgroup mh-inc nil + "Incorporating your mail." + :prefix "mh-inc-" + :link '(custom-manual "(mh-e)Incorporating Mail") :group 'mh) (defgroup mh-index nil - "Indexed searching." - :link '(custom-manual "(mh-e)Customizing mh-e") - :prefix "mh-" + "Searching." + :link '(custom-manual "(mh-e)Searching") + :prefix "mh-index-" :group 'mh) (defgroup mh-junk nil - "Spam handling." - :link '(custom-manual "(mh-e)Customizing mh-e") + "Dealing with junk mail." + :link '(custom-manual "(mh-e)Junk") :prefix "mh-junk-" :group 'mh) -(defgroup mh-show nil - "Message display." +(defgroup mh-letter nil + "Editing a draft." :prefix "mh-" - :link '(custom-manual "(mh-e)Customizing Reading") + :link '(custom-manual "(mh-e)Editing Drafts") :group 'mh) -(defgroup mh-faces nil - "Faces used in MH-E." - :link '(custom-manual "(mh-e)Customizing mh-e") +(defgroup mh-ranges nil + "Ranges." :prefix "mh-" - :group 'faces + :link '(custom-manual "(mh-e)Ranges") :group 'mh) -(defgroup mh-letter nil - "Composing messages." +(defgroup mh-scan-line-formats nil + "Scan line formats." + :link '(custom-manual "(mh-e)Scan Line Formats") :prefix "mh-" - :link '(custom-manual "(mh-e)Customizing Sending") :group 'mh) -(defgroup mh-alias nil - "Alias handling." - :link '(custom-manual "(mh-e)Customizing mh-e") - :prefix "mh-alias-" +(defgroup mh-sending-mail nil + "Sending mail." + :prefix "mh-" + :link '(custom-manual "(mh-e)Sending Mail") :group 'mh) -(defgroup mh-identity nil - "Multiple personalities." - :link '(custom-manual "(mh-e)Customizing mh-e") +(defgroup mh-sequences nil + "Sequences." + :prefix "mh-" + :link '(custom-manual "(mh-e)Sequences") + :group 'mh) + +(defgroup mh-show nil + "Reading your mail." :prefix "mh-" + :link '(custom-manual "(mh-e)Reading Mail") + :group 'mh) + +(defgroup mh-speed nil + "The speedbar." + :prefix "mh-speed-" + :link '(custom-manual "(mh-e)Speedbar") + :group 'mh) + +(defgroup mh-toolbar nil + "The toolbar" + :link '(custom-manual "(mh-e)Toolbar") + :prefix "mh-" + :group 'mh) + +(defgroup mh-faces nil + "Faces used in MH-E." + :link '(custom-manual "(mh-e)Top") + :prefix "mh-" + :group 'faces :group 'mh) (defgroup mh-hooks nil "MH-E hooks." - :link '(custom-manual "(mh-e)Customizing mh-e") + :link '(custom-manual "(mh-e)Top") :prefix "mh-" :group 'mh) ;;; Faces -(defgroup mh-speed-faces nil - "Faces used in speedbar." - :link '(custom-manual "(mh-e)Customizing mh-e") - :prefix "mh-" - :group 'mh-faces - :group 'mh-speed) - (defgroup mh-folder-faces nil "Faces used in scan listing." - :link '(custom-manual "(mh-e)Customizing mh-e") + :link '(custom-manual "(mh-e)Organizing") :prefix "mh-" :group 'mh-faces - :group 'mh-folder) + :group 'mh-show) (defgroup mh-index-faces nil - "Faces used in indexed searches." - :link '(custom-manual "(mh-e)Customizing mh-e") + "Faces used in searching." + :link '(custom-manual "(mh-e)Searching") :prefix "mh-" :group 'mh-faces :group 'mh-index) +(defgroup mh-letter-faces nil + "Faces used in message drafts." + :link '(custom-manual "(mh-e)Sending Mail") + :prefix "mh-" + :group 'mh-faces + :group 'mh-letter) + (defgroup mh-show-faces nil "Faces used in message display." - :link '(custom-manual "(mh-e)Customizing mh-e") + :link '(custom-manual "(mh-e)Reading Mail") :prefix "mh-" :group 'mh-faces :group 'mh-show) -(defgroup mh-letter-faces nil - "Faces used when composing messages." - :link '(custom-manual "(mh-e)Customizing mh-e") +(defgroup mh-speed-faces nil + "Faces used in speedbar." + :link '(custom-manual "(mh-e)Speedbar") :prefix "mh-" :group 'mh-faces - :group 'mh-letter) + :group 'mh-speed) -;;; MH-E Customization (:group mh) - -;;; Toolbar configuration (:group 'mh-toolbar) - -(defcustom mh-tool-bar-search-function 'mh-search-folder - "*Function called by the tool-bar search button. -See `mh-search-folder' and `mh-index-search' for details." - :type '(choice (const mh-search-folder) - (const mh-index-search) - (function :tag "Other function")) - :group 'mh-toolbar) - -;; Functions called from the tool bar -(defun mh-tool-bar-search (&optional arg) - "Interactively call `mh-tool-bar-search-function'. -Optional argument ARG is not used." - (interactive "P") - (call-interactively mh-tool-bar-search-function)) - -(defun mh-tool-bar-customize () - "Call `mh-customize' from the toolbar." - (interactive) - (mh-customize t)) - -(defun mh-tool-bar-folder-help () - "Visit \"(mh-e)Top\"." - (interactive) - (Info-goto-node "(mh-e)Top") - (delete-other-windows)) - -(defun mh-tool-bar-letter-help () - "Visit \"(mh-e)Draft Editing\"." - (interactive) - (Info-goto-node "(mh-e)Draft Editing") - (delete-other-windows)) - -(defmacro mh-tool-bar-reply-generator (function recipient folder-buffer-flag) - "Generate FUNCTION that replies to RECIPIENT. -If FOLDER-BUFFER-FLAG is nil then the function generated -When INCLUDE-FLAG is non-nil, include message body being replied to." - `(defun ,function (&optional arg) - ,(format "Reply to \"%s\".\nWhen ARG is non-nil include message in reply." - recipient) - (interactive "P") - ,(if folder-buffer-flag nil '(set-buffer mh-show-folder-buffer)) - (mh-reply (mh-get-msg-num nil) ,recipient arg))) - -(mh-tool-bar-reply-generator mh-tool-bar-reply-from "from" t) -(mh-tool-bar-reply-generator mh-show-tool-bar-reply-from "from" nil) -(mh-tool-bar-reply-generator mh-tool-bar-reply-to "to" t) -(mh-tool-bar-reply-generator mh-show-tool-bar-reply-to "to" nil) -(mh-tool-bar-reply-generator mh-tool-bar-reply-all "all" t) -(mh-tool-bar-reply-generator mh-show-tool-bar-reply-all "all" nil) +;;; Emacs interface to the MH mail system (:group mh) +(eval-when (compile) + (setq mh-variant 'none)) + +(defcustom mh-variant 'autodetect + "*Specifies the variant used by MH-E. + +The default setting of this option is `Auto-detect' which means that MH-E will +automatically choose the first of nmh, MH, or GNU mailutils that it finds in +the directories listed in `mh-path', `mh-sys-path', and `exec-path'. If, for +example, you have both nmh and mailutils installed and `mh-variant-in-use' was +initialized to nmh but you want to use mailutils, then you can set this option +to `mailutils'. + +When this variable is changed, MH-E resets `mh-progs', `mh-lib', +`mh-lib-progs', `mh-flists-present-flag', and `mh-variant-in-use' +accordingly." + :type `(radio + (const :tag "Auto-detect" autodetect) + ,@(mapcar (lambda (x) `(const ,(car x))) (mh-variants))) + :set (lambda (symbol value) + (set-default symbol value) ;Done in mh-variant-set-variant! + (mh-variant-set value)) + :group 'mh) -;; XEmacs has a couple of extra customizations... -(mh-do-in-xemacs - (defcustom mh-xemacs-use-toolbar-flag (if (and (featurep 'toolbar) - (featurep 'xpm) - (device-on-window-system-p)) - t - nil) - "*If non-nil, use toolbar. + -This will default to t if you are in an environment that supports -toolbars and xpm." - :type 'boolean - :group 'mh-toolbar) +;;; Aliases (:group 'mh-alias) - (defcustom mh-xemacs-toolbar-position (if mh-xemacs-use-toolbar-flag - 'default - nil) - "*Where to put the toolbar. +(defcustom mh-alias-completion-ignore-case-flag t + "*Non-nil means don't consider case significant in MH alias completion. +As MH ignores case in the aliases, so too does MH-E. However, you may turn +this option off to make case significant which can be used to segregate +completion of your aliases. You might use lowercase for mailing lists and +uppercase for people." + :type 'boolean + :group 'mh-alias) -Valid non-nil values are \"default\", \"top\", \"bottom\", \"left\", -\"right\". These match the four edges of the frame, with \"default\" -meaning \"use the same position as the default-toolbar\". +(defcustom mh-alias-expand-aliases-flag nil + "*Non-nil means to expand aliases entered in the minibuffer. +In other words, aliases entered in the minibuffer will be expanded to the full +address in the message draft. By default, this expansion is not performed." + :type 'boolean + :group 'mh-alias) -A nil value means do not use a toolbar. +(defcustom mh-alias-flash-on-comma t + "*Specify whether to flash address or warn on translation. +This option controls the behavior when a [comma] is pressed while entering +aliases or addresses. The default setting flashes the address associated with +an address in the minibuffer briefly, but does not display a warning if the +alias is not found." + :type '(choice (const :tag "Flash but Don't Warn If No Alias" t) + (const :tag "Flash and Warn If No Alias" 1) + (const :tag "Don't Flash Nor Warn If No Alias" nil)) + :group 'mh-alias) -If this variable is set to anything other than \"default\" and the -default-toolbar has a different positional setting from the value of -this variable, then two toolbars will be displayed. The MH-E toolbar -and the default-toolbar." - :type '(radio (const :tag "Same position as the \"default-toolbar\"" - :value default) - (const :tag "Along the top edge of the frame" - :value top) - (const :tag "Along the bottom edge of the frame" - :value bottom) - (const :tag "Along the left edge of the frame" - :value left) - (const :tag "Along the right edge of the frame" - :value right) - (const :tag "Don't use a toolbar" nil)) - :group 'mh-toolbar)) +(defcustom mh-alias-insert-file nil + "*Filename used to store a new MH-E alias. +The default setting of this option is `Use Aliasfile Profile Component'. This +option can also hold the name of a file or a list a file names. If this option +is set to a list of file names, or the `Aliasfile:' profile component contains +more than one file name, MH-E will prompt for one of them when MH-E adds an +alias." + :type '(choice (const :tag "Use Aliasfile Profile Component" nil) + (file :tag "Alias File") + (repeat :tag "List of Alias Files" file)) + :group 'mh-alias) -(defmacro mh-tool-bar-define (defaults &rest buttons) - "Define a tool bar for MH-E. -DEFAULTS is the list of buttons that are present by default. It is a list of -lists where the sublists are of the following form: +(defcustom mh-alias-insertion-location 'sorted + "Specifies where new aliases are entered in alias files. +This option is set to `Alphabetical' by default. If you organize your alias +file in other ways, then adding aliases to the `Top' or `Bottom' of your alias +file might be more appropriate." + :type '(choice (const :tag "Alphabetical" sorted) + (const :tag "Top" top) + (const :tag "Bottom" bottom)) + :group 'mh-alias) - (:KEYWORD FUNC1 FUNC2 FUNC3 ...) +(defcustom mh-alias-local-users t + "*If on, local users are added to alias completion. -Here :KEYWORD is one of :folder or :letter. If it is :folder then the default -buttons in the folder and show mode buffers are being specified. If it is -:letter then the default buttons in the letter mode are listed. FUNC1, FUNC2, -FUNC3, ... are the names of the functions that the buttons would execute. +Aliases are created from `/etc/passwd' entries with a user ID larger than +a magical number, typically 200. This can be a handy tool on a machine where +you and co-workers exchange messages. These aliases have the form +`local.first.last' if a real name is present in the password file. +Otherwise, the alias will have the form `local.login'. -Each element of BUTTONS is a list consisting of four mandatory items and one -optional item as follows: +If you're on a system with thousands of users you don't know, and the loading +of local aliases slows MH-E down noticeably, then turn this option off. - (FUNCTION MODES ICON DOC &optional ENABLE-EXPR) +This option also takes a string which is executed to generate the password +file. For example, use \"ypcat passwd\" to obtain the NIS password file." + :type '(choice (boolean) (string)) + :group 'mh-alias) -where, +(defcustom mh-alias-local-users-prefix "local." + "*String prepended to the real names of users from the password file. +This option can also be set to `Use Login'. - FUNCTION is the name of the function that will be executed when the button - is clicked. +For example, consider the following password file entry: - MODES is a list of symbols. List elements must be from `folder', `letter' and - `sequence'. If `folder' is present then the button is available in the - folder and show buffer. If the name of FUNCTION is of the form \"mh-foo\", - where foo is some arbitrary string, then we check if the function - `mh-show-foo' exists. If it exists then that function is used in the show - buffer. Otherwise the original function `mh-foo' is used in the show buffer - as well. Presence of `sequence' is handled similar to the above. The only - difference is that the button is shown only when the folder is narrowed to a - sequence. If `letter' is present in MODES, then the button is available - during draft editing and runs FUNCTION when clicked. + psg:x:1000:1000:Peter S Galbraith,,,:/home/psg:/bin/tcsh - ICON is the icon that is drawn in the button. +The following settings of this option will produce the associated aliases: - DOC is the documentation for the button. It is used in tool-tips and in - providing other help to the user. GNU Emacs uses only the first line of the - string. So the DOC should be formatted such that the first line is useful and - complete without the rest of the string. + \"local.\" local.peter.galbraith + \"\" peter.galbraith + Use Login psg - Optional item ENABLE-EXPR is an arbitrary lisp expression. If it evaluates - to nil, then the button is deactivated, otherwise it is active. If is in't - present then the button is always active." - ;; The following variable names have been carefully chosen to make code - ;; generation easier. Modifying the names should be done carefully. - (let (folder-buttons folder-docs folder-button-setter sequence-button-setter - show-buttons show-button-setter show-seq-button-setter - letter-buttons letter-docs letter-button-setter - folder-defaults letter-defaults - folder-vectors show-vectors letter-vectors) - (dolist (x defaults) - (cond ((eq (car x) :folder) (setq folder-defaults (cdr x))) - ((eq (car x) :letter) (setq letter-defaults (cdr x))))) - (dolist (button buttons) - (unless (and (listp button) - (or (equal (length button) 4) (equal (length button) 5))) - (error "Incorrect MH-E tool-bar button specification: %s" button)) - (let* ((name (nth 0 button)) - (name-str (symbol-name name)) - (icon (nth 2 button)) - (xemacs-icon (mh-do-in-xemacs - (cdr (assoc (intern icon) mh-xemacs-icon-map)))) - (full-doc (nth 3 button)) - (doc (if (string-match "\\(.*\\)\n" full-doc) - (match-string 1 full-doc) - full-doc)) - (enable-expr (or (nth 4 button) t)) - (modes (nth 1 button)) - functions show-sym) - (when (memq 'letter modes) (setq functions `(:letter ,name))) - (when (or (memq 'folder modes) (memq 'sequence modes)) - (setq functions - (append `(,(if (memq 'folder modes) :folder :sequence) ,name) - functions)) - (setq show-sym - (if (string-match "^mh-\\(.*\\)$" name-str) - (intern (concat "mh-show-" (match-string 1 name-str))) - name)) - (setq functions - (append `(,(if (memq 'folder modes) :show :show-seq) - ,(if (fboundp show-sym) show-sym name)) - functions))) - (do ((functions functions (cddr functions))) - ((null functions)) - (let* ((type (car functions)) - (function (cadr functions)) - (type1 (substring (symbol-name type) 1)) - (vector-list (cond ((eq type :show) 'show-vectors) - ((eq type :show-seq) 'show-vectors) - ((eq type :letter) 'letter-vectors) - (t 'folder-vectors))) - (list (cond ((eq type :letter) 'mh-tool-bar-letter-buttons) - (t 'mh-tool-bar-folder-buttons))) - (key (intern (concat "mh-" type1 "toolbar-" name-str))) - (setter (intern (concat type1 "-button-setter"))) - (mbuttons (cond ((eq type :letter) 'letter-buttons) - ((eq type :show) 'show-buttons) - ((eq type :show-seq) 'show-buttons) - (t 'folder-buttons))) - (docs (cond ((eq mbuttons 'letter-buttons) 'letter-docs) - ((eq mbuttons 'folder-buttons) 'folder-docs)))) - (add-to-list vector-list `[,xemacs-icon ,function t ,full-doc]) - (add-to-list - setter `(when (member ',name ,list) - (mh-funcall-if-exists - tool-bar-add-item ,icon ',function ',key - :help ,doc :enable ',enable-expr))) - (add-to-list mbuttons name) - (if docs (add-to-list docs doc)))))) - (setq folder-buttons (nreverse folder-buttons) - letter-buttons (nreverse letter-buttons) - show-buttons (nreverse show-buttons) - letter-docs (nreverse letter-docs) - folder-docs (nreverse folder-docs) - folder-vectors (nreverse folder-vectors) - show-vectors (nreverse show-vectors) - letter-vectors (nreverse letter-vectors)) - (dolist (x folder-defaults) - (unless (memq x folder-buttons) - (error "Folder defaults contains unknown button '%s'" x))) - (dolist (x letter-defaults) - (unless (memq x letter-buttons) - (error "Letter defaults contains unknown button '%s'" x))) - `(eval-when (compile load eval) - (defvar mh-folder-tool-bar-map nil) - (defvar mh-folder-seq-tool-bar-map nil) - (defvar mh-show-tool-bar-map nil) - (defvar mh-show-seq-tool-bar-map nil) - (defvar mh-letter-tool-bar-map nil) - ;; GNU Emacs tool bar specific code - (mh-do-in-gnu-emacs - ;; Custom setter functions - (defun mh-tool-bar-folder-buttons-set (symbol value) - "Construct toolbar for `mh-folder-mode' and `mh-show-mode'." - (set-default symbol value) - (setq mh-folder-tool-bar-map - (let ((tool-bar-map (make-sparse-keymap))) - ,@(nreverse folder-button-setter) - tool-bar-map)) - (setq mh-show-tool-bar-map - (let ((tool-bar-map (make-sparse-keymap))) - ,@(nreverse show-button-setter) - tool-bar-map)) - (setq mh-show-seq-tool-bar-map - (let ((tool-bar-map (copy-keymap mh-show-tool-bar-map))) - ,@(nreverse show-seq-button-setter) - tool-bar-map)) - (setq mh-folder-seq-tool-bar-map - (let ((tool-bar-map (copy-keymap mh-folder-tool-bar-map))) - ,@(nreverse sequence-button-setter) - tool-bar-map))) - (defun mh-tool-bar-letter-buttons-set (symbol value) - "Construct toolbar for `mh-letter-mode'." - (set-default symbol value) - (setq mh-letter-tool-bar-map - (let ((tool-bar-map (make-sparse-keymap))) - ,@(nreverse letter-button-setter) - tool-bar-map)))) - ;; XEmacs specific code - (mh-do-in-xemacs - (defvar mh-toolbar-folder-vector-map - ',(loop for button in folder-buttons - for vector in folder-vectors - collect (cons button vector))) - (defvar mh-toolbar-show-vector-map - ',(loop for button in show-buttons - for vector in show-vectors - collect (cons button vector))) - (defvar mh-toolbar-letter-vector-map - ',(loop for button in letter-buttons - for vector in letter-vectors - collect (cons button vector))) - (defvar mh-toolbar-folder-buttons nil) - (defvar mh-toolbar-show-buttons nil) - (defvar mh-toolbar-letter-buttons nil) - ;; Custom setter functions - (defun mh-tool-bar-letter-buttons-set (symbol value) - (set-default symbol value) - (setq mh-toolbar-letter-buttons - (loop for b in value - collect (cdr (assoc b mh-toolbar-letter-vector-map))))) - (defun mh-tool-bar-folder-buttons-set (symbol value) - (set-default symbol value) - (setq mh-toolbar-folder-buttons - (loop for b in value - collect (cdr (assoc b mh-toolbar-folder-vector-map)))) - (setq mh-toolbar-show-buttons - (loop for b in value - collect (cdr (assoc b mh-toolbar-show-vector-map))))) - ;; Initialize toolbar - (defun mh-toolbar-init (mode) - "Install toolbar in MODE." - (let ((toolbar (cond ((eq mode :folder) mh-toolbar-folder-buttons) - ((eq mode :letter) mh-toolbar-letter-buttons) - ((eq mode :show) mh-toolbar-show-buttons))) - (height 37) - (width 40) - (buffer (current-buffer))) - (when (and mh-xemacs-toolbar-position mh-xemacs-use-toolbar-flag) - (cond - ((eq mh-xemacs-toolbar-position 'top) - (set-specifier top-toolbar toolbar buffer) - (set-specifier top-toolbar-visible-p t) - (set-specifier top-toolbar-height height)) - ((eq mh-xemacs-toolbar-position 'bottom) - (set-specifier bottom-toolbar toolbar buffer) - (set-specifier bottom-toolbar-visible-p t) - (set-specifier bottom-toolbar-height height)) - ((eq mh-xemacs-toolbar-position 'left) - (set-specifier left-toolbar toolbar buffer) - (set-specifier left-toolbar-visible-p t) - (set-specifier left-toolbar-width width)) - ((eq mh-xemacs-toolbar-position 'right) - (set-specifier right-toolbar toolbar buffer) - (set-specifier right-toolbar-visible-p t) - (set-specifier right-toolbar-width width)) - (t (set-specifier default-toolbar toolbar buffer))))))) - ;; Declare customizable toolbars - (custom-declare-variable - 'mh-tool-bar-folder-buttons - '(list ,@(mapcar (lambda (x) `(quote ,x)) folder-defaults)) - "Choose buttons to include in MH-E folder/show toolbar." - :group 'mh-toolbar :set 'mh-tool-bar-folder-buttons-set - :type '(set ,@(loop for x in folder-buttons - for y in folder-docs - collect `(const :tag ,y ,x)))) - (custom-declare-variable - 'mh-tool-bar-letter-buttons - '(list ,@(mapcar (lambda (x) `(quote ,x)) letter-defaults)) - "Choose buttons to include in MH-E letter toolbar." - :group 'mh-toolbar :set 'mh-tool-bar-letter-buttons-set - :type '(set ,@(loop for x in letter-buttons - for y in letter-docs - collect `(const :tag ,y ,x))))))) +This option has no effect if variable `mh-alias-local-users' is turned off." + :type '(choice (const :tag "Use Login" nil) + (string)) + :group 'mh-alias) -(mh-tool-bar-define - ((:folder mh-inc-folder mh-mime-save-parts mh-previous-undeleted-msg - mh-page-msg mh-next-undeleted-msg mh-delete-msg mh-refile-msg - mh-undo mh-execute-commands mh-toggle-tick mh-reply - mh-alias-grab-from-field mh-send mh-rescan-folder - mh-tool-bar-search mh-visit-folder - mh-tool-bar-customize mh-tool-bar-folder-help mh-widen) - (:letter mh-send-letter mh-compose-insertion ispell-message save-buffer - undo kill-region menu-bar-kill-ring-save yank mh-fully-kill-draft - mh-tool-bar-customize mh-tool-bar-letter-help)) - ;; Folder/Show buffer buttons - (mh-inc-folder (folder) "mail" - "Incorporate new mail in Inbox -This button runs `mh-inc-folder' which drags any -new mail into your Inbox folder.") - (mh-mime-save-parts (folder) "attach" - "Save MIME parts from this message -This button runs `mh-mime-save-parts' which saves a message's -different parts into separate files.") - (mh-previous-undeleted-msg (folder) "left_arrow" - "Go to the previous undeleted message -This button runs `mh-previous-undeleted-msg'") - (mh-page-msg (folder) "page-down" - "Page the current message forwards\nThis button runs `mh-page-msg'") - (mh-next-undeleted-msg (folder) "right_arrow" - "Go to the next undeleted message\nThe button runs `mh-next-undeleted-msg'") - (mh-delete-msg (folder) "close" - "Mark this message for deletion\nThis button runs `mh-delete-msg'") - (mh-refile-msg (folder) "refile" - "Refile this message\nThis button runs `mh-refile-msg'") - (mh-undo (folder) "undo" "Undo last operation\nThis button runs `undo'") - (mh-execute-commands (folder) "execute" - "Perform moves and deletes\nThis button runs `mh-execute-commands'") - (mh-toggle-tick (folder) "highlight" - "Toggle tick mark\nThis button runs `mh-toggle-tick'") - (mh-toggle-showing (folder) "show" - "Toggle showing message\nThis button runs `mh-toggle-showing'") - (mh-tool-bar-reply-from (folder) "reply-from" "Reply to \"from\"") - (mh-tool-bar-reply-to (folder) "reply-to" "Reply to \"to\"") - (mh-tool-bar-reply-all (folder) "reply-all" "Reply to \"all\"") - (mh-reply (folder) "mail/reply2" - "Reply to this message\nThis button runs `mh-reply'") - (mh-alias-grab-from-field (folder) "alias" - "Grab From alias\nThis button runs `mh-alias-grab-from-field'" - (mh-alias-from-has-no-alias-p)) - (mh-send (folder) "mail_compose" - "Compose new message\nThis button runs `mh-send'") - (mh-rescan-folder (folder) "rescan" - "Rescan this folder\nThis button runs `mh-rescan-folder'") - (mh-pack-folder (folder) "repack" - "Repack this folder\nThis button runs `mh-pack-folder'") - (mh-tool-bar-search (folder) "search" - "Search\nThis button runs `mh-tool-bar-search-function'") - (mh-visit-folder (folder) "fld_open" - "Visit other folder\nThis button runs `mh-visit-folder'") - ;; Letter buffer buttons - (mh-send-letter (letter) "mail_send" "Send this letter") - (mh-compose-insertion (letter) "attach" "Insert attachment") - (ispell-message (letter) "spell" "Check spelling") - (save-buffer (letter) "save" "Save current buffer to its file") - (undo (letter) "undo" "Undo last operation") - (kill-region (letter) "cut" - "Cut (kill) text in region between mark and current position") - (menu-bar-kill-ring-save (letter) "copy" - "Copy text in region between mark and current position") - (yank (letter) "paste" "Paste (yank) text cut or copied earlier") - (mh-fully-kill-draft (letter) "close" "Kill this draft") - ;; Common buttons - (mh-tool-bar-customize (folder letter) "preferences" "MH-E Preferences") - (mh-tool-bar-folder-help (folder) "help" - "Help! (general help)\nThis button runs `Info-goto-node'") - (mh-tool-bar-letter-help (letter) "help" - "Help! (general help)\nThis button runs `Info-goto-node'") - ;; Folder narrowed to sequence buttons - (mh-widen (sequence) "widen" - "Widen from the sequence\nThis button runs `mh-widen'")) +(defcustom mh-alias-passwd-gecos-comma-separator-flag t + "*Non-nil means the gecos field in the password file uses a comma separator. +In the example in `mh-alias-local-users-prefix', commas are used to separate +different values within the so-called gecos field. This is a fairly common +usage. However, in the rare case that the gecos field in your password file is +not separated by commas and whose contents may contain commas, you can turn +this option off." + :type 'boolean + :group 'mh-alias) -;;; Speedbar and folder configuration (:group 'mh-speed) - -(defcustom mh-large-folder 200 - "The number of messages that indicates a large folder. -If a folder is deemed to be large, that is the number of messages in it exceed -this value, then confirmation is needed when it is visited. Even when -`mh-show-threads-flag' is non-nil, the folder is not automatically threaded, if -it is large. If set to nil all folders are treated as if they are small." - :type '(choice (const :tag "No limit") integer) - :group 'mh-speed) - -(defcustom mh-speed-flists-interval 60 - "Time between calls to flists in seconds. -If 0, flists is not called repeatedly." - :type 'integer - :group 'mh-speed) +;;; Organizing Your Mail with Folders (:group 'mh-folder) -(defcustom mh-speed-run-flists-flag t - "Non-nil means flists is used. -If non-nil, flists is executed every `mh-speed-flists-interval' seconds to -update the display of the number of unseen and total messages in each folder. -If resources are limited, this can be set to nil and the speedbar display can -be updated manually with the \\[mh-speed-flists] command." +(defcustom mh-recenter-summary-flag nil + "*Non-nil means to recenter the summary window. +If this option is turned on, recenter the summary window when the show window +is toggled off." :type 'boolean - :group 'mh-speed) + :group 'mh-folder) -;;; Options for controlling scan listing (:group 'mh-folder) +;;; Folder Selection (:group 'mh-folder-selection) -(defcustom mh-adaptive-cmd-note-flag t - "*Non-nil means that the message number width is determined dynamically. -This is done once when a folder is first opened by running scan on the last -message of the folder. The message number for the last message is extracted -and its width calculated. This width is used when calling `mh-set-cmd-note'. - -If you prefer fixed-width message numbers, set this variable to nil and call -`mh-set-cmd-note' with the width specified by the scan format in -`mh-scan-format-file'. For example, the default width is 4, so you would use -\"(mh-set-cmd-note 4)\" if `mh-scan-format-file' were nil." - :type 'boolean - :group 'mh-folder) +(defcustom mh-default-folder-for-message-function nil + "Function to select a default folder for refiling or `Fcc'. +The current buffer is set to the message being refiled with point at the start +of the message. This function should return the default folder as a string +with a leading `+' sign. It can also return nil so that the last folder name +is used as the default, or an empty string to suppress the default entirely." + :type 'function + :group 'mh-folder-selection) (defcustom mh-default-folder-list nil - "*Alist of addresses and folders. -When refiling messages, these folders are the default that is provided if the -sender (or recipient if the Check Recipient checkbox has been selected) has -the associated address, a regexp. The first entry to match will be used, so -order them according to the wanted priority. You do not need to list your -aliases here as that lookup is already performed. + "*List of addresses and folders. +The folder name associated with the first address found in this list is used +as the default for `mh-refile-msg' and similar functions. Each element in this +list contains a `Check Recipient' item. If this item is turned on, then the +address is checked against the recipient instead of the sender. This is useful +for mailing lists. See `mh-prompt-for-refile-folder' and `mh-folder-from-address' for more information." :type '(repeat (list (regexp :tag "Address") (string :tag "Folder") (boolean :tag "Check Recipient"))) - :group 'mh-folder) + :group 'mh-folder-selection) (defcustom mh-default-folder-must-exist-flag t "*Non-nil means guessed folder name must exist to be used. -If this variable is t, then the guessed name is only used if the folder -already exists\; if the folder doesn't exist, then the last folder name used -is suggested. This is useful if you get mail from various people for whom you -have an alias, but file them all in the same project folder. +If the derived folder does not exist, and this option is on, then the last +folder name used is suggested. This is useful if you get mail from various +people for whom you have an alias, but file them all in the same project +folder. + See `mh-prompt-for-refile-folder' and `mh-folder-from-address' for more information." :type 'boolean - :group 'mh-folder) + :group 'mh-folder-selection) (defcustom mh-default-folder-prefix "" - "*Prefix used for guessed folder names. -This can be used to put folders associated with your aliases in a sub-folder -so as to not clutter your mail directory. + "*Prefix used for folder names generated from aliases. +The prefix is used to prevent clutter in your mail directory. + See `mh-prompt-for-refile-folder' and `mh-folder-from-address' for more information." :type 'string - :group 'mh-folder) - -(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." - :type 'string - :group 'mh-folder) - -(defcustom mh-inc-spool-list nil - "*Alist of alternate spool files, corresponding folders and keybindings. -Here's an example. Suppose you have subscribed to the MH-E devel mailing -list. You could filter its mail into a separate spool file named -~/mail/mh-e using Procmail and a .procmailrc entry like: - -MAILDIR=$HOME/mail #you'd better make sure it exists -:0: -* ^From mh-e-devel-admin@lists.sourceforge.net -mh-e - -If you wanted to incorporate that spool file into an MH folder called -mh-e by pressing \"I m\" in folder-mode or by `M-x mh-inc-spool-mh-e', -you would setup `mh-inc-spool-list' with an entry: - - Spool file: ~/mail/mh-e - Folder: mh-e - Key binding: m - -Then, you could also install `xbuffy' and configure an extra mailbox like so: - -box ~/mail/mh-e - title mh-e - origMode - polltime 10 - headertime 0 - command gnudoit -q '(mh-inc-spool-mh-e)' - -Note that the entry above uses the gnuserv package to communicate the -command `mh-inc-spool-mh-e' to Emacs. It will incorporate the spool file -when clicking the xbuffy box with the middle mouse button." - :type '(repeat (list (file :tag "Spool file") - (string :tag "Folder") - (character :tag "Key binding"))) - :set 'mh-inc-spool-list-set - :group 'mh-folder) + :group 'mh-folder-selection) -(defcustom mh-interpret-number-as-range-flag t - "Non-nil means interpret a number as a range. -If the variable is non-nil, and you use an integer, N, when asked for a -range to scan, then MH-E uses the range \"last:N\"." - :type 'boolean - :group 'mh-folder) - -(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]'." - :type 'string - :group 'mh-folder) + -(defcustom mh-mime-save-parts-default-directory t - "Default directory to use for `mh-mime-save-parts'. -If nil, prompt and set for next time the command is used during same session. -If t, prompt always" - :type '(choice (const :tag "Prompt the first time" nil) - (const :tag "Prompt always" t) - directory) - :group 'mh-folder) +;;; Identities (:group 'mh-identity) -(defcustom mh-print-background-flag nil - "*Non-nil means messages should be printed in the background. -WARNING: do not delete the messages until printing is finished; -otherwise, your output may be truncated." - :type 'boolean - :group 'mh-folder) +(defcustom mh-identity-list nil + "*List of identities. + +Each element consists of an identity label, and a collection of header fields +and a signature to insert if the identity is selected (see +`mh-identity-default', `mh-insert-identity' and the `Identity' menu in a +MH-Letter buffer). The `Value Menu' contains the common header fields `From' +and `Organization'. Other header fields may be added using the `Other Field' +menu item. The `Signature' menu item is used to insert a signature with +`mh-insert-signature'. The `GPG Key ID' menu item is used to specify a +different key to sign or encrypt messages." + :type '(repeat (list :tag "" + (string :tag "Label") + (repeat :tag "Add at least one item below" + (choice + (cons :tag "From Field" + (const "From") + (string :tag "Value")) + (cons :tag "Organization Field" + (const "Organization") + (string :tag "Value")) + (cons :tag "Other Field" + (string :tag "Field") + (string :tag "Value")) + (cons :tag "Attribution Verb" + (const ":attribution-verb") + (string :tag "Value")) + (cons :tag "Signature" + (const :tag "Signature" + ":signature") + (choice + (const :tag "mh-signature-file-name" + nil) + (file) + (function))) + (cons :tag "GPG Key ID" + (const :tag "GPG Key ID" + ":pgg-default-user-id") + (string :tag "Value")))))) + :set 'mh-identity-list-set + :group 'mh-identity) -(defcustom mh-recenter-summary-flag nil - "*Non-nil means to recenter the summary window. -Recenter the summary window when the show window is toggled off if non-nil." - :type 'boolean - :group 'mh-folder) +(defcustom mh-auto-fields-list nil + "List of recipients for which header lines are automatically inserted. +Each element consists of the recipient, which is a regular expression, and a +collection of header fields and identities to insert if the message is sent to +this recipient. The `Value Menu' contains the common header fields `Fcc' and +`Mail-Followup-To'. Other header fields may be added using the `Other Field' +menu item. The `Identity' menu item is used to insert entire identities with +`mh-insert-identity'." + :type `(repeat + (list :tag "" + (string :tag "Recipient") + (repeat :tag "Add at least one item below" + (choice + (cons :tag "Identity" + (const ":identity") + ,(append + '(radio) + (mapcar + (function (lambda (arg) `(const ,arg))) + (mapcar 'car mh-identity-list)))) + (cons :tag "Fcc Field" + (const "fcc") + (string :tag "Value")) + (cons :tag "Mail-Followup-To Field" + (const "Mail-Followup-To") + (string :tag "Value")) + (cons :tag "Other Field" + (string :tag "Field") + (string :tag "Value")))))) + :group 'mh-identity) -(defcustom mh-recursive-folders-flag nil - "*Non-nil means that commands which operate on folders do so recursively." +(defcustom mh-auto-fields-prompt-flag t + "*Non-nil means to prompt before sending if fields inserted. +See `mh-auto-fields-list'." :type 'boolean - :group 'mh-folder) + :group 'mh-identity) -;;; If `mh-unpropagated-sequences' becomes a defcustom, add the following tot -;;; he docstring: "Additional sequences that should not to be preserved can be -;;; specified by setting `mh-unpropagated-sequences' appropriately." XXX +(defcustom mh-identity-default nil + "Default identity to use when `mh-letter-mode' is called." + :type (append + '(radio) + (cons '(const :tag "None" nil) + (mapcar (function (lambda (arg) `(const ,arg))) + (mapcar 'car mh-identity-list)))) + :group 'mh-identity) -(defcustom mh-refile-preserves-sequences-flag t - "*Non-nil means that sequences are preserved when messages are refiled. -If this variable is non-nil and a message belonging to a sequence other than -cur or Previous-Sequence (see mh-profile 5) is refiled then it is put in the -same sequence in the destination folder." - :type 'boolean - :group 'mh-folder) +(defcustom mh-identity-handlers + '(("default" . mh-identity-handler-bottom) + ("from" . mh-identity-handler-top) + (":attribution-verb" . mh-identity-handler-attribution-verb) + (":signature" . mh-identity-handler-signature) + (":pgg-default-user-id" . mh-identity-handler-gpg-identity)) + "Handler functions for fields in `mh-identity-list'. +This is an alist of fields (strings) and handlers (functions). Strings are +lowercase. Use \":signature\" for Signature and \":pgg-default-user-id\" for +GPG Key ID. The function associated with the string \"default\" is used if no +other functions are appropriate. For this reason, don't name a header field +\"Default\"." + :type '(repeat (cons (string :tag "Field") function)) + :group 'mh-identity) -(defcustom mh-scan-format-file t - "Specifies the format file to pass to the scan program. -If t, the format string will be taken from the either `mh-scan-format-mh' -or `mh-scan-format-nmh' depending on whether MH or nmh is in use. -If nil, the default scan output will be used. + -If you customize the scan format, you may need to modify a few variables -containing regexps that MH-E uses to identify specific portions of the output. -Use `M-x apropos RET mh-scan.*regexp' to obtain a list of these variables. You -may also have to call `mh-set-cmd-note' with the width of your message -numbers. See also `mh-adaptive-cmd-note-flag'." - :type '(choice (const :tag "Use MH-E scan format" t) - (const :tag "Use default scan format" nil) - (file :tag "Specify a scan format file")) - :group 'mh-folder) +;;; Incorporating Your Mail (:group 'mh-inc) -(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." +(defcustom mh-inc-prog "inc" + "*Program to run to incorporate new mail into a folder. +Normally \"inc\". This program is relative to the `mh-progs' directory unless +it is an absolute pathname." :type 'string - :group 'mh-folder) -(make-variable-buffer-local 'mh-scan-prog) - -(defcustom mh-show-threads-flag nil - "Non-nil means new folders start in threaded mode. -Threading large number of messages can be time consuming. So if the flag is -non-nil then threading will be done only if the number of messages being -threaded is less than `mh-large-folder'." - :type 'boolean - :group 'mh-folder) - -(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." - :type '(choice (const :tag "Current" nil) - directory) - :group 'mh-folder) + :group 'mh-inc) -(defcustom mh-tick-seq 'tick - "The name of the MH tick sequence." - :type '(choice (const :tag "Disable ticking" nil) - symbol) - :group 'mh-folder) - -(defcustom mh-update-sequences-after-mh-show-flag t - "*Non-nil means `mh-update-sequence' is called from `mh-show-mode'. -If set, `mh-update-sequence' is run every time a message is shown, telling -MH or nmh that this is your current message. It's useful, for example, to -display MIME content using \"M-! mhshow RET\"" - :type 'boolean - :group 'mh-folder) +(defcustom mh-inc-spool-list nil + "*Alist of alternate spool files, corresponding folders and keybindings. +This option will be described by example. + +Suppose you have subscribed to the mh-e-devel mailing list and you use +procmail to filter its mail into `~/mail/mh-e' with the following +`.procmailrc' recipe: + + MAILDIR=$HOME/mail + :0: + * ^From mh-e-devel-admin@lists.sourceforge.net + mh-e + +If you wanted to incorporate that spool file into an MH folder called mh-e +with the \"I m\" or \\[mh-inc-spool-mh-e] commands, you would use the +following: + + Spool File: ~/mail/mh-e + Folder: mh-e + Key Binding: m + +Then, you could also install `xbuffy' and configure an extra mailbox using the +gnuserv package to run the `mh-inc-spool-mh-e' command in Emacs: + + box ~/mail/mh-e + title mh-e + origMode + polltime 10 + headertime 0 + command gnudoit -q '(mh-inc-spool-mh-e)' + +To incorporate the spool file, click the xbuffy box with the middle mouse +button." + :type '(repeat (list (file :tag "Spool File") + (string :tag "Folder") + (character :tag "Key Binding"))) + :set 'mh-inc-spool-list-set + :group 'mh-inc) -;;; Indexed searching (:group 'mh-index) +;;; Searching (:group 'mh-index) (defcustom mh-index-new-messages-folders t - "Folders searched for `mh-unseen-seq'. -If t, then `mh-inbox' is searched. If nil, all the top level folders are -searched. Otherwise the list of folders specified as strings are searched. + "Folders searched for the `unseen' sequence. +This option can be set to `Inbox' to search the `+inbox' folder or `All' to +search all of the top level folders. Otherwise, list the folders that should +be searched with the `Choose Folders' menu item. + See also `mh-recursive-folders-flag'." :group 'mh-index :type '(choice (const :tag "Inbox" t) (const :tag "All" nil) - (repeat :tag "Choose folders" (string :tag "Folder")))) + (repeat :tag "Choose Folders" (string :tag "Folder")))) (defcustom mh-index-program nil "Indexing program that MH-E shall use. -The possible choices are swish++, swish-e, mairix, namazu, glimpse, pick and -grep. By default this variable is nil which means that the programs are tried -in order and the first one found is used. +The default setting of this option is `Auto-detect' which means that MH-E will +automatically choose one of swish++, swish-e, mairix, namazu, pick and grep in +that order. If, for example, you have both swish++ and mairix installed and +you want to use mairix, then you can set this option to `mairix'. More information about setting up an indexing program to use with MH-E can be found in the documentation of `mh-index-search'." @@ -850,33 +621,34 @@ found in the documentation of `mh-index-search'." (const :tag "swish-e" swish) (const :tag "mairix" mairix) (const :tag "namazu" namazu) - (const :tag "glimpse" glimpse) (const :tag "pick" pick) (const :tag "grep" grep)) :group 'mh-index) (defcustom mh-index-ticked-messages-folders t "Folders searched for `mh-tick-seq'. -If t, then `mh-inbox' is searched. If nil, all the top level folders are -searched. Otherwise the list of folders specified as strings are searched. +This option can be set to `Inbox' to search the `+inbox' folder or `All' to +search all of the top level folders. Otherwise, list the folders that should +be searched with the `Choose Folders' menu item. + See also `mh-recursive-folders-flag'." :group 'mh-index :type '(choice (const :tag "Inbox" t) (const :tag "All" nil) - (repeat :tag "Choose folders" (string :tag "Folder")))) + (repeat :tag "Choose Folders" (string :tag "Folder")))) -;;; Spam Handling (:group 'mh-junk) +;;; Dealing with Junk Mail (:group 'mh-junk) ;; Spam fighting program chosen (defvar mh-junk-choice nil) ;; Available spam filter interfaces (defvar mh-junk-function-alist - '((bogofilter mh-bogofilter-blacklist mh-bogofilter-whitelist) - (spamprobe mh-spamprobe-blacklist mh-spamprobe-whitelist) - (spamassassin mh-spamassassin-blacklist mh-spamassassin-whitelist)) + '((spamassassin mh-spamassassin-blacklist mh-spamassassin-whitelist) + (bogofilter mh-bogofilter-blacklist mh-bogofilter-whitelist) + (spamprobe mh-spamprobe-blacklist mh-spamprobe-whitelist)) "Available choices of spam programs to use. This is an alist. For each element there are functions that blacklist a message as spam and whitelist a message incorrectly classified as spam.") @@ -894,28 +666,348 @@ bound to the new value of `mh-junk-program'. The function sets the variable finally return (car element))))) ;; User customizable variables -(defcustom mh-junk-mail-folder nil - "Folder to put spam mail in. -If nil then the spam is deleted." - :type '(choice (const :tag "Delete spam" nil) - (string :tag "Spam folder")) +(defcustom mh-junk-disposition nil + "Disposition of junk mail." + :type '(choice (const :tag "Delete Spam" nil) + (string :tag "Spam Folder")) :group 'mh-junk) (defcustom mh-junk-program nil - "Spam program that MH-E shall use. -The possible choices are bogofilter, spamprobe, and spamassassin. By default -this variable is nil which means that the programs are tried in order and the -first one found is used." - :type '(choice (const :tag "auto-detect" nil) - (const :tag "bogofilter" bogofilter) - (const :tag "spamprobe" spamprobe) - (const :tag "spamassassin" spamassassin)) + "Spam program that MH-E should use. +The default setting of this option is `Auto-detect' which means that MH-E will +automatically choose one of SpamAssassin, Bogofilter, or SpamProbe in that +order. If, for example, you have both SpamAssassin and Bogofilter installed +and you want to use BogoFilter, then you can set this option to `Bogofilter'." + :type '(choice (const :tag "Auto-detect" nil) + (const :tag "SpamAssassin" spamassassin) + (const :tag "Bogofilter" bogofilter) + (const :tag "SpamProbe" spamprobe)) :set 'mh-junk-choose :group 'mh-junk) +(defcustom mh-junk-background nil + "If on, spam programs are run in background. +By default, the programs are run in the foreground, but this can be slow when +junking large numbers of messages. If you have enough memory or don't junk +that many messages at the same time, you might try turning on this option." + :type '(choice (const :tag "Off" nil) + (const :tag "On" 0)) + :group 'mh-junk) + + + +;;; Editing a Draft (:group 'mh-letter) + +(defcustom mh-mml-method-default (if mh-gnus-pgp-support-flag "pgpmime" "none") + "Default method to use in security directives." + :type '(choice (const :tag "PGP (MIME)" "pgpmime") + (const :tag "PGP" "pgp") + (const :tag "S/MIME" "smime") + (const :tag "None" "none")) + :group 'mh-letter) + +(defcustom mh-compose-forward-as-mime-flag t + "Non-nil means that messages are forwarded as a MIME part." + :type 'boolean + :group 'mh-letter) + +(defcustom mh-compose-insertion (if (locate-library "mml") 'gnus 'mhn) + "Type of MIME message directives in messages. + +By default, this option is set to `Gnus' if it is supported. This option can +also be set manually to `mhn' if mhn directives are preferred." + :type '(choice (const :tag "Gnus" gnus) + (const :tag "mhn" mhn)) + :group 'mh-letter) + +(defcustom mh-compose-skipped-header-fields + '("From" "Organization" "References" "In-Reply-To" + "X-Face" "Face" "X-Image-URL" "X-Mailer") + "List of header fields to skip over when navigating in draft." + :type '(repeat (string :tag "Field")) + :group 'mh-letter) + +(defcustom mh-compose-space-does-completion-flag nil + "*Non-nil means that <SPC> does completion in message header." + :type 'boolean + :group 'mh-letter) + +(defcustom mh-delete-yanked-msg-window-flag nil + "*Non-nil means delete any window displaying the message. +If this option is on, yanking the current message into a draft letter with +\\<mh-letter-mode-map>\\[mh-yank-cur-msg] deletes any windows displaying the +message." + :type 'boolean + :group 'mh-letter) + +(defcustom mh-extract-from-attribution-verb "wrote:" + "*Verb to use for attribution when a message is yanked by \\<mh-letter-mode-map>\\[mh-yank-cur-msg]." + :type '(choice (const "wrote:") + (const "a écrit:") + (const "schrieb:") + (string :tag "Custom String")) + :group 'mh-letter) + +(defcustom mh-ins-buf-prefix "> " + "*String to put before each non-blank line of a yanked or inserted message. +Used when the message is inserted into an outgoing letter +by \\<mh-letter-mode-map>\\[mh-insert-letter] or \\[mh-yank-cur-msg]." + :type 'string + :group 'mh-letter) + +(defcustom mh-insert-x-mailer-flag t + "*Non-nil means append an X-Mailer field to the header." + :type 'boolean + :group 'mh-letter) + +(defcustom mh-letter-complete-function 'ispell-complete-word + "*Function to call when completing outside of address or folder fields. +By default, this is set to `ispell-complete-word'." + :type '(choice function (const nil)) + :group 'mh-letter) + +(defcustom mh-letter-fill-column 72 + "*Fill column to use in `mh-letter-mode'. +This is usually less than in other text modes because email messages get +quoted by some prefix (sometimes many times) when they are replied to, +and it's best to avoid quoted lines that span more than 80 columns." + :type 'integer + :group 'mh-letter) + +(defcustom mh-reply-show-message-flag t + "*Non-nil means the show buffer is displayed using \\<mh-letter-mode-map>\\[mh-reply]. + +The setting of this variable determines whether the MH `show-buffer' is +displayed with the current message when using `mh-reply' without a prefix +argument. Set it to nil if you already include the message automatically +in your draft using + repl: -filter repl.filter +in your ~/.mh_profile file." + :type 'boolean + :group 'mh-letter) + +(defcustom mh-signature-file-name "~/.signature" + "*Source of user's signature. + +By default, the text of your signature is taken from the file `~/.signature'. +You can read from other files by changing this option. This file may contain a +vCard in which case an attachment is added with the vCard. + +This option may also be a symbol, in which case that function is called. You +may not want a signature separator to be added for you; instead you may want +to insert one yourself. Variables that you may find useful to do this include +`mh-signature-separator' (when inserting a signature separator) and +`mh-signature-separator-regexp' (for finding said separator). The function +`mh-signature-separator-p', which reports t if the buffer contains a +separator, may be useful as well. + +The signature is inserted into your message with the command +\\<mh-letter-mode-map>\\[mh-insert-signature] or with the `mh-identity-list' +option." + :type 'file + :group 'mh-letter) + +(defcustom mh-signature-separator-flag t + "*Non-nil means a signature separator should be inserted. +It is not recommended that you change this option since various mail user +agents, including MH-E, use the separator to present the signature +differently, and to suppress the signature when replying or yanking a letter +into a draft." + :type 'boolean + :group 'mh-letter) + +(defcustom mh-x-face-file "~/.face" + "*File containing face header field to insert in outgoing mail. + +If the file starts with either of the strings `X-Face:', `Face:' or +`X-Image-URL:' then the contents are added to the message header verbatim. +Otherwise it is assumed that the file contains the value of the `X-Face:' +header field. + +The `X-Face:' header field, which is a low-resolution, black and white image, +can be generated using the `compface' command, which can be obtained from +ftp://ftp.cs.indiana.edu/pub/faces/compface/compface.tar.Z. The \"Online +X-Face Convertor\" at http://www.dairiki.org/xface/ is a useful resource for +quick conversion of images into `X-Face:' header fields. + +Use the `make-face' script (http://quimby.gnus.org/circus/face/make-face) to +convert a JPEG image to the higher resolution, color, `Face:' header field. + +The URL of any image can be used for the `X-Image-URL:' field and no +processing of the image is required. + +To prevent the setting of any of these header fields, either set +`mh-x-face-file' to nil, or simply ensure that the file defined by this option +doesn't exist." + :type 'file + :group 'mh-letter) + +(defcustom mh-yank-from-start-of-msg 'attribution + "*Controls which part of a message is yanked by \\<mh-letter-mode-map>\\[mh-yank-cur-msg]. +If t, include the entire message, with full headers. This is historically +here for use with supercite, but is now deprecated in favor of the setting +`supercite' below. + +If the symbol `body', then yank the message minus the header. + +If the symbol `supercite', include the entire message, with full headers. +This also causes the invocation of `sc-cite-original' without the setting +of `mail-citation-hook', now deprecated practice. + +If the symbol `autosupercite', do as for `supercite' automatically when +show buffer matches the message being replied-to. When this option is used, +the -noformat switch is passed to the repl program to override a -filter or +-format switch. + +If the symbol `attribution', then yank the message minus the header and add +a simple attribution line at the top. + +If the symbol `autoattrib', do as for `attribution' automatically when show +buffer matches the message being replied-to. You can make sure this is +always the case by setting `mh-reply-show-message-flag' to t (which is the +default) and optionally `mh-delete-yanked-msg-window-flag' to t as well such +that the show window is never displayed. When the `autoattrib' option is +used, the -noformat switch is passed to the repl program to override a +-filter or -format switch. + +If nil, yank only the portion of the message following the point. + +If the show buffer has a region, this variable is ignored unless its value is +one of `attribution' or `autoattrib' in which case the attribution is added +to the yanked region." + :type '(choice (const :tag "Below point" nil) + (const :tag "Without header" body) + (const :tag "Invoke supercite" supercite) + (const :tag "Invoke supercite, automatically" autosupercite) + (const :tag "Without header, with attribution" attribution) + (const :tag "Without header, with attribution, automatically" + autoattrib) + (const :tag "Entire message with headers" t)) + :group 'mh-letter) + -;;; Message display (:group 'mh-show) +;;; Ranges (:group 'mh-ranges) + +(defcustom mh-interpret-number-as-range-flag t + "Non-nil means interpret a number as a range. +If the variable is non-nil, and you use an integer, N, when asked for a +range to scan, then MH-E uses the range \"last:N\"." + :type 'boolean + :group 'mh-ranges) + + + +;;; Scan Line Formats (:group 'mh-scan-line-formats) + +(defcustom mh-adaptive-cmd-note-flag t + "*Non-nil means that the message number width is determined dynamically. +This is done once when a folder is first opened by running scan on the last +message of the folder. The message number for the last message is extracted +and its width calculated. This width is used when calling `mh-set-cmd-note'. + +If you prefer fixed-width message numbers, set this variable to nil and call +`mh-set-cmd-note' with the width specified by the scan format in +`mh-scan-format-file'. For example, the default width is 4, so you would use +\"(mh-set-cmd-note 4)\" if `mh-scan-format-file' were nil." + :type 'boolean + :group 'mh-scan-line-formats) + +(defcustom mh-scan-format-file t + "Specifies the format file to pass to the scan program. +If t, the format string will be taken from the either `mh-scan-format-mh' +or `mh-scan-format-nmh' depending on whether MH or nmh is in use. +If nil, the default scan output will be used. + +If you customize the scan format, you may need to modify a few variables +containing regexps that MH-E uses to identify specific portions of the output. +Use `M-x apropos RET mh-scan.*regexp' to obtain a list of these variables. You +may also have to call `mh-set-cmd-note' with the width of your message +numbers. See also `mh-adaptive-cmd-note-flag'." + :type '(choice (const :tag "Use MH-E scan Format" t) + (const :tag "Use Default scan Format" nil) + (file :tag "Specify a scan Format File")) + :group 'mh-scan-line-formats) + +(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." + :type 'string + :group 'mh-scan-line-formats) +(make-variable-buffer-local 'mh-scan-prog) + + + +;;; Sending Mail (:group 'mh-sending-mail) + +(defcustom mh-compose-letter-function nil + "Invoked when setting up a letter draft. +It is passed three arguments: TO recipients, SUBJECT, and CC recipients." + :type '(choice (const nil) function) + :group 'mh-sending-mail) + +(defcustom mh-compose-prompt-flag nil + "*Non-nil means prompt for header fields when composing a new draft." + :type 'boolean + :group 'mh-sending-mail) + +(defcustom mh-forward-subject-format "%s: %s" + "*Format to generate the Subject: line contents for a forwarded message. +The two string arguments to the format are the sender of the original +message and the original subject line." + :type 'string + :group 'mh-sending-mail) + +(defcustom mh-reply-default-reply-to nil + "*Sets the person or persons to whom a reply will be sent. +If nil, prompt for recipient. If non-nil, then \\<mh-folder-mode-map>`\\[mh-reply]' will use this +value and it should be one of \"from\", \"to\", \"cc\", or \"all\". +The values \"cc\" and \"all\" do the same thing." + :type '(choice (const :tag "Prompt" nil) + (const "from") (const "to") + (const "cc") (const "all")) + :group 'mh-sending-mail) + + + +;;; Sequences (:group 'mh-sequences) + +;;; If `mh-unpropagated-sequences' becomes a defcustom, add the following to +;;; the docstring: "Additional sequences that should not to be preserved can be +;;; specified by setting `mh-unpropagated-sequences' appropriately." XXX + +(defcustom mh-refile-preserves-sequences-flag t + "*Non-nil means that sequences are preserved when messages are refiled. +If this variable is non-nil and a message belonging to a sequence other than +cur or Previous-Sequence (see mh-profile 5) is refiled then it is put in the +same sequence in the destination folder." + :type 'boolean + :group 'mh-sequences) + +(defcustom mh-tick-seq 'tick + "The name of the MH sequence for ticked messages. +You would change this option if you already use the `tick' sequence for your +own use. You can also disable all of the ticking functions by choosing the +`Disable Ticking' item but there isn't much advantage to that." + :type '(choice (const :tag "Disable Ticking" nil) + symbol) + :group 'mh-sequences) + +(defcustom mh-update-sequences-after-mh-show-flag t + "*Non-nil means flush MH sequences to disk after message is shown. +Three sequences are maintained internally by MH-E and pushed out to MH when a +message is shown. They include the sequence specified by your +`Unseen-Sequence:' profile entry, `cur', and the sequence listed by +the `mh-tick-seq' option which is `tick' by default. +If you do not like this behavior, set this option to nil. You can then update +the state manually with the \\<mh-folder-mode-map>`\\[mh-execute-commands]', `\\[mh-quit]', or `\\[mh-update-sequences]' commands." + :type 'boolean + :group 'mh-sequences) + + + +;;; Reading Your Mail (:group 'mh-show) (defcustom mh-bury-show-buffer-flag t "*Non-nil means that the displayed show buffer for a folder is buried." @@ -923,10 +1015,11 @@ first one found is used." :group 'mh-show) (defcustom mh-clean-message-header-flag t - "*Non-nil means clean headers of messages that are displayed or inserted. -The variable `mh-invisible-headers' if set determines the header fields that -are displayed. If it isn't set, then the variable `mh-invisible-headers' -determines the header fields that are removed." + "*Non-nil means remove extraneous header fields. +The header fields listed in the `mh-invisible-header-fields-default' option +are hidden, although you can check off any field that you would like to see. +Header fields that you would like to hide that aren't listed can be added to +the `mh-invisible-header-fields' option." :type 'boolean :group 'mh-show) @@ -960,19 +1053,28 @@ question." :type 'boolean :group 'mh-show) -(defcustom mh-fetch-x-image-url nil - "Control fetching of X-Image-URL header field image. -This setting only has effect if `mh-show-use-xface-flag' is non-nil. +(defcustom mh-fetch-x-image-url 'ask + "*Control fetching of `X-Image-URL:' header field image. +If set to \"Always fetch\" (t), the image is always fetched. You probably want +to avoid this setting for privacy and DOS (denial of service) reasons. For +example, fetching a URL can tip off a spammer that you've read his email. +Someone may also flood your network and fill your disk drive by sending a +torrent of messages, each specifying a unique URL to a very large file. + +If set to \"Ask before fetching\" ('ask), you are prompted before the image is +fetched. MH-E will remember your reply and will either use the already fetched +image the next time the same URL is encountered or silently skip it if you +didn't fetch it the first time. This is the default. + +If set to \"Never fetch\" (nil), images are never fetched and only displayed +if they are already present in the cache. -If set to t, the image is fetched. +The cache of images is found in the directory `.mhe-x-image-cache' within your +MH directory. To see how you can add your own face to the `From:' field, see +`mh-x-face-file'. -If set to 'ask, the user is prompted before the image is fetched. MH-E will -remember your reply and will either use the already fetched image the next time -the same URL is encountered or silently skip it if you didn't fetch it the -first time. +This setting only has effect if `mh-show-use-xface-flag' is non-nil." -If set to nil, the default, images are not fetched and only displayed if they -are already present in the cache." :type '(choice (const :tag "Always fetch" t) (const :tag "Ask before fetching" ask) (const :tag "Never fetch" nil)) @@ -1002,28 +1104,8 @@ The gnus method uses a different color for each indentation." (const :tag "Don't fontify" nil)) :group 'mh-show) -(defvar mh-invisible-headers nil - "*Regexp matching lines in a message header that are not to be shown. -Customize the variable `mh-invisible-header-fields' to generate this variable; -It will in turn automatically use the function `mh-invisible-headers' to -generate this variable. -If the variable `mh-visible-headers' is non-nil, it is used instead to specify -what to keep.") - -(defun mh-invisible-headers () - "Make or remake the variable `mh-invisible-headers'. -Done using `mh-invisible-header-fields' as input." - (if mh-invisible-header-fields - (setq mh-invisible-headers - (concat - "^" - (let ((max-specpdl-size 1000) ;workaround for insufficient default - (fields mh-invisible-header-fields)) - (regexp-opt fields t)))) - (setq mh-invisible-headers nil))) - ;; Keep fields alphabetized. Mention source, if known. -(defcustom mh-invisible-header-fields +(defvar mh-invisible-header-fields-internal '("Approved:" "Autoforwarded:" "Bestservhost:" @@ -1053,12 +1135,13 @@ Done using `mh-invisible-header-fields' as input." "Old-Return-Path:" "Original-Encoded-Information-Types:" ; X400 "Original-Lines:" ; mail to news - "Original-Newsgroups:" ; mail to news "Original-NNTP-" ; mail to news + "Original-Newsgroups:" ; mail to news "Original-Path:" ; mail to news "Original-Received:" ; mail to news "Original-To:" ; mail to news "Original-X-" ; mail to news + "Originator:" "P1-Content-Type:" ; X400 "P1-Message-Id:" ; X400 "P1-Recipient:" ; X400 @@ -1074,13 +1157,17 @@ Done using `mh-invisible-header-fields' as input." "Return-Path:" ; RFC 822 "Sensitivity:" ; MS Outlook "Status:" ; sendmail + "Thread-" "Ua-Content-Id:" ; X400 ;; "User-Agent:" ; Similar to X-Mailer, so display it. "Via:" ; MH "X-Abuse-Info:" + "X-Abuse-and-DMCA-" "X-Accept-Language:" "X-Accept-Language:" ; Netscape/Mozilla "X-Ack:" + "X-Administrivia-To:" + "X-AntiAbuse:" ; cPanel "X-Apparently-From:" ; MS Outlook "X-Apparently-To:" ; Egroups/yahoogroups mailing list manager "X-Authentication-Warning:" ; sendmail @@ -1088,13 +1175,18 @@ Done using `mh-invisible-header-fields' as input." "X-Bogosity:" ; bogofilter "X-Complaints-To:" "X-Cron-Env:" + "X-DMCA" "X-Delivered" + "X-ELNK-Trace:" ; Earthlink mailer + "X-Envelope-Date:" ; GNU mailutils + "X-Envelope-From:" "X-Envelope-Sender:" "X-Envelope-To:" "X-Face:" "X-Folder:" ; Spam "X-From-Line" "X-Gnus-Mail-Source:" ; gnus + "X-Greylist:" ; milter-greylist-1.2.1 "X-Habeas-SWE-1:" ; Spam "X-Habeas-SWE-2:" ; Spam "X-Habeas-SWE-3:" ; Spam @@ -1104,27 +1196,35 @@ Done using `mh-invisible-header-fields' as input." "X-Habeas-SWE-7:" ; Spam "X-Habeas-SWE-8:" ; Spam "X-Habeas-SWE-9:" ; Spam + "X-Image-URL:" ; URL equivalent of X-Face and Face "X-Info:" ; NTMail "X-Juno-" ; Juno "X-List-Host:" ; Unknown mailing list managers "X-List-Subscribe:" ; Unknown mailing list managers "X-List-Unsubscribe:" ; Unknown mailing list managers + "X-Listprocessor-" ; ListProc(tm) by CREN "X-Listserver:" ; Unknown mailing list managers "X-Loop:" ; Unknown mailing list managers + "X-MHE-Checksum" ; Checksum added during index search "X-MIME-Autoconverted:" ; sendmail "X-MIMETrack:" - "X-MS-TNEF-Correlator:" ; MS Outlook + "X-Mms-" ; T-Mobile pictures + "X-MS-" ; MS Outlook + "X-MailScanner" ; ListProc(tm) by CREN "X-Mailing-List:" ; Unknown mailing list managers "X-Mailman-Version:" ; Mailman mailing list manager "X-Majordomo:" ; Majordomo mailing list manager "X-Message-Id" - "X-MHE-Checksum" ; Checksum added during index search + "X-MessageWall-Score:" ; Unknown mailing list manager, AUC TeX "X-MimeOLE:" ; MS Outlook "X-Mozilla-Status:" ; Netscape/Mozilla "X-Msmail-" ; MS Outlook + "X-NAI-Spam-" ; Network Associates Inc. SpamKiller "X-News:" ; News "X-No-Archive:" "X-Notes-Item:" ; Lotus Notes Domino structured header + "X-OperatingSystem:" + ;;"X-Operator:" ; Similar to X-Mailer, so display it "X-Orcl-Content-Type:" "X-Original-Complaints-To:" "X-Original-Date:" ; SourceForge mailing list manager @@ -1132,8 +1232,10 @@ Done using `mh-invisible-header-fields' as input." "X-Original-Trace:" "X-OriginalArrivalTime:" ; Hotmail "X-Originating-IP:" ; Hotmail + "X-Postfilter:" "X-Priority:" ; MS Outlook "X-Qotd-" ; User added + "X-RM" "X-Received-Date:" "X-Received:" "X-Request-" @@ -1141,38 +1243,108 @@ Done using `mh-invisible-header-fields' as input." "X-SBNote:" ; Spam "X-SBPass:" ; Spam "X-SBRule:" ; Spam + "X-SMTP-" "X-Scanned-By" "X-Sender:" "X-Server-Date:" "X-Server-Uuid:" "X-Sieve:" ; Sieve filtering - "X-Spam-Checker-Version:" ; Spamassassin - "X-Spam-Level:" ; Spamassassin - "X-Spam-Score:" ; Spamassassin - "X-Spam-Status:" ; Spamassassin + "X-Source" + "X-Spam-" ; Spamassassin "X-SpamBouncer:" ; Spam + "X-Status" + "X-Submissions-To:" + "X-Telecom-Digest" "X-Trace:" + "X-UID" "X-UIDL:" "X-UserInfo1:" "X-VSMLoop:" ; NTMail "X-Vms-To:" + "X-WebTV-Signature:" "X-Wss-Id:" ; Worldtalk gateways + "X-Yahoo" "X-eGroups-" ; Egroups/yahoogroups mailing list manager "X-pgp:" "X-submission-address:" "X400-" ; X400 "Xref:") -"*List of header fields that are not to be shown. -Regexps are not allowed. Unique fields should have a \":\" suffix; otherwise, -the element can be used to render invisible an entire class of fields that -start with the same prefix. -This variable is ignored if the variable `mh-visible-headers' is set." + "List of default header fields that are not to be shown. +Do not alter this variable directly. Instead, add entries from here that you +would like to be displayed in `mh-invisible-header-fields-default' +and add entries to hide in `mh-invisible-header-fields'.") + +(defvar mh-invisible-header-fields-compiled nil + "*Regexp matching lines in a message header that are not to be shown. +Do not alter this variable directly. Instead, customize +`mh-invisible-header-fields-default' checking for fields normally +hidden that you wish to display, and add extra entries to hide in +`mh-invisible-header-fields'.") + +(defun mh-invisible-headers () + "Make or remake the variable `mh-invisible-header-fields-compiled'. +Done using `mh-invisible-header-fields-internal' as input, from which entries +from `mh-invisible-header-fields-default' are removed and entries +from `mh-invisible-header-fields' are added." + (let ((fields mh-invisible-header-fields-internal)) + (when mh-invisible-header-fields-default + ;; Remove entries from `mh-invisible-header-fields-default' + (setq fields + (loop for x in fields + unless (member x mh-invisible-header-fields-default) + collect x))) + (when (and (boundp 'mh-invisible-header-fields) + mh-invisible-header-fields) + (dolist (x mh-invisible-header-fields) + (unless (member x fields) (setq fields (cons x fields))))) + (if fields + (setq mh-invisible-header-fields-compiled + (concat + "^" + ;; workaround for insufficient default + (let ((max-specpdl-size 1000)) + (regexp-opt fields t)))) + (setq mh-invisible-header-fields-compiled nil)))) + +(defcustom mh-invisible-header-fields-default nil + "*List of hidden header fields. +The header fields listed in this option are hidden, although you can check off +any field that you would like to see. Header fields that you would like to +hide that aren't listed can be added to the `mh-invisible-header-fields' +option. + +See also `mh-clean-message-header-flag'." + :type `(set ,@(mapcar (lambda (x) `(const ,x)) + mh-invisible-header-fields-internal)) + :set (lambda (symbol value) + (set-default symbol value) + (mh-invisible-headers)) + :group 'mh-show) + +(defcustom mh-invisible-header-fields nil + "*Additional header fields to hide. +Header fields that you would like to hide that aren't listed in +`mh-invisible-header-fields-default' can be added to this option with a couple +of caveats. Regular expressions are not allowed. Unique fields should have a +`:' suffix; otherwise, the element can be used to render invisible an entire +class of fields that start with the same prefix. + +See also `mh-clean-message-header-flag'." + :type '(repeat (string :tag "Header field")) :set (lambda (symbol value) (set-default symbol value) (mh-invisible-headers)) :group 'mh-show) +(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]'." + :type 'string + :group 'mh-show) + (defcustom mh-max-inline-image-height nil "*Maximum inline image height if Content-Disposition is not present. If nil, image will be displayed if its height is smaller than the height of @@ -1187,6 +1359,27 @@ window." :type '(choice (const nil) integer) :group 'mh-show) +(defcustom mh-mime-save-parts-default-directory t + "Default directory to use for `mh-mime-save-parts'. +If nil, prompt and set for next time the command is used during same session. +If t, prompt always" + :type '(choice (const :tag "Prompt the first time" nil) + (const :tag "Prompt always" t) + directory) + :group 'mh-show) + +(defcustom mh-print-background-flag nil + "*Non-nil means messages should be printed in the background. +WARNING: do not delete the messages until printing is finished; +otherwise, your output may be truncated." + :type 'boolean + :group 'mh-show) + +(defcustom mh-recursive-folders-flag nil + "*Non-nil means that commands which operate on folders do so recursively." + :type 'boolean + :group 'mh-show) + (defcustom mh-show-maximum-size 0 "*Maximum size of message (in bytes) to display automatically. Provides an opportunity to skip over large messages which may be slow to load. @@ -1194,6 +1387,14 @@ Use a value of 0 to display all messages automatically regardless of size." :type 'integer :group 'mh-show) +(defcustom mh-show-threads-flag nil + "Non-nil means new folders start in threaded mode. +Threading large number of messages can be time consuming. So if the flag is +non-nil then threading will be done only if the number of messages being +threaded is less than `mh-large-folder'." + :type 'boolean + :group 'mh-show) + ;; Use goto-addr if it was already loaded (which probably sets this ;; variable to t), or if this variable is otherwise set to t. (defcustom mh-show-use-goto-addr-flag (and (boundp 'goto-address-highlight-p) @@ -1205,80 +1406,55 @@ The `goto-addr' module is used." (defcustom mh-show-use-xface-flag (>= emacs-major-version 21) "*Non-nil means display face images in `mh-show-mode'. -This flag controls the display of three kinds of faces. - -The first is the traditional X-Face header field. For GNU Emacs 21 -and above, the `uncompface' binary is required to be in the execute -PATH for the display of X-Face images. It can be obtained from -ftp://ftp.cs.indiana.edu/pub/faces/compface/compface.tar.Z. - -If the XEmacs you are using has internal support for X-Face images, then MH-E -will display X-Face images in XEmacs \"out of the box\". Even if you don't have -X-Face support compiled into your XEmacs, you can still see the X-Face images -in MH-E with the aid of an external x-face package and `uncompface'. It is -available from ftp://ftp.jpl.org/pub/elisp/. Download it, put its files in the -`load-path' and MH-E will invoke it automatically. - -Second, MH-E supports the display of the Gnus-specific Face -header field in GNU Emacs >= 21 and XEmacs. No external packages -are required. More information about the Face header can be found -at: http://quimby.gnus.org/circus/face/. - -Finally, MH-E can also display images from the X-Image-URL header field. The -display of the images requires the `wget' program, available from -http://www.gnu.org/software/wget/wget.html, to fetch the image and the -`convert' program from the ImageMagick suite, available from + +MH-E can display the content of `Face:', `X-Face:', and `X-Image-URL:' header +fields. If any of these fields occur in the header of your message, the +sender's face will appear in the `From:' header field. If more than one of +these fields appear, then the first field found in the order `Face:', +`X-Face:', and `X-Image-URL:' will be used. Note that versions of GNU Emacs +prior to 21.1 don't support the display of inline images, so face images are +not displayed in these versions. + +The option `mh-show-use-xface-flag' is used to turn this feature on and off. +This feature will be turned on by default if your system supports it. + +The first header field used, if present, is the Gnus-specific `Face:' field. +The `Face:' field appeared in GNU Emacs 21 and XEmacs. For more information, +see http://quimby.gnus.org/circus/face/. Next is the traditional `X-Face:' +header field. The display of this field requires the `uncompface' program +which can be obtained from +ftp://ftp.cs.indiana.edu/pub/faces/compface/compface.tar.Z. Recent versions of +XEmacs have internal support for `X-Face:' images. If your version of XEmacs +does not, then you'll need both `uncompface' and the x-face package which is +available at ftp://ftp.jpl.org/pub/elisp/. + +Finally, MH-E will display images referenced by the `X-Image-URL:' header +field if neither the `Face:' nor the `X-Face:' fields are present. The display +of the images requires `wget' (available from +http://www.gnu.org/software/wget/wget.html), `fetch', or `curl' to fetch the +image and the `convert' program from the ImageMagick suite, available from http://www.imagemagick.org/. Of the three header fields this is the most efficient in terms of network usage since the image doesn't need to be -transmitted with every single mail. However its display needs the recipient to -fetch a URL and this can be misused. So it is disabled by default. It can be -enabled by customizing `mh-fetch-x-image-url'. Setting that to ask for -confirmation before fetching seems like a good choice. +transmitted with every single mail. -Versions of GNU Emacs prior to 21.1 don't support the display of -inline images. So face images are not displayed in these versions." +The option `mh-fetch-x-image-url' controls the fetching of the `X-Image-URL:' +header field image." :type 'boolean :group 'mh-show) +(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." + :type '(choice (const :tag "Current" nil) + directory) + :group 'mh-show) + (defcustom mh-summary-height nil "*Number of lines in MH-Folder window (including the mode line)." :type '(choice (const :tag "Automatic" nil) (integer :tag "Fixed sized")) :group 'mh-show) -(defvar mh-visible-headers nil - "*Regexp matching lines in a message header that are to be shown. -Customize the variable `mh-visible-header-fields' to generate this variable; -It will in turn automatically use the function `mh-visible-headers' to -generate this variable. -Only used if `mh-clean-message-header-flag' is non-nil. Setting it overrides -the variable `mh-invisible-headers'.") - -(defun mh-visible-headers () - "Make or remake the variable `mh-visible-headers'. -Done using `mh-visible-header-fields' as input." - (if mh-visible-header-fields - (setq mh-visible-headers - (concat - "^" - (let ((max-specpdl-size 1000) ;workaround for insufficient default - (fields mh-visible-header-fields)) - (regexp-opt fields t)))) - (setq mh-visible-headers nil))) - -(defcustom mh-visible-header-fields nil -"*List of header fields that are to be shown. -Regexps are not allowed. Unique fields should have a \":\" suffix; otherwise, -the element can be used to render visible an entire class of fields that -start with the same prefix. -Only used if `mh-clean-message-header-flag' is non-nil. -Setting it overrides the variable `mh-invisible-headers'." - :type '(repeat (string :tag "Header field")) - :set (lambda (symbol value) - (set-default symbol value) - (mh-visible-headers)) - :group 'mh-show) - (defcustom mhl-formfile nil "*Name of format file to be used by mhl to show and print messages. A value of t means use the default format file. @@ -1292,387 +1468,489 @@ the message continues to conform to RFC 822 and MH-E can parse the headers." -;;; Composing messages (:group 'mh-letter) - -(defcustom mh-compose-insertion (if (locate-library "mml") 'gnus 'mhn) - "Use either 'gnus or 'mhn to insert MIME message directives in messages." - :type '(choice (const :tag "Use Gnus" gnus) - (const :tag "Use mhn" mhn)) - :group 'mh-letter) - -(defcustom mh-compose-letter-function nil - "Invoked when setting up a letter draft. -It is passed three arguments: TO recipients, SUBJECT, and CC recipients." - :type '(choice (const nil) function) - :group 'mh-letter) +;;; The Speedbar (:group 'mh-speed) -(defcustom mh-compose-prompt-flag nil - "*Non-nil means prompt for header fields when composing a new draft." - :type 'boolean - :group 'mh-letter) - -(defcustom mh-compose-skipped-header-fields - '("from" "organization" "references" "in-reply-to" "x-face" "face" - "x-mailer") - "List of header fields to skip over when navigating in draft." - :type '(repeat (string :tag "Field")) - :group 'mh-letter) - -(defcustom mh-compose-space-does-completion-flag nil - "*Non-nil means that SPACE does completion in message header." - :type 'boolean - :group 'mh-letter) - -(defcustom mh-delete-yanked-msg-window-flag nil - "*Non-nil means delete any window displaying the message. -Controls window display when a message is yanked by \\<mh-letter-mode-map>\\[mh-yank-cur-msg]. -If non-nil, yanking the current message into a draft letter deletes any -windows displaying the message." - :type 'boolean - :group 'mh-letter) - -(defcustom mh-extract-from-attribution-verb "wrote:" - "*Verb to use for attribution when a message is yanked by \\<mh-letter-mode-map>\\[mh-yank-cur-msg]." - :type '(choice (const "wrote:") - (const "a écrit :") - (string :tag "Custom string")) - :group 'mh-letter) - -(defcustom mh-forward-subject-format "%s: %s" - "*Format to generate the Subject: line contents for a forwarded message. -The two string arguments to the format are the sender of the original -message and the original subject line." - :type 'string - :group 'mh-letter) - -(defcustom mh-ins-buf-prefix "> " - "*String to put before each non-blank line of a yanked or inserted message. -\\<mh-letter-mode-map>Used when the message is inserted into an outgoing letter -by \\[mh-insert-letter] or \\[mh-yank-cur-msg]." - :type 'string - :group 'mh-letter) - -(defcustom mh-insert-x-mailer-flag t - "*Non-nil means append an X-Mailer field to the header." - :type 'boolean - :group 'mh-letter) - -(defcustom mh-letter-complete-function 'ispell-complete-word - "*Function to call when completing outside of fields specific to aliases." - :type '(choice function (const nil)) - :group 'mh-letter) +(defcustom mh-large-folder 200 + "The number of messages that indicates a large folder. +If a folder is deemed to be large, that is the number of messages in it exceed +this value, then confirmation is needed when it is visited. Even when +`mh-show-threads-flag' is non-nil, the folder is not automatically threaded, if +it is large. If set to nil all folders are treated as if they are small." + :type '(choice (const :tag "No limit") integer) + :group 'mh-speed) -(defcustom mh-letter-fill-column 72 - "*Fill column to use in `mh-letter-mode'. -This is usually less than in other text modes because email messages get -quoted by some prefix (sometimes many times) when they are replied to, -and it's best to avoid quoted lines that span more than 80 columns." +(defcustom mh-speed-flists-interval 60 + "Time between calls to flists in seconds. +If 0, flists is not called repeatedly." :type 'integer - :group 'mh-letter) - -(defcustom mh-reply-default-reply-to nil - "*Sets the person or persons to whom a reply will be sent. -If nil, prompt for recipient. If non-nil, then \\<mh-folder-mode-map>`\\[mh-reply]' will use this -value and it should be one of \"from\", \"to\", \"cc\", or \"all\". -The values \"cc\" and \"all\" do the same thing." - :type '(choice (const :tag "Prompt" nil) - (const "from") (const "to") - (const "cc") (const "all")) - :group 'mh-letter) - -(defcustom mh-reply-show-message-flag t - "*Non-nil means the show buffer is displayed using \\<mh-letter-mode-map>\\[mh-reply]. + :group 'mh-speed) -The setting of this variable determines whether the MH `show-buffer' is -displayed with the current message when using `mh-reply' without a prefix -argument. Set it to nil if you already include the message automatically -in your draft using - repl: -filter repl.filter -in your ~/.mh_profile file." +(defcustom mh-speed-run-flists-flag t + "Non-nil means flists is used. +If non-nil, flists is executed every `mh-speed-flists-interval' seconds to +update the display of the number of unseen and total messages in each folder. +If resources are limited, this can be set to nil and the speedbar display can +be updated manually with the \\[mh-speed-flists] command." :type 'boolean - :group 'mh-letter) - -(defcustom mh-signature-file-name "~/.signature" - "*Name of file containing the user's signature. -Inserted into message by \\<mh-letter-mode-map>\\[mh-insert-signature]." - :type 'file - :group 'mh-letter) - -(defcustom mh-x-face-file "~/.face" - "*File containing X-Face or Face header field to insert in outgoing mail. - -If the file starts with either of the strings \"X-Face: \", \"Face: \" or -\"X-Image-URL: \" then it is assumed to contain the whole field and is added to -the message header verbatim. Otherwise it is assumed that the file contains the -value of the X-Face header field. - -X-Face header fields can be generated using `compface', which can be obtained -from ftp://ftp.cs.indiana.edu/pub/faces/compface/compface.tar.Z. The \"Online -X-Face Convertor\" at http://www.dairiki.org/xface/ is a useful resource for -quick conversion of images into X-Face header fields. - -There is a `make-face' script that converts a jpeg image to a Face header -field at http://quimby.gnus.org/circus/face/make-face. - -The URL of any image can be used for the X-Image-URL field and no processing -of the image is required. + :group 'mh-speed) -If nil, or the file does not exist, nothing is added to the message header." - :type 'file - :group 'mh-letter) + -(defcustom mh-yank-from-start-of-msg 'attribution - "*Controls which part of a message is yanked by \\<mh-letter-mode-map>\\[mh-yank-cur-msg]. -If t, include the entire message, with full headers. This is historically -here for use with supercite, but is now deprecated in favor of the setting -`supercite' below. +;;; The Toolbar (:group 'mh-toolbar) -If the symbol `body', then yank the message minus the header. +(defcustom mh-tool-bar-search-function 'mh-search-folder + "*Function called by the tool-bar search button. +See `mh-search-folder' and `mh-index-search' for details." + :type '(choice (const mh-search-folder) + (const mh-index-search) + (function :tag "Other function")) + :group 'mh-toolbar) -If the symbol `supercite', include the entire message, with full headers. -This also causes the invocation of `sc-cite-original' without the setting -of `mail-citation-hook', now deprecated practice. +;; Functions called from the tool bar +(defun mh-tool-bar-search (&optional arg) + "Interactively call `mh-tool-bar-search-function'. +Optional argument ARG is not used." + (interactive "P") + (call-interactively mh-tool-bar-search-function)) -If the symbol `autosupercite', do as for `supercite' automatically when -show buffer matches the message being replied-to. When this option is used, -the -noformat switch is passed to the repl program to override a -filter or --format switch. +(defun mh-tool-bar-customize () + "Call `mh-customize' from the toolbar." + (interactive) + (mh-customize t)) -If the symbol `attribution', then yank the message minus the header and add -a simple attribution line at the top. +(defun mh-tool-bar-folder-help () + "Visit \"(mh-e)Top\"." + (interactive) + (Info-goto-node "(mh-e)Top") + (delete-other-windows)) -If the symbol `autoattrib', do as for `attribution' automatically when show -buffer matches the message being replied-to. You can make sure this is -always the case by setting `mh-reply-show-message-flag' to t (which is the -default) and optionally `mh-delete-yanked-msg-window-flag' to t as well such -that the show window is never displayed. When the `autoattrib' option is -used, the -noformat switch is passed to the repl program to override a --filter or -format switch. +(defun mh-tool-bar-letter-help () + "Visit \"(mh-e)Draft Editing\"." + (interactive) + (Info-goto-node "(mh-e)Draft Editing") + (delete-other-windows)) -If nil, yank only the portion of the message following the point. +(defmacro mh-tool-bar-reply-generator (function recipient folder-buffer-flag) + "Generate FUNCTION that replies to RECIPIENT. +If FOLDER-BUFFER-FLAG is nil then the function generated +When INCLUDE-FLAG is non-nil, include message body being replied to." + `(defun ,function (&optional arg) + ,(format "Reply to \"%s\".\nWhen ARG is non-nil include message in reply." + recipient) + (interactive "P") + ,(if folder-buffer-flag nil '(set-buffer mh-show-folder-buffer)) + (mh-reply (mh-get-msg-num nil) ,recipient arg))) -If the show buffer has a region, this variable is ignored unless its value is -one of `attribution' or `autoattrib' in which case the attribution is added -to the yanked region." - :type '(choice (const :tag "Below point" nil) - (const :tag "Without header" body) - (const :tag "Invoke supercite" supercite) - (const :tag "Invoke supercite, automatically" autosupercite) - (const :tag "Without header, with attribution" attribution) - (const :tag "Without header, with attribution, automatically" - autoattrib) - (const :tag "Entire message with headers" t)) - :group 'mh-letter) +(mh-tool-bar-reply-generator mh-tool-bar-reply-from "from" t) +(mh-tool-bar-reply-generator mh-show-tool-bar-reply-from "from" nil) +(mh-tool-bar-reply-generator mh-tool-bar-reply-to "to" t) +(mh-tool-bar-reply-generator mh-show-tool-bar-reply-to "to" nil) +(mh-tool-bar-reply-generator mh-tool-bar-reply-all "all" t) +(mh-tool-bar-reply-generator mh-show-tool-bar-reply-all "all" nil) - +;; XEmacs has a couple of extra customizations... +(mh-do-in-xemacs + (defcustom mh-xemacs-use-toolbar-flag (if (and (featurep 'toolbar) + (featurep 'xpm) + (device-on-window-system-p)) + t + nil) + "*If non-nil, use toolbar. -;;; Alias handling (:group 'mh-alias) +This will default to t if you are in an environment that supports +toolbars and xpm." + :type 'boolean + :group 'mh-toolbar) -(defcustom mh-alias-completion-ignore-case-flag t - "*Non-nil means don't consider case significant in MH alias completion. -This is the default in plain MH, so it is the default here as well. It -can be useful to set this to t if, for example, you use lowercase -aliases for people and uppercase for mailing lists." - :type 'boolean - :group 'mh-alias) + (defcustom mh-xemacs-toolbar-position (if mh-xemacs-use-toolbar-flag + 'default + nil) + "*Where to put the toolbar. -(defcustom mh-alias-expand-aliases-flag nil - "*Non-nil means to expand aliases entered in the minibuffer. -In other words, aliases entered in the minibuffer will be expanded to the full -address in the message draft. By default, this expansion is not performed." - :type 'boolean - :group 'mh-alias) +Valid non-nil values are \"default\", \"top\", \"bottom\", \"left\", +\"right\". These match the four edges of the frame, with \"default\" +meaning \"use the same position as the default-toolbar\". -(defcustom mh-alias-flash-on-comma t - "*Specify whether to flash or warn on translation. -When a [comma] is pressed while entering aliases or addresses, setting this -variable to the following values has the listed effects: -t Flash alias translation but don't warn if there is no translation. -1 Flash alias translation and warn if there is no translation. -nil Do not flash alias translation nor warn if there is no translation." - :type '(choice (const :tag "Flash but don't warn if no translation" t) - (const :tag "Flash and warn if no translation" 1) - (const :tag "Don't flash nor warn if no translation" nil)) - :group 'mh-alias) +A nil value means do not use a toolbar. -(defcustom mh-alias-insert-file nil - "*Filename to use to store new MH-E aliases. -This variable can also be a list of filenames, in which case MH-E will prompt -for one of them. If nil, the default, then MH-E will use the first file found -in the \"AliasFile\" component of the MH profile." - :type '(choice (const :tag "Use AliasFile MH profile component" nil) - (file :tag "Alias file") - (repeat :tag "List of alias files" file)) - :group 'mh-alias) +If this variable is set to anything other than \"default\" and the +default-toolbar has a different positional setting from the value of +this variable, then two toolbars will be displayed. The MH-E toolbar +and the default-toolbar." + :type '(radio (const :tag "Same position as the \"default-toolbar\"" + :value default) + (const :tag "Along the top edge of the frame" + :value top) + (const :tag "Along the bottom edge of the frame" + :value bottom) + (const :tag "Along the left edge of the frame" + :value left) + (const :tag "Along the right edge of the frame" + :value right) + (const :tag "Don't use a toolbar" nil)) + :group 'mh-toolbar)) -(defcustom mh-alias-insertion-location 'sorted - "Specifies where new aliases are entered in alias files. -Options are sorted alphabetically, at the top of the file or at the bottom." - :type '(choice (const :tag "Sorted alphabetically" sorted) - (const :tag "At the top of file" top) - (const :tag "At the bottom of file" bottom)) - :group 'mh-alias) +(defmacro mh-tool-bar-define (defaults &rest buttons) + "Define a tool bar for MH-E. +DEFAULTS is the list of buttons that are present by default. It is a list of +lists where the sublists are of the following form: -(defcustom mh-alias-local-users t - "*If t, local users are completed in MH-E To: and Cc: prompts. + (:KEYWORD FUNC1 FUNC2 FUNC3 ...) -Users with a userid greater than some magic number (usually 200) are available -for completion. +Here :KEYWORD is one of :folder or :letter. If it is :folder then the default +buttons in the folder and show mode buffers are being specified. If it is +:letter then the default buttons in the letter mode are listed. FUNC1, FUNC2, +FUNC3, ... are the names of the functions that the buttons would execute. -If you set this variable to a string, it will be executed to generate a -password file. A value of \"ypcat passwd\" is helpful if NIS is in use." - :type '(choice (boolean) (string)) - :group 'mh-alias) +Each element of BUTTONS is a list consisting of four mandatory items and one +optional item as follows: -(defcustom mh-alias-local-users-prefix "local." - "*String prepended to the real names of users from the passwd file. -If nil, use the username string unmodified instead of the real name from -the gecos field of the passwd file. + (FUNCTION MODES ICON DOC &optional ENABLE-EXPR) -For example, given the following passwd file line: +where, - psg:x:1000:1000:Peter S Galbraith,,,:/home/psg:/bin/tcsh + FUNCTION is the name of the function that will be executed when the button + is clicked. -here are the derived aliases for different values of this variable: + MODES is a list of symbols. List elements must be from `folder', `letter' and + `sequence'. If `folder' is present then the button is available in the + folder and show buffer. If the name of FUNCTION is of the form \"mh-foo\", + where foo is some arbitrary string, then we check if the function + `mh-show-foo' exists. If it exists then that function is used in the show + buffer. Otherwise the original function `mh-foo' is used in the show buffer + as well. Presence of `sequence' is handled similar to the above. The only + difference is that the button is shown only when the folder is narrowed to a + sequence. If `letter' is present in MODES, then the button is available + during draft editing and runs FUNCTION when clicked. - \"local.\" -> local.peter.galbraith - \"\" -> peter.galbraith - nii -> psg + ICON is the icon that is drawn in the button. -This variable is only meaningful if the variable `mh-alias-local-users' is -non-nil." - :type '(choice (const :tag "Use username instead of real name" nil) - (string)) - :group 'mh-alias) + DOC is the documentation for the button. It is used in tool-tips and in + providing other help to the user. GNU Emacs uses only the first line of the + string. So the DOC should be formatted such that the first line is useful and + complete without the rest of the string. -(defcustom mh-alias-passwd-gecos-comma-separator-flag t - "*Non-nil means the gecos field in the passwd file uses comma as a separator. -Used to construct aliases for users in the passwd file." - :type 'boolean - :group 'mh-alias) + Optional item ENABLE-EXPR is an arbitrary lisp expression. If it evaluates + to nil, then the button is deactivated, otherwise it is active. If is in't + present then the button is always active." + ;; The following variable names have been carefully chosen to make code + ;; generation easier. Modifying the names should be done carefully. + (let (folder-buttons folder-docs folder-button-setter sequence-button-setter + show-buttons show-button-setter show-seq-button-setter + letter-buttons letter-docs letter-button-setter + folder-defaults letter-defaults + folder-vectors show-vectors letter-vectors) + (dolist (x defaults) + (cond ((eq (car x) :folder) (setq folder-defaults (cdr x))) + ((eq (car x) :letter) (setq letter-defaults (cdr x))))) + (dolist (button buttons) + (unless (and (listp button) + (or (equal (length button) 4) (equal (length button) 5))) + (error "Incorrect MH-E tool-bar button specification: %s" button)) + (let* ((name (nth 0 button)) + (name-str (symbol-name name)) + (icon (nth 2 button)) + (xemacs-icon (mh-do-in-xemacs + (cdr (assoc (intern icon) mh-xemacs-icon-map)))) + (full-doc (nth 3 button)) + (doc (if (string-match "\\(.*\\)\n" full-doc) + (match-string 1 full-doc) + full-doc)) + (enable-expr (or (nth 4 button) t)) + (modes (nth 1 button)) + functions show-sym) + (when (memq 'letter modes) (setq functions `(:letter ,name))) + (when (or (memq 'folder modes) (memq 'sequence modes)) + (setq functions + (append `(,(if (memq 'folder modes) :folder :sequence) ,name) + functions)) + (setq show-sym + (if (string-match "^mh-\\(.*\\)$" name-str) + (intern (concat "mh-show-" (match-string 1 name-str))) + name)) + (setq functions + (append `(,(if (memq 'folder modes) :show :show-seq) + ,(if (fboundp show-sym) show-sym name)) + functions))) + (do ((functions functions (cddr functions))) + ((null functions)) + (let* ((type (car functions)) + (function (cadr functions)) + (type1 (substring (symbol-name type) 1)) + (vector-list (cond ((eq type :show) 'show-vectors) + ((eq type :show-seq) 'show-vectors) + ((eq type :letter) 'letter-vectors) + (t 'folder-vectors))) + (list (cond ((eq type :letter) 'mh-tool-bar-letter-buttons) + (t 'mh-tool-bar-folder-buttons))) + (key (intern (concat "mh-" type1 "toolbar-" name-str))) + (setter (intern (concat type1 "-button-setter"))) + (mbuttons (cond ((eq type :letter) 'letter-buttons) + ((eq type :show) 'show-buttons) + ((eq type :show-seq) 'show-buttons) + (t 'folder-buttons))) + (docs (cond ((eq mbuttons 'letter-buttons) 'letter-docs) + ((eq mbuttons 'folder-buttons) 'folder-docs)))) + (add-to-list vector-list `[,xemacs-icon ,function t ,full-doc]) + (add-to-list + setter `(when (member ',name ,list) + (mh-funcall-if-exists + tool-bar-add-item ,icon ',function ',key + :help ,doc :enable ',enable-expr))) + (add-to-list mbuttons name) + (if docs (add-to-list docs doc)))))) + (setq folder-buttons (nreverse folder-buttons) + letter-buttons (nreverse letter-buttons) + show-buttons (nreverse show-buttons) + letter-docs (nreverse letter-docs) + folder-docs (nreverse folder-docs) + folder-vectors (nreverse folder-vectors) + show-vectors (nreverse show-vectors) + letter-vectors (nreverse letter-vectors)) + (dolist (x folder-defaults) + (unless (memq x folder-buttons) + (error "Folder defaults contains unknown button '%s'" x))) + (dolist (x letter-defaults) + (unless (memq x letter-buttons) + (error "Letter defaults contains unknown button '%s'" x))) + `(eval-when (compile load eval) + (defvar mh-folder-tool-bar-map nil) + (defvar mh-folder-seq-tool-bar-map nil) + (defvar mh-show-tool-bar-map nil) + (defvar mh-show-seq-tool-bar-map nil) + (defvar mh-letter-tool-bar-map nil) + ;; GNU Emacs tool bar specific code + (mh-do-in-gnu-emacs + ;; Custom setter functions + (defun mh-tool-bar-folder-buttons-set (symbol value) + "Construct toolbar for `mh-folder-mode' and `mh-show-mode'." + (set-default symbol value) + (setq mh-folder-tool-bar-map + (let ((tool-bar-map (make-sparse-keymap))) + ,@(nreverse folder-button-setter) + tool-bar-map)) + (setq mh-show-tool-bar-map + (let ((tool-bar-map (make-sparse-keymap))) + ,@(nreverse show-button-setter) + tool-bar-map)) + (setq mh-show-seq-tool-bar-map + (let ((tool-bar-map (copy-keymap mh-show-tool-bar-map))) + ,@(nreverse show-seq-button-setter) + tool-bar-map)) + (setq mh-folder-seq-tool-bar-map + (let ((tool-bar-map (copy-keymap mh-folder-tool-bar-map))) + ,@(nreverse sequence-button-setter) + tool-bar-map))) + (defun mh-tool-bar-letter-buttons-set (symbol value) + "Construct toolbar for `mh-letter-mode'." + (set-default symbol value) + (setq mh-letter-tool-bar-map + (let ((tool-bar-map (make-sparse-keymap))) + ,@(nreverse letter-button-setter) + tool-bar-map)))) + ;; XEmacs specific code + (mh-do-in-xemacs + (defvar mh-toolbar-folder-vector-map + ',(loop for button in folder-buttons + for vector in folder-vectors + collect (cons button vector))) + (defvar mh-toolbar-show-vector-map + ',(loop for button in show-buttons + for vector in show-vectors + collect (cons button vector))) + (defvar mh-toolbar-letter-vector-map + ',(loop for button in letter-buttons + for vector in letter-vectors + collect (cons button vector))) + (defvar mh-toolbar-folder-buttons nil) + (defvar mh-toolbar-show-buttons nil) + (defvar mh-toolbar-letter-buttons nil) + ;; Custom setter functions + (defun mh-tool-bar-letter-buttons-set (symbol value) + (set-default symbol value) + (when mh-xemacs-has-toolbar-flag + (setq mh-toolbar-letter-buttons + (loop for b in value + collect (cdr (assoc b mh-toolbar-letter-vector-map)))))) + (defun mh-tool-bar-folder-buttons-set (symbol value) + (set-default symbol value) + (when mh-xemacs-has-toolbar-flag + (setq mh-toolbar-folder-buttons + (loop for b in value + collect (cdr (assoc b mh-toolbar-folder-vector-map)))) + (setq mh-toolbar-show-buttons + (loop for b in value + collect (cdr (assoc b mh-toolbar-show-vector-map)))))) + ;; Initialize toolbar + (defun mh-toolbar-init (mode) + "Install toolbar in MODE." + (let ((toolbar (cond ((eq mode :folder) mh-toolbar-folder-buttons) + ((eq mode :letter) mh-toolbar-letter-buttons) + ((eq mode :show) mh-toolbar-show-buttons))) + (height 37) + (width 40) + (buffer (current-buffer))) + (when (and mh-xemacs-toolbar-position mh-xemacs-use-toolbar-flag + mh-xemacs-has-toolbar-flag) + (cond + ((eq mh-xemacs-toolbar-position 'top) + (set-specifier top-toolbar toolbar buffer) + (set-specifier top-toolbar-visible-p t) + (set-specifier top-toolbar-height height)) + ((eq mh-xemacs-toolbar-position 'bottom) + (set-specifier bottom-toolbar toolbar buffer) + (set-specifier bottom-toolbar-visible-p t) + (set-specifier bottom-toolbar-height height)) + ((eq mh-xemacs-toolbar-position 'left) + (set-specifier left-toolbar toolbar buffer) + (set-specifier left-toolbar-visible-p t) + (set-specifier left-toolbar-width width)) + ((eq mh-xemacs-toolbar-position 'right) + (set-specifier right-toolbar toolbar buffer) + (set-specifier right-toolbar-visible-p t) + (set-specifier right-toolbar-width width)) + (t (set-specifier default-toolbar toolbar buffer))))))) + ;; Declare customizable toolbars + (custom-declare-variable + 'mh-tool-bar-folder-buttons + '(list ,@(mapcar (lambda (x) `(quote ,x)) folder-defaults)) + "Choose buttons to include in MH-E folder/show toolbar." + :group 'mh-toolbar :set 'mh-tool-bar-folder-buttons-set + :type '(set ,@(loop for x in folder-buttons + for y in folder-docs + collect `(const :tag ,y ,x)))) + (custom-declare-variable + 'mh-tool-bar-letter-buttons + '(list ,@(mapcar (lambda (x) `(quote ,x)) letter-defaults)) + "Choose buttons to include in MH-E letter toolbar." + :group 'mh-toolbar :set 'mh-tool-bar-letter-buttons-set + :type '(set ,@(loop for x in letter-buttons + for y in letter-docs + collect `(const :tag ,y ,x))))))) -(defcustom mh-alias-system-aliases - '("/etc/nmh/MailAliases" "/usr/lib/mh/MailAliases" "/etc/passwd") - "*A list of system files from which to cull aliases. -If these files are modified, they are automatically reread. This list need -include only system aliases and the passwd file, since personal alias files -listed in your \"AliasFile\" MH profile component are automatically included. -You can update the alias list manually using \\[mh-alias-reload]." - :type '(choice (file) (repeat file)) - :group 'mh-alias) +(mh-tool-bar-define + ((:folder mh-inc-folder mh-mime-save-parts mh-previous-undeleted-msg + mh-page-msg mh-next-undeleted-msg mh-delete-msg mh-refile-msg + mh-undo mh-execute-commands mh-toggle-tick mh-reply + mh-alias-grab-from-field mh-send mh-rescan-folder + mh-tool-bar-search mh-visit-folder + mh-tool-bar-customize mh-tool-bar-folder-help mh-widen) + (:letter mh-send-letter mh-compose-insertion ispell-message save-buffer + undo kill-region menu-bar-kill-ring-save yank mh-fully-kill-draft + mh-tool-bar-customize mh-tool-bar-letter-help)) + ;; Folder/Show buffer buttons + (mh-inc-folder (folder) "mail" + "Incorporate new mail in Inbox +This button runs `mh-inc-folder' which drags any +new mail into your Inbox folder.") + (mh-mime-save-parts (folder) "attach" + "Save MIME parts from this message +This button runs `mh-mime-save-parts' which saves a message's +different parts into separate files.") + (mh-previous-undeleted-msg (folder) "left_arrow" + "Go to the previous undeleted message +This button runs `mh-previous-undeleted-msg'") + (mh-page-msg (folder) "page-down" + "Page the current message forwards\nThis button runs `mh-page-msg'") + (mh-next-undeleted-msg (folder) "right_arrow" + "Go to the next undeleted message\nThe button runs `mh-next-undeleted-msg'") + (mh-delete-msg (folder) "close" + "Mark this message for deletion\nThis button runs `mh-delete-msg'") + (mh-refile-msg (folder) "refile" + "Refile this message\nThis button runs `mh-refile-msg'") + (mh-undo (folder) "undo" "Undo last operation\nThis button runs `undo'" + (mh-outstanding-commands-p)) + (mh-execute-commands (folder) "execute" + "Perform moves and deletes\nThis button runs `mh-execute-commands'" + (mh-outstanding-commands-p)) + (mh-toggle-tick (folder) "highlight" + "Toggle tick mark\nThis button runs `mh-toggle-tick'") + (mh-toggle-showing (folder) "show" + "Toggle showing message\nThis button runs `mh-toggle-showing'") + (mh-tool-bar-reply-from (folder) "reply-from" "Reply to \"from\"") + (mh-tool-bar-reply-to (folder) "reply-to" "Reply to \"to\"") + (mh-tool-bar-reply-all (folder) "reply-all" "Reply to \"all\"") + (mh-reply (folder) "mail/reply2" + "Reply to this message\nThis button runs `mh-reply'") + (mh-alias-grab-from-field (folder) "alias" + "Grab From alias\nThis button runs `mh-alias-grab-from-field'" + (and (mh-extract-from-header-value) (not (mh-alias-for-from-p)))) + (mh-send (folder) "mail_compose" + "Compose new message\nThis button runs `mh-send'") + (mh-rescan-folder (folder) "rescan" + "Rescan this folder\nThis button runs `mh-rescan-folder'") + (mh-pack-folder (folder) "repack" + "Repack this folder\nThis button runs `mh-pack-folder'") + (mh-tool-bar-search (folder) "search" + "Search\nThis button runs `mh-tool-bar-search-function'") + (mh-visit-folder (folder) "fld_open" + "Visit other folder\nThis button runs `mh-visit-folder'") + ;; Letter buffer buttons + (mh-send-letter (letter) "mail_send" "Send this letter") + (mh-compose-insertion (letter) "attach" "Insert attachment") + (ispell-message (letter) "spell" "Check spelling") + (save-buffer (letter) "save" "Save current buffer to its file" + (buffer-modified-p)) + (undo (letter) "undo" "Undo last operation") + (kill-region (letter) "cut" + "Cut (kill) text in region between mark and current position") + (menu-bar-kill-ring-save (letter) "copy" + "Copy text in region between mark and current position") + (yank (letter) "paste" "Paste (yank) text cut or copied earlier") + (mh-fully-kill-draft (letter) "close" "Kill this draft") + ;; Common buttons + (mh-tool-bar-customize (folder letter) "preferences" "MH-E Preferences") + (mh-tool-bar-folder-help (folder) "help" + "Help! (general help)\nThis button runs `Info-goto-node'") + (mh-tool-bar-letter-help (letter) "help" + "Help! (general help)\nThis button runs `Info-goto-node'") + ;; Folder narrowed to sequence buttons + (mh-widen (sequence) "widen" + "Widen from the sequence\nThis button runs `mh-widen'")) -;;; Multiple personalities (:group 'mh-identity) +;;; Hooks (:group 'mh-hooks + group where hook described) -(defcustom mh-identity-list nil - "*List holding MH-E identity. -Omit the colon and trailing space from the field names. -The keyword name \"none\" is reserved for internal use. -Use the keyname name \"signature\" to specify either a signature file or a -function to call to insert a signature at point. - -Providing an empty Value (\"\") will cause the field to be deleted. - -Example entries using the customize interface: - Keyword name: work - From - Value: John Doe <john@work.com> - Organization - Value: Acme Inc. - Keyword name: home - From - Value: John Doe <johndoe@home.net> - Organization - Value: - -This would produce the equivalent of: - (setq mh-identity-list - '((\"work\" - ((\"From\" . \"John Doe <john@work.com>\") - (\"Organization\" . \"Acme Inc.\"))) - (\"home\" - ((\"From\" . \"John Doe <johndoe@home.net>\") - (\"Organization\" . \"\")))))" - :type '(repeat (list :tag "" - (string :tag "Keyword name") - (repeat :tag "At least one pair from below" - (choice (cons :tag "From field" - (const "From") - (string :tag "Value")) - (cons :tag "Organization field" - (const "Organization") - (string :tag "Value")) - (cons :tag "Signature" - (const "signature") - (choice (file) (function))) - (cons :tag "Other field & value pair" - (string :tag "Field") - (string :tag "Value")))))) - :set 'mh-identity-list-set - :group 'mh-identity) +(defcustom mail-citation-hook nil + "*Hook for modifying a citation just inserted in the mail buffer. +Each hook function can find the citation between point and mark. +And each hook function should leave point and mark around the citation +text as modified. -(defcustom mh-auto-fields-list nil - "Alist of addresses for which header lines are automatically inserted. -Each element has the form (REGEXP ((KEYWORD VALUE) (KEYWORD VALUE)). -When the REGEXP appears in the To or cc fields of a message, the corresponding -KEYWORD header field is insert with its VALUE in the message header. +If this hook is entirely empty (nil), the text of the message is inserted +with `mh-ins-buf-prefix' prefixed to each line. -There is one special case for KEYWORD, that of \"identity\", which means to -insert that identity using `mh-insert-identity'. +See also the variable `mh-yank-from-start-of-msg', which controls how +much of the message passed to the hook. -The common KEYWORD cases of \"Mail-Followup-To\" and \"fcc\" are also -prompted for in the customization interface." - :type `(repeat - (list :tag "" - (string :tag "Regular expression to match") - (repeat :tag "At least one pair from below" - (choice - (cons :tag "Identity entry" - (const "identity") - ,(append - '(radio) - (mapcar (function (lambda (arg) `(const ,arg))) - (mapcar 'car mh-identity-list)))) - (cons :tag "fcc field" - (const "fcc") - (string :tag "Value")) - (cons :tag "Mail-Followup-To field" - (const "Mail-Followup-To") - (string :tag "Value")) - (cons :tag "Other field and value pair" - (string :tag "Field") - (string :tag "Value")))))) - :group 'mh-identity) +This hook was historically provided to set up supercite. You may now leave +this nil and set up supercite by setting the variable +`mh-yank-from-start-of-msg' to 'supercite or, for more automatic insertion, +to 'autosupercite. -(defcustom mh-identity-default nil - "Default identity to use when `mh-letter-mode' is called." - ;; Dynamically render :type corresponding to `mh-identity-list' entries, - ;; e.g.: - ;; :type '(radio (const :tag "none" nil) - ;; (const "home") - ;; (const "work")) - :type (append - '(radio) - (cons '(const :tag "None" nil) - (mapcar (function (lambda (arg) `(const ,arg))) - (mapcar 'car mh-identity-list)))) - :group 'mh-identity) - - - -;;; Hooks (:group 'mh-hooks + group where hook defined) +The hook 'trivial-cite is NOT part of Emacs. It is provided from tc.el, +available here: + http://shasta.cs.uiuc.edu/~lrclause/tc.html +If you use it, customize `mh-yank-from-start-of-msg' to + \"Entire message with headers\"." + :type 'hook + :options '(trivial-cite) + :group 'mh-hooks + :group 'mh-letter) -;;; These are alphabetized. All hooks should be placed in the 'mh-hook group; -;;; in addition, add the group in which the hook is defined in the manual (or, -;;; if it is new, where it would be defined). +(defcustom mh-alias-reloaded-hook nil + "Invoked by `mh-alias-reload' after reloading aliases." + :type 'hook + :group 'mh-hooks + :group 'mh-alias) (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-hooks - :group 'mh-folder) + :group 'mh-show) (defcustom mh-before-send-letter-hook nil "Invoked at the beginning of the \\<mh-letter-mode-map>\\[mh-send-letter] command." @@ -1684,7 +1962,7 @@ See also `mh-quit-hook'." "Invoked after marking each message for deletion." :type 'hook :group 'mh-hooks - :group 'mh-folder) + :group 'mh-show) (defcustom mh-edit-mhn-hook nil "Invoked on the formatted letter by \\<mh-letter-mode-map>\\[mh-edit-mhn]." @@ -1696,13 +1974,13 @@ See also `mh-quit-hook'." "Invoked by `mh-find-path' after reading the user's MH profile." :type 'hook :group 'mh-hooks - :group 'mh-folder) + :group 'mh-show) (defcustom mh-folder-mode-hook nil "Invoked in `mh-folder-mode' on a new folder." :type 'hook :group 'mh-hooks - :group 'mh-folder) + :group 'mh-show) (defcustom mh-folder-updated-hook nil "Invoked when the folder actions (such as moves and deletes) are performed. @@ -1712,11 +1990,17 @@ current folder, `mh-current-folder'." :type 'hook :group 'mh-hooks) +(defcustom mh-forward-hook nil + "Invoked on the forwarded letter by \\<mh-folder-mode-map>\\[mh-forward]." + :type 'hook + :group 'mh-hooks + :group 'mh-folder) + (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-hooks - :group 'mh-folder) + :group 'mh-inc) (defcustom mh-kill-folder-suppress-prompt-hook '(mh-index-p) "Invoked at the beginning of the \\<mh-folder-mode-map>`\\[mh-kill-folder]' command. @@ -1733,13 +2017,12 @@ t on +inbox and you hit \\<mh-folder-mode-map>`\\[mh-kill-folder]' by accident in the +inbox buffer, you will not be happy." :type 'hook :group 'mh-hooks - :group 'mh-folder) + :group 'mh-show) (defcustom mh-letter-insert-signature-hook nil - "Invoked at the beginning of the \\<mh-letter-mode-map>\\[mh-insert-signature] command. -Can be used to determine which signature file to use based on message content. -On return, if `mh-signature-file-name' is non-nil that file will be inserted at -the current point in the buffer." + "Invoked after signature has been inserted. +This hook may access the actual name of the file or the function used to +insert the signature with `mh-signature-file-name'." :type 'hook :group 'mh-hooks :group 'mh-letter) @@ -1748,26 +2031,26 @@ the current point in the buffer." "Invoked in `mh-letter-mode' on a new letter." :type 'hook :group 'mh-hooks - :group 'mh-letter) + :group 'mh-sending-mail) (defcustom mh-pick-mode-hook nil "Invoked upon entry to `mh-pick-mode'." :type 'hook :group 'mh-hooks - :group 'mh-folder) + :group 'mh-index) (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-hooks - :group 'mh-folder) + :group 'mh-show) (defcustom mh-refile-msg-hook nil "Invoked after marking each message for refiling." :type 'hook :group 'mh-hooks - :group 'mh-folder) + :group 'mh-show) (defcustom mh-show-hook nil "Invoked after \\<mh-folder-mode-map>`\\[mh-show]' shows a message." @@ -1787,44 +2070,13 @@ The variable `mh-seen-list' can be used to obtain the list of messages which will be removed from the unseen sequence." :type 'hook :group 'mh-hooks - :group 'mh-folder) + :group 'mh-show) -;;; Faces - -;;; Faces used in speedbar (:group mh-speed-faces) - -(defface mh-speedbar-folder-face - '((((class color) (background light)) - (:foreground "blue4")) - (((class color) (background dark)) - (:foreground "light blue"))) - "Face used for folders in the speedbar buffer." - :group 'mh-speed-faces) - -(defface mh-speedbar-selected-folder-face - '((((class color) (background light)) - (:foreground "red" :underline t)) - (((class color) (background dark)) - (:foreground "red" :underline t)) - (t (:underline t))) - "Face used for the current folder." - :group 'mh-speed-faces) - -(defface mh-speedbar-folder-with-unseen-messages-face - '((t (:inherit mh-speedbar-folder-face :bold t))) - "Face used for folders in the speedbar buffer which have unread messages." - :group 'mh-speed-faces) - -(defface mh-speedbar-selected-folder-with-unseen-messages-face - '((t (:inherit mh-speedbar-selected-folder-face :bold t))) - "Face used for the current folder when it has unread messages." - :group 'mh-speed-faces) - - +;;; Faces (:group 'mh-*-faces + group where faces described) -;;; Faces used in scan listing (:group mh-folder-faces) +;;; Faces Used in Scan Listing (:group 'mh-folder-faces) (defvar mh-folder-body-face 'mh-folder-body-face "Face for highlighting body text in MH-Folder buffers.") @@ -1962,7 +2214,36 @@ will be removed from the unseen sequence." -;;; Faces used in message display (:group mh-show-faces) +;;; Faces Used in Searching (:group 'mh-index-faces) + +(defvar mh-index-folder-face 'mh-index-folder-face + "Face for highlighting folders in MH-Index buffers.") +(defface mh-index-folder-face + '((((class color) (background light)) + (:foreground "dark green" :bold t)) + (((class color) (background dark)) + (:foreground "indian red" :bold t)) + (t + (:bold t))) + "Face for highlighting folders in MH-Index buffers." + :group 'mh-index-faces) + + + +;;; Faces Used in Message Drafts (:group 'mh-letter-faces) + +(defface mh-letter-header-field-face + '((((class color) (background light)) + (:background "gray90")) + (((class color) (background dark)) + (:background "gray10")) + (t (:bold t))) + "Face for displaying header fields in draft buffers." + :group 'mh-letter-faces) + + + +;;; Faces Used in Message Display (:group 'mh-show-faces) (defvar mh-show-cc-face 'mh-show-cc-face "Face for highlighting cc header fields.") @@ -2002,6 +2283,11 @@ will be removed from the unseen sequence." "Face used to deemphasize unspecified header fields." :group 'mh-show-faces) +(defface mh-show-signature-face + '((t (:italic t))) + "Face for highlighting message signature." + :group 'mh-show-faces) + (defvar mh-show-to-face 'mh-show-to-face "Face for highlighting the To: header field.") (if (boundp 'facemenu-unlisted-faces) @@ -2041,32 +2327,34 @@ The background and foreground is used in the image." -;;; Faces used in indexed searches (:group mh-index-faces) +;;; Faces Used in Speedbar (:group 'mh-speed-faces) -(defvar mh-index-folder-face 'mh-index-folder-face - "Face for highlighting folders in MH-Index buffers.") -(defface mh-index-folder-face +(defface mh-speedbar-folder-face '((((class color) (background light)) - (:foreground "dark green" :bold t)) + (:foreground "blue4")) (((class color) (background dark)) - (:foreground "indian red" :bold t)) - (t - (:bold t))) - "Face for highlighting folders in MH-Index buffers." - :group 'mh-index-faces) - - - -;;; Faces used when composing messages. + (:foreground "light blue"))) + "Face used for folders in the speedbar buffer." + :group 'mh-speed-faces) -(defface mh-letter-header-field-face +(defface mh-speedbar-selected-folder-face '((((class color) (background light)) - (:background "gray90")) + (:foreground "red" :underline t)) (((class color) (background dark)) - (:background "gray10")) - (t (:bold t))) - "Face for displaying header fields in draft buffers." - :group 'mh-letter-faces) + (:foreground "red" :underline t)) + (t (:underline t))) + "Face used for the current folder." + :group 'mh-speed-faces) + +(defface mh-speedbar-folder-with-unseen-messages-face + '((t (:inherit mh-speedbar-folder-face :bold t))) + "Face used for folders in the speedbar buffer which have unread messages." + :group 'mh-speed-faces) + +(defface mh-speedbar-selected-folder-with-unseen-messages-face + '((t (:inherit mh-speedbar-selected-folder-face :bold t))) + "Face used for the current folder when it has unread messages." + :group 'mh-speed-faces) ;;; Local Variables: ;;; indent-tabs-mode: nil diff --git a/lisp/mh-e/mh-e.el b/lisp/mh-e/mh-e.el index e72304c4412..5cb7f90f1e3 100644 --- a/lisp/mh-e/mh-e.el +++ b/lisp/mh-e/mh-e.el @@ -5,7 +5,7 @@ ;; Author: Bill Wohler <wohler@newt.com> ;; Maintainer: Bill Wohler <wohler@newt.com> -;; Version: 7.4.4 +;; Version: 7.4.80 ;; Keywords: mail ;; This file is part of GNU Emacs. @@ -75,24 +75,19 @@ ;; 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 -;; Maintenance picked up by Bill Wohler <wohler@newt.com> and the -;; SourceForge Crew <http://mh-e.sourceforge.net/>. 2001. +;; Rewritten for GNU Emacs, James Larus, 1985. +;; Modified by Stephen Gildea, 1988. +;; Maintenance picked up by Bill Wohler and the +;; SourceForge Crew <http://mh-e.sourceforge.net/>, 2001. ;;; Code: (provide 'mh-e) -(require 'mh-utils) -(mh-require-cl) - -(defvar recursive-load-depth-limit) -(eval-when (compile load eval) - (if (and (boundp 'recursive-load-depth-limit) - (integerp recursive-load-depth-limit) - (> 50 recursive-load-depth-limit)) - (setq recursive-load-depth-limit 50))) +(eval-when-compile (require 'mh-acros)) +(mh-require-cl) +(require 'mh-utils) +(require 'mh-init) (require 'mh-inc) (require 'gnus-util) (require 'easymenu) @@ -101,35 +96,27 @@ (defvar font-lock-auto-fontify) (defvar font-lock-defaults) -(defconst mh-version "7.4.4" "Version number of MH-E.") +(defconst mh-version "7.4.80" "Version number of MH-E.") ;;; Autoloads (autoload 'Info-goto-node "info") - - -(defvar mh-note-deleted "D" - "String whose first character is used to notate deleted messages.") - -(defvar mh-note-refiled "^" - "String whose first character is used to notate refiled messages.") - -(defvar mh-note-cur "+" - "String whose first character is used to notate the current message.") - (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.") + +;;; Scan Line Formats + ;;; 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. -;; The following scan formats are passed to the scan program if the -;; setting of `mh-scan-format-file' above is nil. They are identical -;; except the later one makes use of the nmh `decode' function to -;; decode RFC 2047 encodings. If you just want to change the width of -;; the msg number, use the `mh-set-cmd-note' function. +;; The following scan formats are passed to the scan program if the setting of +;; `mh-scan-format-file' is t. They are identical except the later one makes +;; use of the nmh `decode' function to decode RFC 2047 encodings. If you just +;; want to change the width of the msg number, use the `mh-set-cmd-note' +;; function. (defvar mh-scan-format-mh (concat @@ -150,11 +137,10 @@ This format is identical to the default except that additional hints for fontification have been added to the fifth column (remember that in Emacs, the first column is 0). -The values of the fifth column, in priority order, are: `-' if the -message has been replied to, t if an address on the To: line matches -one of the mailboxes of the current user, `c' if the Cc: line matches, -`b' if the Bcc: line matches, and `n' if a non-empty Newsgroups: header -is present.") +The values of the fifth column, in priority order, are: `-' if the message has +been replied to, t if an address on the To: line matches one of the +mailboxes of the current user, `c' if the Cc: line matches, `b' if the Bcc: +line matches, and `n' if a non-empty Newsgroups: header is present.") (defvar mh-scan-format-nmh (concat @@ -176,78 +162,94 @@ This format is identical to the default except that additional hints for fontification have been added to the fifth column (remember that in Emacs, the first column is 0). -The values of the fifth column, in priority order, are: `-' if the -message has been replied to, t if an address on the To: line matches -one of the mailboxes of the current user, `c' if the Cc: line matches, -`b' if the Bcc: line matches, and `n' if a non-empty Newsgroups: header -is present.") +The values of the fifth column, in priority order, are: `-' if the message has +been replied to, t if an address on the To: field matches one of the +mailboxes of the current user, `c' if the Cc: field matches, `b' if the Bcc: +field matches, and `n' if a non-empty Newsgroups: field is present.") + +(defvar mh-note-deleted ?D + "Deleted messages are marked by this character. +See also `mh-scan-deleted-msg-regexp'.") + +(defvar mh-note-refiled ?^ + "Refiled messages are marked by this character. +See also `mh-scan-refiled-msg-regexp'.") + +(defvar mh-note-cur ?+ + "The current message (in MH) is marked by this character. +See also `mh-scan-cur-msg-number-regexp'.") (defvar mh-scan-good-msg-regexp "^\\( *[0-9]+\\)[^D^0-9]" - "Regexp specifying the scan lines that are 'good' messages. -The default `mh-folder-font-lock-keywords' expects this expression to contain -at least one parenthesized expression which matches the message number.") + "This regexp specifies the scan lines that are 'good' messages. +Note that the default setting of `mh-folder-font-lock-keywords' expects this +expression to contain at least one parenthesized expression which matches the +message number as in the default of \"^\\\\( *[0-9]+\\\\)[^D^0-9]\".") (defvar mh-scan-deleted-msg-regexp "^\\( *[0-9]+\\)D" - "Regexp matching scan lines of deleted messages. -The default `mh-folder-font-lock-keywords' expects this expression to contain -at least one parenthesized expression which matches the message number.") + "This regexp matches deleted messages. +Note that the default setting of `mh-folder-font-lock-keywords' expects this +expression to contain at least one parenthesized expression which matches the +message number as in the default of \"^\\\\( *[0-9]+\\\\)D\". +See also `mh-note-deleted'.") (defvar mh-scan-refiled-msg-regexp "^\\( *[0-9]+\\)\\^" - "Regexp matching scan lines of refiled messages. -The default `mh-folder-font-lock-keywords' expects this expression to contain -at least one parenthesized expression which matches the message number.") + "This regexp matches refiled messages. +Note that the default setting of `mh-folder-font-lock-keywords' expects this +expression to contain at least one parenthesized expression which matches the +message number as in the default of \"^\\\\( *[0-9]+\\\\)\\\\^\". +See also `mh-note-refiled'.") (defvar mh-scan-valid-regexp "^ *[0-9]" - "Regexp matching scan lines for messages (not error messages).") + "This regexp matches scan lines for messages (not error messages).") (defvar mh-scan-cur-msg-number-regexp "^\\( *[0-9]+\\+\\).*" - "Regexp matching scan line for the current message. -The default `mh-folder-font-lock-keywords' expects this expression to contain -at least one parenthesized expression which matches the message number. -Don't disable this regexp as it's needed by non fontifying functions.") - -(defvar mh-scan-cur-msg-regexp "^\\( *[0-9]+\\+DISABLED.*\\)" - "Regexp matching scan line for the current message. -The default `mh-folder-font-lock-keywords' expects this expression to contain -at least one parenthesized expression which matches the whole line. -To enable this feature, remove the string DISABLED from the regexp.") + "This regexp matches the current message. +Note that the default setting of `mh-folder-font-lock-keywords' expects this +expression to contain at least one parenthesized expression which matches the +message number as in the default of \"^\\\\( *[0-9]+\\\\+\\\\).*\". Don't +disable this regexp as it's needed by non-fontifying functions. +See also `mh-note-cur'.") (defvar mh-scan-date-regexp "\\([0-9][0-9]/[0-9][0-9]\\)" - "Regexp matching a valid date in scan lines. -The default `mh-folder-font-lock-keywords' expects this expression to contain -only one parenthesized expression which matches the date field -\(see `mh-scan-format-regexp').") + "This regexp matches a valid date. +Note that the default setting of `mh-folder-font-lock-keywords' expects this +expression to contain only one parenthesized expression which matches the date +field as in the default of \"\\\\([0-9][0-9]/[0-9][0-9]\\\\)\"}. +See also `mh-scan-format-regexp'.") (defvar mh-scan-rcpt-regexp "\\(To:\\)\\(..............\\)" - "Regexp specifying the recipient in scan lines for messages we sent. -The default `mh-folder-font-lock-keywords' expects this expression to contain -two parenthesized expressions. The first is expected to match the To: -that the default scan format file generates. The second is expected to match -the recipient's name.") + "This regexp specifies the recipient in messages you sent. +Note that the default setting of `mh-folder-font-lock-keywords' +expects this expression to contain two parenthesized expressions. The +first is expected to match the `To:' that the default scan format +file generates. The second is expected to match the recipient's name +as in the default of \"\\\\(To:\\\\)\\\\(..............\\\\)\".") (defvar mh-scan-body-regexp "\\(<<\\([^\n]+\\)?\\)" - "Regexp matching the message body beginning displayed in scan lines. -The default `mh-folder-font-lock-keywords' expects this expression to contain -at least one parenthesized expression which matches the body text.") + "This regexp matches the message body fragment displayed in scan lines. +Note that the default setting of `mh-folder-font-lock-keywords' expects this +expression to contain at least one parenthesized expression which matches the +body text as in the default of \"\\\\(<<\\\\([^\\n]+\\\\)?\\\\)\".") (defvar mh-scan-subject-regexp - ;;"^ *[0-9]+........[ ]*...................\\([Rr][Ee]:\\s-*\\)*\\([^<\n]*\\)" "^ *[0-9]+........[ ]*...................\\([Rr][Ee]\\(\\[[0-9]+\\]\\)?:\\s-*\\)*\\([^<\n]*\\)" - "*Regexp matching the subject string in MH folder mode. -The default `mh-folder-font-lock-keywords' expects this expression to contain -at least tree parenthesized expressions. The first is expected to match the Re: -string, if any. The second matches an optional bracketed number after Re, -such as in Re[2]: and the third is expected to match the subject line itself.") + "This regexp matches the subject. +Note that the default setting of `mh-folder-font-lock-keywords' expects this +expression to contain at least three parenthesized expressions. The first is +expected to match the `Re:' string, if any. The second matches an optional +bracketed number after `Re:', such as in `Re[2]:' (and is thus a +sub-expression of the first expression) and the third is expected to match +the subject line itself as in the default of \"^ *[0-9]+........[ ]*...................\\\\([Rr][Ee]\\\\(\\\\\\=[[0-9]+\\\\]\\\\)?:\\\\s-*\\\\)*\\\\([^<\\n]*\\\\)\".") (defvar mh-scan-format-regexp (concat "\\([bct]\\)" mh-scan-date-regexp " *\\(..................\\)") - "Regexp matching the output of scan. -The default value is based upon the default values of either -`mh-scan-format-mh' or `mh-scan-format-nmh'. -The default `mh-folder-font-lock-keywords' expects this expression to contain -at least three parenthesized expressions. The first should match the -fontification hint, the second is found in `mh-scan-date-regexp', and the -third should match the user name.") + "This regexp matches the output of scan. +Note that the default setting of `mh-folder-font-lock-keywords' expects this +expression to contain at least three parenthesized expressions. The first +should match the fontification hint, the second is found in +`mh-scan-date-regexp', and the third should match the user name as in the +default of \"(concat \"\\\\([bct]\\\\)\" mh-scan-date-regexp + \"*\\\\(..................\\\\)\")\".") @@ -279,10 +281,7 @@ third should match the user name.") ;; scan font-lock name (list mh-scan-format-regexp '(1 mh-folder-date-face) - '(3 mh-folder-scan-format-face)) - ;; Current message line - (list mh-scan-cur-msg-regexp - '(1 mh-folder-cur-msg-face prepend t))) + '(3 mh-folder-scan-format-face))) "Regexp keywords used to fontify the MH-Folder buffer.") (defvar mh-scan-cmd-note-width 1 @@ -356,46 +355,6 @@ This column will only ever have spaces in it.") ;; Fontifify unseen mesages in bold. -(defvar mh-folder-unseen-seq-name nil - "Name of unseen sequence. -The default for this is provided by the function `mh-folder-unseen-seq-name' -On nmh systems.") - -(defun mh-folder-unseen-seq-name () - "Provide name of unseen sequence from mhparam." - (or mh-progs (mh-find-path)) - (save-excursion - (let ((unseen-seq-name "unseen")) - (with-temp-buffer - (unwind-protect - (progn - (call-process (expand-file-name "mhparam" mh-progs) - nil '(t t) nil "-component" "Unseen-Sequence") - (goto-char (point-min)) - (if (re-search-forward "Unseen-Sequence: \\(.*\\)$" nil t) - (setq unseen-seq-name (match-string 1)))))) - unseen-seq-name))) - -(defun mh-folder-unseen-seq-list () - "Return a list of unseen message numbers for current folder." - (if (not mh-folder-unseen-seq-name) - (setq mh-folder-unseen-seq-name (mh-folder-unseen-seq-name))) - (cond - ((not mh-folder-unseen-seq-name) - nil) - (t - (let ((folder mh-current-folder)) - (save-excursion - (with-temp-buffer - (unwind-protect - (progn - (call-process (expand-file-name "mark" mh-progs) - nil '(t t) nil - folder "-seq" mh-folder-unseen-seq-name - "-list") - (goto-char (point-min)) - (sort (mh-read-msg-list) '<))))))))) - (defmacro mh-generate-sequence-font-lock (seq prefix face) "Generate the appropriate code to fontify messages in SEQ. PREFIX is used to generate unique names for the variables and functions @@ -492,6 +451,8 @@ is done highlighting.") ;Rememeber original notation that ;is overwritten by `mh-note-seq'. +(defvar mh-colors-available-flag nil) ;Are colors available? + ;;; Macros and generic functions: (defun mh-mapc (function list) @@ -503,7 +464,7 @@ is done highlighting.") (defun mh-scan-format () "Return the output format argument for the scan program." (if (equal mh-scan-format-file t) - (list "-format" (if mh-nmh-flag + (list "-format" (if (mh-variant-p 'nmh 'mu-mh) (list (mh-update-scan-format mh-scan-format-nmh mh-cmd-note)) (list (mh-update-scan-format @@ -519,7 +480,7 @@ is done highlighting.") (defun mh-rmail (&optional arg) "Inc(orporate) new mail with MH. Scan an MH folder if ARG is non-nil. This function is an entry point to MH-E, -the Emacs front end to the MH mail system." +the Emacs interface to the MH mail system." (interactive "P") (mh-find-path) (if arg @@ -532,7 +493,7 @@ the Emacs front end to the MH mail system." (defun mh-nmail (&optional arg) "Check for new mail in inbox folder. Scan an MH folder if ARG is non-nil. This function is an entry point to MH-E, -the Emacs front end to the MH mail system." +the Emacs interface to the MH mail system." (interactive "P") (mh-find-path) ; init mh-inbox (if arg @@ -616,6 +577,7 @@ Do not call this function from outside MH-E; use \\[mh-rmail] instead." (setq folder mh-inbox)) (let ((threading-needed-flag nil)) (let ((config (current-window-configuration))) + (delete-other-windows) (cond ((not (get-buffer folder)) (mh-make-folder folder) (setq threading-needed-flag mh-show-threads-flag) @@ -659,25 +621,26 @@ last undeleted message then pause for a second after printing message." (if wait-after-complaining-flag (sit-for 1))))) (defun mh-folder-from-address () - "Determine folder name from address in From field. -Takes the address in the From: header field, and returns one of: + "Derive folder name from sender. + +The name of the folder is derived as follows: - a) The folder name associated with the address in the alist - `mh-default-folder-list'. If the `Check Recipient' boolean - is set, then the `mh-default-folder-list' addresses are - checked against the recipient instead of the originator - (making possible to use this feature for mailing lists). - The first match found in `mh-default-folder-list' is used. + a) The folder name associated with the first address found in the list + `mh-default-folder-list' is used. Each element in this list contains a + `Check Recipient' item. If this item is turned on, then the address is + checked against the recipient instead of the sender. This is useful for + mailing lists. - b) The address' corresponding alias from the user's personal - aliases file prefixed by `mh-default-folder-prefix'. + b) An alias prefixed by `mh-default-folder-prefix' corresponding to the + address is used. The prefix is used to prevent clutter in your mail + directory. -Returns nil if the address was not found in either place or if the variable -`mh-default-folder-must-exist-flag' is nil and the folder does not exist." +Return nil if a folder name was not derived, or if the variable +`mh-default-folder-must-exist-flag' is t and the folder does not exist." ;; Loop for all entries in mh-default-folder-list (save-restriction (goto-char (point-min)) - (re-search-forward "\n\n" nil t) + (re-search-forward "\n\n" nil 'limit) (narrow-to-region (point-min) (point)) (let ((to/cc (concat (or (message-fetch-field "to") "") ", " (or (message-fetch-field "cc") ""))) @@ -715,25 +678,24 @@ Returns nil if the address was not found in either place or if the variable "Prompt the user for a folder in which the message should be filed. The folder is returned as a string. -If `mh-default-folder-for-message-function' is a function then the message -being refiled is yanked into a temporary buffer and the function is called to -intelligently guess where the message is to be refiled. - -Otherwise, a default folder name is generated by `mh-folder-from-address'." +The default folder name is generated by the option +`mh-default-folder-for-message-function' if it is non-nil or +`mh-folder-from-address'." (mh-prompt-for-folder "Destination" - (let ((refile-file (mh-msg-filename (mh-get-msg-num t)))) - (save-excursion - (set-buffer (get-buffer-create mh-temp-buffer)) - (erase-buffer) - (insert-file-contents refile-file) - (or (and mh-default-folder-for-message-function - (let ((buffer-file-name refile-file)) - (funcall mh-default-folder-for-message-function))) - (mh-folder-from-address) - (and (eq 'refile (car mh-last-destination-folder)) - (symbol-name (cdr mh-last-destination-folder))) - ""))) + (let ((refile-file (ignore-errors (mh-msg-filename (mh-get-msg-num t))))) + (if (null refile-file) "" + (save-excursion + (set-buffer (get-buffer-create mh-temp-buffer)) + (erase-buffer) + (insert-file-contents refile-file) + (or (and mh-default-folder-for-message-function + (let ((buffer-file-name refile-file)) + (funcall mh-default-folder-for-message-function))) + (mh-folder-from-address) + (and (eq 'refile (car mh-last-destination-folder)) + (symbol-name (cdr mh-last-destination-folder))) + "")))) t)) (defun mh-refile-msg (range folder &optional dont-update-last-destination-flag) @@ -872,7 +834,9 @@ are skipped." (setq count (1- count))) (not (car unread-sequence))) (message "No more unread messages")) - (t (mh-goto-msg (car unread-sequence)))))) + (t (loop for msg in unread-sequence + when (mh-goto-msg msg t) return nil + finally (message "No more unread messages")))))) (defun mh-goto-next-button (backward-flag &optional criterion) "Search for next button satisfying criterion. @@ -1090,7 +1054,7 @@ interactive use." (if (not (mh-outstanding-commands-p)) (mh-set-folder-modified-p nil))) -;;;###mh-autoload + (defun mh-folder-line-matches-show-buffer-p () "Return t if the message under point in folder-mode is in the show buffer. Return nil in any other circumstance (no message under point, no show buffer, @@ -1123,7 +1087,6 @@ compiled then macro expansion happens at compile time." (defun mh-version () "Display version information about MH-E and the MH mail handling system." (interactive) - (mh-find-progs) (set-buffer (get-buffer-create mh-info-buffer)) (erase-buffer) ;; MH-E version. @@ -1140,19 +1103,12 @@ compiled then macro expansion happens at compile time." ;; Emacs version. (insert (emacs-version) "\n\n") ;; MH version. - (let ((help-start (point))) - (condition-case err-data - (mh-exec-cmd-output "inc" nil (if mh-nmh-flag "-version" "-help")) - (file-error (insert (mapconcat 'concat (cdr err-data) ": ") "\n"))) - (goto-char help-start) - (if mh-nmh-flag - (search-forward "inc -- " nil t) - (search-forward "version: " nil t)) - (delete-region help-start (point))) - (goto-char (point-max)) - (insert " mh-progs:\t" mh-progs "\n" - " mh-lib:\t" mh-lib "\n" - " mh-lib-progs:\t" mh-lib-progs "\n\n") + (if mh-variant-in-use + (insert mh-variant-in-use "\n" + " mh-progs:\t" mh-progs "\n" + " mh-lib:\t" mh-lib "\n" + " mh-lib-progs:\t" mh-lib-progs "\n\n") + (insert "No MH variant detected\n")) ;; Linux version. (condition-case () (call-process "uname" nil t nil "-a") @@ -1202,7 +1158,7 @@ used to avoid problems in corner cases involving folders whose names end with a (defun mh-folder-size-flist (folder) "Find size of FOLDER using `flist'." (with-temp-buffer - (call-process (expand-file-name "flist" mh-progs) nil t nil + (call-process (expand-file-name "flist" mh-progs) nil t nil "-showzero" "-norecurse" folder "-sequence" (symbol-name mh-unseen-seq)) (goto-char (point-min)) (multiple-value-bind (folder unseen total) @@ -1236,6 +1192,7 @@ regardless of the size of the `mh-large-folder' variable." (let ((config (current-window-configuration)) (current-buffer (current-buffer)) (threaded-view-flag mh-show-threads-flag)) + (delete-other-windows) (save-excursion (when (get-buffer folder) (set-buffer folder) @@ -1258,12 +1215,11 @@ regardless of the size of the `mh-large-folder' variable." (mh-toggle-threads)) (mh-index-data (mh-index-insert-folder-headers))) - (unless mh-showing-mode (delete-other-windows)) (unless (eq current-buffer (current-buffer)) (setq mh-previous-window-config config))) nil) -;;;###mh-autoload + (defun mh-update-sequences () "Update MH's Unseen-Sequence and current folder and message. Flush MH-E's state out to MH. The message at the cursor becomes current." @@ -1334,7 +1290,7 @@ arguments, after the message has been refiled." (mh-exec-cmd "refile" (mh-get-msg-num t) "-link" "-src" mh-current-folder (symbol-name folder)) - (message "Message not copied."))) + (message "Message not copied"))) (t (mh-set-folder-modified-p t) (cond ((null (assoc folder mh-refile-list)) @@ -1381,7 +1337,9 @@ With optional argument COUNT, COUNT-1 unread messages are skipped." (setq count (1- count))) (not (car unread-sequence))) (message "No more unread messages")) - (t (mh-goto-msg (car unread-sequence)))))) + (t (loop for msg in unread-sequence + when (mh-goto-msg msg t) return nil + finally (message "No more unread messages")))))) (defun mh-set-scan-mode () "Display the scan listing buffer, but do not show a message." @@ -1472,12 +1430,12 @@ Make it the current folder." ["Go to First Message" mh-first-msg t] ["Go to Last Message" mh-last-msg t] ["Go to Message by Number..." mh-goto-msg t] - ["Modify Message" mh-modify] + ["Modify Message" mh-modify t] ["Delete Message" mh-delete-msg (mh-get-msg-num nil)] ["Refile Message" mh-refile-msg (mh-get-msg-num nil)] - ["Undo Delete/Refile" mh-undo t] - ["Process Delete/Refile" mh-execute-commands - (or mh-refile-list mh-delete-list)] + ["Undo Delete/Refile" mh-undo (mh-outstanding-commands-p)] + ["Execute Delete/Refile" mh-execute-commands + (mh-outstanding-commands-p)] "--" ["Compose a New Message" mh-send t] ["Reply to Message..." mh-reply (mh-get-msg-num nil)] @@ -1501,7 +1459,7 @@ Make it the current folder." ["Incorporate New Mail" mh-inc-folder t] ["Toggle Show/Folder" mh-toggle-showing t] ["Execute Delete/Refile" mh-execute-commands - (or mh-refile-list mh-delete-list)] + (mh-outstanding-commands-p)] ["Rescan Folder" mh-rescan-folder t] ["Thread Folder" mh-toggle-threads (not (memq 'unthread mh-view-ops))] @@ -1541,6 +1499,12 @@ is used in previous versions and XEmacs." (defvar tool-bar-map) (defvar desktop-save-buffer)) ;Emacs 21.4 +;; Register mh-folder-mode as supporting which-function-mode... +(load "which-func" t t) +(when (and (boundp 'which-func-modes) + (not (member 'mh-folder-mode which-func-modes))) + (push 'mh-folder-mode which-func-modes)) + (define-derived-mode mh-folder-mode fundamental-mode "MH-Folder" "Major MH-E mode for \"editing\" an MH folder scan listing.\\<mh-folder-mode-map> @@ -1548,16 +1512,49 @@ You can show the message the cursor is pointing to, and step through the messages. Messages can be marked for deletion or refiling into another folder; these commands are executed all at once with a separate command. -A prefix argument (\\[universal-argument]) to delete, refile, list, or undo -applies the action to a message sequence. If `transient-mark-mode', -is non-nil, the action is applied to the region. - Options that control this mode can be changed with \\[customize-group]; specify the \"mh\" group. In particular, please see the `mh-scan-format-file' option if you wish to modify scan's format. When a folder is visited, the hook `mh-folder-mode-hook' is run. +Ranges +====== +Many commands that operate on individual messages, such as `mh-forward' or +`mh-refile-msg' take a RANGE argument. This argument can be used in several +ways. + +If you provide the prefix argument (\\[universal-argument]) to these commands, +then you will be prompted for the message range. This can be any legal MH +range which can include messages, sequences, and the abbreviations (described +in the mh(1) man page): + +<num1>-<num2> + Indicates all messages in the range <num1> to <num2>, inclusive. The range + must be nonempty. + +`<num>:N' +`<num>:+N' +`<num>:-N' + Up to N messages beginning with (or ending with) message num. Num may be + any of the pre-defined symbols: first, prev, cur, next or last. + +`first:N' +`prev:N' +`next:N' +`last:N' + The first, previous, next or last messages, if they exist. + +`all' + All of the messages. + +For example, a range that shows all of these things is `1 2 3 5-10 last:5 +unseen'. + +If the option `transient-mark-mode' is set to t and you set a region in the +MH-Folder buffer, then the MH-E command will perform the operation on all +messages in that region. + \\{mh-folder-mode-map}" (make-local-variable 'font-lock-defaults) @@ -1565,10 +1562,15 @@ When a folder is visited, the hook `mh-folder-mode-hook' is run. (make-local-variable 'desktop-save-buffer) (setq desktop-save-buffer t) (mh-make-local-vars + 'mh-colors-available-flag (mh-colors-available-p) + ; Do we have colors available 'mh-current-folder (buffer-name) ; Name of folder, a string 'mh-show-buffer (format "show-%s" (buffer-name)) ; Buffer that displays msgs 'mh-folder-filename ; e.g. "/usr/foobar/Mail/inbox/" (file-name-as-directory (mh-expand-file-name (buffer-name))) + 'mh-display-buttons-for-inline-parts-flag + mh-display-buttons-for-inline-parts-flag ; Allow for display of buttons to + ; be toggled. 'mh-arrow-marker (make-marker) ; Marker where arrow is displayed 'overlay-arrow-position nil ; Allow for simultaneous display in 'overlay-arrow-string ">" ; different MH-E buffers. @@ -1597,6 +1599,8 @@ When a folder is visited, the hook `mh-folder-mode-hook' is run. 'mh-sequence-notation-history (make-hash-table) ; Remember what is overwritten by ; mh-note-seq. + 'imenu-create-index-function 'mh-index-create-imenu-index + ; Setup imenu support 'mh-previous-window-config nil) ; Previous window configuration (mh-remove-xemacs-horizontal-scrollbar) (setq truncate-lines t) @@ -1620,6 +1624,26 @@ When a folder is visited, the hook `mh-folder-mode-hook' is run. font-lock-auto-fontify) (turn-on-font-lock))) ; Force font-lock in XEmacs. +(defun mh-toggle-mime-buttons () + "Toggle display of buttons for inline MIME parts." + (interactive) + (setq mh-display-buttons-for-inline-parts-flag + (not mh-display-buttons-for-inline-parts-flag)) + (mh-show nil t)) + +(defun mh-colors-available-p () + "Check if colors are available in the Emacs being used." + (or mh-xemacs-flag + (let ((color-cells + (or (ignore-errors (mh-funcall-if-exists display-color-cells)) + (ignore-errors (mh-funcall-if-exists + x-display-color-cells))))) + (and (numberp color-cells) (>= color-cells 8))))) + +(defun mh-colors-in-use-p () + "Check if colors are being used in the folder buffer." + (and mh-colors-available-flag font-lock-mode)) + (defun mh-make-local-vars (&rest pairs) "Initialize local variables according to the variable-value PAIRS." @@ -1631,7 +1655,11 @@ When a folder is visited, the hook `mh-folder-mode-hook' is run. (defun mh-restore-desktop-buffer (desktop-buffer-file-name desktop-buffer-name desktop-buffer-misc) - "Restore an MH folder buffer specified in a desktop file." + "Restore an MH folder buffer specified in a desktop file. +When desktop creates a buffer, DESKTOP-BUFFER-FILE-NAME holds the file name to +visit, DESKTOP-BUFFER-NAME holds the desired buffer name, and +DESKTOP-BUFFER-MISC holds a list of miscellaneous info used by the +`desktop-buffer-handlers' functions." (mh-find-path) (mh-visit-folder desktop-buffer-name) (current-buffer)) @@ -1641,6 +1669,8 @@ When a folder is visited, the hook `mh-folder-mode-hook' is run. If the optional argument DONT-EXEC-PENDING is non-nil then pending deletes and refiles aren't carried out. Return in the folder's buffer." + (when (stringp range) + (setq range (delete "" (split-string range "[ \t\n]")))) (cond ((null (get-buffer folder)) (mh-make-folder folder)) (t @@ -1693,7 +1723,9 @@ If UPDATE, append the scan lines, otherwise replace." (goto-char scan-start) (cond ((looking-at "scan: no messages in") (keep-lines mh-scan-valid-regexp)) ; Flush random scan lines - ((looking-at "scan: bad message list ") + ((looking-at (if (mh-variant-p 'mu-mh) + "scan: message set .* does not exist" + "scan: bad message list ")) (keep-lines mh-scan-valid-regexp)) ((looking-at "scan: ")) ; Keep error messages (t @@ -1869,46 +1901,21 @@ in what is now stored in the buffer-local variable `mh-mode-line-annotation'." ("")))))) (mh-logo-display)))) -;;; XXX: Remove this function, if no one uses it any more... -(defun mh-unmark-all-headers (remove-all-flags) - "Remove all '+' flags from the folder listing. -With non-nil argument REMOVE-ALL-FLAGS, remove all 'D', '^' and '%' flags too. -Optimized for speed (i.e., no regular expressions). - -This function is deprecated. Use `mh-remove-all-notation' instead." - (save-excursion - (let ((case-fold-search nil) - (last-line (1- (point-max))) - char) - (mh-first-msg) - (while (<= (point) last-line) - (forward-char mh-cmd-note) - (setq char (following-char)) - (if (or (and remove-all-flags - (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 (= (following-char) (aref mh-note-seq 0)) - (progn - (delete-char 1) - (insert " "))))) - (forward-line))))) - (defun mh-add-sequence-notation (msg internal-seq-flag) "Add sequence notation to the MSG on the current line. -If INTERNAL-SEQ-FLAG is non-nil, then just remove text properties from the -current line, so that font-lock would automatically refontify it." +If INTERNAL-SEQ-FLAG is non-nil, then refontify the scan line if font-lock is +turned on." (with-mh-folder-updating (t) (save-excursion (beginning-of-line) (if internal-seq-flag - (mh-notate nil nil mh-cmd-note) + (progn + ;; Change the buffer so that if transient-mark-mode is active + ;; and there is an active region it will get deactivated as in + ;; the case of user sequences. + (mh-notate nil nil mh-cmd-note) + (when font-lock-mode + (font-lock-fontify-region (point) (line-end-position)))) (forward-char (1+ mh-cmd-note)) (let ((stack (gethash msg mh-sequence-notation-history))) (setf (gethash msg mh-sequence-notation-history) @@ -1930,7 +1937,11 @@ If ALL is non-nil, then all sequence marks on the scan line are removed." (while (and all (cdr stack)) (setq stack (cdr stack))) (when stack - (mh-notate nil (car stack) (1+ mh-cmd-note))) + (save-excursion + (beginning-of-line) + (forward-char (1+ mh-cmd-note)) + (delete-char 1) + (insert (car stack)))) (setf (gethash msg mh-sequence-notation-history) (cdr stack)))))) (defun mh-remove-cur-notation () @@ -1953,7 +1964,7 @@ If ALL is non-nil, then all sequence marks on the scan line are removed." (mh-remove-sequence-notation msg nil t)) (clrhash mh-sequence-notation-history))) -;;;###mh-autoload + (defun mh-goto-cur-msg (&optional minimal-changes-flag) "Position the cursor at the current message. When optional argument MINIMAL-CHANGES-FLAG is non-nil, the function doesn't @@ -2102,7 +2113,10 @@ with no arguments, after the unseen sequence is updated." (defun mh-outstanding-commands-p () "Return non-nil if there are outstanding deletes or refiles." - (or mh-delete-list mh-refile-list)) + (save-excursion + (when (eq major-mode 'mh-show-mode) + (set-buffer mh-show-folder-buffer)) + (or mh-delete-list mh-refile-list))) (defun mh-coalesce-msg-list (messages) "Given a list of MESSAGES, return a list of message number ranges. @@ -2223,7 +2237,7 @@ numbers, a sequence, a region in a cons cell. If nil all messages are notated." "Return non-nil if NAME is the name of an internal MH-E sequence." (or (memq name mh-internal-seqs) (eq name mh-unseen-seq) - (and mh-tick-seq (eq name mh-tick-seq)) + (and (mh-colors-in-use-p) mh-tick-seq (eq name mh-tick-seq)) (eq name mh-previous-seq) (mh-folder-name-p name))) @@ -2264,6 +2278,15 @@ change." (when (and (eq sequence mh-unseen-seq) (mh-speed-flists-active-p)) (apply #'mh-speed-flists t folders-changed))))) +(defun mh-catchup (range) + "Delete RANGE from the `mh-unseen-seq' sequence. + +Check the document of `mh-interactive-range' to see how RANGE is read in +interactive use." + (interactive (list (mh-interactive-range "Catchup" + (cons (point-min) (point-max))))) + (mh-delete-msg-from-seq range mh-unseen-seq)) + (defun mh-delete-a-msg-from-seq (msg sequence internal-flag) "Delete MSG from SEQUENCE. If INTERNAL-FLAG is non-nil, then do not inform MH of the change." @@ -2291,23 +2314,6 @@ Signals an error if SEQ is an illegal name." "-sequence" (symbol-name seq) (mh-coalesce-msg-list msgs))))) -(defun mh-map-over-seqs (function seq-list) - "Apply FUNCTION to each sequence in SEQ-LIST. -The sequence name and the list of messages are passed as arguments." - (while seq-list - (funcall function - (mh-seq-name (car seq-list)) - (mh-seq-msgs (car seq-list))) - (setq seq-list (cdr seq-list)))) - -(defun mh-notate-if-in-one-seq (msg character offset seq) - "Notate MSG. -The CHARACTER is placed at the given OFFSET from the beginning of the listing. -The notation is performed if the MSG is only in SEQ." - (let ((in-seqs (mh-seq-containing-msg msg nil))) - (if (and (eq seq (car in-seqs)) (null (cdr in-seqs))) - (mh-notate msg character offset)))) - (defun mh-seq-containing-msg (msg &optional include-internal-flag) "Return a list of the sequences containing MSG. If INCLUDE-INTERNAL-FLAG non-nil, include MH-E internal sequences in list." @@ -2362,7 +2368,6 @@ If INCLUDE-INTERNAL-FLAG non-nil, include MH-E internal sequences in list." "g" mh-goto-msg "i" mh-inc-folder "k" mh-delete-subject-or-thread - "l" mh-print-msg "m" mh-alt-send "n" mh-next-undeleted-msg "\M-n" mh-next-unread-msg @@ -2382,6 +2387,7 @@ If INCLUDE-INTERNAL-FLAG non-nil, include MH-E internal sequences in list." "?" mh-prefix-help "'" mh-index-ticked-messages "S" mh-sort-folder + "c" mh-catchup "f" mh-alt-visit-folder "i" mh-index-search "k" mh-kill-folder @@ -2402,6 +2408,17 @@ If INCLUDE-INTERNAL-FLAG non-nil, include MH-E internal sequences in list." "b" mh-junk-blacklist "w" mh-junk-whitelist) +(gnus-define-keys (mh-ps-print-map "P" mh-folder-mode-map) + "?" mh-prefix-help + "A" mh-ps-print-toggle-mime + "C" mh-ps-print-toggle-color + "F" mh-ps-print-toggle-faces + "M" mh-ps-print-toggle-mime + "f" mh-ps-print-msg-file + "l" mh-print-msg + "p" mh-ps-print-msg + "s" mh-ps-print-msg-show) + (gnus-define-keys (mh-sequence-map "S" mh-folder-mode-map) "'" mh-narrow-to-tick "?" mh-prefix-help @@ -2446,8 +2463,10 @@ If INCLUDE-INTERNAL-FLAG non-nil, include MH-E internal sequences in list." (gnus-define-keys (mh-mime-map "K" mh-folder-mode-map) "?" mh-prefix-help "a" mh-mime-save-parts + "e" mh-display-with-external-viewer "i" mh-folder-inline-mime-part "o" mh-folder-save-mime-part + "t" mh-toggle-mime-buttons "v" mh-folder-toggle-mime-part "\t" mh-next-button [backtab] mh-prev-button @@ -2484,6 +2503,9 @@ If INCLUDE-INTERNAL-FLAG non-nil, include MH-E internal sequences in list." (?F "[l]ist; [v]isit folder;\n" "[n]ew messages; [']ticked messages; [s]earch; [i]ndexed search;\n" "[p]ack; [S]ort; [r]escan; [k]ill") + (?P "PS [p]rint message; [l]non-PS print;\n" + "PS Print [s]how window, message to [f]ile;\n" + "Toggle printing of [M]IME parts, [C]olor, [F]aces") (?S "[p]ut message in sequence, [n]arrow, [']narrow to ticked, [w]iden,\n" "[s]equences, [l]ist,\n" "[d]elete message from sequence, [k]ill sequence") diff --git a/lisp/mh-e/mh-funcs.el b/lisp/mh-e/mh-funcs.el index 46201860e2a..ef745f4c06f 100644 --- a/lisp/mh-e/mh-funcs.el +++ b/lisp/mh-e/mh-funcs.el @@ -1,6 +1,6 @@ ;;; mh-funcs.el --- MH-E functions not everyone will use right away -;; Copyright (C) 1993, 1995, 2001, 02, 2003 Free Software Foundation, Inc. +;; Copyright (C) 1993, 1995, 2001, 02, 03, 2004 Free Software Foundation, Inc. ;; Author: Bill Wohler <wohler@newt.com> ;; Maintainer: Bill Wohler <wohler@newt.com> @@ -34,6 +34,8 @@ ;;; Code: +(eval-when-compile (require 'mh-acros)) +(mh-require-cl) (require 'mh-e) ;;; Customization @@ -45,11 +47,13 @@ prefix argument. Normally default arguments to sortm are specified in the MH profile. For example, '(\"-nolimit\" \"-textfield\" \"subject\") is a useful setting.") +;;; Scan Line Formats + (defvar mh-note-copied "C" - "String whose first character is used to notate copied messages.") + "Copied messages are marked by this character.") (defvar mh-note-printed "P" - "String whose first character is used to notate printed messages.") + "Messages that have been printed are marked by this character.") ;;; Functions @@ -233,60 +237,6 @@ Otherwise just send the message's body without the headers." (mh-recenter 0))) ;;;###mh-autoload -(defun mh-print-msg (range) - "Print RANGE on printer. - -Check the documentation of `mh-interactive-range' to see how RANGE is read in -interactive use. - -The variable `mh-lpr-command-format' is used to generate the print command. -The messages are formatted by mhl. See the variable `mhl-formfile'." - (interactive (list (mh-interactive-range "Print"))) - (message "Printing...") - (let (msgs) - ;; Gather message numbers and add them to "printed" sequence. - (mh-iterate-on-range msg range - (mh-add-msgs-to-seq msg 'printed t) - (mh-notate nil mh-note-printed mh-cmd-note) - (push msg msgs)) - (setq msgs (nreverse msgs)) - ;; Print scan listing if we have more than one message. - (if (> (length msgs) 1) - (let* ((msgs-string - (mapconcat 'identity (mh-list-to-string - (mh-coalesce-msg-list msgs)) " ")) - (lpr-command - (format mh-lpr-command-format - (cond ((listp range) - (format "Folder: %s, Messages: %s" - mh-current-folder msgs-string)) - ((symbolp range) - (format "Folder: %s, Sequence: %s" - mh-current-folder range))))) - (scan-command - (format "scan %s | %s" msgs-string lpr-command))) - (if mh-print-background-flag - (mh-exec-cmd-daemon shell-file-name nil "-c" scan-command) - (call-process shell-file-name nil nil nil "-c" scan-command)))) - ;; Print the messages - (dolist (msg msgs) - (let* ((mhl-command (format "%s %s %s" - (expand-file-name "mhl" mh-lib-progs) - (if mhl-formfile - (format " -form %s" mhl-formfile) - "") - (mh-msg-filename msg))) - (lpr-command - (format mh-lpr-command-format - (format "%s/%s" mh-current-folder msg))) - (print-command - (format "%s | %s" mhl-command lpr-command))) - (if mh-print-background-flag - (mh-exec-cmd-daemon shell-file-name nil "-c" print-command) - (call-process shell-file-name nil nil nil "-c" print-command))))) - (message "Printing...done")) - -;;;###mh-autoload (defun mh-sort-folder (&optional extra-args) "Sort the messages in the current folder by date. Calls the MH program sortm to do the work. @@ -307,9 +257,8 @@ argument EXTRA-ARGS is given." (mh-index-data (mh-index-insert-folder-headers))))) ;;;###mh-autoload -(defun mh-undo-folder (&rest ignore) - "Undo all pending deletes and refiles in current folder. -Argument IGNORE is deprecated." +(defun mh-undo-folder () + "Undo all pending deletes and refiles in current folder." (interactive) (cond ((or mh-do-not-confirm-flag (yes-or-no-p "Undo all commands in folder? ")) @@ -320,10 +269,7 @@ Argument IGNORE is deprecated." (with-mh-folder-updating (nil) (mh-remove-all-notation))) (t - (message "Commands not undone.") - ;; Remove by 2003-06-30 if nothing seems amiss. XXX - ;; (sit-for 2) - ))) + (message "Commands not undone")))) ;;;###mh-autoload (defun mh-store-msg (directory) @@ -413,11 +359,15 @@ Default directory is the last directory used, or initially the value of ;;;###mh-autoload (defun mh-help () - "Display cheat sheet for the MH-Folder commands in minibuffer." + "Display cheat sheet for the MH-E commands." (interactive) - (mh-ephem-message - (substitute-command-keys - (mapconcat 'identity (cdr (assoc nil mh-help-messages)) "")))) + (with-electric-help + (function + (lambda () + (insert + (substitute-command-keys + (mapconcat 'identity (cdr (assoc nil mh-help-messages)) "")))) + mh-help-buffer))) ;;;###mh-autoload (defun mh-prefix-help () @@ -430,9 +380,14 @@ Default directory is the last directory used, or initially the value of ;; from the recent keys. (let* ((keys (recent-keys)) (prefix-char (elt keys (- (length keys) 2)))) - (mh-ephem-message - (substitute-command-keys - (mapconcat 'identity (cdr (assoc prefix-char mh-help-messages)) ""))))) + (with-electric-help + (function + (lambda () + (insert + (substitute-command-keys + (mapconcat 'identity + (cdr (assoc prefix-char mh-help-messages)) ""))))) + mh-help-buffer))) (provide 'mh-funcs) diff --git a/lisp/mh-e/mh-gnus.el b/lisp/mh-e/mh-gnus.el index 0a893efa3c9..b850c8fdc43 100644 --- a/lisp/mh-e/mh-gnus.el +++ b/lisp/mh-e/mh-gnus.el @@ -1,6 +1,6 @@ ;;; mh-gnus.el --- Make MH-E compatible with installed version of Gnus. -;; Copyright (C) 2003 Free Software Foundation, Inc. +;; Copyright (C) 2003, 2004 Free Software Foundation, Inc. ;; Author: Satyaki Das <satyaki@theforce.stanford.edu> ;; Maintainer: Bill Wohler <wohler@newt.com> @@ -34,6 +34,7 @@ (load "mm-uu" t t) ; Non-fatal dependency (load "mailcap" t t) ; Non-fatal dependency (load "smiley" t t) ; Non-fatal dependency +(load "mailabbrev" t t) (defmacro mh-defun-compat (function arg-list &rest body) "This is a macro to define functions which are not defined. @@ -74,12 +75,28 @@ BODY." (put-text-property 0 (length (car handle)) parameter value (car handle)))) +;; Copy of function from mm-view.el +(mh-defun-compat mm-inline-text-vcard (handle) + (let (buffer-read-only) + (mm-insert-inline + handle + (concat "\n-- \n" + (ignore-errors + (if (fboundp 'vcard-pretty-print) + (vcard-pretty-print (mm-get-part handle)) + (vcard-format-string + (vcard-parse-string (mm-get-part handle) + 'vcard-standard-filter)))))))) + +;; Function from mm-decode.el used in PGP messages. Just define it with older +;; gnus to avoid compiler warning. +(mh-defun-compat mm-possibly-verify-or-decrypt (parts ctl) + nil) + ;; Copy of original macro is in mm-decode.el (mh-defmacro-compat mm-handle-multipart-ctl-parameter (handle parameter) `(get-text-property 0 ,parameter (car ,handle))) -(mh-do-in-xemacs (defvar default-enable-multibyte-characters)) - ;; Copy of original function in mm-decode.el (mh-defun-compat mm-readable-p (handle) "Say whether the content of HANDLE is readable." @@ -134,10 +151,23 @@ BODY." file))) (mm-save-part-to-file handle file)))) +(defun mh-mm-text-html-renderer () + "Find the renderer gnus is using to display text/html MIME parts." + (or (and (boundp 'mm-inline-text-html-renderer) mm-inline-text-html-renderer) + (and (boundp 'mm-text-html-renderer) mm-text-html-renderer))) + +(defun mh-mail-abbrev-make-syntax-table () + "Call `mail-abbrev-make-syntax-table' if available." + (when (fboundp 'mail-abbrev-make-syntax-table) + (mail-abbrev-make-syntax-table))) + (provide 'mh-gnus) + ;;; Local Variables: ;;; no-byte-compile: t ;;; no-update-autoloads: t +;;; indent-tabs-mode: nil +;;; sentence-end-double-space: nil ;;; End: ;; arch-tag: 1e3638af-cad3-4c69-8427-bc8eb6e5e4fa diff --git a/lisp/mh-e/mh-identity.el b/lisp/mh-e/mh-identity.el index f4edc7a2087..2b430a52fe1 100644 --- a/lisp/mh-e/mh-identity.el +++ b/lisp/mh-e/mh-identity.el @@ -39,47 +39,50 @@ ;;; Code: - -(require 'mh-utils) +(eval-when-compile (require 'mh-acros)) (mh-require-cl) - -(eval-when (compile load eval) - (defvar mh-comp-loaded nil) - (unless mh-comp-loaded - (setq mh-comp-loaded t) - (require 'mh-comp))) ;Since we do this on sending +(require 'mh-comp) (autoload 'mml-insert-tag "mml") +(defvar mh-identity-pgg-default-user-id nil + "Holds the GPG key ID to be used by pgg.el. +This is normally set as part of an Identity in `mh-identity-list'.") +(make-variable-buffer-local 'mh-identity-pgg-default-user-id) + ;;;###mh-autoload (defun mh-identity-make-menu () - "Build (or rebuild) the Identity menu (e.g. after the list is modified)." - (when (and mh-identity-list (boundp 'mh-letter-mode-map)) - (easy-menu-define mh-identity-menu mh-letter-mode-map - "mh-e identity menu" - (append - '("Identity") - ;; Dynamically render :type corresponding to `mh-identity-list' - ;; e.g.: - ;; ["home" (mh-insert-identity "home") - ;; :style radio :active (not (equal mh-identity-local "home")) - ;; :selected (equal mh-identity-local "home")] - '(["Insert Auto Fields" (mh-insert-auto-fields) mh-auto-fields-list] - "--") - (mapcar (function - (lambda (arg) - `[,arg (mh-insert-identity ,arg) :style radio - :active (not (equal mh-identity-local ,arg)) - :selected (equal mh-identity-local ,arg)])) - (mapcar 'car mh-identity-list)) - '("--" - ["none" (mh-insert-identity "none") mh-identity-local] - ["Set Default for Session" - (setq mh-identity-default mh-identity-local) t] - ["Save as Default" - (customize-save-variable - 'mh-identity-default mh-identity-local) t] - ))))) + "Build the Identity menu. +This should be called any time `mh-identity-list' or `mh-auto-fields-list' +change." + (easy-menu-define mh-identity-menu mh-letter-mode-map + "MH-E identity menu" + (append + '("Identity") + ;; Dynamically render :type corresponding to `mh-identity-list' + ;; e.g.: + ;; ["Home" (mh-insert-identity "Home") + ;; :style radio :active (not (equal mh-identity-local "Home")) + ;; :selected (equal mh-identity-local "Home")] + '(["Insert Auto Fields" + (mh-insert-auto-fields) mh-auto-fields-list] + "--") + + (mapcar (function + (lambda (arg) + `[,arg (mh-insert-identity ,arg) :style radio + :selected (equal mh-identity-local ,arg)])) + (mapcar 'car mh-identity-list)) + '(["None" + (mh-insert-identity "None") :style radio + :selected (not mh-identity-local)] + "--" + ["Set Default for Session" + (setq mh-identity-default mh-identity-local) t] + ["Save as Default" + (customize-save-variable 'mh-identity-default mh-identity-local) t] + ["Customize Identities" (customize-variable 'mh-identity-list) t] + )))) ;;;###mh-autoload (defun mh-identity-list-set (symbol value) @@ -97,21 +100,36 @@ customization). This is called after 'customize is used to alter (defun mh-header-field-delete (field value-only) "Delete FIELD in the mail header, or only its value if VALUE-ONLY is t. Return t if anything is deleted." - (when (mh-goto-header-field field) - (if (not value-only) - (beginning-of-line) - (forward-char)) - (delete-region (point) - (progn (mh-header-field-end) - (if (not value-only) (forward-char 1)) - (point))) - t)) + (let ((field-colon (if (string-match "^.*:$" field) + field + (concat field ":")))) + (when (mh-goto-header-field field-colon) + (if (not value-only) + (beginning-of-line) + (forward-char)) + (delete-region (point) + (progn (mh-header-field-end) + (if (not value-only) (forward-char 1)) + (point))) + t))) (defvar mh-identity-signature-start nil "Marker for the beginning of a signature inserted by `mh-insert-identity'.") (defvar mh-identity-signature-end nil "Marker for the end of a signature inserted by `mh-insert-identity'.") +(defun mh-identity-field-handler (field) + "Return the handler for a FIELD or nil if none set. +The field name is downcased. If the FIELD begins with the character +`:', then it must have a special handler defined in +`mh-identity-handlers', else return an error since it is not a legal +message header." + (or (cdr (assoc (downcase field) mh-identity-handlers)) + (and (eq (aref field 0) ?:) + (error (format "Field %s - unknown mh-identity-handler" field))) + (cdr (assoc "default" mh-identity-handlers)) + 'mh-identity-handler-default)) + ;;;###mh-autoload (defun mh-insert-identity (identity) "Insert proper fields for given IDENTITY. @@ -120,7 +138,7 @@ Edit the `mh-identity-list' variable to define identity." (list (completing-read "Identity: " (if mh-identity-local - (cons '("none") + (cons '("None") (mapcar 'list (mapcar 'car mh-identity-list))) (mapcar 'list (mapcar 'car mh-identity-list))) nil t))) @@ -129,83 +147,135 @@ Edit the `mh-identity-list' variable to define identity." (when mh-identity-local (let ((pers-list (cadr (assoc mh-identity-local mh-identity-list)))) (while pers-list - (let ((field (concat (caar pers-list) ":"))) - (cond - ((string-equal "signature:" field) - (when (and (boundp 'mh-identity-signature-start) - (markerp mh-identity-signature-start)) - (goto-char mh-identity-signature-start) - (forward-char -1) - (delete-region (point) mh-identity-signature-end))) - ((mh-header-field-delete field nil)))) + (let* ((field (caar pers-list)) + (handler (mh-identity-field-handler field))) + (funcall handler field 'remove)) (setq pers-list (cdr pers-list))))) ;; Then insert the replacement - (when (not (equal "none" identity)) + (when (not (equal "None" identity)) (let ((pers-list (cadr (assoc identity mh-identity-list)))) (while pers-list - (let ((field (concat (caar pers-list) ":")) - (value (cdar pers-list))) - (cond - ;; No value, remove field - ((or (not value) - (string= value "")) - (mh-header-field-delete field nil)) - ;; Existing field, replace - ((mh-header-field-delete field t) - (insert value)) - ;; Handle "signature" special case. Insert file or call function. - ((and (string-equal "signature:" field) - (or (and (stringp value) - (file-readable-p value)) - (fboundp value))) - (goto-char (point-max)) - (if (not (looking-at "^$")) - (insert "\n")) - (insert "\n") - (save-restriction - (narrow-to-region (point) (point)) - (set (make-local-variable 'mh-identity-signature-start) - (make-marker)) - (set-marker mh-identity-signature-start (point)) - (cond - ;; If MIME composition done, insert signature at the end as - ;; an inline MIME part. - ((mh-mhn-directive-present-p) - (insert "#\n" "Content-Description: Signature\n")) - ((mh-mml-directive-present-p) - (mml-insert-tag 'part 'type "text/plain" - 'disposition "inline" - 'description "Signature"))) - (if (stringp value) - (insert-file-contents value) - (funcall value)) - (goto-char (point-min)) - (when (not (re-search-forward "^--" nil t)) - (cond ((mh-mhn-directive-present-p) - (forward-line 2)) - ((mh-mml-directive-present-p) - (forward-line 1))) - (insert "-- \n")) - (set (make-local-variable 'mh-identity-signature-end) - (make-marker)) - (set-marker mh-identity-signature-end (point-max)))) - ;; Handle "From" field differently, adding it at the beginning. - ((string-equal "From:" field) - (goto-char (point-min)) - (insert "From: " value "\n")) - ;; Skip empty signature (Can't remove what we don't know) - ((string-equal "signature:" field)) - ;; Other field, add at end - (t ;Otherwise, add the end. - (goto-char (point-min)) - (mh-goto-header-end 0) - (mh-insert-fields field value)))) + (let* ((field (caar pers-list)) + (value (cdar pers-list)) + (handler (mh-identity-field-handler field))) + (funcall handler field 'add value)) (setq pers-list (cdr pers-list)))))) ;; Remember what is in use in this buffer - (if (equal "none" identity) + (if (equal "None" identity) (setq mh-identity-local nil) (setq mh-identity-local identity))) +;;;###mh-autoload +(defun mh-identity-handler-gpg-identity (field action &optional value) + "For FIELD \"pgg-default-user-id\", process for ACTION 'remove or 'add. +The buffer-local variable `mh-identity-pgg-default-user-id' is set to VALUE +when action 'add is selected." + (cond + ((or (equal action 'remove) + (not value) + (string= value "")) + (setq mh-identity-pgg-default-user-id nil)) + ((equal action 'add) + (setq mh-identity-pgg-default-user-id value)))) + +;;;###mh-autoload +(defun mh-identity-handler-signature (field action &optional value) + "For FIELD \"signature\", process headers for ACTION 'remove or 'add. +The VALUE is added." + (cond + ((equal action 'remove) + (when (and (markerp mh-identity-signature-start) + (markerp mh-identity-signature-end)) + (delete-region mh-identity-signature-start + mh-identity-signature-end))) + (t + ;; Insert "signature". Nil value means to use `mh-signature-file-name'. + (when (not (mh-signature-separator-p)) ;...unless already present + (goto-char (point-max)) + (save-restriction + (narrow-to-region (point) (point)) + (if (null value) + (mh-insert-signature) + (mh-insert-signature value)) + (set (make-local-variable 'mh-identity-signature-start) + (point-min-marker)) + (set-marker-insertion-type mh-identity-signature-start t) + (set (make-local-variable 'mh-identity-signature-end) + (point-max-marker))))))) + +(defvar mh-identity-attribution-verb-start nil + "Marker for the beginning of the attribution verb.") +(defvar mh-identity-attribution-verb-end nil + "Marker for the end of the attribution verb.") + +;;;###mh-autoload +(defun mh-identity-handler-attribution-verb (field action &optional value) + "For FIELD \"attribution_verb\", process headers for ACTION 'remove or 'add. +The VALUE is added." + (when (and (markerp mh-identity-attribution-verb-start) + (markerp mh-identity-attribution-verb-end)) + (delete-region mh-identity-attribution-verb-start + mh-identity-attribution-verb-end) + (goto-char mh-identity-attribution-verb-start) + (cond + ((equal action 'remove) ; Replace with default + (mh-identity-insert-attribution-verb nil)) + (t ; Insert attribution verb. + (mh-identity-insert-attribution-verb value))))) + +;;;###mh-autoload +(defun mh-identity-insert-attribution-verb (value) + "Insert VALUE as attribution verb, setting up delimiting markers. +If VALUE is nil, use `mh-extract-from-attribution-verb'." + (save-restriction + (narrow-to-region (point) (point)) + (if (null value) + (insert mh-extract-from-attribution-verb) + (insert value)) + (set (make-local-variable 'mh-identity-attribution-verb-start) + (point-min-marker)) + (set-marker-insertion-type mh-identity-attribution-verb-start t) + (set (make-local-variable 'mh-identity-attribution-verb-end) + (point-max-marker)))) + +(defun mh-identity-handler-default (field action top &optional value) + "For FIELD, process mh-identity headers for ACTION 'remove or 'add. +if TOP is non-nil, add the field and it's VALUE at the top of the header, else +add it at the bottom of the header." + (let ((field-colon (if (string-match "^.*:$" field) + field + (concat field ":")))) + (cond + ((equal action 'remove) + (mh-header-field-delete field-colon nil)) + (t + (cond + ;; No value, remove field + ((or (not value) + (string= value "")) + (mh-header-field-delete field-colon nil)) + ;; Existing field, replace + ((mh-header-field-delete field-colon t) + (insert value)) + ;; Other field, add at end or top + (t + (goto-char (point-min)) + (if (not top) + (mh-goto-header-end 0)) + (insert field-colon " " value "\n"))))))) + +;;;###mh-autoload +(defun mh-identity-handler-top (field action &optional value) + "For FIELD, process mh-identity headers for ACTION 'remove or 'add. +If the field wasn't present, the VALUE is added at the top of the header." + (mh-identity-handler-default field action t value)) + +;;;###mh-autoload +(defun mh-identity-handler-bottom (field action &optional value) + "For FIELD, process mh-identity headers for ACTION 'remove or 'add. +If the field wasn't present, the VALUE is added at the bottom of the header." + (mh-identity-handler-default field action nil value)) + (provide 'mh-identity) ;;; Local Variables: diff --git a/lisp/mh-e/mh-inc.el b/lisp/mh-e/mh-inc.el index 1c052b140bd..42ca018506f 100644 --- a/lisp/mh-e/mh-inc.el +++ b/lisp/mh-e/mh-inc.el @@ -1,6 +1,6 @@ ;;; mh-inc.el --- MH-E `inc' and separate mail spool handling ;; -;; Copyright (C) 2003 Free Software Foundation, Inc. +;; Copyright (C) 2003, 2004 Free Software Foundation, Inc. ;; Author: Peter S. Galbraith <psg@debian.org> ;; Maintainer: Bill Wohler <wohler@newt.com> @@ -34,7 +34,8 @@ ;;; Code: -(eval-when-compile (require 'cl)) +(eval-when-compile (require 'mh-acros)) +(mh-require-cl) (defvar mh-inc-spool-map (make-sparse-keymap) "Keymap for MH-E's mh-inc-spool commands.") @@ -46,7 +47,8 @@ '(lambda () (interactive) (if mh-inc-spool-map-help - (mh-ephem-message (substring mh-inc-spool-map-help 0 -1)) + (let ((mh-help-messages (list (list nil mh-inc-spool-map-help)))) + (mh-help)) (mh-ephem-message "There are no keys defined yet. Customize `mh-inc-spool-list'")))) diff --git a/lisp/mh-e/mh-index.el b/lisp/mh-e/mh-index.el index 734ce938616..91eed420e2e 100644 --- a/lisp/mh-e/mh-index.el +++ b/lisp/mh-e/mh-index.el @@ -31,7 +31,6 @@ ;;; swish-e ;;; mairix ;;; namazu -;;; glimpse ;;; grep ;;; ;;; (2) To use this package, you first have to build an index. Please read @@ -43,7 +42,7 @@ ;;; Code: -(require 'mh-utils) +(eval-when-compile (require 'mh-acros)) (mh-require-cl) (require 'mh-e) (require 'mh-mime) @@ -66,8 +65,6 @@ mh-mairix-regexp-builder) (namazu mh-namazu-binary mh-namazu-execute-search mh-namazu-next-result nil) - (glimpse - mh-glimpse-binary mh-glimpse-execute-search mh-glimpse-next-result nil) (pick mh-pick-binary mh-pick-execute-search mh-pick-next-result mh-pick-regexp-builder) @@ -200,7 +197,8 @@ This function should only be called in the appropriate index folder buffer." (call-process "rm" nil nil nil (format "%s%s/%s" mh-user-path (substring mh-current-folder 1) msg)) - (remhash omsg (gethash ofolder mh-index-data)))) + (when (gethash ofolder mh-index-data) + (remhash omsg (gethash ofolder mh-index-data))))) (t (setf (gethash msg mh-index-msg-checksum-map) checksum) (when origin-map @@ -301,7 +299,8 @@ list of messages in that sequence." (pair (gethash checksum mh-index-checksum-origin-map)) (ofolder (car pair)) (omsg (cdr pair))) - (loop for seq in (gethash omsg (gethash ofolder seq-hash)) + (loop for seq in (ignore-errors + (gethash omsg (gethash ofolder seq-hash))) do (if (assoc seq seq-list) (push msg (cdr (assoc seq seq-list))) (push (list seq msg) seq-list))))) @@ -374,7 +373,6 @@ index for each program: - `mh-swish-execute-search' - `mh-mairix-execute-search' - `mh-namazu-execute-search' - - `mh-glimpse-execute-search' If none of these programs are present then we use pick. If desired grep can be used instead. Details about these methods can be found in: @@ -436,7 +434,7 @@ This has the effect of renaming already present X-MHE-Checksum headers." (save-excursion (mh-exec-cmd-quiet nil "rmf" buffer-name)) (mh-exec-cmd-quiet nil "folder" "-create" "-fast" buffer-name) (setq index-folder buffer-name)) - (setq index-folder (mh-index-new-folder index-folder))) + (setq index-folder (mh-index-new-folder index-folder search-regexp))) (let ((folder-path (format "%s%s" mh-user-path (substring folder 1))) (folder-results-map (make-hash-table :test #'equal)) @@ -587,13 +585,6 @@ PROC is used to convert the value to actual data." mh-previous-window-config) (error "No search terms")))) -(defun mh-replace-string (old new) - "Replace all occurrences of OLD with NEW in the current buffer." - (goto-char (point-min)) - (let ((case-fold-search t)) - (while (search-forward old nil t) - (replace-match new t t)))) - ;;;###mh-autoload (defun mh-index-parse-search-regexp (input-string) "Construct parse tree for INPUT-STRING. @@ -739,28 +730,48 @@ results." "Check if MSG exists in FOLDER." (file-exists-p (format "%s%s/%s" mh-user-path (substring folder 1) msg))) -(defun mh-index-new-folder (name) - "Create and return an MH folder name based on NAME. -If the folder NAME already exists then check if NAME<2> exists. If it doesn't -then it is created and returned. Otherwise try NAME<3>. This is repeated till -we find a new folder name." +(defun mh-index-new-folder (name search-regexp) + "Return a folder name based on NAME for search results of SEARCH-REGEXP. + +If folder NAME already exists and was generated for the same SEARCH-REGEXP +then it is reused. + +Otherwise if the folder NAME was generated from a different search then check +if NAME<2> can be used. Otherwise try NAME<3>. This is repeated till we find a +new folder name. + +If the folder returned doesn't exist then it is created." (unless (mh-folder-name-p name) (error "The argument should be a valid MH folder name")) - (let ((chosen-name name)) - (block unique-name - (unless (mh-folder-exists-p name) - (return-from unique-name)) - (loop for index from 2 - do (let ((new-name (format "%s<%s>" name index))) - (unless (mh-folder-exists-p new-name) - (setq chosen-name new-name) - (return-from unique-name))))) + (let ((chosen-name + (loop for i from 1 + for candidate = (if (equal i 1) name (format "%s<%s>" name i)) + when (or (not (mh-folder-exists-p candidate)) + (equal (mh-index-folder-search-regexp candidate) + search-regexp)) + return candidate))) + ;; Do pending refiles/deletes... + (when (get-buffer chosen-name) + (mh-process-or-undo-commands chosen-name)) + ;; Recreate folder... + (save-excursion (mh-exec-cmd-quiet nil "rmf" chosen-name)) (mh-exec-cmd-quiet nil "folder" "-create" "-fast" chosen-name) (mh-remove-from-sub-folders-cache chosen-name) (when (boundp 'mh-speed-folder-map) (mh-speed-add-folder chosen-name)) chosen-name)) +(defun mh-index-folder-search-regexp (folder) + "If FOLDER was created by a index search, return the search regexp. +Return nil if FOLDER doesn't exist or the .mhe_index file is garbled." + (ignore-errors + (with-temp-buffer + (insert-file-contents + (format "%s%s/%s" mh-user-path (substring folder 1) mh-index-data-file)) + (goto-char (point-min)) + (forward-list 3) + (cadr (read (current-buffer)))))) + ;;;###mh-autoload (defun mh-index-insert-folder-headers () "Annotate the search results with original folder names." @@ -777,8 +788,27 @@ we find a new folder name." (insert (if last-folder "\n" "") current-folder "\n") (setq last-folder current-folder)) (forward-line)) - (when cur-msg (mh-goto-msg cur-msg t)) - (set-buffer-modified-p old-buffer-modified-flag))) + (when cur-msg + (mh-notate-cur) + (mh-goto-msg cur-msg t)) + (set-buffer-modified-p old-buffer-modified-flag)) + (mh-index-create-imenu-index)) + +;;;###mh-autoload +(defun mh-index-create-imenu-index () + "Create alist of folder names and positions in index folder buffers." + (save-excursion + (setq which-func-mode t) + (let ((alist ())) + (goto-char (point-min)) + (while (re-search-forward "^+" nil t) + (save-excursion + (beginning-of-line) + (push (cons (buffer-substring-no-properties + (point) (line-end-position)) + (set-marker (make-marker) (point))) + alist))) + (setq imenu--index-alist (nreverse alist))))) ;;;###mh-autoload (defun mh-index-group-by-folder () @@ -837,23 +867,6 @@ list of messages originally from that folder." folder (loop for x being the hash-keys of (gethash folder mh-index-data) when (mh-msg-exists-p x folder) collect x))))) -;;;###mh-autoload -(defun mh-index-update-unseen (msg) - "Remove counterpart of MSG in source folder from `mh-unseen-seq'. -Also `mh-update-unseen' is called in the original folder, if we have it open." - (let* ((checksum (gethash msg mh-index-msg-checksum-map)) - (folder-msg-pair (gethash checksum mh-index-checksum-origin-map)) - (orig-folder (car folder-msg-pair)) - (orig-msg (cdr folder-msg-pair))) - (when (mh-index-match-checksum orig-msg orig-folder checksum) - (when (get-buffer orig-folder) - (save-excursion - (set-buffer orig-folder) - (unless (member orig-msg mh-seen-list) (push orig-msg mh-seen-list)) - (mh-update-unseen))) - (mh-exec-cmd-daemon "mark" #'ignore orig-folder (format "%s" orig-msg) - "-sequence" (symbol-name mh-unseen-seq) "-del")))) - (defun mh-index-match-checksum (msg folder checksum) "Check if MSG in FOLDER has X-MHE-Checksum header value of CHECKSUM." (with-temp-buffer @@ -973,90 +986,6 @@ update the source folder buffer if present." -;; Glimpse interface - -(defvar mh-glimpse-binary (executable-find "glimpse")) -(defvar mh-glimpse-directory ".glimpse") - -;;;###mh-autoload -(defun mh-glimpse-execute-search (folder-path search-regexp) - "Execute glimpse and read the results. - -In the examples below, replace /home/user/Mail with the path to your MH -directory. - -First create the directory /home/user/Mail/.glimpse. Then create the file -/home/user/Mail/.glimpse/.glimpse_exclude with the following contents: - - */.* - */#* - */,* - */*~ - ^/home/user/Mail/.glimpse - ^/home/user/Mail/mhe-index - -If there are any directories you would like to ignore, append lines like the -following to .glimpse_exclude: - - ^/home/user/Mail/scripts - -You do not want to index the folders that hold the results of your searches -since they tend to be ephemeral and the original messages are indexed anyway. -The configuration file above assumes that the results are found in sub-folders -of `mh-index-folder' which is +mhe-index by default. - -Use the following command line to generate the glimpse index. Run this -daily from cron: - - glimpseindex -H /home/user/Mail/.glimpse /home/user/Mail - -FOLDER-PATH is the directory in which SEARCH-REGEXP is used to search." - (set-buffer (get-buffer-create mh-index-temp-buffer)) - (erase-buffer) - (call-process mh-glimpse-binary nil '(t nil) nil - ;(format "-%s" fuzz) - "-i" "-y" - "-H" (format "%s%s" mh-user-path mh-glimpse-directory) - "-F" (format "^%s" folder-path) - search-regexp) - (goto-char (point-min))) - -(defun mh-glimpse-next-result () - "Read the next result. -Parse it and return the message folder, message index and the match. If no -other matches left then return nil. If the current record is invalid return -'error." - (prog1 - (block nil - (when (eobp) - (return nil)) - (let ((eol-pos (line-end-position)) - (bol-pos (line-beginning-position)) - folder-start msg-end) - (goto-char bol-pos) - (unless (search-forward mh-user-path eol-pos t) - (return 'error)) - (setq folder-start (point)) - (unless (search-forward ": " eol-pos t) - (return 'error)) - (let ((match (buffer-substring-no-properties (point) eol-pos))) - (forward-char -2) - (setq msg-end (point)) - (unless (search-backward "/" folder-start t) - (return 'error)) - (list (format "+%s" (buffer-substring-no-properties - folder-start (point))) - (let ((val (ignore-errors (read-from-string - (buffer-substring-no-properties - (1+ (point)) msg-end))))) - (if (and (consp val) (integerp (car val))) - (car val) - (return 'error))) - match)))) - (forward-line))) - - - ;; Pick interface (defvar mh-index-pick-folder) @@ -1319,16 +1248,12 @@ then the folders are searched recursively. All parameters ARGS are ignored." ;;;###mh-autoload (defun mh-index-sequenced-messages (folders sequence) "Display messages from FOLDERS in SEQUENCE. -By default the folders specified by `mh-index-new-messages-folders' are -searched. With a prefix argument, enter a space-separated list of folders, or -nothing to search all folders. - -Argument SEQUENCE defaults to `mh-unseen-seq' and is the sequence that the -function searches for in each of the FOLDERS. With a prefix argument, enter a -sequence to use." +All messages in the sequence you provide from the folders in +`mh-index-new-messages-folders' are listed. With a prefix argument, enter a +space-separated list of folders, or nothing to search all folders." (interactive (list (if current-prefix-arg - (split-string (read-string "Search folder(s) [all]? ")) + (split-string (read-string "Search folder(s): [all] ")) mh-index-new-messages-folders) (mh-read-seq-default "Search" nil))) (unless sequence (setq sequence mh-unseen-seq)) @@ -1367,26 +1292,26 @@ sequence to use." ;;;###mh-autoload (defun mh-index-new-messages (folders) "Display unseen messages. -All messages in the `unseen' sequence from FOLDERS are displayed. -By default the folders specified by `mh-index-new-messages-folders' -are searched. With a prefix argument, enter a space-separated list of -folders, or nothing to search all folders." +If you use a program such as `procmail' to use `rcvstore' to file your +incoming mail automatically, you can display new, unseen, messages using this +command. All messages in the `unseen' sequence from the folders in +`mh-index-new-messages-folders' are listed. With a prefix argument, enter a +space-separated list of FOLDERS, or nothing to search all folders." (interactive (list (if current-prefix-arg - (split-string (read-string "Search folder(s) [all]? ")) + (split-string (read-string "Search folder(s): [all] ")) mh-index-new-messages-folders))) (mh-index-sequenced-messages folders mh-unseen-seq)) ;;;###mh-autoload (defun mh-index-ticked-messages (folders) "Display ticked messages. -All messages in the `tick' sequence from FOLDERS are displayed. -By default the folders specified by `mh-index-ticked-messages-folders' -are searched. With a prefix argument, enter a space-separated list of -folders, or nothing to search all folders." +All messages in `mh-tick-seq' from the folders in +`mh-index-ticked-messages-folders' are listed. With a prefix argument, enter a +space-separated list of FOLDERS, or nothing to search all folders." (interactive (list (if current-prefix-arg - (split-string (read-string "Search folder(s) [all]? ")) + (split-string (read-string "Search folder(s): [all] ")) mh-index-ticked-messages-folders))) (mh-index-sequenced-messages folders mh-tick-seq)) diff --git a/lisp/mh-e/mh-junk.el b/lisp/mh-e/mh-junk.el index 42ec4c444d3..095a8c3c3fd 100644 --- a/lisp/mh-e/mh-junk.el +++ b/lisp/mh-e/mh-junk.el @@ -1,6 +1,6 @@ ;;; mh-junk.el --- Interface to anti-spam measures -;; Copyright (C) 2003 Free Software Foundation, Inc. +;; Copyright (C) 2003, 2004 Free Software Foundation, Inc. ;; Author: Satyaki Das <satyaki@theforce.stanford.edu>, ;; Bill Wohler <wohler@newt.com> @@ -32,6 +32,8 @@ ;;; Code: +(eval-when-compile (require 'mh-acros)) +(mh-require-cl) (require 'mh-e) ;; Interactive functions callable from the folder buffer @@ -39,36 +41,33 @@ (defun mh-junk-blacklist (range) "Blacklist RANGE as spam. -Check the documentation of `mh-interactive-range' to see how RANGE is read in -interactive use. +This command trains the spam program in use (see the `mh-junk-program' option) +with the content of the range (see `mh-interactive-range') and then handles +the message(s) as specified by the `mh-junk-disposition' option. -First the appropriate function is called depending on the value of -`mh-junk-choice'. Then if `mh-junk-mail-folder' is a string then the message is -refiled to that folder. If nil, the message is deleted. - -To change the spam program being used, customize `mh-junk-program'. Directly -setting `mh-junk-choice' is not recommended. - -The documentation for the following functions describes what setup is needed -for the different spam fighting programs: +For more information about using your particular spam fighting program, see: + - `mh-spamassassin-blacklist' - `mh-bogofilter-blacklist' - - `mh-spamprobe-blacklist' - - `mh-spamassassin-blacklist'" + - `mh-spamprobe-blacklist'" (interactive (list (mh-interactive-range "Blacklist"))) (let ((blacklist-func (nth 1 (assoc mh-junk-choice mh-junk-function-alist)))) (unless blacklist-func (error "Customize `mh-junk-program' appropriately")) - (let ((dest (cond ((null mh-junk-mail-folder) nil) - ((equal mh-junk-mail-folder "") "+") - ((eq (aref mh-junk-mail-folder 0) ?+) - mh-junk-mail-folder) - ((eq (aref mh-junk-mail-folder 0) ?@) + (let ((dest (cond ((null mh-junk-disposition) nil) + ((equal mh-junk-disposition "") "+") + ((eq (aref mh-junk-disposition 0) ?+) + mh-junk-disposition) + ((eq (aref mh-junk-disposition 0) ?@) (concat mh-current-folder "/" - (substring mh-junk-mail-folder 1))) - (t (concat "+" mh-junk-mail-folder))))) + (substring mh-junk-disposition 1))) + (t (concat "+" mh-junk-disposition))))) (mh-iterate-on-range msg range + (message (format "Blacklisting message %d..." msg)) (funcall (symbol-function blacklist-func) msg) + (message (format "Blacklisting message %d...done" msg)) + (if (not (memq msg mh-seen-list)) + (setq mh-seen-list (cons msg mh-seen-list))) (if dest (mh-refile-a-msg nil (intern dest)) (mh-delete-a-msg nil))) @@ -76,231 +75,124 @@ for the different spam fighting programs: ;;;###mh-autoload (defun mh-junk-whitelist (range) - "Whitelist RANGE incorrectly classified as spam. - -Check the documentation of `mh-interactive-range' to see how RANGE is read in -interactive use. + "Whitelist RANGE as ham. -First the appropriate function is called depending on the value of -`mh-junk-choice'. Then the message is refiled to `mh-inbox'. +This command reclassifies a range of messages (see `mh-interactive-range') as +ham if it were incorrectly classified as spam. It then refiles the message +into the `+inbox' folder. -To change the spam program being used, customize `mh-junk-program'. Directly -setting `mh-junk-choice' is not recommended." +The `mh-junk-program' option specifies the spam program in use." (interactive (list (mh-interactive-range "Whitelist"))) (let ((whitelist-func (nth 2 (assoc mh-junk-choice mh-junk-function-alist)))) (unless whitelist-func (error "Customize `mh-junk-program' appropriately")) (mh-iterate-on-range msg range + (message (format "Whitelisting message %d..." msg)) (funcall (symbol-function whitelist-func) msg) + (message (format "Whitelisting message %d...done" msg)) (mh-refile-a-msg nil (intern mh-inbox))) (mh-next-msg))) -;; Bogofilter Interface - -(defvar mh-bogofilter-executable (executable-find "bogofilter")) - -(defun mh-bogofilter-blacklist (msg) - "Classify MSG as spam. -Tell bogofilter that the message is spam. +;; Spamassassin Interface -Bogofilter is a Bayesian spam filtering program. Get it from your local -distribution or from: - http://bogofilter.sourceforge.net/ +(defvar mh-spamassassin-executable (executable-find "spamassassin")) +(defvar mh-sa-learn-executable (executable-find "sa-learn")) -You first need to teach bogofilter. This is done by running +(defun mh-spamassassin-blacklist (msg) + "Blacklist MSG with SpamAssassin. - bogofilter -n < good-message +SpamAssassin is one of the more popular spam filtering programs. Get it from +your local distribution or from http://spamassassin.org/. -on every good message, and +To use SpamAssassin, add the following recipes to `.procmailrc': - bogofilter -s < spam-message + MAILDIR=$HOME/`mhparam Path` -on every spam message. Most Bayesian filters need 1000 to 5000 of each to -start doing a good job. + # Fight spam with SpamAssassin. + :0fw + | spamc -To use bogofilter, add the following .procmailrc recipes which you can also -find in the bogofilter man page: + # Anything with a spam level of 10 or more is junked immediately. + :0: + * ^X-Spam-Level: .......... + /dev/null - # Bogofilter - :0fw - | bogofilter -u -e -p + :0: + * ^X-Spam-Status: Yes + spam/. - :0 - * ^X-Bogosity: Yes, tests=bogofilter - $SPAM +If you don't use `spamc', use `spamassassin -P -a'. -Bogofilter continues to feed the messages it classifies back into its -database. Occasionally it misses, and those messages need to be reclassified. -MH-E can do this for you. Use \\[mh-junk-blacklist] to reclassify messges in -your +inbox as spam, and \\[mh-junk-whitelist] to reclassify messages in your -spambox as good messages." - (unless mh-bogofilter-executable - (error "Couldn't find the bogofilter executable")) - (let ((msg-file (mh-msg-filename msg mh-current-folder))) - (call-process mh-bogofilter-executable msg-file 0 nil "-Ns"))) +Note that one of the recipes above throws away messages with a score greater +than or equal to 10. Here's how you can determine a value that works best for +you. -(defun mh-bogofilter-whitelist (msg) - "Reinstate incorrectly filtered MSG. -Train bogofilter to think of the message as non-spam." - (unless mh-bogofilter-executable - (error "Couldn't find the bogofilter executable")) - (let ((msg-file (mh-msg-filename msg mh-current-folder))) - (call-process mh-bogofilter-executable msg-file 0 nil "-Sn"))) +First, run `spamassassin -t' on every mail message in your archive and use +Gnumeric to verify that the average plus the standard deviation of good mail +is under 5, the SpamAssassin default for \"spam\". - +Using Gnumeric, sort the messages by score and view the messages with the +highest score. Determine the score which encompasses all of your interesting +messages and add a couple of points to be conservative. Add that many dots to +the `X-Spam-Level:' header field above to send messages with that score down +the drain. -;; Spamprobe Interface +In the example above, messages with a score of 5-9 are set aside in the +`+spam' folder for later review. The major weakness of rules-based filters is +a plethora of false positives so it is worthwhile to check. -(defvar mh-spamprobe-executable (executable-find "spamprobe")) +If SpamAssassin classifies a message incorrectly, or is unsure, you can use +the MH-E commands \\[mh-junk-blacklist] and \\[mh-junk-whitelist]. -(defun mh-spamprobe-blacklist (msg) - "Classify MSG as spam. -Tell spamprobe that the message is spam. - -Spamprobe is a Bayesian spam filtering program. More info about the program can -be found at: - http://spamprobe.sourceforge.net - -Here is a procmail recipe to stores incoming spam mail into the folder +spam -and good mail in /home/user/Mail/mdrop/mbox. This recipe is provided as an -example in the spamprobe man page. - - PATH=/bin:/usr/bin:/usr/local/bin - DEFAULT=/home/user/Mail/mdrop/mbox - SPAM=/home/user/Mail/spam/. - - # Spamprobe filtering - :0 - SCORE=| spamprobe receive - :0 wf - | formail -I \"X-SpamProbe: $SCORE\" - :0 a: - *^X-SpamProbe: SPAM - $SPAM - -Occasionally some good mail gets misclassified as spam. You can use -\\[mh-junk-whitelist] to reclassify that as good mail." - (unless mh-spamprobe-executable - (error "Couldn't find the spamprobe executable")) - (let ((msg-file (mh-msg-filename msg mh-current-folder))) - (call-process mh-spamprobe-executable msg-file 0 nil "spam"))) +The \\[mh-junk-blacklist] command adds a `blacklist_from' entry to +`~/spamassassin/user_prefs', deletes the message, and sends the message to the +Razor, so that others might not see this spam. If the `sa-learn' command is +available, the message is also recategorized as spam. -(defun mh-spamprobe-whitelist (msg) - "Reinstate incorrectly filtered MSG. -Train spamprobe to think of the message as non-spam." - (unless mh-spamprobe-executable - (error "Couldn't find the spamprobe executable")) - (let ((msg-file (mh-msg-filename msg mh-current-folder))) - (call-process mh-spamprobe-executable msg-file 0 nil "good"))) +The \\[mh-junk-whitelist] command adds a `whitelist_from' rule to the +`~/.spamassassin/user_prefs' file. If the `sa-learn' command is available, the +message is also recategorized as ham. - +Over time, you'll observe that the same host or domain occurs repeatedly in +the `blacklist_from' entries, so you might think that you could avoid future +spam by blacklisting all mail from a particular domain. The utility function +`mh-spamassassin-identify-spammers' helps you do precisely that. This function +displays a frequency count of the hosts and domains in the `blacklist_from' +entries from the last blank line in `~/.spamassassin/user_prefs' to the end of +the file. This information can be used so that you can replace multiple +`blacklist_from' entries with a single wildcard entry such as: -;; Spamassassin Interface + blacklist_from *@*amazingoffersdirect2u.com -(defvar mh-spamassassin-executable (executable-find "spamassassin")) -(defvar mh-sa-learn-executable (executable-find "sa-learn")) +In versions of SpamAssassin (2.50 and on) that support a Bayesian classifier, +\\[mh-junk-blacklist] uses the `sa-learn' program to recategorize the message +as spam. Neither MH-E, nor SpamAssassin, rebuilds the database after adding +words, so you will need to run `sa-learn --rebuild' periodically. This can be +done by adding the following to your crontab: -(defun mh-spamassassin-blacklist (msg) - "Blacklist MSG. -This is done by sending the message to Razor and by appending the sender to -~/.spamassassin/user_prefs in a blacklist_from rule. If sa-learn is available, -the message is also recategorized as spam. - -Spamassassin is an excellent spam filter. For more information, see: - http://spamassassin.org/. - -I ran \"spamassassin -t\" on every mail message in my archive and ran an -analysis in Gnumeric to find that the standard deviation of good mail -scored under 5 (coincidentally, the spamassassin default for \"spam\"). - -Furthermore, I observed that there weren't any messages with a score of 8 -or more that were interesting, so I added a couple of points to be -conservative and send any message with a score of 10 or more down the -drain. You might want to use a score of 12 or 13 to be really conservative. -I have found that this really decreases the amount of junk to review. - -Messages with a score of 5-9 are set aside for later review. The major -weakness of rules-based filters is a plethora of false positives\; I catch one -or two legitimate messages in here a week, so it is worthwhile to check. - -You might choose to do this analysis yourself to pick a good score for -deleting spam sight unseen, or you might pick a score out of a hat, or you -might choose to be very conservative and not delete any messages at all. - -Based upon this discussion, here is what the associated ~/.procmailrc -entries look like. These rules appear before my list filters so that spam -sent to mailing lists gets pruned too. - - # - # Spam - # - :0fw - | spamc - - # Anything with a spam level of 10 or more is junked immediately. - :0: - * ^X-Spam-Level: .......... - /dev/null - - :0 - * ^X-Spam-Status: Yes - $SPAM - -If you don't use \"spamc\", use \"spamassassin -P -a\". - -A handful of spam does find its way into +inbox. In this case, use -\\[mh-junk-blacklist] to add a \"blacklist_from\" line to -~/spamassassin/user_prefs, delete the message, and send the message to the -Razor, so that others might not see this spam. - -Over time, you see some patterns in the blacklisted addresses and can -replace several lines with wildcards. For example, it is clear that High -Speed Media is the biggest bunch of jerks on the Net. Here are some of the -entries I have for them, and the list continues to grow. - - blacklist_from *@*-hsm-*.com - blacklist_from *@*182*643*.com - blacklist_from *@*antarhsm*.com - blacklist_from *@*h*speed* - blacklist_from *@*hsm*182*.com - blacklist_from *@*hsm*643*.com - blacklist_from *@*hsmridi2983cslt227.com - blacklist_from *@*list*hsm*.com - blacklist_from *@h*s*media* - blacklist_from *@hsmdrct.com - blacklist_from *@hsmridi2983csltsite.com - -The function `mh-spamassassin-identify-spammers' is provided that shows the -frequency counts of the host and domain names in your blacklist_from -entries. This can be helpful when editing the blacklist_from entries. - -In versions of spamassassin (2.50 and on) that support a Bayesian classifier, -\\[mh-junk-blacklist] uses the sa-learn program to recategorize the message as -spam. Neither MH-E, nor spamassassin, rebuilds the database after adding -words, so you will need to run \"sa-learn --rebuild\" periodically. This can -be done by adding the following to your crontab: - - 0 * * * * sa-learn --rebuild > /dev/null 2>&1" + 0 * * * * sa-learn --rebuild > /dev/null 2>&1" (unless mh-spamassassin-executable - (error "Couldn't find the spamassassin executable")) + (error "Unable to find the spamassassin executable")) (let ((current-folder mh-current-folder) (msg-file (mh-msg-filename msg mh-current-folder)) (sender)) (save-excursion - (message "Giving this message the Razor...") + (message (format "Reporting message %d..." msg)) (mh-truncate-log-buffer) (call-process mh-spamassassin-executable msg-file mh-log-buffer nil - "--report" "--remove-from-whitelist") + ;;"--report" "--remove-from-whitelist" + "-r" "-R") ; spamassassin V2.20 (when mh-sa-learn-executable (message "Recategorizing this message as spam...") (call-process mh-sa-learn-executable msg-file mh-log-buffer nil "--single" "--spam" "--local" "--no-rebuild")) - (message "Blacklisting address...") + (message (format "Blacklisting message %d..." msg)) (set-buffer (get-buffer-create mh-temp-buffer)) (erase-buffer) - (call-process (expand-file-name mh-scan-prog mh-progs) nil t nil + (call-process (expand-file-name mh-scan-prog mh-progs) mh-junk-background + t nil (format "%s" msg) current-folder "-format" "%<(mymbox{from})%|%(addr{from})%>") (goto-char (point-min)) @@ -308,15 +200,19 @@ be done by adding the following to your crontab: (progn (setq sender (match-string 0)) (mh-spamassassin-add-rule "blacklist_from" sender) - (message "Blacklisting address...done")) - (message "Blacklisting address...not done (from my address)"))))) + (message (format "Blacklisting message %d...done" msg))) + (message (format "Blacklisting message %d...not done (from my address)" msg)))))) (defun mh-spamassassin-whitelist (msg) - "Whitelist MSG. -Add a whitelist_from rule to the ~/.spamassassin/user_prefs file. If sa-learn -is available, then the message is recategorized as ham." + "Whitelist MSG with SpamAssassin. + +The \\[mh-junk-whitelist] command adds a `whitelist_from' rule to the +`~/.spamassassin/user_prefs' file. If the `sa-learn' command is available, the +message is also recategorized as ham. + +See `mh-spamassassin-blacklist' for more information." (unless mh-spamassassin-executable - (error "Couldn't find the spamassassin executable")) + (error "Unable to find the spamassassin executable")) (let ((msg-file (mh-msg-filename msg mh-current-folder)) (show-buffer (get-buffer mh-show-buffer)) from) @@ -325,7 +221,8 @@ is available, then the message is recategorized as ham." (erase-buffer) (message "Removing spamassassin markup from message...") (call-process mh-spamassassin-executable msg-file mh-temp-buffer nil - "--remove-markup") + ;; "--remove-markup" + "-d") ; spamassassin V2.20 (if show-buffer (kill-buffer show-buffer)) (write-file msg-file) @@ -333,15 +230,17 @@ is available, then the message is recategorized as ham." (message "Recategorizing this message as ham...") (call-process mh-sa-learn-executable msg-file mh-temp-buffer nil "--single" "--ham" "--local --no-rebuild")) - (message "Whitelisting address...") - (setq from (car (ietf-drums-parse-address (mh-get-header-field "From:")))) + (message (format "Whitelisting message %d..." msg)) + (setq from + (car (mh-funcall-if-exists + ietf-drums-parse-address (mh-get-header-field "From:")))) (kill-buffer nil) - (unless (equal from "") + (unless (or (null from) (equal from "")) (mh-spamassassin-add-rule "whitelist_from" from)) - (message "Whitelisting address...done")))) + (message (format "Whitelisting message %d...done" msg))))) (defun mh-spamassassin-add-rule (rule body) - "Add a new rule to ~/.spamassassin/user_prefs. + "Add a new rule to `~/.spamassassin/user_prefs'. The name of the rule is RULE and its body is BODY." (save-window-excursion (let* ((line (format "%s\t%s\n" rule body)) @@ -358,15 +257,15 @@ The name of the rule is RULE and its body is BODY." (kill-buffer nil))))) (defun mh-spamassassin-identify-spammers () - "Identifies spammers who are repeat offenders. + "Identify spammers who are repeat offenders. -For each blacklist_from entry from the last blank line of -~/.spamassassin/user_prefs to the end of the file, a list of host and domain -names along with their frequency counts is displayed. This information can be -used to replace multiple blacklist_from entries with a single wildcard entry -such as: +This function displays a frequency count of the hosts and domains in the +`blacklist_from' entries from the last blank line in +`~/.spamassassin/user_prefs' to the end of the file. This information can be +used so that you can replace multiple `blacklist_from' entries with a single +wildcard entry such as: - blacklist_from *@*amazingoffersdirect2u.com" + blacklist_from *@*amazingoffersdirect2u.com" (interactive) (let* ((file (expand-file-name "~/.spamassassin/user_prefs")) (domains (make-hash-table :test 'equal))) @@ -385,7 +284,7 @@ such as: ;; Add counts for each host and domain part. (while host (setq value (gethash (car host) domains)) - (puthash (car host) (1+ (if (not value) 0 value)) domains) + (setf (gethash (car host) domains) (1+ (if (not value) 0 value))) (setq host (cdr host)))))) ;; Output @@ -400,6 +299,121 @@ such as: (reverse-region (point-min) (point-max)) (goto-char (point-min)))) + + +;; Bogofilter Interface + +(defvar mh-bogofilter-executable (executable-find "bogofilter")) + +(defun mh-bogofilter-blacklist (msg) + "Blacklist MSG with Bogofilter. + +Bogofilter is a Bayesian spam filtering program. Get it from your local +distribution or from http://bogofilter.sourceforge.net/. + +Bogofilter is taught by running: + + bogofilter -n < good-message + +on every good message, and + + bogofilter -s < spam-message + +on every spam message. This is called a full training; three other +training methods are described in the FAQ that is distributed with bogofilter. +Note that most Bayesian filters need 1000 to 5000 of each type of message to +start doing a good job. + +To use Bogofilter, add the following recipes to `.procmailrc': + + MAILDIR=$HOME/`mhparam Path` + + # Fight spam with Bogofilter. + :0fw + | bogofilter -3 -e -p + + :0: + * ^X-Bogosity: Yes, tests=bogofilter + spam/. + + :0: + * ^X-Bogosity: Unsure, tests=bogofilter + spam/unsure/. + +If Bogofilter classifies a message incorrectly, or is unsure, you can use the +MH-E commands \\[mh-junk-blacklist] and \\[mh-junk-whitelist] to update +Bogofilter's training. + +The \"Bogofilter FAQ\" suggests that you run the following +occasionally to shrink the database: + + bogoutil -d wordlist.db | bogoutil -l wordlist.db.new + mv wordlist.db wordlist.db.prv + mv wordlist.db.new wordlist.db + +The \"Bogofilter tuning HOWTO\" describes how you can fine-tune Bogofilter." + (unless mh-bogofilter-executable + (error "Unable to find the bogofilter executable")) + (let ((msg-file (mh-msg-filename msg mh-current-folder))) + (call-process mh-bogofilter-executable msg-file mh-junk-background + nil "-s"))) + +(defun mh-bogofilter-whitelist (msg) + "Whitelist MSG with Bogofilter. + +See `mh-bogofilter-blacklist' for more information." + (unless mh-bogofilter-executable + (error "Unable to find the bogofilter executable")) + (let ((msg-file (mh-msg-filename msg mh-current-folder))) + (call-process mh-bogofilter-executable msg-file mh-junk-background + nil "-n"))) + + + +;; Spamprobe Interface + +(defvar mh-spamprobe-executable (executable-find "spamprobe")) + +(defun mh-spamprobe-blacklist (msg) + "Blacklist MSG with SpamProbe. + +SpamProbe is a Bayesian spam filtering program. Get it from your local +distribution or from http://spamprobe.sourceforge.net. + +To use SpamProbe, add the following recipes to `.procmailrc': + + MAILDIR=$HOME/`mhparam Path` + + # Fight spam with SpamProbe. + :0 + SCORE=| spamprobe receive + + :0 wf + | formail -I \"X-SpamProbe: $SCORE\" + + :0: + *^X-SpamProbe: SPAM + spam/. + +If SpamProbe classifies a message incorrectly, you can use the MH-E commands +\\[mh-junk-blacklist] and \\[mh-junk-whitelist] to update SpamProbe's +training." + (unless mh-spamprobe-executable + (error "Unable to find the spamprobe executable")) + (let ((msg-file (mh-msg-filename msg mh-current-folder))) + (call-process mh-spamprobe-executable msg-file mh-junk-background + nil "spam"))) + +(defun mh-spamprobe-whitelist (msg) + "Whitelist MSG with SpamProbe. + +See `mh-spamprobe-blacklist' for more information." + (unless mh-spamprobe-executable + (error "Unable to find the spamprobe executable")) + (let ((msg-file (mh-msg-filename msg mh-current-folder))) + (call-process mh-spamprobe-executable msg-file mh-junk-background + nil "good"))) + (provide 'mh-junk) ;;; Local Variables: diff --git a/lisp/mh-e/mh-loaddefs.el b/lisp/mh-e/mh-loaddefs.el index a5578760845..43119b62be0 100644 --- a/lisp/mh-e/mh-loaddefs.el +++ b/lisp/mh-e/mh-loaddefs.el @@ -11,22 +11,24 @@ ;;;;;; mh-beginning-of-word mh-complete-word mh-open-line mh-fully-kill-draft ;;;;;; mh-yank-cur-msg mh-insert-letter mh-send-letter mh-insert-auto-fields ;;;;;; mh-check-whom mh-insert-signature mh-to-fcc mh-to-field mh-fill-paragraph-function -;;;;;; mh-send-other-window mh-send mh-reply mh-redistribute mh-forward -;;;;;; mh-extract-rejected-mail mh-edit-again) "mh-comp" "mh-comp.el" -;;;;;; (16625 53169)) +;;;;;; mh-get-header-field mh-send-other-window mh-send mh-reply +;;;;;; mh-redistribute mh-forward mh-extract-rejected-mail mh-edit-again) +;;;;;; "mh-comp" "mh-comp.el" (16665 55172)) ;;; Generated autoloads from mh-comp.el (autoload (quote mh-edit-again) "mh-comp" "\ Clean up a draft or a message MSG previously sent and make it resendable. Default is the current message. The variable `mh-new-draft-cleaned-headers' specifies the headers to remove. -See also documentation for `\\[mh-send]' function." t nil) + +See also `mh-send'." t nil) (autoload (quote mh-extract-rejected-mail) "mh-comp" "\ Extract message MSG returned by the mail system and make it resendable. Default is the current message. The variable `mh-new-draft-cleaned-headers' gives the headers to clean out of the original message. -See also documentation for `\\[mh-send]' function." t nil) + +See also `mh-send'." t nil) (autoload (quote mh-forward) "mh-comp" "\ Forward messages to the recipients TO and CC. @@ -36,7 +38,7 @@ Default is the displayed message. Check the documentation of `mh-interactive-range' to see how RANGE is read in interactive use. -See also documentation for `\\[mh-send]' function." t nil) +See also `mh-send'." t nil) (autoload (quote mh-redistribute) "mh-comp" "\ Redistribute displayed message to recipients TO and CC. @@ -55,11 +57,12 @@ to reply to: If optional prefix argument INCLUDEP provided, then include the message in the reply using filter `mhl.reply' in your MH directory. If the file named by `mh-repl-formfile' exists, it is used as a skeleton -for the reply. See also documentation for `\\[mh-send]' function." t nil) +for the reply. + +See also `mh-send'." t nil) (autoload (quote mh-send) "mh-comp" "\ Compose and send a letter. - Do not call this function from outside MH-E; use \\[mh-smail] instead. The file named by `mh-comp-formfile' will be used as the form. @@ -70,7 +73,6 @@ passed three arguments: TO, CC, and SUBJECT." t nil) (autoload (quote mh-send-other-window) "mh-comp" "\ Compose and send a letter in another window. - Do not call this function from outside MH-E; use \\[mh-smail-other-window] instead. @@ -80,6 +82,11 @@ details. If `mh-compose-letter-function' is defined, it is called on the draft and passed three arguments: TO, CC, and SUBJECT." t nil) +(autoload (quote mh-get-header-field) "mh-comp" "\ +Find and return the body of FIELD in the mail header. +Returns the empty string if the field is not in the header of the +current buffer." nil nil) + (autoload (quote mh-fill-paragraph-function) "mh-comp" "\ Fill paragraph at or after point. Prefix ARG means justify as well. This function enables `fill-paragraph' to @@ -96,9 +103,12 @@ Insert an Fcc: FOLDER field in the current message. Prompt for the field name with a completion list of the current folders." t nil) (autoload (quote mh-insert-signature) "mh-comp" "\ -Insert the file named by `mh-signature-file-name' at point. +Insert the signature specified by `mh-signature-file-name' or FILE at point. +A signature separator (`-- ') will be added if the signature block does not +contain one and `mh-signature-separator-flag' is on. The value of `mh-letter-insert-signature-hook' is a list of functions to be -called, with no arguments, before the signature is actually inserted." t nil) +called, with no arguments, after the signature is inserted. +The signature can also be inserted with `mh-identity-list'." t nil) (autoload (quote mh-check-whom) "mh-comp" "\ Verify recipients of the current letter, showing expansion of any aliases." t nil) @@ -109,7 +119,9 @@ Sets buffer-local `mh-insert-auto-fields-done-local' when done and inserted something. If NON-INTERACTIVE is non-nil, do not be verbose and only attempt matches if `mh-insert-auto-fields-done-local' is nil. -An `identity' entry is skipped if one was already entered manually." t nil) +An `identity' entry is skipped if one was already entered manually. + +Return t if fields added; otherwise return nil." t nil) (autoload (quote mh-send-letter) "mh-comp" "\ Send the draft letter in the current buffer. @@ -117,13 +129,12 @@ If optional prefix argument ARG is provided, monitor delivery. The value of `mh-before-send-letter-hook' is a list of functions to be called, with no arguments, before doing anything. Run `\\[mh-edit-mhn]' if mhn directives are present; otherwise -run `\\[mh-mml-to-mime]' if mml directives are present. -Insert X-Mailer field if variable `mh-insert-x-mailer-flag' is set. -Insert X-Face field if the file specified by `mh-x-face-file' exists." t nil) +run `\\[mh-mml-to-mime]' if mml directives are present." t nil) (autoload (quote mh-insert-letter) "mh-comp" "\ Insert a message into the current letter. -Removes the header fields according to the variable `mh-invisible-headers'. +Removes the header fields according to the variable +`mh-invisible-header-fields-compiled'. Prefixes each non-blank line with `mh-ins-buf-prefix', unless `mh-yank-from-start-of-msg' is set for supercite in which case supercite is used to format the message. @@ -168,42 +179,11 @@ If we are at the first header field go to the start of the message body." t nil) ;;;*** -;;;### (autoloads (mh-customize) "mh-customize" "mh-customize.el" -;;;;;; (16625 53481)) -;;; Generated autoloads from mh-customize.el - -(autoload (quote mh-customize) "mh-customize" "\ -Customize MH-E variables. -With optional argument DELETE-OTHER-WINDOWS-FLAG, other windows in the frame -are removed." t nil) - -;;;*** - -;;;### (autoloads (mh-goto-cur-msg mh-update-sequences mh-folder-line-matches-show-buffer-p) -;;;;;; "mh-e" "mh-e.el" (16627 22341)) -;;; Generated autoloads from mh-e.el - -(autoload (quote mh-folder-line-matches-show-buffer-p) "mh-e" "\ -Return t if the message under point in folder-mode is in the show buffer. -Return nil in any other circumstance (no message under point, no show buffer, -the message in the show buffer doesn't match." nil nil) - -(autoload (quote mh-update-sequences) "mh-e" "\ -Update MH's Unseen-Sequence and current folder and message. -Flush MH-E's state out to MH. The message at the cursor becomes current." t nil) - -(autoload (quote mh-goto-cur-msg) "mh-e" "\ -Position the cursor at the current message. -When optional argument MINIMAL-CHANGES-FLAG is non-nil, the function doesn't -recenter the folder buffer." nil nil) - -;;;*** - ;;;### (autoloads (mh-prefix-help mh-help mh-ephem-message mh-store-buffer -;;;;;; mh-store-msg mh-undo-folder mh-sort-folder mh-print-msg mh-page-digest-backwards +;;;;;; mh-store-msg mh-undo-folder mh-sort-folder mh-page-digest-backwards ;;;;;; mh-page-digest mh-pipe-msg mh-pack-folder mh-list-folders ;;;;;; mh-kill-folder mh-copy-msg mh-burst-digest) "mh-funcs" "mh-funcs.el" -;;;;;; (16625 54011)) +;;;;;; (16671 49652)) ;;; Generated autoloads from mh-funcs.el (autoload (quote mh-burst-digest) "mh-funcs" "\ @@ -245,15 +225,6 @@ Advance displayed message to next digested message." t nil) (autoload (quote mh-page-digest-backwards) "mh-funcs" "\ Back up displayed message to previous digested message." t nil) -(autoload (quote mh-print-msg) "mh-funcs" "\ -Print RANGE on printer. - -Check the documentation of `mh-interactive-range' to see how RANGE is read in -interactive use. - -The variable `mh-lpr-command-format' is used to generate the print command. -The messages are formatted by mhl. See the variable `mhl-formfile'." t nil) - (autoload (quote mh-sort-folder) "mh-funcs" "\ Sort the messages in the current folder by date. Calls the MH program sortm to do the work. @@ -261,8 +232,7 @@ The arguments in the list `mh-sortm-args' are passed to sortm if the optional argument EXTRA-ARGS is given." t nil) (autoload (quote mh-undo-folder) "mh-funcs" "\ -Undo all pending deletes and refiles in current folder. -Argument IGNORE is deprecated." t nil) +Undo all pending deletes and refiles in current folder." t nil) (autoload (quote mh-store-msg) "mh-funcs" "\ Store the file(s) contained in the current message into DIRECTORY. @@ -280,19 +250,24 @@ Default directory is the last directory used, or initially the value of Display STRING in the minibuffer momentarily." nil nil) (autoload (quote mh-help) "mh-funcs" "\ -Display cheat sheet for the MH-Folder commands in minibuffer." t nil) +Display cheat sheet for the MH-E commands." t nil) (autoload (quote mh-prefix-help) "mh-funcs" "\ Display cheat sheet for the commands of the current prefix in minibuffer." t nil) ;;;*** -;;;### (autoloads (mh-insert-identity mh-identity-list-set mh-identity-make-menu) -;;;;;; "mh-identity" "mh-identity.el" (16625 54171)) +;;;### (autoloads (mh-identity-handler-bottom mh-identity-handler-top +;;;;;; mh-identity-insert-attribution-verb mh-identity-handler-attribution-verb +;;;;;; mh-identity-handler-signature mh-identity-handler-gpg-identity +;;;;;; mh-insert-identity mh-identity-list-set mh-identity-make-menu) +;;;;;; "mh-identity" "mh-identity.el" (16665 55172)) ;;; Generated autoloads from mh-identity.el (autoload (quote mh-identity-make-menu) "mh-identity" "\ -Build (or rebuild) the Identity menu (e.g. after the list is modified)." nil nil) +Build the Identity menu. +This should be called any time `mh-identity-list' or `mh-auto-fields-list' +change." nil nil) (autoload (quote mh-identity-list-set) "mh-identity" "\ Update the `mh-identity-list' variable, and rebuild the menu. @@ -304,10 +279,35 @@ customization). This is called after 'customize is used to alter Insert proper fields for given IDENTITY. Edit the `mh-identity-list' variable to define identity." t nil) +(autoload (quote mh-identity-handler-gpg-identity) "mh-identity" "\ +For FIELD \"pgg-default-user-id\", process for ACTION 'remove or 'add. +The buffer-local variable `mh-identity-pgg-default-user-id' is set to VALUE +when action 'add is selected." nil nil) + +(autoload (quote mh-identity-handler-signature) "mh-identity" "\ +For FIELD \"signature\", process headers for ACTION 'remove or 'add. +The VALUE is added." nil nil) + +(autoload (quote mh-identity-handler-attribution-verb) "mh-identity" "\ +For FIELD \"attribution_verb\", process headers for ACTION 'remove or 'add. +The VALUE is added." nil nil) + +(autoload (quote mh-identity-insert-attribution-verb) "mh-identity" "\ +Insert VALUE as attribution verb, setting up delimiting markers. +If VALUE is nil, use `mh-extract-from-attribution-verb'." nil nil) + +(autoload (quote mh-identity-handler-top) "mh-identity" "\ +For FIELD, process mh-identity headers for ACTION 'remove or 'add. +If the field wasn't present, the VALUE is added at the top of the header." nil nil) + +(autoload (quote mh-identity-handler-bottom) "mh-identity" "\ +For FIELD, process mh-identity headers for ACTION 'remove or 'add. +If the field wasn't present, the VALUE is added at the bottom of the header." nil nil) + ;;;*** -;;;### (autoloads (mh-inc-spool-list-set) "mh-inc" "mh-inc.el" (16625 -;;;;;; 54212)) +;;;### (autoloads (mh-inc-spool-list-set) "mh-inc" "mh-inc.el" (16671 +;;;;;; 49652)) ;;; Generated autoloads from mh-inc.el (autoload (quote mh-inc-spool-list-set) "mh-inc" "\ @@ -319,14 +319,14 @@ This is called after 'customize is used to alter `mh-inc-spool-list'." nil nil) ;;;### (autoloads (mh-index-choose mh-namazu-execute-search mh-swish++-execute-search ;;;;;; mh-swish-execute-search mh-index-ticked-messages mh-index-new-messages -;;;;;; mh-index-sequenced-messages mh-glimpse-execute-search mh-index-delete-from-sequence -;;;;;; mh-index-add-to-sequence mh-index-execute-commands mh-index-update-unseen -;;;;;; mh-index-visit-folder mh-index-delete-folder-headers mh-index-group-by-folder +;;;;;; mh-index-sequenced-messages mh-index-delete-from-sequence +;;;;;; mh-index-add-to-sequence mh-index-execute-commands mh-index-visit-folder +;;;;;; mh-index-delete-folder-headers mh-index-group-by-folder mh-index-create-imenu-index ;;;;;; mh-index-insert-folder-headers mh-index-previous-folder mh-index-next-folder ;;;;;; mh-index-parse-search-regexp mh-index-do-search mh-index-p ;;;;;; mh-index-read-data mh-index-search mh-index-create-sequences ;;;;;; mh-create-sequence-map mh-index-update-maps) "mh-index" "mh-index.el" -;;;;;; (16625 54348)) +;;;;;; (16665 55172)) ;;; Generated autoloads from mh-index.el (autoload (quote mh-index-update-maps) "mh-index" "\ @@ -367,7 +367,6 @@ index for each program: - `mh-swish-execute-search' - `mh-mairix-execute-search' - `mh-namazu-execute-search' - - `mh-glimpse-execute-search' If none of these programs are present then we use pick. If desired grep can be used instead. Details about these methods can be found in: @@ -411,6 +410,9 @@ Jump to the previous folder marker." t nil) (autoload (quote mh-index-insert-folder-headers) "mh-index" "\ Annotate the search results with original folder names." nil nil) +(autoload (quote mh-index-create-imenu-index) "mh-index" "\ +Create alist of folder names and positions in index folder buffers." nil nil) + (autoload (quote mh-index-group-by-folder) "mh-index" "\ Partition the messages based on source folder. Returns an alist with the the folder names in the car and the cdr being the @@ -422,10 +424,6 @@ Delete the folder headers." nil nil) (autoload (quote mh-index-visit-folder) "mh-index" "\ Visit original folder from where the message at point was found." t nil) -(autoload (quote mh-index-update-unseen) "mh-index" "\ -Remove counterpart of MSG in source folder from `mh-unseen-seq'. -Also `mh-update-unseen' is called in the original folder, if we have it open." nil nil) - (autoload (quote mh-index-execute-commands) "mh-index" "\ Delete/refile the actual messages. The copies in the searched folder are then deleted/refiled to get the desired @@ -442,62 +440,25 @@ Delete from SEQ the messages in MSGS. This function updates the source folder sequences. Also makes an attempt to update the source folder buffer if present." nil nil) -(autoload (quote mh-glimpse-execute-search) "mh-index" "\ -Execute glimpse and read the results. - -In the examples below, replace /home/user/Mail with the path to your MH -directory. - -First create the directory /home/user/Mail/.glimpse. Then create the file -/home/user/Mail/.glimpse/.glimpse_exclude with the following contents: - - */.* - */#* - */,* - */*~ - ^/home/user/Mail/.glimpse - ^/home/user/Mail/mhe-index - -If there are any directories you would like to ignore, append lines like the -following to .glimpse_exclude: - - ^/home/user/Mail/scripts - -You do not want to index the folders that hold the results of your searches -since they tend to be ephemeral and the original messages are indexed anyway. -The configuration file above assumes that the results are found in sub-folders -of `mh-index-folder' which is +mhe-index by default. - -Use the following command line to generate the glimpse index. Run this -daily from cron: - - glimpseindex -H /home/user/Mail/.glimpse /home/user/Mail - -FOLDER-PATH is the directory in which SEARCH-REGEXP is used to search." nil nil) - (autoload (quote mh-index-sequenced-messages) "mh-index" "\ Display messages from FOLDERS in SEQUENCE. -By default the folders specified by `mh-index-new-messages-folders' are -searched. With a prefix argument, enter a space-separated list of folders, or -nothing to search all folders. - -Argument SEQUENCE defaults to `mh-unseen-seq' and is the sequence that the -function searches for in each of the FOLDERS. With a prefix argument, enter a -sequence to use." t nil) +All messages in the sequence you provide from the folders in +`mh-index-new-messages-folders' are listed. With a prefix argument, enter a +space-separated list of folders, or nothing to search all folders." t nil) (autoload (quote mh-index-new-messages) "mh-index" "\ Display unseen messages. -All messages in the `unseen' sequence from FOLDERS are displayed. -By default the folders specified by `mh-index-new-messages-folders' -are searched. With a prefix argument, enter a space-separated list of -folders, or nothing to search all folders." t nil) +If you use a program such as `procmail' to use `rcvstore' to file your +incoming mail automatically, you can display new, unseen, messages using this +command. All messages in the `unseen' sequence from the folders in +`mh-index-new-messages-folders' are listed. With a prefix argument, enter a +space-separated list of FOLDERS, or nothing to search all folders." t nil) (autoload (quote mh-index-ticked-messages) "mh-index" "\ Display ticked messages. -All messages in the `tick' sequence from FOLDERS are displayed. -By default the folders specified by `mh-index-ticked-messages-folders' -are searched. With a prefix argument, enter a space-separated list of -folders, or nothing to search all folders." t nil) +All messages in `mh-tick-seq' from the folders in +`mh-index-ticked-messages-folders' are listed. With a prefix argument, enter a +space-separated list of FOLDERS, or nothing to search all folders." t nil) (autoload (quote mh-swish-execute-search) "mh-index" "\ Execute swish-e and read the results. @@ -620,54 +581,69 @@ system." nil nil) ;;;*** +;;;### (autoloads (mh-variants mh-variant-p mh-variant-set) "mh-init" +;;;;;; "mh-init.el" (16671 49652)) +;;; Generated autoloads from mh-init.el + +(autoload (quote mh-variant-set) "mh-init" "\ +Set the MH variant to VARIANT. +Sets `mh-progs', `mh-lib', `mh-lib-progs' and `mh-flists-present-flag'. +If the VARIANT is `autodetect', then first try nmh, then MH and finally +GNU mailutils." t nil) + +(autoload (quote mh-variant-p) "mh-init" "\ +Return t if variant is any of VARIANTS. +Currently known variants are 'mh and 'nmh." nil nil) + +(autoload (quote mh-variants) "mh-init" "\ +Return a list of installed variants of MH on the system. +This function looks for MH in `mh-sys-path', `mh-path' and +`exec-path'. The format of the list of variants that is returned is described +by the variable `mh-variants'." nil nil) + +;;;*** + ;;;### (autoloads (mh-junk-whitelist mh-junk-blacklist) "mh-junk" -;;;;;; "mh-junk.el" (16625 54386)) +;;;;;; "mh-junk.el" (16671 49652)) ;;; Generated autoloads from mh-junk.el (autoload (quote mh-junk-blacklist) "mh-junk" "\ Blacklist RANGE as spam. -Check the documentation of `mh-interactive-range' to see how RANGE is read in -interactive use. - -First the appropriate function is called depending on the value of -`mh-junk-choice'. Then if `mh-junk-mail-folder' is a string then the message is -refiled to that folder. If nil, the message is deleted. - -To change the spam program being used, customize `mh-junk-program'. Directly -setting `mh-junk-choice' is not recommended. +This command trains the spam program in use (see the `mh-junk-program' option) +with the content of the range (see `mh-interactive-range') and then handles +the message(s) as specified by the `mh-junk-disposition' option. -The documentation for the following functions describes what setup is needed -for the different spam fighting programs: +For more information about using your particular spam fighting program, see: + - `mh-spamassassin-blacklist' - `mh-bogofilter-blacklist' - - `mh-spamprobe-blacklist' - - `mh-spamassassin-blacklist'" t nil) + - `mh-spamprobe-blacklist'" t nil) (autoload (quote mh-junk-whitelist) "mh-junk" "\ -Whitelist RANGE incorrectly classified as spam. +Whitelist RANGE as ham. -Check the documentation of `mh-interactive-range' to see how RANGE is read in -interactive use. +This command reclassifies a range of messages (see `mh-interactive-range') as +ham if it were incorrectly classified as spam. It then refiles the message +into the `+inbox' folder. -First the appropriate function is called depending on the value of -`mh-junk-choice'. Then the message is refiled to `mh-inbox'. - -To change the spam program being used, customize `mh-junk-program'. Directly -setting `mh-junk-choice' is not recommended." t nil) +The `mh-junk-program' option specifies the spam program in use." t nil) ;;;*** -;;;### (autoloads (mh-mime-inline-part mh-mime-save-part mh-push-button -;;;;;; mh-press-button mh-mime-display mh-decode-message-header -;;;;;; mh-mime-save-parts mh-display-emphasis mh-display-smileys -;;;;;; mh-add-missing-mime-version-header mh-destroy-postponed-handles -;;;;;; mh-mime-cleanup mh-mml-directive-present-p mh-mml-secure-message-encrypt-pgpmime -;;;;;; mh-mml-secure-message-sign-pgpmime mh-mml-attach-file mh-mml-forward-message -;;;;;; mh-mml-to-mime mh-mhn-directive-present-p mh-revert-mhn-edit -;;;;;; mh-edit-mhn mh-mhn-compose-forw mh-mhn-compose-external-compressed-tar -;;;;;; mh-mhn-compose-anon-ftp mh-mhn-compose-insertion mh-compose-forward -;;;;;; mh-compose-insertion) "mh-mime" "mh-mime.el" (16625 54523)) +;;;### (autoloads (mh-display-with-external-viewer mh-mime-inline-part +;;;;;; mh-mime-save-part mh-push-button mh-press-button mh-mime-display +;;;;;; mh-decode-message-header mh-mime-save-parts mh-display-emphasis +;;;;;; mh-display-smileys mh-add-missing-mime-version-header mh-destroy-postponed-handles +;;;;;; mh-mime-cleanup mh-mml-directive-present-p mh-mml-secure-message-signencrypt +;;;;;; mh-mml-secure-message-encrypt mh-mml-secure-message-sign +;;;;;; mh-mml-unsecure-message mh-mml-attach-file mh-mml-query-cryptographic-method +;;;;;; mh-mml-forward-message mh-mml-to-mime mh-mhn-directive-present-p +;;;;;; mh-revert-mhn-edit mh-edit-mhn mh-mhn-compose-forw mh-mhn-compose-external-type +;;;;;; mh-mhn-compose-external-compressed-tar mh-mhn-compose-anon-ftp +;;;;;; mh-mhn-compose-insertion mh-file-mime-type mh-have-file-command +;;;;;; mh-compose-forward mh-compose-insertion) "mh-mime" "mh-mime.el" +;;;;;; (16665 55171)) ;;; Generated autoloads from mh-mime.el (autoload (quote mh-compose-insertion) "mh-mime" "\ @@ -686,6 +662,14 @@ come. Optional argument MESSAGE is the message to forward. If any of the optional arguments are absent, they are prompted for." t nil) +(autoload (quote mh-have-file-command) "mh-mime" "\ +Return t if 'file' command is on the system. +'file -i' is used to get MIME type of composition insertion." nil nil) + +(autoload (quote mh-file-mime-type) "mh-mime" "\ +Return MIME type of FILENAME from file command. +Returns nil if file command not on system." nil nil) + (autoload (quote mh-mhn-compose-insertion) "mh-mime" "\ Add a directive to insert a MIME message part from a file. This is the typical way to insert non-text parts in a message. @@ -718,6 +702,18 @@ DESCRIPTION, a line of text for the Content-description header. See also \\[mh-edit-mhn]." t nil) +(autoload (quote mh-mhn-compose-external-type) "mh-mime" "\ +Add a directive to include a MIME reference to a remote file. +The file should be available via anonymous ftp. This directive tells MH to +include a reference to a message/external-body part. + +Arguments are ACCESS-TYPE, HOST and FILENAME, which tell where to find the +file and TYPE which is the MIME Content-Type. Optional arguments include +DESCRIPTION, a line of text for the Content-description header, ATTRIBUTES, +EXTRA-PARAMS, and COMMENT. + +See also \\[mh-edit-mhn]." t nil) + (autoload (quote mh-mhn-compose-forw) "mh-mime" "\ Add a forw directive to this message, to forward a message with MIME. This directive tells MH to include the named messages in this one. @@ -758,7 +754,9 @@ Undo the effect of \\[mh-edit-mhn] by reverting to the backup file. Optional non-nil argument NOCONFIRM means don't ask for confirmation." t nil) (autoload (quote mh-mhn-directive-present-p) "mh-mime" "\ -Check if the current buffer has text which might be a MHN directive." nil nil) +Check if the text between BEGIN and END might be a MHN directive. +The optional argument BEGIN defaults to the beginning of the buffer, while END +defaults to the the end of the buffer." nil nil) (autoload (quote mh-mml-to-mime) "mh-mime" "\ Compose MIME message from mml directives. @@ -770,6 +768,9 @@ Forward a message as attachment. The function will prompt the user for a DESCRIPTION, a FOLDER and MESSAGE number." nil nil) +(autoload (quote mh-mml-query-cryptographic-method) "mh-mime" "\ +Read the cryptographic method to use." nil nil) + (autoload (quote mh-mml-attach-file) "mh-mime" "\ Attach a file to the outgoing MIME message. The file is not inserted or encoded until you send the message with @@ -781,12 +782,18 @@ This is basically `mml-attach-file' from gnus, modified such that a prefix argument yields an `inline' disposition and Content-Type is determined automatically." nil nil) -(autoload (quote mh-mml-secure-message-sign-pgpmime) "mh-mime" "\ -Add directive to encrypt/sign the entire message." t nil) +(autoload (quote mh-mml-unsecure-message) "mh-mime" "\ +Remove any secure message directives. +The IGNORE argument is not used." t nil) -(autoload (quote mh-mml-secure-message-encrypt-pgpmime) "mh-mime" "\ -Add directive to encrypt and sign the entire message. -If called with a prefix argument DONTSIGN, only encrypt (do NOT sign)." t nil) +(autoload (quote mh-mml-secure-message-sign) "mh-mime" "\ +Add security directive to sign the entire message using METHOD." t nil) + +(autoload (quote mh-mml-secure-message-encrypt) "mh-mime" "\ +Add security directive to encrypt the entire message using METHOD." t nil) + +(autoload (quote mh-mml-secure-message-signencrypt) "mh-mime" "\ +Add security directive to encrypt and sign the entire message using METHOD." t nil) (autoload (quote mh-mml-directive-present-p) "mh-mime" "\ Check if the current buffer has text which may be an MML directive." nil nil) @@ -840,10 +847,13 @@ Save MIME part at point." t nil) (autoload (quote mh-mime-inline-part) "mh-mime" "\ Toggle display of the raw MIME part." t nil) +(autoload (quote mh-display-with-external-viewer) "mh-mime" "\ +View MIME PART-INDEX externally." t nil) + ;;;*** -;;;### (autoloads (mh-do-search mh-pick-do-search mh-do-pick-search -;;;;;; mh-search-folder) "mh-pick" "mh-pick.el" (16625 54571)) +;;;### (autoloads (mh-do-search mh-pick-do-search mh-search-folder) +;;;;;; "mh-pick" "mh-pick.el" (16671 49652)) ;;; Generated autoloads from mh-pick.el (autoload (quote mh-search-folder) "mh-pick" "\ @@ -853,13 +863,6 @@ Add the messages found to the sequence named `search'. Argument WINDOW-CONFIG is the current window configuration and is used when the search folder is dismissed." t nil) -(autoload (quote mh-do-pick-search) "mh-pick" "\ -Find messages that match the qualifications in the current pattern buffer. -Messages are searched for in the folder named in `mh-searching-folder'. -Add the messages found to the sequence named `search'. - -This is a deprecated function and `mh-pick-do-search' should be used instead." t nil) - (autoload (quote mh-pick-do-search) "mh-pick" "\ Find messages that match the qualifications in the current pattern buffer. Messages are searched for in the folder named in `mh-searching-folder'. @@ -873,19 +876,62 @@ indexing program specified in `mh-index-program' is used." t nil) ;;;*** +;;;### (autoloads (mh-print-msg mh-ps-print-toggle-mime mh-ps-print-toggle-color +;;;;;; mh-ps-print-toggle-faces mh-ps-print-msg-show mh-ps-print-msg-file +;;;;;; mh-ps-print-msg) "mh-print" "mh-print.el" (16671 49652)) +;;; Generated autoloads from mh-print.el + +(autoload (quote mh-ps-print-msg) "mh-print" "\ +Print the messages in RANGE. + +Check the documentation of `mh-interactive-range' to see how RANGE is read in +interactive use." t nil) + +(autoload (quote mh-ps-print-msg-file) "mh-print" "\ +Print to FILE the messages in RANGE. + +Check the documentation of `mh-interactive-range' to see how RANGE is read in +interactive use." t nil) + +(autoload (quote mh-ps-print-msg-show) "mh-print" "\ +Print current show buffer to FILE." t nil) + +(autoload (quote mh-ps-print-toggle-faces) "mh-print" "\ +Toggle whether printing is done with faces or not." t nil) + +(autoload (quote mh-ps-print-toggle-color) "mh-print" "\ +Toggle whether color is used in printing messages." t nil) + +(autoload (quote mh-ps-print-toggle-mime) "mh-print" "\ +Cycle through available choices on how MIME parts should be printed. +The available settings are: + 1. Print only inline MIME parts. + 2. Print all MIME parts. + 3. Print no MIME parts." t nil) + +(autoload (quote mh-print-msg) "mh-print" "\ +Print RANGE on printer. + +Check the documentation of `mh-interactive-range' to see how RANGE is read in +interactive use. + +The variable `mh-lpr-command-format' is used to generate the print command. +The messages are formatted by mhl. See the variable `mhl-formfile'." t nil) + +;;;*** + ;;;### (autoloads (mh-narrow-to-tick mh-toggle-tick mh-thread-refile ;;;;;; mh-thread-delete mh-thread-ancestor mh-thread-previous-sibling ;;;;;; mh-thread-next-sibling mh-thread-forget-message mh-toggle-threads ;;;;;; mh-thread-add-spaces mh-thread-update-scan-line-map mh-thread-inc ;;;;;; mh-delete-subject-or-thread mh-delete-subject mh-narrow-to-range ;;;;;; mh-narrow-to-to mh-narrow-to-cc mh-narrow-to-from mh-narrow-to-subject -;;;;;; mh-region-to-msg-list mh-interactive-range mh-range-to-msg-list -;;;;;; mh-iterate-on-range mh-iterate-on-messages-in-region mh-add-to-sequence -;;;;;; mh-notate-cur mh-notate-seq mh-map-to-seq-msgs mh-rename-seq -;;;;;; mh-translate-range mh-read-range mh-read-seq-default mh-notate-deleted-and-refiled -;;;;;; mh-widen mh-put-msg-in-seq mh-narrow-to-seq mh-msg-is-in-seq -;;;;;; mh-list-sequences mh-delete-seq) "mh-seq" "mh-seq.el" (16625 -;;;;;; 54690)) +;;;;;; mh-interactive-range mh-range-to-msg-list mh-iterate-on-range +;;;;;; mh-iterate-on-messages-in-region mh-add-to-sequence mh-notate-cur +;;;;;; mh-rename-seq mh-translate-range mh-read-range mh-read-seq-default +;;;;;; mh-notate-deleted-and-refiled mh-widen mh-put-msg-in-seq +;;;;;; mh-narrow-to-seq mh-msg-is-in-seq mh-list-sequences mh-delete-seq) +;;;;;; "mh-seq" "mh-seq.el" (16668 22297)) ;;; Generated autoloads from mh-seq.el (autoload (quote mh-delete-seq) "mh-seq" "\ @@ -895,8 +941,9 @@ Delete the SEQUENCE." t nil) List the sequences defined in the folder being visited." t nil) (autoload (quote mh-msg-is-in-seq) "mh-seq" "\ -Display the sequences that contain MESSAGE. -Default is the displayed message." t nil) +Display the sequences in which the current message appears. +Use a prefix argument to display the sequences in which another MESSAGE +appears." t nil) (autoload (quote mh-narrow-to-seq) "mh-seq" "\ Restrict display of this folder to just messages in SEQUENCE. @@ -909,10 +956,8 @@ Check the documentation of `mh-interactive-range' to see how RANGE is read in interactive use." t nil) (autoload (quote mh-widen) "mh-seq" "\ -Remove last restriction from current folder. -If optional prefix argument ALL-FLAG is non-nil, then unwind to the beginning -of the view stack thereby showing all messages that the buffer originally -contained." t nil) +Restore the previous limit. +If optional prefix argument ALL-FLAG is non-nil, remove all limits." t nil) (autoload (quote mh-notate-deleted-and-refiled) "mh-seq" "\ Notate messages marked for deletion or refiling. @@ -965,16 +1010,6 @@ In FOLDER, translate the string EXPR to a list of messages numbers." nil nil) (autoload (quote mh-rename-seq) "mh-seq" "\ Rename SEQUENCE to have NEW-NAME." t nil) -(autoload (quote mh-map-to-seq-msgs) "mh-seq" "\ -Invoke the FUNC at each message in the SEQ. -SEQ can either be a list of messages or a MH sequence. The remaining ARGS are -passed as arguments to FUNC." nil nil) - -(autoload (quote mh-notate-seq) "mh-seq" "\ -Mark the scan listing. -All messages in SEQ are marked with NOTATION at OFFSET from the beginning of -the line." nil nil) - (autoload (quote mh-notate-cur) "mh-seq" "\ Mark the MH sequence cur. In addition to notating the current message with `mh-note-cur' the function @@ -1019,37 +1054,44 @@ RANGE-PROMPT. A list of messages in that range is returned. If a MH range is given, say something like last:20, then a list containing the messages in that range is returned. +If DEFAULT non-nil then it is returned. + Otherwise, the message number at point is returned. This function is usually used with `mh-iterate-on-range' in order to provide a uniform interface to MH-E functions." nil nil) -(autoload (quote mh-region-to-msg-list) "mh-seq" "\ -Return a list of messages within the region between BEGIN and END." nil nil) - (autoload (quote mh-narrow-to-subject) "mh-seq" "\ -Narrow to a sequence containing all following messages with same subject." t nil) +Limit to messages with same subject. +With a prefix argument, edit PICK-EXPR. + +Use \\<mh-folder-mode-map>\\[mh-widen] to undo this command." t nil) (autoload (quote mh-narrow-to-from) "mh-seq" "\ -Limit to messages with the same From header field as the message at point. -With a prefix argument, prompt for the regular expression, REGEXP given to -pick." t nil) +Limit to messages with the same `From:' field. +With a prefix argument, edit PICK-EXPR. + +Use \\<mh-folder-mode-map>\\[mh-widen] to undo this command." t nil) (autoload (quote mh-narrow-to-cc) "mh-seq" "\ -Limit to messages with the same Cc header field as the message at point. -With a prefix argument, prompt for the regular expression, REGEXP given to -pick." t nil) +Limit to messages with the same `Cc:' field. +With a prefix argument, edit PICK-EXPR. + +Use \\<mh-folder-mode-map>\\[mh-widen] to undo this command." t nil) (autoload (quote mh-narrow-to-to) "mh-seq" "\ -Limit to messages with the same To header field as the message at point. -With a prefix argument, prompt for the regular expression, REGEXP given to -pick." t nil) +Limit to messages with the same `To:' field. +With a prefix argument, edit PICK-EXPR. + +Use \\<mh-folder-mode-map>\\[mh-widen] to undo this command." t nil) (autoload (quote mh-narrow-to-range) "mh-seq" "\ Limit to messages in RANGE. Check the documentation of `mh-interactive-range' to see how RANGE is read in -interactive use." t nil) +interactive use. + +Use \\<mh-folder-mode-map>\\[mh-widen] to undo this command." t nil) (autoload (quote mh-delete-subject) "mh-seq" "\ Mark all following messages with same subject to be deleted. @@ -1103,14 +1145,15 @@ Mark current message and all its children for refiling to FOLDER." t nil) Toggle tick mark of all messages in RANGE." t nil) (autoload (quote mh-narrow-to-tick) "mh-seq" "\ -Restrict display of this folder to just messages in `mh-tick-seq'. +Limit to messages in `mh-tick-seq'. + Use \\<mh-folder-mode-map>\\[mh-widen] to undo this command." t nil) ;;;*** ;;;### (autoloads (mh-speed-add-folder mh-speed-invalidate-map mh-speed-flists ;;;;;; mh-speed-view mh-speed-toggle mh-folder-speedbar-buttons) -;;;;;; "mh-speed" "mh-speed.el" (16625 54721)) +;;;;;; "mh-speed" "mh-speed.el" (16665 55171)) ;;; Generated autoloads from mh-speed.el (autoload (quote mh-folder-speedbar-buttons) "mh-speed" "\ @@ -1145,31 +1188,24 @@ The function invalidates the latest ancestor that is present." nil nil) ;;;*** -;;;### (autoloads (mh-get-msg-num mh-goto-address-find-address-at-point) -;;;;;; "mh-utils" "mh-utils.el" (16625 54979)) -;;; Generated autoloads from mh-utils.el - -(autoload (quote mh-goto-address-find-address-at-point) "mh-utils" "\ -Find e-mail address around or before point. -Then search backwards to beginning of line for the start of an e-mail -address. If no e-mail address found, return nil." nil nil) - -(autoload (quote mh-get-msg-num) "mh-utils" "\ -Return the message number of the displayed message. -If the argument ERROR-IF-NO-MESSAGE is non-nil, then complain if the cursor is -not pointing to a message." nil nil) - -;;;*** - ;;;### (autoloads (mh-alias-apropos mh-alias-add-address-under-point -;;;;;; mh-alias-grab-from-field mh-alias-add-alias mh-alias-from-has-no-alias-p +;;;;;; mh-alias-grab-from-field mh-alias-add-alias mh-alias-for-from-p ;;;;;; mh-alias-address-to-alias mh-alias-letter-expand-alias mh-alias-minibuffer-confirm-address ;;;;;; mh-read-address mh-alias-reload-maybe mh-alias-reload) "mh-alias" -;;;;;; "mh-alias.el" (16625 53006)) +;;;;;; "mh-alias.el" (16671 49553)) ;;; Generated autoloads from mh-alias.el (autoload (quote mh-alias-reload) "mh-alias" "\ -Load MH aliases into `mh-alias-alist'." t nil) +Reload MH aliases. + +Since aliases are updated frequently, MH-E will reload aliases automatically +whenever an alias lookup occurs if an alias source (a file listed in your +`Aliasfile:' profile component and your password file if variable +`mh-alias-local-users' is non-nil) has changed. However, you can reload your +aliases manually by calling this command directly. + +The value of `mh-alias-reloaded-hook' is a list of functions to be called, +with no arguments, after the aliases have been loaded." t nil) (autoload (quote mh-alias-reload-maybe) "mh-alias" "\ Load new MH aliases." nil nil) @@ -1186,26 +1222,25 @@ Expand mail alias before point." nil nil) (autoload (quote mh-alias-address-to-alias) "mh-alias" "\ Return the ADDRESS alias if defined, or nil." nil nil) -(autoload (quote mh-alias-from-has-no-alias-p) "mh-alias" "\ -Return t is From has no current alias set. -In the exceptional situation where there isn't a From header in the message the -function returns nil." nil nil) +(autoload (quote mh-alias-for-from-p) "mh-alias" "\ +Return t if sender's address has a corresponding alias." nil nil) (autoload (quote mh-alias-add-alias) "mh-alias" "\ *Add ALIAS for ADDRESS in personal alias file. -Prompts for confirmation if the address already has an alias. -If the alias is already is use, `mh-alias-add-alias-to-file' will prompt." t nil) +This function prompts you for an alias and address. If the alias exists +already, you will have the choice of inserting the new alias before or after +the old alias. In the former case, this alias will be used when sending mail +to this alias. In the latter case, the alias serves as an additional folder +name hint when filing messages." t nil) (autoload (quote mh-alias-grab-from-field) "mh-alias" "\ -*Add ALIAS for ADDRESS in personal alias file. -Prompts for confirmation if the alias is already in use or if the address -already has an alias." t nil) +*Add alias for the sender of the current message." t nil) (autoload (quote mh-alias-add-address-under-point) "mh-alias" "\ -Insert an alias for email address under point." t nil) +Insert an alias for address under point." t nil) (autoload (quote mh-alias-apropos) "mh-alias" "\ -Show all aliases that match REGEXP either in name or content." t nil) +Show all aliases or addresses that match REGEXP." t nil) ;;;*** diff --git a/lisp/mh-e/mh-mime.el b/lisp/mh-e/mh-mime.el index 91cbcec0c06..6b3c5793be5 100644 --- a/lisp/mh-e/mh-mime.el +++ b/lisp/mh-e/mh-mime.el @@ -34,7 +34,7 @@ ;;; Code: -(require 'mh-utils) +(eval-when-compile (require 'mh-acros)) (mh-require-cl) (require 'mh-comp) (require 'gnus-util) @@ -46,8 +46,7 @@ (autoload 'gnus-eval-format "gnus-spec") (autoload 'widget-convert-button "wid-edit") (autoload 'message-options-set-recipient "message") -(autoload 'mml-secure-message-sign-pgpmime "mml-sec") -(autoload 'mml-secure-message-encrypt-pgpmime "mml-sec") +(autoload 'mml-unsecure-message "mml-sec") (autoload 'mml-minibuffer-read-file "mml") (autoload 'mml-minibuffer-read-description "mml") (autoload 'mml-insert-empty-tag "mml") @@ -82,7 +81,7 @@ If any of the optional arguments are absent, they are prompted for." (read-string "Forw Content-description: ") (mh-prompt-for-folder "Message from" mh-sent-from-folder nil) (read-string (format "Messages%s: " - (if mh-sent-from-msg + (if (numberp mh-sent-from-msg) (format " [%d]" mh-sent-from-msg) ""))))) (if (equal mh-compose-insertion 'gnus) @@ -114,6 +113,7 @@ MH profile.") ;; the variable, so things should work exactly as before. (defvar mh-have-file-command) +;;;###mh-autoload (defun mh-have-file-command () "Return t if 'file' command is on the system. 'file -i' is used to get MIME type of composition insertion." @@ -129,7 +129,8 @@ MH profile.") (defvar mh-file-mime-type-substitutions '(("application/msword" "\.xls" "application/ms-excel") - ("application/msword" "\.ppt" "application/ms-powerpoint")) + ("application/msword" "\.ppt" "application/ms-powerpoint") + ("text/plain" "\.vcf" "text/x-vcard")) "Substitutions to make for Content-Type returned from file command. The first element is the Content-Type returned by the file command. The second element is a regexp matching the file name, usually the extension. @@ -151,6 +152,7 @@ Substitutions are made from the `mh-file-mime-type-substitutions' variable." (setq subst (cdr subst)))) answer)) +;;;###mh-autoload (defun mh-file-mime-type (filename) "Return MIME type of FILENAME from file command. Returns nil if file command not on system." @@ -192,12 +194,38 @@ Returns nil if file command not on system." ("message/external-body") ("message/partial") ("message/rfc822") ("text/enriched") ("text/html") ("text/plain") ("text/rfc822-headers") - ("text/richtext") ("text/xml") + ("text/richtext") ("text/x-vcard") ("text/xml") ("video/mpeg") ("video/quicktime")) "Legal MIME content types. See documentation for \\[mh-edit-mhn].") +;; RFC 2045 - Multipurpose Internet Mail Extensions (MIME) Part One: +;; Format of Internet Message Bodies. +;; RFC 2046 - Multipurpose Internet Mail Extensions (MIME) Part Two: +;; Media Types. +;; RFC 2049 - Multipurpose Internet Mail Extensions (MIME) Part Five: +;; Conformance Criteria and Examples. +;; RFC 2017 - Definition of the URL MIME External-Body Access-Type +;; RFC 1738 - Uniform Resource Locators (URL) +(defvar mh-access-types + '(("anon-ftp") ; RFC2046 Anonymous File Transfer Protocol + ("file") ; RFC1738 Host-specific file names + ("ftp") ; RFC2046 File Transfer Protocol + ("gopher") ; RFC1738 The Gopher Protocol + ("http") ; RFC1738 Hypertext Transfer Protocol + ("local-file") ; RFC2046 Local file access + ("mail-server") ; RFC2046 mail-server Electronic mail address + ("mailto") ; RFC1738 Electronic mail address + ("news") ; RFC1738 Usenet news + ("nntp") ; RFC1738 Usenet news using NNTP access + ("propspero") ; RFC1738 Prospero Directory Service + ("telnet") ; RFC1738 Telnet + ("tftp") ; RFC2046 Trivial File Transfer Protocol + ("url") ; RFC2017 URL scheme MIME access-type Protocol + ("wais")) ; RFC1738 Wide Area Information Servers + "Legal MIME access-type values.") + ;;;###mh-autoload (defun mh-mhn-compose-insertion (filename type description attributes) "Add a directive to insert a MIME message part from a file. @@ -286,7 +314,7 @@ See also \\[mh-edit-mhn]." "type=tar; conversions=x-compress" "mode=image")) - +;;;###mh-autoload (defun mh-mhn-compose-external-type (access-type host filename type &optional description attributes extra-params @@ -301,6 +329,18 @@ DESCRIPTION, a line of text for the Content-description header, ATTRIBUTES, EXTRA-PARAMS, and COMMENT. See also \\[mh-edit-mhn]." + (interactive (list + (completing-read "Access Type: " mh-access-types) + (read-string "Remote host: ") + (read-string "Remote url-path: ") + (completing-read "Content-Type: " + (if (fboundp 'mailcap-mime-types) + (mapcar 'list (mailcap-mime-types)) + mh-mime-content-types)) + (if current-prefix-arg (read-string "Content-description: ")) + (if current-prefix-arg (read-string "Attributes: ")) + (if current-prefix-arg (read-string "Extra Parameters: ")) + (if current-prefix-arg (read-string "Comment: ")))) (beginning-of-line) (insert "#@" type) (and attributes @@ -314,7 +354,9 @@ See also \\[mh-edit-mhn]." (insert "access-type=" access-type "; ") (insert "site=" host) (insert "; name=" (file-name-nondirectory filename)) - (insert "; directory=\"" (file-name-directory filename) "\"") + (let ((directory (file-name-directory filename))) + (and directory + (insert "; directory=\"" directory "\""))) (and extra-params (insert "; " extra-params)) (insert "\n")) @@ -332,7 +374,7 @@ See also \\[mh-edit-mhn]." (read-string "Forw Content-description: ") (mh-prompt-for-folder "Message from" mh-sent-from-folder nil) (read-string (format "Messages%s: " - (if mh-sent-from-msg + (if (numberp mh-sent-from-msg) (format " [%d]" mh-sent-from-msg) ""))))) (beginning-of-line) @@ -349,7 +391,7 @@ See also \\[mh-edit-mhn]." (let ((start (point))) (insert " " messages) (subst-char-in-region start (point) ?, ? )) - (if mh-sent-from-msg + (if (numberp mh-sent-from-msg) (insert " " (int-to-string mh-sent-from-msg)))) (insert "\n")) @@ -380,10 +422,11 @@ arguments, after performing the conversion. The mhn program is part of MH version 6.8 or later." (interactive "*P") + (mh-mhn-quote-unescaped-sharp) (save-buffer) (message "mhn editing...") (cond - (mh-nmh-flag + ((mh-variant-p 'nmh) (mh-exec-cmd-error nil "mhbuild" (if extra-args mh-mhn-args) buffer-file-name)) (t @@ -393,6 +436,19 @@ The mhn program is part of MH version 6.8 or later." (message "mhn editing...done") (run-hooks 'mh-edit-mhn-hook)) +(defun mh-mhn-quote-unescaped-sharp () + "Quote `#' characters that haven't been quoted for `mhbuild'. +If the `#' character is present in the first column, but it isn't part of a +MHN directive then `mhbuild' gives an error. This function will quote all such +characters." + (save-excursion + (goto-char (point-min)) + (while (re-search-forward "^#" nil t) + (beginning-of-line) + (unless (mh-mhn-directive-present-p (point) (line-end-position)) + (insert "#")) + (goto-char (line-end-position))))) + ;;;###mh-autoload (defun mh-revert-mhn-edit (noconfirm) "Undo the effect of \\[mh-edit-mhn] by reverting to the backup file. @@ -422,18 +478,24 @@ Optional non-nil argument NOCONFIRM means don't ask for confirmation." (after-find-file nil))) ;;;###mh-autoload -(defun mh-mhn-directive-present-p () - "Check if the current buffer has text which might be a MHN directive." +(defun mh-mhn-directive-present-p (&optional begin end) + "Check if the text between BEGIN and END might be a MHN directive. +The optional argument BEGIN defaults to the beginning of the buffer, while END +defaults to the the end of the buffer." + (unless begin (setq begin (point-min))) + (unless end (setq end (point-max))) (save-excursion (block 'search-for-mhn-directive - (goto-char (point-min)) - (while (re-search-forward "^#" nil t) + (goto-char begin) + (while (re-search-forward "^#" end t) (let ((s (buffer-substring-no-properties (point) (line-end-position)))) (cond ((equal s "")) ((string-match "^forw[ \t\n]+" s) (return-from 'search-for-mhn-directive t)) (t (let ((first-token (car (split-string s "[ \t;@]")))) - (when (string-match mh-media-type-regexp first-token) + (when (and first-token + (string-match mh-media-type-regexp + first-token)) (return-from 'search-for-mhn-directive t))))))) nil))) @@ -450,14 +512,23 @@ function may be called manually before sending the draft as well." (require 'message) (when mh-gnus-pgp-support-flag ;; This is only needed for PGP (message-options-set-recipient)) - (mml-to-mime)) + (let ((saved-text (buffer-string)) + (buffer (current-buffer)) + (modified-flag (buffer-modified-p))) + (condition-case err (mml-to-mime) + (error + (with-current-buffer buffer + (delete-region (point-min) (point-max)) + (insert saved-text) + (set-buffer-modified-p modified-flag)) + (error (error-message-string err)))))) ;;;###mh-autoload (defun mh-mml-forward-message (description folder message) "Forward a message as attachment. The function will prompt the user for a DESCRIPTION, a FOLDER and MESSAGE number." - (let ((msg (if (equal message "") + (let ((msg (if (and (equal message "") (numberp mh-sent-from-msg)) mh-sent-from-msg (car (read-from-string message))))) (cond ((integerp msg) @@ -473,6 +544,19 @@ number." description))) (t (error "The message number, %s is not a integer!" msg))))) +(defvar mh-mml-cryptographic-method-history ()) + +;;;###mh-autoload +(defun mh-mml-query-cryptographic-method () + "Read the cryptographic method to use." + (if current-prefix-arg + (let ((def (or (car mh-mml-cryptographic-method-history) + mh-mml-method-default))) + (completing-read (format "Method: [%s] " def) + '(("pgp") ("pgpmime") ("smime")) + nil t nil 'mh-mml-cryptographic-method-history def)) + mh-mml-method-default)) + ;;;###mh-autoload (defun mh-mml-attach-file (&optional disposition) "Attach a file to the outgoing MIME message. @@ -499,22 +583,56 @@ automatically." (mml-insert-empty-tag 'part 'type type 'filename file 'disposition dispos 'description description))) -;;;###mh-autoload -(defun mh-mml-secure-message-sign-pgpmime () - "Add directive to encrypt/sign the entire message." - (interactive) +(defun mh-secure-message (method mode &optional identity) + "Add directive to Encrypt/Sign an entire message. +METHOD should be one of: \"pgpmime\", \"pgp\", \"smime\". +MODE should be one of: \"sign\", \"encrypt\", \"signencrypt\", \"none\". +IDENTITY is optionally the default-user-id to use." (if (not mh-gnus-pgp-support-flag) (error "Sorry. Your version of gnus does not support PGP/GPG") - (mml-secure-message-sign-pgpmime))) + ;; Check the arguments + (let ((valid-methods (list "pgpmime" "pgp" "smime")) + (valid-modes (list "sign" "encrypt" "signencrypt" "none"))) + (if (not (member method valid-methods)) + (error (format "Sorry. METHOD \"%s\" is invalid." method))) + (if (not (member mode valid-modes)) + (error (format "Sorry. MODE \"%s\" is invalid" mode))) + (mml-unsecure-message) + (if (not (string= mode "none")) + (save-excursion + (goto-char (point-min)) + (mh-goto-header-end 1) + (if mh-identity-pgg-default-user-id + (mml-insert-tag 'secure 'method method 'mode mode + 'sender mh-identity-pgg-default-user-id) + (mml-insert-tag 'secure 'method method 'mode mode))))))) ;;;###mh-autoload -(defun mh-mml-secure-message-encrypt-pgpmime (&optional dontsign) - "Add directive to encrypt and sign the entire message. -If called with a prefix argument DONTSIGN, only encrypt (do NOT sign)." +(defun mh-mml-unsecure-message (&optional ignore) + "Remove any secure message directives. +The IGNORE argument is not used." (interactive "P") (if (not mh-gnus-pgp-support-flag) (error "Sorry. Your version of gnus does not support PGP/GPG") - (mml-secure-message-encrypt-pgpmime dontsign))) + (mml-unsecure-message))) + +;;;###mh-autoload +(defun mh-mml-secure-message-sign (method) + "Add security directive to sign the entire message using METHOD." + (interactive (list (mh-mml-query-cryptographic-method))) + (mh-secure-message method "sign" mh-identity-pgg-default-user-id)) + +;;;###mh-autoload +(defun mh-mml-secure-message-encrypt (method) + "Add security directive to encrypt the entire message using METHOD." + (interactive (list (mh-mml-query-cryptographic-method))) + (mh-secure-message method "encrypt" mh-identity-pgg-default-user-id)) + +;;;###mh-autoload +(defun mh-mml-secure-message-signencrypt (method) + "Add security directive to encrypt and sign the entire message using METHOD." + (interactive (list (mh-mml-query-cryptographic-method))) + (mh-secure-message method "signencrypt" mh-identity-pgg-default-user-id)) ;;;###mh-autoload (defun mh-mml-directive-present-p () @@ -667,19 +785,19 @@ actual storing." (folder (if (eq major-mode 'mh-show-mode) mh-show-folder-buffer mh-current-folder)) - (command (if mh-nmh-flag "mhstore" "mhn")) + (command (if (mh-variant-p 'nmh) "mhstore" "mhn")) (directory (cond ((and (or arg (equal nil mh-mime-save-parts-default-directory) (equal t mh-mime-save-parts-default-directory)) (not mh-mime-save-parts-directory)) - (read-file-name "Store in what directory? " nil nil t nil)) + (read-file-name "Store in directory: " nil nil t nil)) ((and (or arg (equal t mh-mime-save-parts-default-directory)) mh-mime-save-parts-directory) (read-file-name (format - "Store in what directory? [%s] " + "Store in directory: [%s] " mh-mime-save-parts-directory) "" mh-mime-save-parts-directory t "")) ((stringp mh-mime-save-parts-default-directory) @@ -689,7 +807,7 @@ actual storing." (if (and (equal directory "") mh-mime-save-parts-directory) (setq directory mh-mime-save-parts-directory)) (if (not (file-directory-p directory)) - (message "No directory specified.") + (message "No directory specified") (if (equal nil mh-mime-save-parts-default-directory) (setq mh-mime-save-parts-directory directory)) (save-excursion @@ -766,17 +884,18 @@ displayed." (mh-mime-handles (mh-buffer-data)))) (unless handles (mh-decode-message-body))) - (when (and handles - (or (not (stringp (car handles))) (cdr handles))) - ;; Goto start of message body - (goto-char (point-min)) - (or (search-forward "\n\n" nil t) (goto-char (point-max))) + (cond ((and handles + (or (not (stringp (car handles))) (cdr handles))) + ;; Goto start of message body + (goto-char (point-min)) + (or (search-forward "\n\n" nil t) (goto-char (point-max))) - ;; Delete the body - (delete-region (point) (point-max)) + ;; Delete the body + (delete-region (point) (point-max)) - ;; Display the MIME handles - (mh-mime-display-part handles))) + ;; Display the MIME handles + (mh-mime-display-part handles)) + (t (mh-signature-highlight)))) (error (message "Please report this error. The error message is:\n %s" (error-message-string err)) @@ -874,7 +993,7 @@ This is only useful if a Content-Disposition header is not present." (save-restriction (widen) (goto-char (point-min)) - (not (re-search-forward "^-- $" nil t))))))) + (not (mh-signature-separator-p))))))) (defun mh-mime-display-single (handle) "Display a leaf node, HANDLE in the MIME tree." @@ -904,7 +1023,8 @@ This is only useful if a Content-Disposition header is not present." (insert "\n") (mh-insert-mime-button handle (mh-mime-part-index handle) nil)) ((and displayp (not mh-display-buttons-for-inline-parts-flag)) - (or (mm-display-part handle) (mm-display-part handle))) + (or (mm-display-part handle) (mm-display-part handle)) + (mh-signature-highlight handle)) ((and displayp mh-display-buttons-for-inline-parts-flag) (insert "\n") (mh-insert-mime-button handle (mh-mime-part-index handle) nil) @@ -912,6 +1032,28 @@ This is only useful if a Content-Disposition header is not present." (mh-mm-display-part handle))) (goto-char (point-max))))) +(defun mh-signature-highlight (&optional handle) + "Highlight message signature in HANDLE. +The optional argument, HANDLE is a MIME handle if the function is being used +to highlight the signature in a MIME part." + (let ((regexp + (cond ((not handle) "^-- $") + ((not (and (equal (mm-handle-media-supertype handle) "text") + (equal (mm-handle-media-subtype handle) "html"))) + "^-- $") + ((eq (mh-mm-text-html-renderer) 'lynx) "^ --$") + (t "^--$")))) + (save-excursion + (goto-char (point-max)) + (when (re-search-backward regexp nil t) + (mh-do-in-gnu-emacs + (let ((ov (make-overlay (point) (point-max)))) + (overlay-put ov 'face 'mh-show-signature-face) + (overlay-put ov 'evaporate t))) + (mh-do-in-xemacs + (set-extent-property (make-extent (point) (point-max)) + 'face 'mh-show-signature-face)))))) + (mh-do-in-xemacs (defvar dots) (defvar type)) @@ -954,7 +1096,9 @@ like \"K v\" which operate on individual MIME parts." :action 'mh-widget-press-button :button-keymap mh-mime-button-map :help-echo - "Mouse-2 click or press RET (in show buffer) to toggle display"))) + "Mouse-2 click or press RET (in show buffer) to toggle display") + (dolist (ov (mh-funcall-if-exists overlays-in begin end)) + (mh-funcall-if-exists overlay-put ov 'evaporate t)))) ;; There is a bug in Gnus inline image display due to which an extra line ;; gets inserted every time it is viewed. To work around that problem we are @@ -1009,7 +1153,8 @@ like \"K v\" which operate on individual MIME parts." (when (eq mh-highlight-citation-p 'gnus) (mh-gnus-article-highlight-citation)) (mh-display-smileys) - (mh-display-emphasis)) + (mh-display-emphasis) + (mh-signature-highlight handle)) (setq region (cons (progn (goto-char (point-min)) (point-marker)) (progn (goto-char (point-max)) @@ -1098,6 +1243,31 @@ button." (goto-char point) (set-buffer-modified-p nil))) +;;;###mh-autoload +(defun mh-display-with-external-viewer (part-index) + "View MIME PART-INDEX externally." + (interactive "P") + (when (consp part-index) (setq part-index (car part-index))) + (mh-folder-mime-action + part-index + #'(lambda () + (let* ((part (get-text-property (point) 'mh-data)) + (type (mm-handle-media-type part)) + (methods (mapcar (lambda (x) (list (cdr (assoc 'viewer x)))) + (mailcap-mime-info type 'all))) + (def (caar methods)) + (prompt (format "Viewer: %s" (if def (format "[%s] " def) ""))) + (method (completing-read prompt methods nil nil nil nil def)) + (folder mh-show-folder-buffer) + (buffer-read-only nil)) + (when (string-match "^[^% \t]+$" method) + (setq method (concat method " %s"))) + (flet ((mm-handle-set-external-undisplayer (handle function) + (mh-handle-set-external-undisplayer folder handle function))) + (unwind-protect (mm-display-external part method) + (set-buffer-modified-p nil))))) + nil)) + (defun mh-widget-press-button (widget el) "Callback for widget, WIDGET. Parameter EL is unused." @@ -1106,9 +1276,9 @@ Parameter EL is unused." (defun mh-mime-display-security (handle) "Display PGP encrypted/signed message, HANDLE." - (insert "\n") (save-restriction (narrow-to-region (point) (point)) + (insert "\n") (mh-insert-mime-security-button handle) (mh-mime-display-mixed (cdr handle)) (insert "\n") @@ -1116,9 +1286,7 @@ Parameter EL is unused." mh-mime-security-button-end-line-format)) (mh-insert-mime-security-button handle)) (mm-set-handle-multipart-parameter - handle 'mh-region - (cons (set-marker (make-marker) (point-min)) - (set-marker (make-marker) (point-max)))))) + handle 'mh-region (cons (point-min-marker) (point-max-marker))))) ;;; I rewrote the security part because Gnus doesn't seem to ever minimize ;;; the button. That is once the mime-security button is pressed there seems @@ -1149,8 +1317,22 @@ Parameter EL is unused." (defun mh-mime-security-press-button (handle) "Callback from security button for part HANDLE." - (when (mm-handle-multipart-ctl-parameter handle 'gnus-info) - (mh-mime-security-show-details handle))) + (if (mm-handle-multipart-ctl-parameter handle 'gnus-info) + (mh-mime-security-show-details handle) + (let ((region (mm-handle-multipart-ctl-parameter handle 'mh-region)) + point) + (setq point (point)) + (goto-char (car region)) + (delete-region (car region) (cdr region)) + (with-current-buffer (mm-handle-multipart-ctl-parameter handle 'buffer) + (let* ((mm-verify-option 'known) + (mm-decrypt-option 'known) + (new (mm-possibly-verify-or-decrypt (cdr handle) handle))) + (unless (eq new (cdr handle)) + (mm-destroy-parts (cdr handle)) + (setcdr handle new)))) + (mh-mime-display-security handle) + (goto-char point)))) ;; These variables should already be initialized in mm-decode.el if we have a ;; recent enough Gnus. The defvars are here to avoid compiler warnings. @@ -1191,6 +1373,8 @@ Parameter EL is unused." :action 'mh-widget-press-button :button-keymap mh-mime-security-button-map :help-echo "Mouse-2 click or press RET (in show buffer) to see security details.") + (dolist (ov (mh-funcall-if-exists overlays-in begin end)) + (mh-funcall-if-exists overlay-put ov 'evaporate t)) (when (equal info "Failed") (let* ((type (if (equal (car handle) "multipart/signed") "verification" "decryption")) @@ -1204,8 +1388,8 @@ The function decodes the message and displays it. It avoids decoding the same message multiple times." (let ((b (point)) (clean-message-header mh-clean-message-header-flag) - (invisible-headers mh-invisible-headers) - (visible-headers mh-visible-headers)) + (invisible-headers mh-invisible-header-fields-compiled) + (visible-headers nil)) (save-excursion (save-restriction (narrow-to-region b b) diff --git a/lisp/mh-e/mh-pick.el b/lisp/mh-e/mh-pick.el index a888f02154f..b92a98f26cc 100644 --- a/lisp/mh-e/mh-pick.el +++ b/lisp/mh-e/mh-pick.el @@ -1,6 +1,6 @@ ;;; mh-pick.el --- make a search pattern and search for a message in MH-E -;; Copyright (C) 1993, 1995, 2001, 2003 Free Software Foundation, Inc. +;; Copyright (C) 1993, 1995, 2001, 2003, 2004 Free Software Foundation, Inc. ;; Author: Bill Wohler <wohler@newt.com> ;; Maintainer: Bill Wohler <wohler@newt.com> @@ -32,6 +32,8 @@ ;;; Code: +(eval-when-compile (require 'mh-acros)) +(mh-require-cl) (require 'mh-e) (require 'easymenu) (require 'gnus-util) @@ -44,6 +46,9 @@ (defvar mh-searching-folder nil) ;Folder this pick is searching. (defvar mh-searching-function nil) +(defconst mh-pick-single-dash '(cc date from subject to) + "Search components that are supported by single-dash option in pick.") + ;;;###mh-autoload (defun mh-search-folder (folder window-config) "Search FOLDER for messages matching a pattern. @@ -139,16 +144,6 @@ with no arguments, upon entry to this mode. (run-hooks 'mh-pick-mode-hook)) ;;;###mh-autoload -(defun mh-do-pick-search () - "Find messages that match the qualifications in the current pattern buffer. -Messages are searched for in the folder named in `mh-searching-folder'. -Add the messages found to the sequence named `search'. - -This is a deprecated function and `mh-pick-do-search' should be used instead." - (interactive) - (mh-pick-do-search)) - -;;;###mh-autoload (defun mh-pick-do-search () "Find messages that match the qualifications in the current pattern buffer. Messages are searched for in the folder named in `mh-searching-folder'. @@ -260,6 +255,13 @@ COMPONENT is the component to search." "-rbrace")) (t (error "Unknown operator '%s' seen" (car expr))))) +;; All implementations of pick have special options -cc, -date, -from and +;; -subject that allow to search for corresponding components. Any other +;; component is searched using option --COMPNAME, for example: `pick +;; --x-mailer mh-e'. Mailutils `pick' supports this option using a certain +;; kludge, but it prefers the following syntax for this purpose: +;; `--component=COMPNAME --pattern=PATTERN'. +;; -- Sergey Poznyakoff, Aug 2003 (defun mh-pick-regexp-builder (pattern-list) "Generate pick search expression from PATTERN-LIST." (let ((result ())) @@ -267,9 +269,18 @@ COMPONENT is the component to search." (when (cdr pattern) (setq result `(,@result "-and" "-lbrace" ,@(mh-pick-construct-regexp - (cdr pattern) (if (car pattern) - (format "-%s" (car pattern)) - "-search")) + (if (and (mh-variant-p 'mu-mh) (car pattern)) + (format "--pattern=%s" (cdr pattern)) + (cdr pattern)) + (if (car pattern) + (cond + ((mh-variant-p 'mu-mh) + (format "--component=%s" (car pattern))) + ((member (car pattern) mh-pick-single-dash) + (format "-%s" (car pattern))) + (t + (format "--%s" (car pattern)))) + "-search")) "-rbrace")))) (cdr result))) diff --git a/lisp/mh-e/mh-seq.el b/lisp/mh-e/mh-seq.el index 20950d36c4c..8d2369ed19a 100644 --- a/lisp/mh-e/mh-seq.el +++ b/lisp/mh-e/mh-seq.el @@ -70,7 +70,7 @@ ;;; Code: -(require 'mh-utils) +(eval-when-compile (require 'mh-acros)) (mh-require-cl) (require 'mh-e) @@ -78,15 +78,15 @@ (defvar tool-bar-mode) ;;; Data structures (used in message threading)... -(defstruct (mh-thread-message (:conc-name mh-message-) - (:constructor mh-thread-make-message)) +(mh-defstruct (mh-thread-message (:conc-name mh-message-) + (:constructor mh-thread-make-message)) (id nil) (references ()) (subject "") (subject-re-p nil)) -(defstruct (mh-thread-container (:conc-name mh-container-) - (:constructor mh-thread-make-container)) +(mh-defstruct (mh-thread-container (:conc-name mh-container-) + (:constructor mh-thread-make-container)) message parent children (real-child-p t)) @@ -201,12 +201,15 @@ redone to get the new thread tree. This makes incremental threading easier.") ;;;###mh-autoload (defun mh-msg-is-in-seq (message) - "Display the sequences that contain MESSAGE. -Default is the displayed message." - (interactive (list (mh-get-msg-num t))) + "Display the sequences in which the current message appears. +Use a prefix argument to display the sequences in which another MESSAGE +appears." + (interactive "P") + (if (not message) + (setq message (mh-get-msg-num t))) (let* ((dest-folder (loop for seq in mh-refile-list - until (member message (cdr seq)) - finally return (car seq))) + when (member message (cdr seq)) return (car seq) + finally return nil)) (deleted-flag (unless dest-folder (member message mh-delete-list)))) (message "Message %d%s is in sequences: %s" message @@ -269,12 +272,11 @@ interactive use." (let* ((internal-seq-flag (mh-internal-seq sequence)) (original-msgs (mh-seq-msgs (mh-find-seq sequence))) (folders (list mh-current-folder)) - (msg-list ())) + (msg-list (mh-range-to-msg-list range))) + (mh-add-msgs-to-seq msg-list sequence nil t) (mh-iterate-on-range m range - (push m msg-list) (unless (memq m original-msgs) (mh-add-sequence-notation m internal-seq-flag))) - (mh-add-msgs-to-seq msg-list sequence nil t) (if (not internal-seq-flag) (setq mh-last-seq-used sequence)) (when mh-index-data @@ -292,10 +294,8 @@ OP is one of 'widen and 'unthread." ;;;###mh-autoload (defun mh-widen (&optional all-flag) - "Remove last restriction from current folder. -If optional prefix argument ALL-FLAG is non-nil, then unwind to the beginning -of the view stack thereby showing all messages that the buffer originally -contained." + "Restore the previous limit. +If optional prefix argument ALL-FLAG is non-nil, remove all limits." (interactive "P") (let ((msg (mh-get-msg-num nil))) (when mh-folder-view-stack @@ -533,28 +533,6 @@ should be replaced with: (rplaca old-seq new-name))) ;;;###mh-autoload -(defun mh-map-to-seq-msgs (func seq &rest args) - "Invoke the FUNC at each message in the SEQ. -SEQ can either be a list of messages or a MH sequence. The remaining ARGS are -passed as arguments to FUNC." - (save-excursion - (let ((msgs (if (listp seq) seq (mh-seq-to-msgs seq)))) - (while msgs - (if (mh-goto-msg (car msgs) t t) - (apply func (car msgs) args)) - (setq msgs (cdr msgs)))))) - -;;;###mh-autoload -(defun mh-notate-seq (seq notation offset) - "Mark the scan listing. -All messages in SEQ are marked with NOTATION at OFFSET from the beginning of -the line." - (let ((msg-list (mh-seq-to-msgs seq))) - (mh-iterate-on-messages-in-region msg (point-min) (point-max) - (when (member msg msg-list) - (mh-notate nil notation offset))))) - -;;;###mh-autoload (defun mh-notate-cur () "Mark the MH sequence cur. In addition to notating the current message with `mh-note-cur' the function @@ -577,14 +555,6 @@ uses `overlay-arrow-position' to put a marker in the fringe." "-sequence" (symbol-name seq) (mh-coalesce-msg-list msgs))))) -;; This has a tricky bug. mh-map-to-seq-msgs uses mh-goto-msg, which assumes -;; that the folder buffer is sorted. However in this case that assumption -;; doesn't hold. So we will do this the dumb way. -;(defun mh-copy-seq-to-point (seq location) -; ;; Copy the scan listing of the messages in SEQUENCE to after the point -; ;; LOCATION in the current buffer. -; (mh-map-to-seq-msgs 'mh-copy-line-to-point seq location)) - (defvar mh-thread-last-ancestor) (defun mh-copy-seq-to-eob (seq) @@ -614,21 +584,6 @@ uses `overlay-arrow-position' to put a marker in the fringe." (mh-index-data (mh-index-insert-folder-headers))))))) -(defun mh-copy-line-to-point (msg location) - "Copy current message line to a specific location. -The argument MSG is not used. The message in the current line is copied to -LOCATION." - ;; msg is not used? - ;; Copy the current line to the LOCATION in the current buffer. - (beginning-of-line) - (save-excursion - (let ((beginning-of-line (point)) - end) - (forward-line 1) - (setq end (point)) - (goto-char location) - (insert-buffer-substring (current-buffer) beginning-of-line end)))) - ;;;###mh-autoload (defmacro mh-iterate-on-messages-in-region (var begin end &rest body) "Iterate over region. @@ -702,7 +657,7 @@ a region in a cons cell." (nreverse msg-list))) ;;;###mh-autoload -(defun mh-interactive-range (range-prompt) +(defun mh-interactive-range (range-prompt &optional default) "Return interactive specification for message, sequence, range or region. By convention, the name of this argument is RANGE. @@ -715,24 +670,17 @@ RANGE-PROMPT. A list of messages in that range is returned. If a MH range is given, say something like last:20, then a list containing the messages in that range is returned. +If DEFAULT non-nil then it is returned. + Otherwise, the message number at point is returned. This function is usually used with `mh-iterate-on-range' in order to provide a uniform interface to MH-E functions." (cond ((mh-mark-active-p t) (cons (region-beginning) (region-end))) (current-prefix-arg (mh-read-range range-prompt nil nil t t)) + (default default) (t (mh-get-msg-num t)))) -;;;###mh-autoload -(defun mh-region-to-msg-list (begin end) - "Return a list of messages within the region between BEGIN and END." - ;; If end is end of buffer back up one position - (setq end (if (equal end (point-max)) (1- end) end)) - (let ((result)) - (mh-iterate-on-messages-in-region index begin end - (when (numberp index) (push index result))) - result)) - ;;; Commands to handle new 'subject sequence. @@ -772,7 +720,7 @@ Return number of messages put in the sequence: (if (or (not (looking-at mh-scan-subject-regexp)) (not (match-string 3)) (string-equal "" (match-string 3))) - (progn (message "No subject line.") + (progn (message "No subject line") nil) (let ((subject (match-string-no-properties 3)) (list)) @@ -835,61 +783,57 @@ This function can only be used the folder is threaded." (mh-container-message (gethash (gethash msg mh-thread-index-id-map) mh-thread-id-table))))) -;;;###mh-autoload -(defun mh-narrow-to-subject () - "Narrow to a sequence containing all following messages with same subject." - (interactive) - (let ((num (mh-get-msg-num nil)) - (count (mh-subject-to-sequence t))) - (cond - ((not count) ; No subject line, delete msg anyway - nil) - ((= 0 count) ; No other msgs, delete msg anyway. - (message "No other messages with same Subject following this one.") - nil) - (t ; We have a subject sequence. - (message "Found %d messages for subject sequence." count) - (mh-narrow-to-seq 'subject) - (if (numberp num) - (mh-goto-msg num t t)))))) - -(defun mh-read-pick-regexp (default) - "With prefix arg read a pick regexp. +(defun mh-edit-pick-expr (default) + "With prefix arg edit a pick expression. If no prefix arg is given, then return DEFAULT." (let ((default-string (loop for x in default concat (format " %s" x)))) (if (or current-prefix-arg (equal default-string "")) - (delete "" (split-string (read-string "Pick regexp: " default-string))) + (delete "" (split-string (read-string "Pick expression: " + default-string))) default))) ;;;###mh-autoload -(defun mh-narrow-to-from (&optional regexp) - "Limit to messages with the same From header field as the message at point. -With a prefix argument, prompt for the regular expression, REGEXP given to -pick." +(defun mh-narrow-to-subject (&optional pick-expr) + "Limit to messages with same subject. +With a prefix argument, edit PICK-EXPR. + +Use \\<mh-folder-mode-map>\\[mh-widen] to undo this command." (interactive - (list (mh-read-pick-regexp (mh-current-message-header-field 'from)))) - (mh-narrow-to-header-field 'from regexp)) + (list (mh-edit-pick-expr (mh-current-message-header-field 'subject)))) + (mh-narrow-to-header-field 'subject pick-expr)) ;;;###mh-autoload -(defun mh-narrow-to-cc (&optional regexp) - "Limit to messages with the same Cc header field as the message at point. -With a prefix argument, prompt for the regular expression, REGEXP given to -pick." +(defun mh-narrow-to-from (&optional pick-expr) + "Limit to messages with the same `From:' field. +With a prefix argument, edit PICK-EXPR. + +Use \\<mh-folder-mode-map>\\[mh-widen] to undo this command." (interactive - (list (mh-read-pick-regexp (mh-current-message-header-field 'cc)))) - (mh-narrow-to-header-field 'cc regexp)) + (list (mh-edit-pick-expr (mh-current-message-header-field 'from)))) + (mh-narrow-to-header-field 'from pick-expr)) ;;;###mh-autoload -(defun mh-narrow-to-to (&optional regexp) - "Limit to messages with the same To header field as the message at point. -With a prefix argument, prompt for the regular expression, REGEXP given to -pick." +(defun mh-narrow-to-cc (&optional pick-expr) + "Limit to messages with the same `Cc:' field. +With a prefix argument, edit PICK-EXPR. + +Use \\<mh-folder-mode-map>\\[mh-widen] to undo this command." (interactive - (list (mh-read-pick-regexp (mh-current-message-header-field 'to)))) - (mh-narrow-to-header-field 'to regexp)) + (list (mh-edit-pick-expr (mh-current-message-header-field 'cc)))) + (mh-narrow-to-header-field 'cc pick-expr)) -(defun mh-narrow-to-header-field (header-field regexp) - "Limit to messages whose HEADER-FIELD match REGEXP. +;;;###mh-autoload +(defun mh-narrow-to-to (&optional pick-expr) + "Limit to messages with the same `To:' field. +With a prefix argument, edit PICK-EXPR. + +Use \\<mh-folder-mode-map>\\[mh-widen] to undo this command." + (interactive + (list (mh-edit-pick-expr (mh-current-message-header-field 'to)))) + (mh-narrow-to-header-field 'to pick-expr)) + +(defun mh-narrow-to-header-field (header-field pick-expr) + "Limit to messages whose HEADER-FIELD match PICK-EXPR. The MH command pick is used to do the match." (let ((folder mh-current-folder) (original (mh-coalesce-msg-list @@ -897,7 +841,7 @@ The MH command pick is used to do the match." (msg-list ())) (with-temp-buffer (apply #'mh-exec-cmd-output "pick" nil folder - (append original (list "-list") regexp)) + (append original (list "-list") pick-expr)) (goto-char (point-min)) (while (not (eobp)) (let ((num (read-from-string @@ -939,7 +883,9 @@ The MH command pick is used to do the match." "Limit to messages in RANGE. Check the documentation of `mh-interactive-range' to see how RANGE is read in -interactive use." +interactive use. + +Use \\<mh-folder-mode-map>\\[mh-widen] to undo this command." (interactive (list (mh-interactive-range "Narrow to"))) (when (assoc 'range mh-seq-list) (mh-delete-seq 'range)) (mh-add-msgs-to-seq (mh-range-to-msg-list range) 'range) @@ -958,7 +904,7 @@ subject sequence." ((not count) ; No subject line, delete msg anyway (mh-delete-msg (mh-get-msg-num t))) ((= 0 count) ; No other msgs, delete msg anyway. - (message "No other messages with same Subject following this one.") + (message "No other messages with same Subject following this one") (mh-delete-msg (mh-get-msg-num t))) (t ; We have a subject sequence. (message "Marked %d messages for deletion" count) @@ -1078,13 +1024,12 @@ SUBJECT and REFS fields." message) (container (setf (mh-container-message container) - (mh-thread-make-message :subject subject - :subject-re-p subject-re-p - :id id :references refs))) - (t (let ((message (mh-thread-make-message - :subject subject - :subject-re-p subject-re-p - :id id :references refs))) + (mh-thread-make-message :id id :references refs + :subject subject + :subject-re-p subject-re-p))) + (t (let ((message (mh-thread-make-message :id id :references refs + :subject-re-p subject-re-p + :subject subject))) (prog1 message (mh-thread-get-message-container message))))))) @@ -1450,8 +1395,7 @@ MSG is the message being notated with NOTATION at OFFSET." (cur-scan-line (and mh-thread-scan-line-map (gethash msg mh-thread-scan-line-map))) (old-scan-lines (loop for map in mh-thread-scan-line-map-stack - collect (and map (gethash msg map)))) - (notation (if (stringp notation) (aref notation 0) notation))) + collect (and map (gethash msg map))))) (when cur-scan-line (setf (aref (car cur-scan-line) offset) notation)) (dolist (line old-scan-lines) @@ -1486,7 +1430,8 @@ MSG is the message being notated with NOTATION at OFFSET." (setf (gethash msg mh-thread-scan-line-map) v)))) (when (> (hash-table-count mh-thread-scan-line-map) 0) (insert (if (bobp) "" "\n") (car x) "\n") - (mh-thread-generate-scan-lines thread-tree -2))))))) + (mh-thread-generate-scan-lines thread-tree -2)))) + (mh-index-create-imenu-index)))) (defun mh-thread-folder () "Generate thread view of folder." @@ -1711,11 +1656,12 @@ start of the region and the second is the point at the end." (push msg unticked) (setcdr tick-seq (delq msg (cdr tick-seq))) (when (null (cdr tick-seq)) (setq mh-last-seq-used nil)) - (mh-remove-sequence-notation msg t)) + (mh-remove-sequence-notation msg (mh-colors-in-use-p))) (t (push msg ticked) (setq mh-last-seq-used mh-tick-seq) - (mh-add-sequence-notation msg t)))) + (let ((mh-seq-list (cons `(,mh-tick-seq ,msg) mh-seq-list))) + (mh-add-sequence-notation msg (mh-colors-in-use-p)))))) (mh-add-msgs-to-seq ticked mh-tick-seq nil t) (mh-undefine-sequence mh-tick-seq unticked) (when mh-index-data @@ -1724,16 +1670,16 @@ start of the region and the second is the point at the end." ;;;###mh-autoload (defun mh-narrow-to-tick () - "Restrict display of this folder to just messages in `mh-tick-seq'. + "Limit to messages in `mh-tick-seq'. + Use \\<mh-folder-mode-map>\\[mh-widen] to undo this command." (interactive) (cond ((not mh-tick-seq) (error "Enable ticking by customizing `mh-tick-seq'")) ((null (mh-seq-msgs (mh-find-seq mh-tick-seq))) - (message "No messages in tick sequence")) + (message "No messages in %s sequence" mh-tick-seq)) (t (mh-narrow-to-seq mh-tick-seq)))) - (provide 'mh-seq) ;;; Local Variables: diff --git a/lisp/mh-e/mh-speed.el b/lisp/mh-e/mh-speed.el index 967984d1104..2617a941de1 100644 --- a/lisp/mh-e/mh-speed.el +++ b/lisp/mh-e/mh-speed.el @@ -34,10 +34,11 @@ ;;; Code: ;; Requires -(require 'mh-utils) +(eval-when-compile (require 'mh-acros)) (mh-require-cl) (require 'mh-e) (require 'speedbar) +(require 'timer) ;; Global variables (defvar mh-speed-refresh-flag nil) @@ -90,26 +91,25 @@ BUFFER is the MH-E buffer for which the speedbar buffer is to be created." "+" mh-speed-expand-folder "-" mh-speed-contract-folder "\r" mh-speed-view - "f" mh-speed-flists - "i" mh-speed-invalidate-map) + "r" mh-speed-refresh) (defvar mh-show-speedbar-key-map mh-folder-speedbar-key-map) (defvar mh-letter-speedbar-key-map mh-folder-speedbar-key-map) ;; Menus for speedbar... (defvar mh-folder-speedbar-menu-items - '(["Visit Folder" mh-speed-view + '("--" + ["Visit Folder" mh-speed-view (save-excursion (set-buffer speedbar-buffer) (get-text-property (line-beginning-position) 'mh-folder))] - ["Expand nested folders" mh-speed-expand-folder + ["Expand Nested Folders" mh-speed-expand-folder (and (get-text-property (line-beginning-position) 'mh-children-p) (not (get-text-property (line-beginning-position) 'mh-expanded)))] - ["Contract nested folders" mh-speed-contract-folder + ["Contract Nested Folders" mh-speed-contract-folder (and (get-text-property (line-beginning-position) 'mh-children-p) (get-text-property (line-beginning-position) 'mh-expanded))] - ["Run Flists" mh-speed-flists t] - ["Invalidate cached folders" mh-speed-invalidate-map t]) + ["Refresh Speedbar" mh-speed-refresh t]) "Extra menu items for speedbar.") (defvar mh-show-speedbar-menu-items mh-folder-speedbar-menu-items) @@ -352,6 +352,14 @@ Optional ARGS are ignored." (defvar mh-speed-current-folder nil) (defvar mh-speed-flists-folder nil) +(defmacro mh-process-kill-without-query (process) + "PROCESS can be killed without query on Emacs exit. +Avoid using `process-kill-without-query' if possible since it is now +obsolete." + (if (fboundp 'set-process-query-on-exit-flag) + `(set-process-query-on-exit-flag ,process nil) + `(process-kill-without-query ,process))) + ;;;###mh-autoload (defun mh-speed-flists (force &rest folders) "Execute flists -recurse and update message counts. @@ -396,6 +404,7 @@ only for that one folder." (or mh-speed-flists-folder '("-recurse")))) ;; Run flists on all folders the next time around... (setq mh-speed-flists-folder nil) + (mh-process-kill-without-query mh-speed-flists-process) (set-process-filter mh-speed-flists-process 'mh-speed-parse-flists-output))))))) @@ -494,6 +503,14 @@ next." (when (equal folder "") (clrhash mh-sub-folders-cache))))) +(defun mh-speed-refresh () + "Refresh the speedbar. +Use this function to refresh the speedbar if folders have been added or +deleted or message ranges have been updated outside of MH-E." + (interactive) + (mh-speed-flists t) + (mh-speed-invalidate-map "")) + ;;;###mh-autoload (defun mh-speed-add-folder (folder) "Add FOLDER since it is being created. diff --git a/lisp/mh-e/mh-utils.el b/lisp/mh-e/mh-utils.el index b1966915e86..a57567a7bd3 100644 --- a/lisp/mh-e/mh-utils.el +++ b/lisp/mh-e/mh-utils.el @@ -33,20 +33,14 @@ ;;; Code: -;; Is this XEmacs-land? Located here since needed by mh-customize.el. -(defvar mh-xemacs-flag (featurep 'xemacs) - "Non-nil means the current Emacs is XEmacs.") - -;; The Emacs coding conventions require that the cl package not be required at -;; runtime. However, the cl package in versions of Emacs prior to 21.4 left cl -;; routines in their macro expansions. Use mh-require-cl to provide the cl -;; routines in the best way possible. -(eval-when-compile (require 'cl)) -(defmacro mh-require-cl () - (if (eq (car (macroexpand '(setf (gethash foo bar) baz))) 'cl-puthash) - `(require 'cl) - `(eval-when-compile (require 'cl)))) +(defvar recursive-load-depth-limit) +(eval-and-compile + (if (and (boundp 'recursive-load-depth-limit) + (integerp recursive-load-depth-limit) + (> 50 recursive-load-depth-limit)) + (setq recursive-load-depth-limit 50))) +(eval-when-compile (require 'mh-acros)) (mh-require-cl) (require 'gnus-util) (require 'font-lock) @@ -58,6 +52,7 @@ (load "mm-decode" t t) ; Non-fatal dependency (load "mm-view" t t) ; Non-fatal dependency +(load "vcard" t t) ; Non-fatal dependency (load "hl-line" t t) ; Non-fatal dependency (load "executable" t t) ; Non-fatal dependency on ; executable-find @@ -69,43 +64,12 @@ ;;; Autoloads (autoload 'gnus-article-highlight-citation "gnus-cite") +(autoload 'message-fetch-field "message") +(autoload 'message-tokenize-header "message") (require 'sendmail) -(autoload 'Info-goto-node "info") (unless (fboundp 'make-hash-table) (autoload 'make-hash-table "cl")) -;;; Set for local environment: -;;; mh-progs and mh-lib used to be set in paths.el, which tried to -;;; figure out at build time which of several possible directories MH -;;; was installed into. But if you installed MH after building Emacs, -;;; this would almost certainly be wrong, so now we do it at run time. - -(defvar mh-progs nil - "Directory containing MH commands, such as inc, repl, and rmm.") - -(defvar mh-lib nil - "Directory containing the MH library. -This directory contains, among other things, the components file.") - -(defvar mh-lib-progs nil - "Directory containing MH helper programs. -This directory contains, among other things, the mhl program.") - -(defvar mh-nmh-flag nil - "Non-nil means nmh is installed on this system instead of MH.") - -(defvar mh-flists-present-flag nil - "Non-nil means that we have `flists'.") - -;;;###autoload -(put 'mh-progs 'risky-local-variable t) -;;;###autoload -(put 'mh-lib 'risky-local-variable t) -;;;###autoload -(put 'mh-lib-progs 'risky-local-variable t) -;;;###autoload -(put 'mh-nmh-flag 'risky-local-variable t) - ;;; CL Replacements (defun mh-search-from-end (char string) "Return the position of last occurrence of CHAR in STRING. @@ -115,92 +79,52 @@ of `search' in the CL package." when (equal (aref string index) char) return index finally return nil)) -;;; Macros to generate correct code for different emacs variants - -(defmacro mh-do-in-gnu-emacs (&rest body) - "Execute BODY if in GNU Emacs." - (unless mh-xemacs-flag `(progn ,@body))) -(put 'mh-do-in-gnu-emacs 'lisp-indent-hook 'defun) - -(defmacro mh-do-in-xemacs (&rest body) - "Execute BODY if in GNU Emacs." - (when mh-xemacs-flag `(progn ,@body))) -(put 'mh-do-in-xemacs 'lisp-indent-hook 'defun) - -(defmacro mh-funcall-if-exists (function &rest args) - "Call FUNCTION with ARGS as parameters if it exists." - (if (fboundp function) - `(funcall ',function ,@args))) - -(defmacro mh-make-local-hook (hook) - "Make HOOK local if needed. -XEmacs and versions of GNU Emacs before 21.1 require `make-local-hook' to be -called." - (when (and (fboundp 'make-local-hook) - (not (get 'make-local-hook 'byte-obsolete-info))) - `(make-local-hook ,hook))) - -(defmacro mh-mark-active-p (check-transient-mark-mode-flag) - "A macro that expands into appropriate code in XEmacs and nil in GNU Emacs. -In GNU Emacs if CHECK-TRANSIENT-MARK-MODE-FLAG is non-nil then check if -variable `transient-mark-mode' is active." - (cond (mh-xemacs-flag ;XEmacs - `(and (boundp 'zmacs-regions) zmacs-regions (region-active-p))) - ((not check-transient-mark-mode-flag) ;GNU Emacs - `(and (boundp 'mark-active) mark-active)) - (t ;GNU Emacs - `(and (boundp 'transient-mark-mode) transient-mark-mode - (boundp 'mark-active) mark-active)))) - ;;; Additional header fields that might someday be added: ;;; "Sender: " "Reply-to: " + +;;; Scan Line Formats + (defvar mh-scan-msg-number-regexp "^ *\\([0-9]+\\)" - "Regexp to find the number of a message in a scan line. -The message's number must be surrounded with \\( \\)") + "This regexp is used to extract the message number from a scan line. +Note that the message number must be placed in a parenthesized expression as +in the default of \"^ *\\\\([0-9]+\\\\)\".") (defvar mh-scan-msg-overflow-regexp "^[?0-9][0-9]" - "Regexp to find a scan line in which the message number overflowed. -The message's number is left truncated in this case.") + "This regexp matches scan lines in which the message number overflowed.") (defvar mh-scan-msg-format-regexp "%\\([0-9]*\\)(msg)" - "Regexp to find message number width in an scan format. -The message number width must be surrounded with \\( \\).") + "This regexp is used to find the message number width in a scan format. +Note that the message number must be placed in a parenthesized expression as +in the default of \"%\\\\([0-9]*\\\\)(msg)\".") (defvar mh-scan-msg-format-string "%d" - "Format string for width of the message number in a scan format. + "This is a format string for width of the message number in a scan format. Use `0%d' for zero-filled message numbers.") (defvar mh-scan-msg-search-regexp "^[^0-9]*%d[^0-9]" - "Format string containing a regexp matching the scan listing for a message. -The desired message's number will be an argument to format.") - -(defvar mh-default-folder-for-message-function nil - "Function to select a default folder for refiling or Fcc. -If set to a function, that function is called with no arguments by -`\\[mh-refile-msg]' and `\\[mh-to-fcc]' to get a default when -prompting the user for a folder. The function is called from within a -`save-excursion', with point at the start of the message. It should -return the folder to offer as the refile or Fcc folder, as a string -with a leading `+' sign. It can also return an empty string to use no -default, or nil to calculate the default the usual way. -NOTE: This variable is not an ordinary hook; -It may not be a list of functions.") + "This format string regexp matches the scan line for a particular message. +Use `%d' to represent the location of the message number within the +expression as in the default of \"^[^0-9]*%d[^0-9]\".") + +(defvar mh-cmd-note 4 + "This is the number of characters to skip over before inserting notation. +This variable should be set with the function `mh-set-cmd-note'. This variable +may be updated dynamically if `mh-adaptive-cmd-note-flag' is non-nil and +`mh-scan-format-file' is t.") +(make-variable-buffer-local 'mh-cmd-note) + +(defvar mh-note-seq ?% + "Messages in a user-defined sequence are marked by this character. +Messages in the `search' sequence are marked by this character as well.") + + (defvar mh-show-buffer-mode-line-buffer-id " {show-%s} %d" "Format string to produce `mode-line-buffer-identification' for show buffers. First argument is folder name. Second is message number.") -(defvar mh-cmd-note 4 - "Column to insert notation. -Use `mh-set-cmd-note' to modify it. -This value may be dynamically updated if `mh-adaptive-cmd-note-flag' is -non-nil and `mh-scan-format-file' is t. -Note that the first column is column number 0.") -(make-variable-buffer-local 'mh-cmd-note) - -(defvar mh-note-seq "%" - "String whose first character is used to notate messages in a sequence.") + (defvar mh-mail-header-separator "--------" "*Line used by MH to separate headers from text in messages being composed. @@ -213,11 +137,29 @@ Do not make this a regexp as it may be the argument to `insert' and it is passed through `regexp-quote' before being used by functions like `re-search-forward'.") +(defvar mh-signature-separator-regexp "^-- $" + "Regexp used to find signature separator. +See `mh-signature-separator'.") + +(defvar mh-signature-separator "-- \n" + "Text of a signature separator. +A signature separator is used to separate the body of a message from the +signature. This can be used by user agents such as MH-E to render the +signature differently or to suppress the inclusion of the signature in a +reply. +Use `mh-signature-separator-regexp' when searching for a separator.") + +(defun mh-signature-separator-p () + "Return non-nil if buffer includes \"^-- $\"." + (save-excursion + (goto-char (point-min)) + (re-search-forward mh-signature-separator-regexp nil t))) + ;; Variables for MIME display ;; Structure to keep track of MIME handles on a per buffer basis. -(defstruct (mh-buffer-data (:conc-name mh-mime-) - (:constructor mh-make-buffer-data)) +(mh-defstruct (mh-buffer-data (:conc-name mh-mime-) + (:constructor mh-make-buffer-data)) (handles ()) ; List of MIME handles (handles-cache (make-hash-table)) ; Cache to avoid multiple decodes of ; nested messages @@ -331,7 +273,7 @@ passed through `regexp-quote' before being used by functions like "A regular expression probably matching an e-mail address.") ;; From goto-addr.el, which we don't want to force-load on users. -;;;###mh-autoload + (defun mh-goto-address-find-address-at-point () "Find e-mail address around or before point. Then search backwards to beginning of line for the start of an e-mail @@ -348,7 +290,18 @@ address. If no e-mail address found, return nil." In MH-E we frequently need to find the end of headers in nested messages, where the buffer has been narrowed. This function works in this situation." (save-excursion - (rfc822-goto-eoh) + ;; XXX: The following replaces a call to rfc822-goto-eoh. Occasionally, + ;; mail headers that MH-E has to read contains lines of the form: + ;; From xxx@yyy Mon May 10 11:48:07 2004 + ;; In this situation, rfc822-goto-eoh doesn't go to the end of the + ;; header. The replacement allows From_ lines in the mail header. + (goto-char (point-min)) + (loop for p = (re-search-forward + "^\\([:\n]\\|[^: \t\n]+[ \t\n]\\)" nil 'move) + do (cond ((null p) (return)) + (t (goto-char (match-beginning 0)) + (unless (looking-at "From ") (return)) + (goto-char p)))) (point))) (defun mh-in-header-p () @@ -528,17 +481,20 @@ message about the fontification operation." ;; hidden and can be programmatically removed in mh-quit), and the variable ;; names have the form mh-temp-.*-buffer. (defconst mh-temp-buffer " *mh-temp*") ;scratch +(defconst mh-temp-fetch-buffer " *mh-fetch*") ;wget/curl/fetch output ;; The names of MH-E buffers that are not ephemeral and can be used by the ;; user (and deleted by the user when no longer needed) have a "*MH-E " prefix ;; (so they can be programmatically removed in mh-quit), and the variable ;; names have the form mh-.*-buffer. +(defconst mh-aliases-buffer "*MH-E Aliases*") ;alias lookups (defconst mh-folders-buffer "*MH-E Folders*") ;folder list +(defconst mh-help-buffer "*MH-E Help*") ;quick help (defconst mh-info-buffer "*MH-E Info*") ;version information buffer (defconst mh-log-buffer "*MH-E Log*") ;output of MH commands and so on +(defconst mh-mail-delivery-buffer "*MH-E Mail Delivery*") ;mail delivery log (defconst mh-recipients-buffer "*MH-E Recipients*") ;killed when draft sent (defconst mh-sequences-buffer "*MH-E Sequences*") ;sequences list -(defconst mh-mail-delivery-buffer "*MH-E Mail Delivery*") ;mail delivery log ;; Number of lines to keep in mh-log-buffer. (defvar mh-log-buffer-lines 100) @@ -593,7 +549,6 @@ message about the fontification operation." (cons modeline-buffer-id-left-extent "XEmacs%N:")) (cons modeline-buffer-id-right-extent " %17b"))))) - ;;; This holds a documentation string used by describe-mode. (defun mh-showing-mode (&optional arg) "Change whether messages should be displayed. @@ -614,7 +569,6 @@ With arg, display messages iff ARG is positive." ;; Showing message with headers or normally. (defvar mh-showing-with-headers nil) - ;;; MH-E macros (defmacro with-mh-folder-updating (save-modification-flag &rest body) @@ -742,7 +696,7 @@ of the buffer in the event window is preserved." (unlock-buffer) (setq buffer-file-name nil)) -;;;###mh-autoload + (defun mh-get-msg-num (error-if-no-message) "Return the message number of the displayed message. If the argument ERROR-IF-NO-MESSAGE is non-nil, then complain if the cursor is @@ -915,6 +869,16 @@ still visible.\n") (mh-defun-show-buffer mh-show-index-ticked-messages mh-index-ticked-messages) (mh-defun-show-buffer mh-show-index-sequenced-messages mh-index-sequenced-messages) +(mh-defun-show-buffer mh-show-catchup mh-catchup) +(mh-defun-show-buffer mh-show-ps-print-toggle-mime mh-ps-print-toggle-mime) +(mh-defun-show-buffer mh-show-ps-print-toggle-color mh-ps-print-toggle-color) +(mh-defun-show-buffer mh-show-ps-print-toggle-faces mh-ps-print-toggle-faces) +(mh-defun-show-buffer mh-show-ps-print-msg-file mh-ps-print-msg-file) +(mh-defun-show-buffer mh-show-ps-print-msg mh-ps-print-msg) +(mh-defun-show-buffer mh-show-ps-print-msg-show mh-ps-print-msg-show) +(mh-defun-show-buffer mh-show-toggle-mime-buttons mh-toggle-mime-buttons) +(mh-defun-show-buffer mh-show-display-with-external-viewer + mh-display-with-external-viewer) ;;; Populate mh-show-mode-map (gnus-define-keys mh-show-mode-map @@ -941,7 +905,6 @@ still visible.\n") "g" mh-show-goto-msg "i" mh-show-inc-folder "k" mh-show-delete-subject-or-thread - "l" mh-show-print-msg "m" mh-show-send "n" mh-show-next-undeleted-msg "\M-n" mh-show-next-unread-msg @@ -961,6 +924,7 @@ still visible.\n") "?" mh-prefix-help "'" mh-index-ticked-messages "S" mh-show-sort-folder + "c" mh-show-catchup "f" mh-show-visit-folder "i" mh-index-search "k" mh-show-kill-folder @@ -992,6 +956,17 @@ still visible.\n") "b" mh-show-junk-blacklist "w" mh-show-junk-whitelist) +(gnus-define-keys (mh-show-ps-print-map "P" mh-show-mode-map) + "?" mh-prefix-help + "A" mh-show-ps-print-toggle-mime + "C" mh-show-ps-print-toggle-color + "F" mh-show-ps-print-toggle-faces + "M" mh-show-ps-print-toggle-mime + "f" mh-show-ps-print-msg-file + "l" mh-show-print-msg + "p" mh-show-ps-print-msg + "s" mh-show-ps-print-msg-show) + (gnus-define-keys (mh-show-thread-map "T" mh-show-mode-map) "?" mh-prefix-help "u" mh-show-thread-ancestor @@ -1026,9 +1001,11 @@ still visible.\n") (gnus-define-keys (mh-show-mime-map "K" mh-show-mode-map) "?" mh-prefix-help "a" mh-mime-save-parts + "e" mh-show-display-with-external-viewer "v" mh-show-toggle-mime-part "o" mh-show-save-mime-part "i" mh-show-inline-mime-part + "t" mh-show-toggle-mime-buttons "\t" mh-show-next-button [backtab] mh-show-prev-button "\M-\t" mh-show-prev-button) @@ -1115,7 +1092,10 @@ still visible.\n") (define-derived-mode mh-show-mode text-mode "MH-Show" "Major mode for showing messages in MH-E.\\<mh-show-mode-map> The value of `mh-show-mode-hook' is a list of functions to -be called, with no arguments, upon entry to this mode." +be called, with no arguments, upon entry to this mode. +See also `mh-folder-mode'. + +\\{mh-show-mode-map}" (set (make-local-variable 'mail-header-separator) mh-mail-header-separator) (setq paragraph-start (default-value 'paragraph-start)) (mh-show-unquote-From) @@ -1210,8 +1190,9 @@ be called, with no arguments, upon entry to this mode." (mh-do-in-xemacs (defvar default-enable-multibyte-characters)) (defun mh-face-display-function () - "Display a Face or X-Face header field. -Display Face if both are present." + "Display a Face, X-Face, or X-Image-URL header field. +If more than one of these are present, then the first one found in this order +is used." (save-restriction (goto-char (point-min)) (re-search-forward "\n\n" (point-max) t) @@ -1226,7 +1207,8 @@ Display Face if both are present." type 'png)) (x-face (setq raw (mh-uncompface x-face) type 'pbm)) - (url (setq type 'url))) + (url (setq type 'url)) + (t (multiple-value-setq (type raw) (mh-picon-get-image)))) (when type (goto-char (point-min)) (when (re-search-forward "^from:" (point-max) t) @@ -1261,10 +1243,15 @@ Display Face if both are present." ((and (eq type 'pbm) (fboundp 'x-face-xmas-wl-display-x-face) (fboundp 'executable-find) (executable-find "uncompface")) - (mh-funcall-if-exists x-face-xmas-wl-display-x-face))) + (mh-funcall-if-exists x-face-xmas-wl-display-x-face)) + ;; Picon display + ((and raw (member type '(xpm xbm gif))) + (when (featurep type) + (set-extent-begin-glyph + (make-extent (point) (point)) + (make-glyph (vector type ':data raw)))))) (when raw (insert " ")))))))) - (defun mh-show-xface () "Display X-Face." (when (and window-system mh-show-use-xface-flag @@ -1274,49 +1261,207 @@ Display Face if both are present." +;; Picon display + +;;; XXX: This should be customizable. As a side-effect of setting this +;;; variable, arrange to reset mh-picon-existing-directory-list to 'unset. +(defvar mh-picon-directory-list + '("~/.picons" "~/.picons/users" "~/.picons/usenix" "~/.picons/news" + "~/.picons/domains" "~/.picons/misc" + "/usr/share/picons/" "/usr/share/picons/users" "/usr/share/picons/usenix" + "/usr/share/picons/news" "/usr/share/picons/domains" + "/usr/share/picons/misc") + "List of directories where picons reside. +The directories are searched for in the order they appear in the list.") + +(defvar mh-picon-existing-directory-list 'unset + "List of directories to search in.") + +(defvar mh-picon-cache (make-hash-table :test #'equal)) + +(defvar mh-picon-image-types + (loop for type in '(xpm xbm gif) + when (or (mh-do-in-gnu-emacs + (ignore-errors + (mh-funcall-if-exists image-type-available-p type))) + (mh-do-in-xemacs (featurep type))) + collect type)) + +(defun mh-picon-set-directory-list () + "Update `mh-picon-existing-directory-list' if needed." + (when (eq mh-picon-existing-directory-list 'unset) + (setq mh-picon-existing-directory-list + (loop for x in mh-picon-directory-list + when (file-directory-p x) collect x)))) + +(defun* mh-picon-get-image () + "Find the best possible match and return contents." + (mh-picon-set-directory-list) + (save-restriction + (let* ((from-field (ignore-errors (car (message-tokenize-header + (mh-get-header-field "from:"))))) + (from (car (ignore-errors + (mh-funcall-if-exists ietf-drums-parse-address + from-field)))) + (host (and from + (string-match "\\([^+]*\\)\\(+.*\\)?@\\(.*\\)" from) + (downcase (match-string 3 from)))) + (user (and host (downcase (match-string 1 from)))) + (canonical-address (format "%s@%s" user host)) + (cached-value (gethash canonical-address mh-picon-cache)) + (host-list (and host (delete "" (split-string host "\\.")))) + (match nil)) + (cond (cached-value (return-from mh-picon-get-image cached-value)) + ((not host-list) (return-from mh-picon-get-image nil))) + (setq match + (block 'loop + ;; u@h search + (loop for dir in mh-picon-existing-directory-list + do (loop for type in mh-picon-image-types + ;; [path]user@host + for file1 = (format "%s/%s.%s" + dir canonical-address type) + when (file-exists-p file1) + do (return-from 'loop file1) + ;; [path]user + for file2 = (format "%s/%s.%s" dir user type) + when (file-exists-p file2) + do (return-from 'loop file2) + ;; [path]host + for file3 = (format "%s/%s.%s" dir host type) + when (file-exists-p file3) + do (return-from 'loop file3))) + ;; facedb search + ;; Search order for user@foo.net: + ;; [path]net/foo/user + ;; [path]net/foo/user/face + ;; [path]net/user + ;; [path]net/user/face + ;; [path]net/foo/unknown + ;; [path]net/foo/unknown/face + ;; [path]net/unknown + ;; [path]net/unknown/face + (loop for u in (list user "unknown") + do (loop for dir in mh-picon-existing-directory-list + do (loop for x on host-list by #'cdr + for y = (mh-picon-generate-path x u dir) + do (loop for type in mh-picon-image-types + for z1 = (format "%s.%s" y type) + when (file-exists-p z1) + do (return-from 'loop z1) + for z2 = (format "%s/face.%s" + y type) + when (file-exists-p z2) + do (return-from 'loop z2))))))) + (setf (gethash canonical-address mh-picon-cache) + (mh-picon-file-contents match))))) + +(defun mh-picon-file-contents (file) + "Return details about FILE. +A list of consisting of a symbol for the type of the file and the file +contents as a string is returned. If FILE is nil, then both elements of the +list are nil." + (if (stringp file) + (with-temp-buffer + (let ((type (and (string-match ".*\\.\\(...\\)$" file) + (intern (match-string 1 file))))) + (insert-file-contents-literally file) + (values type (buffer-string)))) + (values nil nil))) + +(defun mh-picon-generate-path (host-list user directory) + "Generate the image file path. +HOST-LIST is the parsed host address of the email address, USER the username +and DIRECTORY is the directory relative to which the path is generated." + (loop with acc = "" + for elem in host-list + do (setq acc (format "%s/%s" elem acc)) + finally return (format "%s/%s%s" directory acc user))) + + + ;; X-Image-URL display (defvar mh-x-image-cache-directory nil "Directory where X-Image-URL images are cached.") - -(defvar mh-convert-executable (executable-find "convert")) -(defvar mh-wget-executable (executable-find "wget")) +(defvar mh-x-image-scaling-function + (cond ((executable-find "convert") + 'mh-x-image-scale-with-convert) + ((and (executable-find "anytopnm") (executable-find "pnmscale") + (executable-find "pnmtopng")) + 'mh-x-image-scale-with-pnm) + (t 'ignore)) + "Function to use to scale image to proper size.") +(defvar mh-wget-executable nil) +(defvar mh-wget-choice + (or (and (setq mh-wget-executable (executable-find "wget")) 'wget) + (and (setq mh-wget-executable (executable-find "fetch")) 'fetch) + (and (setq mh-wget-executable (executable-find "curl")) 'curl))) +(defvar mh-wget-option + (cdr (assoc mh-wget-choice '((curl . "-o") (fetch . "-o") (wget . "-O"))))) (defvar mh-x-image-temp-file nil) (defvar mh-x-image-url nil) (defvar mh-x-image-marker nil) (defvar mh-x-image-url-cache-file nil) +;; Functions to scale image to proper size +(defun mh-x-image-scale-with-pnm (input output) + "Scale image in INPUT file and write to OUTPUT file using pnm tools." + (let ((res (shell-command-to-string + (format "anytopnm < %s | pnmscale -xysize 96 48 | pnmtopng > %s" + input output)))) + (unless (equal res "") + (delete-file output)))) + +(defun mh-x-image-scale-with-convert (input output) + "Scale image in INPUT file and write to OUTPUT file using ImageMagick." + (call-process "convert" nil nil nil "-geometry" "96x48" input output)) + (defun mh-x-image-url-cache-canonicalize (url) "Canonicalize URL. -Replace the ?/ character with a ?! character." - (with-temp-buffer - (insert url) - (goto-char (point-min)) - (while (search-forward "/" nil t) (replace-match "!")) - (format "%s/%s.png" mh-x-image-cache-directory (buffer-string)))) +Replace the ?/ character with a ?! character and append .png." + (format "%s/%s.png" mh-x-image-cache-directory + (with-temp-buffer + (insert url) + (mh-replace-string "/" "!") + (buffer-string)))) + +(defun mh-x-image-set-download-state (file data) + "Setup a symbolic link from FILE to DATA." + (if data + (make-symbolic-link (symbol-name data) file t) + (delete-file file))) + +(defun mh-x-image-get-download-state (file) + "Check the state of FILE by following any symbolic links." + (unless (file-exists-p mh-x-image-cache-directory) + (call-process "mkdir" nil nil nil mh-x-image-cache-directory)) + (cond ((file-symlink-p file) + (intern (file-name-nondirectory (file-chase-links file)))) + ((not (file-exists-p file)) nil) + (t 'ok))) (defun mh-x-image-url-fetch-image (url cache-file marker sentinel) "Fetch and display the image specified by URL. After the image is fetched, it is stored in CACHE-FILE. It will be displayed in a buffer and position specified by MARKER. The actual display is carried out by the SENTINEL function." - (if (and mh-wget-executable - mh-fetch-x-image-url - (or (eq mh-fetch-x-image-url t) - (y-or-n-p (format "Fetch %s? " url)))) - (let ((buffer (get-buffer-create (generate-new-buffer-name " *mh-url*"))) - (filename (make-temp-name "/tmp/mhe-wget"))) + (if mh-wget-executable + (let ((buffer (get-buffer-create (generate-new-buffer-name + mh-temp-fetch-buffer))) + (filename (or (mh-funcall-if-exists make-temp-file "mhe-fetch") + (expand-file-name (make-temp-name "~/mhe-fetch"))))) (save-excursion (set-buffer buffer) (set (make-local-variable 'mh-x-image-url-cache-file) cache-file) (set (make-local-variable 'mh-x-image-marker) marker) (set (make-local-variable 'mh-x-image-temp-file) filename)) (set-process-sentinel - (start-process "*wget*" buffer mh-wget-executable "-O" filename url) + (start-process "*mh-x-image-url-fetch*" buffer + mh-wget-executable mh-wget-option filename url) sentinel)) - ;; Make sure we don't ask about this image again - (when (and mh-wget-executable (eq mh-fetch-x-image-url 'ask)) - (make-symbolic-link mh-x-image-cache-directory cache-file t)))) + ;; Temporary failure + (mh-x-image-set-download-state cache-file 'try-again))) (defun mh-x-image-display (image marker) "Display IMAGE at MARKER." @@ -1326,7 +1471,8 @@ out by the SENTINEL function." (default-enable-multibyte-characters nil) (buffer-modified-flag (buffer-modified-p))) (unwind-protect - (when (and (file-readable-p image) (not (file-symlink-p image))) + (when (and (file-readable-p image) (not (file-symlink-p image)) + (eq marker mh-x-image-marker)) (goto-char marker) (mh-do-in-gnu-emacs (mh-funcall-if-exists insert-image (create-image image 'png))) @@ -1350,32 +1496,56 @@ The argument CHANGE is ignored." (setq marker mh-x-image-marker cache-filename mh-x-image-url-cache-file temp-file mh-x-image-temp-file)) - (when mh-convert-executable - (call-process mh-convert-executable nil nil nil "-resize" "96x48" - temp-file cache-filename)) - (if (file-exists-p cache-filename) - (mh-x-image-display cache-filename marker) - (make-symbolic-link mh-x-image-cache-directory cache-filename t)) + (cond + ;; Check if we have `convert' + ((eq mh-x-image-scaling-function 'ignore) + (message "The `convert' program is needed to display X-Image-URL") + (mh-x-image-set-download-state cache-filename 'try-again)) + ;; Scale fetched image + ((and (funcall mh-x-image-scaling-function temp-file cache-filename) + nil)) + ;; Attempt to display image if we have it + ((file-exists-p cache-filename) + (mh-x-image-display cache-filename marker)) + ;; We didn't find the image. Should we try to display it the next time? + (t (mh-x-image-set-download-state cache-filename 'try-again))) (ignore-errors (set-marker marker nil) (delete-process process) (kill-buffer wget-buffer) (delete-file temp-file))))) +(defun mh-x-image-url-sane-p (url) + "Check if URL is something sensible." + (let ((len (length url))) + (cond ((< len 5) nil) + ((not (equal (substring url 0 5) "http:")) nil) + ((> len 100) nil) + (t t)))) + (defun mh-x-image-url-display (url) "Display image from location URL. If the URL isn't present in the cache then it is fetched with wget." - (let ((cache-filename (mh-x-image-url-cache-canonicalize url)) - (marker (set-marker (make-marker) (point)))) - (cond ((file-exists-p cache-filename) + (let* ((cache-filename (mh-x-image-url-cache-canonicalize url)) + (state (mh-x-image-get-download-state cache-filename)) + (marker (set-marker (make-marker) (point)))) + (set (make-local-variable 'mh-x-image-marker) marker) + (cond ((not (mh-x-image-url-sane-p url))) + ((eq state 'ok) (mh-x-image-display cache-filename marker)) + ((or (not mh-wget-executable) + (eq mh-x-image-scaling-function 'ignore))) + ((eq state 'never)) ((not mh-fetch-x-image-url) (set-marker marker nil)) - ((and (not (file-exists-p mh-x-image-cache-directory)) - (call-process "mkdir" nil nil nil mh-x-image-cache-directory) - nil)) - ((and (file-exists-p mh-x-image-cache-directory) - (file-directory-p mh-x-image-cache-directory)) + ((eq state 'try-again) + (mh-x-image-set-download-state cache-filename nil) + (mh-x-image-url-fetch-image url cache-filename marker + 'mh-x-image-scale-and-display)) + ((and (eq mh-fetch-x-image-url 'ask) + (not (y-or-n-p (format "Fetch %s? " url)))) + (mh-x-image-set-download-state cache-filename 'never)) + ((eq state nil) (mh-x-image-url-fetch-image url cache-filename marker 'mh-x-image-scale-and-display))))) @@ -1386,27 +1556,32 @@ If the URL isn't present in the cache then it is fetched with wget." If optional arg MSG is non-nil, display that message instead." (if mh-showing-mode (mh-show msg))) -(defun mh-show (&optional message) +(defun mh-show (&optional message redisplay-flag) "Show message at cursor. If optional argument MESSAGE is non-nil, display that message instead. Force a two-window display with the folder window on top (size given by the variable `mh-summary-height') and the show buffer below it. If the message is already visible, display the start of the message. +If REDISPLAY-FLAG is non-nil, the default when called interactively, the +message is redisplayed even if the show buffer was already displaying the +correct message. + Display of the message is controlled by setting the variables `mh-clean-message-header-flag' and `mhl-formfile'. The default behavior is to scroll uninteresting headers off the top of the window. Type \"\\[mh-header-display]\" to see the message with all its headers." - (interactive) - (and mh-showing-with-headers - (or mhl-formfile mh-clean-message-header-flag) - (mh-invalidate-show-buffer)) + (interactive (list nil t)) + (when (or redisplay-flag + (and mh-showing-with-headers + (or mhl-formfile mh-clean-message-header-flag))) + (mh-invalidate-show-buffer)) (mh-show-msg message)) -(defun mh-show-mouse (EVENT) +(defun mh-show-mouse (event) "Move point to mouse EVENT and show message." (interactive "e") - (mouse-set-point EVENT) + (mouse-set-point event) (mh-show)) (defun mh-summary-height () @@ -1428,10 +1603,12 @@ arguments, after the message has been displayed." (let ((folder mh-current-folder) (folders (list mh-current-folder)) (clean-message-header mh-clean-message-header-flag) - (show-window (get-buffer-window mh-show-buffer))) + (show-window (get-buffer-window mh-show-buffer)) + (display-mime-buttons-flag mh-display-buttons-for-inline-parts-flag)) (if (not (eq (next-window (minibuffer-window)) (selected-window))) (delete-other-windows)) ; force ourself to the top window (mh-in-show-buffer (mh-show-buffer) + (setq mh-display-buttons-for-inline-parts-flag display-mime-buttons-flag) (if (and show-window (equal (mh-msg-filename msg folder) buffer-file-name)) (progn ;just back up to start @@ -1443,6 +1620,9 @@ arguments, after the message has been displayed." (shrink-window (- (window-height) (or mh-summary-height (mh-summary-height))))) (mh-recenter nil) + ;; The following line is a nop which forces update of the scan line so + ;; that font-lock will update it (if needed)... + (mh-notate nil nil mh-cmd-note) (if (not (memq msg mh-seen-list)) (setq mh-seen-list (cons msg mh-seen-list))) (when mh-update-sequences-after-mh-show-flag @@ -1518,8 +1698,8 @@ Sets the current buffer to the show buffer." ;; Bind variables in folder buffer in case they are local (let ((formfile mhl-formfile) (clean-message-header mh-clean-message-header-flag) - (invisible-headers mh-invisible-headers) - (visible-headers mh-visible-headers) + (invisible-headers mh-invisible-header-fields-compiled) + (visible-headers nil) (msg-filename (mh-msg-filename msg-num folder-name)) (show-buffer mh-show-buffer) (mm-inline-media-tests mh-mm-inline-media-tests)) @@ -1596,7 +1776,10 @@ Sets the current buffer to the show buffer." Header is cleaned from START to the end of the message header. INVISIBLE-HEADERS contains a regular expression specifying lines to delete from the header. VISIBLE-HEADERS contains a regular expression specifying the -lines to display. INVISIBLE-HEADERS is ignored if VISIBLE-HEADERS is non-nil." +lines to display. INVISIBLE-HEADERS is ignored if VISIBLE-HEADERS is non-nil. + +Note that MH-E no longer supports the `mh-visible-headers' variable, so +this function could be trimmed of this feature too." (let ((case-fold-search t) (buffer-read-only nil) (after-change-functions nil)) ;Work around emacs-20 font-lock bug @@ -1639,8 +1822,7 @@ If NOTATION is nil then no change in the buffer occurs." (with-mh-folder-updating (t) (beginning-of-line) (forward-char offset) - (let* ((change-stack-flag (and (stringp notation) - (equal offset (1+ mh-cmd-note)) + (let* ((change-stack-flag (and (equal offset (1+ mh-cmd-note)) (not (eq notation mh-note-seq)))) (msg (and change-stack-flag (or msg (mh-get-msg-num nil)))) (stack (and msg (gethash msg mh-sequence-notation-history))) @@ -1652,7 +1834,7 @@ If NOTATION is nil then no change in the buffer occurs." ;; at the bottom of the stack. If the sequence is deleted, ;; the correct notation will be shown. (setf (gethash msg mh-sequence-notation-history) - (reverse (cons (aref notation 0) (cdr (reverse stack))))) + (reverse (cons notation (cdr (reverse stack))))) ;; Since we don't have any sequence notations in the way, just ;; notate the scan line. (delete-char 1) @@ -1660,25 +1842,6 @@ If NOTATION is nil then no change in the buffer occurs." (when change-stack-flag (mh-thread-update-scan-line-map msg notation offset))))))) -(defun mh-find-msg-get-num (step) - "Return the message number of the message nearest the cursor. -Jumps over non-message lines, such as inc errors. -If we have to search, STEP tells whether to search forward or backward." - (or (mh-get-msg-num nil) - (let ((msg-num nil) - (nreverses 0)) - (while (and (not msg-num) - (< nreverses 2)) - (cond ((eobp) - (setq step -1) - (setq nreverses (1+ nreverses))) - ((bobp) - (setq step 1) - (setq nreverses (1+ nreverses)))) - (forward-line step) - (setq msg-num (mh-get-msg-num nil))) - msg-num))) - (defun mh-goto-msg (number &optional no-error-if-no-message dont-show) "Position the cursor at message NUMBER. Optional non-nil second argument NO-ERROR-IF-NO-MESSAGE means return nil @@ -1699,10 +1862,6 @@ Non-nil third argument DONT-SHOW means not to show the message." (or dont-show (not return-value) (mh-maybe-show number)) return-value)) -(defun mh-msg-search-pat (n) - "Return a search pattern for message N in the scan listing." - (format mh-scan-msg-search-regexp n)) - (defun mh-get-profile-field (field) "Find and return the value of FIELD in the current buffer. Returns nil if the field is not in the buffer." @@ -1716,120 +1875,65 @@ Returns nil if the field is not in the buffer." (end-of-line) (buffer-substring start (point))))))) -(defvar mail-user-agent) -(defvar read-mail-command) - (defvar mh-find-path-run nil "Non-nil if `mh-find-path' has been run already.") (defun mh-find-path () - "Set `mh-progs', `mh-lib', and `mh-lib-progs' variables. + "Set variables from user's MH profile. Set `mh-user-path', `mh-draft-folder', `mh-unseen-seq', `mh-previous-seq', `mh-inbox' from user's MH profile. The value of `mh-find-path-hook' is a list of functions to be called, with no arguments, after these variable have been set." - (mh-find-progs) + (mh-variants) (unless mh-find-path-run (setq mh-find-path-run t) - (setq read-mail-command 'mh-rmail) - (setq mail-user-agent 'mh-e-user-agent)) - (save-excursion - ;; Be sure profile is fully expanded before switching buffers - (let ((profile (expand-file-name (or (getenv "MH") "~/.mh_profile")))) - (set-buffer (get-buffer-create mh-temp-buffer)) - (setq buffer-offer-save nil) ;for people who set default to t - (erase-buffer) - (condition-case err - (insert-file-contents profile) - (file-error - (mh-install profile err))) - (setq mh-user-path (mh-get-profile-field "Path:")) - (if (not mh-user-path) - (setq mh-user-path "Mail")) - (setq mh-user-path - (file-name-as-directory - (expand-file-name mh-user-path (expand-file-name "~")))) - (unless mh-x-image-cache-directory - (setq mh-x-image-cache-directory - (expand-file-name ".mhe-x-image-cache" mh-user-path))) - (setq mh-draft-folder (mh-get-profile-field "Draft-Folder:")) - (if mh-draft-folder - (progn - (if (not (mh-folder-name-p mh-draft-folder)) - (setq mh-draft-folder (format "+%s" mh-draft-folder))) - (if (not (file-exists-p (mh-expand-file-name mh-draft-folder))) - (error "Draft folder \"%s\" not found. Create it and try again" - (mh-expand-file-name mh-draft-folder))))) - (setq mh-inbox (mh-get-profile-field "Inbox:")) - (cond ((not mh-inbox) - (setq mh-inbox "+inbox")) - ((not (mh-folder-name-p mh-inbox)) - (setq mh-inbox (format "+%s" mh-inbox)))) - (setq mh-unseen-seq (mh-get-profile-field "Unseen-Sequence:")) - (if mh-unseen-seq - (setq mh-unseen-seq (intern mh-unseen-seq)) - (setq mh-unseen-seq 'unseen)) ;old MH default? - (setq mh-previous-seq (mh-get-profile-field "Previous-Sequence:")) - (if mh-previous-seq - (setq mh-previous-seq (intern mh-previous-seq))) - (run-hooks 'mh-find-path-hook) - (mh-collect-folder-names)))) + (save-excursion + ;; Be sure profile is fully expanded before switching buffers + (let ((profile (expand-file-name (or (getenv "MH") "~/.mh_profile")))) + (set-buffer (get-buffer-create mh-temp-buffer)) + (setq buffer-offer-save nil) ;for people who set default to t + (erase-buffer) + (condition-case err + (insert-file-contents profile) + (file-error + (mh-install profile err))) + (setq mh-user-path (mh-get-profile-field "Path:")) + (if (not mh-user-path) + (setq mh-user-path "Mail")) + (setq mh-user-path + (file-name-as-directory + (expand-file-name mh-user-path (expand-file-name "~")))) + (unless mh-x-image-cache-directory + (setq mh-x-image-cache-directory + (expand-file-name ".mhe-x-image-cache" mh-user-path))) + (setq mh-draft-folder (mh-get-profile-field "Draft-Folder:")) + (if mh-draft-folder + (progn + (if (not (mh-folder-name-p mh-draft-folder)) + (setq mh-draft-folder (format "+%s" mh-draft-folder))) + (if (not (file-exists-p (mh-expand-file-name mh-draft-folder))) + (error + "Draft folder \"%s\" not found. Create it and try again" + (mh-expand-file-name mh-draft-folder))))) + (setq mh-inbox (mh-get-profile-field "Inbox:")) + (cond ((not mh-inbox) + (setq mh-inbox "+inbox")) + ((not (mh-folder-name-p mh-inbox)) + (setq mh-inbox (format "+%s" mh-inbox)))) + (setq mh-unseen-seq (mh-get-profile-field "Unseen-Sequence:")) + (if mh-unseen-seq + (setq mh-unseen-seq (intern mh-unseen-seq)) + (setq mh-unseen-seq 'unseen)) ;old MH default? + (setq mh-previous-seq (mh-get-profile-field "Previous-Sequence:")) + (if mh-previous-seq + (setq mh-previous-seq (intern mh-previous-seq))) + (run-hooks 'mh-find-path-hook) + (mh-collect-folder-names))))) (defun mh-file-command-p (file) "Return t if file FILE is the name of a executable regular file." (and (file-regular-p file) (file-executable-p file))) -(defun mh-find-progs () - "Find the directories for the installed MH/nmh binaries and config files. -Set the `mh-progs' and `mh-lib', and `mh-lib-progs' variables to the -directory names and set `mh-nmh-flag' if we detect nmh instead of MH." - (unless (and mh-progs mh-lib mh-lib-progs) - (let ((path (or (mh-path-search exec-path "mhparam") - (mh-path-search '("/usr/local/nmh/bin" ; nmh default - "/usr/local/bin/mh/" - "/usr/local/mh/" - "/usr/bin/mh/" ;Ultrix 4.2, Linux - "/usr/new/mh/" ;Ultrix <4.2 - "/usr/contrib/mh/bin/" ;BSDI - "/usr/pkg/bin/" ; NetBSD - "/usr/local/bin/" - ) - "mhparam")))) - (if (not path) - (error "Unable to find the `mhparam' command")) - (save-excursion - (let ((tmp-buffer (get-buffer-create mh-temp-buffer))) - (set-buffer tmp-buffer) - (unwind-protect - (progn - (call-process (expand-file-name "mhparam" path) - nil '(t nil) nil "libdir" "etcdir") - (goto-char (point-min)) - (if (search-forward-regexp "^libdir:\\s-\\(\\S-+\\)\\s-*$" - nil t) - (setq mh-lib-progs (match-string 1) - mh-lib mh-lib-progs - mh-progs path)) - (goto-char (point-min)) - (if (search-forward-regexp "^etcdir:\\s-\\(\\S-+\\)\\s-*$" - nil t) - (setq mh-lib (match-string 1) - mh-nmh-flag t))) - (kill-buffer tmp-buffer)))) - (unless (and mh-progs mh-lib mh-lib-progs) - (error "Unable to determine paths from `mhparam' command")) - (setq mh-flists-present-flag - (file-exists-p (expand-file-name "flists" mh-progs)))))) - -(defun mh-path-search (path file) - "Search PATH, a list of directory names, for FILE. -Returns the element of PATH that contains FILE, or nil if not found." - (while (and path - (not (funcall 'mh-file-command-p - (expand-file-name file (car path))))) - (setq path (cdr path))) - (car path)) - (defvar mh-no-install nil) ;do not run install-mh (defun mh-install (profile error-val) @@ -1911,18 +2015,18 @@ not updated." (let ((entry (mh-find-seq seq)) (internal-seq-flag (mh-internal-seq seq))) (if (and msgs (atom msgs)) (setq msgs (list msgs))) - (unless internal-flag - (mh-add-to-sequence seq msgs) - (when (not dont-annotate-flag) - (mh-iterate-on-range msg msgs - (unless (memq msg (cdr entry)) - (mh-add-sequence-notation msg internal-seq-flag))))) (if (null entry) (setq mh-seq-list (cons (mh-make-seq seq (mh-canonicalize-sequence msgs)) mh-seq-list)) (if msgs (setcdr entry (mh-canonicalize-sequence - (append msgs (mh-seq-msgs entry)))))))) + (append msgs (mh-seq-msgs entry)))))) + (unless internal-flag + (mh-add-to-sequence seq msgs) + (when (not dont-annotate-flag) + (mh-iterate-on-range msg msgs + (unless (memq msg (cdr entry)) + (mh-add-sequence-notation msg internal-seq-flag))))))) (defun mh-canonicalize-sequence (msgs) "Sort MSGS in decreasing order and remove duplicates." @@ -2076,12 +2180,15 @@ aren't usually mail folders are hidden." (goto-char (point-min)) (while (not (and (eolp) (bolp))) (goto-char (line-end-position)) - (let ((has-pos (search-backward " has " (line-beginning-position) t))) + (let ((start-pos (line-beginning-position)) + (has-pos (search-backward " has " (line-beginning-position) t))) (when (integerp has-pos) (while (equal (char-after has-pos) ? ) (decf has-pos)) (incf has-pos) - (let* ((name (buffer-substring (line-beginning-position) has-pos)) + (while (equal (char-after start-pos) ? ) + (incf start-pos)) + (let* ((name (buffer-substring start-pos has-pos)) (first-char (aref name 0)) (last-char (aref name (1- (length name))))) (unless (member first-char '(?. ?# ?,)) @@ -2189,7 +2296,9 @@ whether the completion is over." If ALLOW-ROOT-FOLDER-FLAG is non-nil then \"+\" is allowed to be a folder name corresponding to `mh-user-path'." (mh-normalize-folder-name - (let ((minibuffer-local-completion-map mh-folder-completion-map) + (let ((minibuffer-completing-file-name t) + (completion-root-regexp "^[+/]") + (minibuffer-local-completion-map mh-folder-completion-map) (mh-allow-root-folder-flag allow-root-folder-flag)) (completing-read prompt 'mh-folder-completion-function nil nil nil 'mh-folder-hist default)) @@ -2206,11 +2315,10 @@ non-nil then the function will accept the folder +, which means all folders when used in searching." (if (null default) (setq default "")) - (let* ((default-string (cond (default-string (format " [%s]? " - default-string)) - ((equal "" default) "? ") - (t (format " [%s]? " default)))) - (prompt (format "%s folder%s" prompt default-string)) + (let* ((default-string (cond (default-string (format "[%s] " default-string)) + ((equal "" default) "") + (t (format "[%s] " default)))) + (prompt (format "%s folder: %s" prompt default-string)) (mh-current-folder-name mh-current-folder) read-name folder-name) (while (and (setq read-name (mh-folder-completing-read @@ -2452,6 +2560,13 @@ Put the output into buffer after point. Set mark after inserted text." (setq l (cdr l))) new-list)) +(defun mh-replace-string (old new) + "Replace all occurrences of OLD with NEW in the current buffer." + (goto-char (point-min)) + (let ((case-fold-search t)) + (while (search-forward old nil t) + (replace-match new t t)))) + (defun mh-replace-in-string (regexp newtext string) "Replace REGEXP with NEWTEXT everywhere in STRING and return result. NEWTEXT is taken literally---no \\DIGIT escapes will be recognized. |