Commit a66894d8 authored by Bill Wohler's avatar Bill Wohler
Browse files

Upgraded to MH-E version 7.4.4.

See etc/MH-E-NEWS and lisp/mh-e/ChangeLog for details.
parent 0117451d
2004-07-12 Bill Wohler <>
* NEWS, MH-E-NEWS: Upgraded to MH-E version 7.4.4.
2004-07-08 David Kastrup <>
* NEWS (Lisp changes in 21.4): document (match-data t) change.
Copyright (C) 2003 Free Software Foundation, Inc.
Copyright (C) 2003, 2004 Free Software Foundation, Inc.
Copying and distribution of this file, with or without modification,
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved.
* Changes in MH-E 7.4.4
Version 7.4.4 addresses programmatic issues from the FSF and prepares
MH-E for inclusion into an impending GNU Emacs release (21.4). There
are no user-visible changes (unless you are using XEmacs on DOS or
don't have the cl package installed). Filenames are now unique in
their first 8 characters (DOS 8.3 requirement). The runtime dependency
on the cl package has been removed. Desktop saving and restoration
code moved here from desktop.el.
* Changes in MH-E 7.4.3
Version 7.4.3 fixes the problem where mh-identity-list was not getting
set from .emacs.
* Changes in MH-E 7.4.2
Version 7.4.2 fixes the accidental dependence on nmh (closes SF
* Changes in MH-E 7.4.1
Version 7.4.1 fixes the Makefile so it no longer tries to compile
* Changes in MH-E 7.4
Version 7.4 contains many new useful features including arbitrary MH
range handling, new draft features such as draft form editing, as well
as sequence propagation and manipulation. We've also fixed bugs and
added a handful of new variables.
** New Features in MH-E 7.4
*** Add Arbitrary Ranges to MH-E UI
MH-E now handles any legal MH range (such as last:5 or 4 8 10-12)
wherever you're prompted for a message number or sequence (closes SF
*** Remove Prompting in mh-send
Brian Reid's original mhe didn't do prompting anywhere but used forms
instead. While we won't go that far, we eliminated prompting where a
form is already involved, such as in composing a message.
The new customization variable `mh-compose-prompt-flag' can be set to
t to get the original behavior (closes SF #745622).
*** Use TAB to Switch Fields in Header
When composing a message, TAB and SHIFT-TAB can be used to move
quickly between header fields. The new customization variable,
`mh-compose-skipped-header-fields', contains a list of header fields
that are skipped and truncated if they are too long (closes SF
*** Alias Completion in Composition Buffer
Aliases can be completed in the draft with "M-TAB
(mh-letter-complete)". Or, if the customization variable
`mh-compose-space-does-completion-flag' is set to t, then a "SPC
(mh-letter-complete-or-space)" with do the same thing. If
`mh-alias-flash-on-comma' is non-nil, ", (mh-letter-confirm-address)"
will show the alias expansion in the minibuffer (closes SF #745634).
*** Auto Fields Should be Inserted During Send
Fields that were inserted by the multiple personality code when the
draft was sent now insert the header fields when the draft is composed
to give you a chance to edit them (closes SF #747890).
*** mh-index-tick-messages
The command "F ' (mh-index-ticked-messages)" creates a buffer with all
messages ticked with "' (mh-toggle-tick)" in the folders listed in the
new customization variable `mh-index-ticked-messages-folders'. Chances
are that if you set `mh-index-new-messages-folders', you'll want to
set `mh-index-ticked-messages-folders' accordingly.
In addition, a general function, "F q (mh-index-sequenced-messages)"
has been provided that displays messages in the `mh-unseen-seq' in the
folders listed `mh-index-new-messages-folders', unless a prefix
argument is given, in which case you can provide both a list of
folders and a sequence (closes SF #718833).
*** Narrow to Region
If there is a region, "/ r (mh-narrow-to-range)" will only consider
those messages in the region. In addition, there is now a stack of
folder limits which can be popped with "/ w (mh-widen)". With a prefix
arg, all the restrictions are popped off of the stack (closes SF
*** Narrow to Ticked Sequence
The buffer can now be narrowed to ticked messages with "S '
(mh-narrow-to-tick)" (closes SF #732825).
*** Display Multiple Buttons for multipart/alternative
A new customizable variable,
`mh-display-buttons-for-alternatives-flag', was added to display
buttons for the alternatives. The default value is nil to retain the
current behavior (closes SF #741288).
*** Identity Menu Changes
A menu item has been added that inserts custom fields if the To or Cc
header fields match `mh-auto-fields-list'.
** New Variables in MH-E 7.4
*** mh-alias-local-users-prefix
This string is 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.
*** mh-alias-passwd-gecos-comma-separator-flag
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."
*** mh-interpret-number-as-range-flag
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".
*** mh-kill-folder-suppress-prompt-hook
This new hook is invoked at the beginning of the `F k
(mh-kill-folder)' command. It is a list of functions to be called,
with no arguments, which should return a value of non-nil if you
should not be asked if you're sure that you want to remove the folder.
This is useful for folders that are easily regenerated.
The default value of `mh-index-p' suppresses the prompt on folders
generated by an index search.
WARNING: Use this hook with care. If there is a bug in your hook which
returns t on +inbox and you hit `F k' by accident in the +inbox
buffer, you will not be happy.
*** mh-refile-preserves-sequences-flag
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. Additional
sequences that should not to be preserved can be specified by setting
`mh-unpropagated-sequences' appropriately.
*** mh-visible-header-fields
Customize this instead of `mh-visible-headers', which is now a defvar.
This was done to mimic the relationship between
`mh-invisible-header-fields' and `mh-invisible-fields'.
** Variables Deleted in MH-E 7.4
*** mh-visible-headers
See the paragraph for `mh-visible-header-fields' above.
** Bug Fixes in MH-E 7.4
*** Aliases Constantly Reloaded
The system aliases are not loaded as often as they were, so the
completion speed has been dramatically improved if your passwd file is
large (closes SF #693859).
*** Folders in MH-Index View Not Saved
When you perform a search to produce an MH-Index buffer, the folders
that contain the messages are shown. If the MH-Index buffer was
deleted, or Emacs was restarted and the corresponding folder
rescanned, the folder information would be lost. This has been fixed
by saving the information in a file called ".mhe_index" (closes SF
*** Ticking Messages in +mhe-index/new
If a new message in a buffer created by "F n" was ticked (with "'"),
the message would not be added to the tick sequence in the source
folder. This has been fixed so that any sequence changes in any index
folder (from within MH-E of course) are now reflected back to the
corresponding source folder (closes SF #709664).
*** Custom Vars Set by a Function
The default setting of customization variable `mh-summary-height' is
now `nil' which means MH-E will change the size dynamically according
to the size of the frame (closes SF #723267).
*** Folder Completion Slow
The first folder completion was very slow. This has been fixed (closes
SF #730426).
*** Tick Sequence Persistent When Refiled
Sequences are now preserved when messages are refiled (closes SF
*** Auto-inserted Header Fields Inconsistent
For consistency, all automatically inserted header fields (such as
X-Mailer and X-Face) are added when the draft is first presented to
you. This also gives you a chance to edit or delete them if necessary
(closes SF #745624). Note that we would be distressed if you deleted
the X-Mailer field.
*** Toolbar Spec Error
The following message appeared when displaying a message in XEmacs:
Signaling: (error "Toolbar spec must be list or nil" )
This has been fixed (closes SF #745655).
*** mh-index-search Doesn't Find Short Acronyms
Swish typically ignores words with fewer than four letters, but will
still look for acronyms. Unfortunately, MH-E was downcasing the input
words which defeated this feature. This has been fixed (closes SF
* Changes in MH-E 7.3
......@@ -649,7 +649,7 @@ You can now put the init files .emacs and .emacs_SHELL under
** MH-E changes.
Upgraded to MH-E version 7.3. There have been major changes since
Upgraded to MH-E version 7.4.4. There have been major changes since
version 5.0.2; see MH-E-NEWS for details.
File suppressed by a .gitattributes entry or the file's encoding is unsupported.
This diff is collapsed.
;;; mh-alias.el --- MH-E mail alias completion and expansion
;; Copyright (C) 1994, 95, 96, 1997,
;; 2001, 02, 2003 Free Software Foundation, Inc.
;; 2001, 02, 03, 2004 Free Software Foundation, Inc.
;; Author: Peter S. Galbraith <>
;; Maintainer: Bill Wohler <>
......@@ -128,6 +128,14 @@
;;; Alias Loading
(defmacro mh-assoc-ignore-case (key alist)
"Search for string KEY in ALIST.
This is a wrapper around `assoc-string' or `assoc-ignore-case'. Avoid
`assoc-ignore-case' which is now an obsolete function."
(cond ((fboundp 'assoc-string) `(assoc-string ,key ,alist t))
((fboundp 'assoc-ignore-case) `(assoc-ignore-case ,key ,alist))
(t (error "The macro mh-assoc-ignore-case not implemented properly"))))
(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
......@@ -169,6 +177,29 @@ If ARG is non-nil, filenames listed in `mh-alias-system-aliases' are appended."
(append userlist mh-alias-system-aliases))
(defun mh-alias-gecos-name (gecos-name username comma-separator)
"Return a usable address string from a GECOS-NAME and USERNAME.
Use only part of the GECOS-NAME up to the first comma if COMMA-SEPARATOR is
(let ((res gecos-name))
;; Keep only string until first comma if COMMA-SEPARATOR is t.
(if (and comma-separator
(string-match "^\\([^,]+\\)," res))
(setq res (match-string 1 res)))
;; Replace "&" with capitalized username
(if (string-match "&" res)
(setq res (mh-replace-in-string "&" (capitalize username) res)))
;; Remove " character
(if (string-match "\"" res)
(setq res (mh-replace-in-string "\"" "" res)))
;; If empty string, use username instead
(if (string-equal "" res)
(setq res username))
;; Surround by quotes if doesn't consist of simple characters
(if (not (string-match "^[ a-zA-Z0-9-]+$" res))
(setq res (concat "\"" res "\"")))
(defun mh-alias-local-users ()
"Return an alist of local users from /etc/passwd."
(let (passwd-alist)
......@@ -185,23 +216,23 @@ If ARG is non-nil, filenames listed in `mh-alias-system-aliases' are appended."
(goto-char (point-min))))
(while (< (point) (point-max))
((looking-at "\\([^:]*\\):[^:]*:\\([^:]*\\):[^:]*:\\([^:,]*\\)[:,]")
((looking-at "\\([^:]*\\):[^:]*:\\([^:]*\\):[^:]*:\\([^:]*\\):")
(when (> (string-to-int (match-string 2)) 200)
(let* ((username (match-string 1))
(gecos-name (match-string 3))
(if (string-match "&" gecos-name)
(substring gecos-name 0 (match-beginning 0))
(capitalize username)
(substring gecos-name (match-end 0)))
(realname (mh-alias-gecos-name
gecos-name username
(setq passwd-alist
(cons (list username
(if (string-equal "" realname)
(concat "<" username ">")
(concat realname " <" username ">")))
(list (if mh-alias-local-users-prefix
(concat mh-alias-local-users-prefix
(mh-alias-suggest-alias realname t))
(if (string-equal username realname)
(concat "<" username ">")
(concat realname " <" username ">")))
(forward-line 1)))
......@@ -219,12 +250,12 @@ If ARG is non-nil, filenames listed in `mh-alias-system-aliases' are appended."
((looking-at "^[ \t]")) ;Continuation line
((looking-at "\\(.+\\): .+: .*$") ; A new -blind- MH alias
(when (not (assoc-ignore-case (match-string 1) mh-alias-blind-alist))
(when (not (mh-assoc-ignore-case (match-string 1) mh-alias-blind-alist))
(setq mh-alias-blind-alist
(cons (list (match-string 1)) mh-alias-blind-alist))
(setq mh-alias-alist (cons (list (match-string 1)) mh-alias-alist))))
((looking-at "\\(.+\\): .*$") ; A new MH alias
(when (not (assoc-ignore-case (match-string 1) mh-alias-alist))
(when (not (mh-assoc-ignore-case (match-string 1) mh-alias-alist))
(setq mh-alias-alist
(cons (list (match-string 1)) mh-alias-alist)))))
(forward-line 1)))
......@@ -235,11 +266,12 @@ If ARG is non-nil, filenames listed in `mh-alias-system-aliases' are appended."
(while local-users
(setq user (car local-users))
(if (not (assoc-ignore-case (car user) mh-alias-alist))
(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)))))
(message "Loading MH aliases...done"))
(defun mh-alias-reload-maybe ()
"Load new MH aliases."
(if (or (eq mh-alias-alist 'not-read) ; Doesn't exist, so create it.
......@@ -269,10 +301,10 @@ ali returns the string unchanged if not defined. The same is done here."
"Return expansion for ALIAS.
Blind aliases or users from /etc/passwd are not expanded."
((assoc-ignore-case alias mh-alias-blind-alist)
((mh-assoc-ignore-case alias mh-alias-blind-alist)
alias) ; Don't expand a blind alias
((assoc-ignore-case alias mh-alias-passwd-alist)
(cadr (assoc-ignore-case alias mh-alias-passwd-alist)))
((mh-assoc-ignore-case alias mh-alias-passwd-alist)
(cadr (mh-assoc-ignore-case alias mh-alias-passwd-alist)))
(mh-alias-ali alias))))
......@@ -302,26 +334,12 @@ Blind aliases or users from /etc/passwd are not expanded."
(defun mh-alias-minibuffer-confirm-address ()
"Display the alias expansion if `mh-alias-flash-on-comma' is non-nil."
(if (not mh-alias-flash-on-comma)
(when mh-alias-flash-on-comma
(let* ((case-fold-search t)
(the-name (buffer-substring
(progn (skip-chars-backward " \t")(point))
;; This moves over to previous comma, if any
(progn (or (and (not (= 0 (skip-chars-backward "^,")))
;; the skips over leading whitespace
(skip-chars-forward " "))
;; no comma, then to beginning of word
(skip-chars-backward "^ \t"))
;; In Emacs21, the beginning of the prompt
;; line is accessible, which wasn't the case
;; in emacs20. Skip over it.
(if (looking-at "^[^ \t]+:")
(skip-chars-forward "^ \t"))
(skip-chars-forward " ")
(if (assoc-ignore-case the-name mh-alias-alist)
(beg (mh-beginning-of-word))
(the-name (buffer-substring-no-properties beg (point))))
(if (mh-assoc-ignore-case the-name mh-alias-alist)
(message "%s -> %s" the-name (mh-alias-expand the-name))
;; Check if if was a single word likely to be an alias
(if (and (equal mh-alias-flash-on-comma 1)
......@@ -335,30 +353,26 @@ Blind aliases or users from /etc/passwd are not expanded."
(defun mh-alias-letter-expand-alias ()
"Expand mail alias before point."
(let ((mail-abbrevs mh-alias-alist))
(mh-funcall-if-exists mail-abbrev-complete-alias))
(when mh-alias-expand-aliases-flag
(let* ((end (point))
(syntax-table (syntax-table))
(beg (unwind-protect
(set-syntax-table mail-abbrev-syntax-table)
(backward-word 1)
(set-syntax-table syntax-table)))
(alias (buffer-substring beg end))
(expansion (mh-alias-expand alias)))
(delete-region beg end)
(insert expansion))))
(let* ((end (point))
(begin (mh-beginning-of-word))
(input (buffer-substring-no-properties begin end)))
(mh-complete-word input mh-alias-alist begin end)
(when mh-alias-expand-aliases-flag
(let* ((end (point))
(expansion (mh-alias-expand (buffer-substring begin end))))
(delete-region begin end)
(insert expansion)))))
;;; Adding addresses to alias file.
(defun mh-alias-suggest-alias (string)
"Suggest an alias for STRING."
(defun mh-alias-suggest-alias (string &optional no-comma-swap)
"Suggest an alias for STRING.
Don't reverse the order of strings separated by a comma if NO-COMMA-SWAP is
((string-match "^<\\(.*\\)>$" string)
;; <> -> recurse, stripping brackets.
(mh-alias-suggest-alias (match-string 1 string)))
(mh-alias-suggest-alias (match-string 1 string) no-comma-swap))
((string-match "^\\sw+$" string)
;; One word -> downcase it.
(downcase string))
......@@ -372,47 +386,59 @@ Blind aliases or users from /etc/passwd are not expanded."
(downcase (match-string 1 string)))
((string-match "^\"\\(.*\\)\".*" string)
;; "Some name" <> -> recurse -> "Some name"
(mh-alias-suggest-alias (match-string 1 string)))
(mh-alias-suggest-alias (match-string 1 string) no-comma-swap))
((string-match "^\\(.*\\) +<.*>$" string)
;; Some name <> -> recurse -> Some name
(mh-alias-suggest-alias (match-string 1 string)))
(mh-alias-suggest-alias (match-string 1 string) no-comma-swap))
((string-match (concat mh-address-mail-regexp " +(\\(.*\\))$") string)
;; (Some name) -> recurse -> Some name
(mh-alias-suggest-alias (match-string 1 string)))
(mh-alias-suggest-alias (match-string 1 string) no-comma-swap))
((string-match "^\\(Dr\\|Prof\\)\\.? +\\(.*\\)" string)
;; Strip out title
(mh-alias-suggest-alias (match-string 2 string)))
(mh-alias-suggest-alias (match-string 2 string) no-comma-swap))
((string-match "^\\(.*\\), +\\(Jr\\.?\\|II+\\)$" string)
;; Strip out tails with comma
(mh-alias-suggest-alias (match-string 1 string)))
(mh-alias-suggest-alias (match-string 1 string) no-comma-swap))
((string-match "^\\(.*\\) +\\(Jr\\.?\\|II+\\)$" string)
;; Strip out tails
(mh-alias-suggest-alias (match-string 1 string)))
(mh-alias-suggest-alias (match-string 1 string) no-comma-swap))
((string-match "^\\(\\sw+\\) +[A-Z]\\.? +\\(.*\\)$" string)
;; Strip out initials
(format "%s %s" (match-string 1 string) (match-string 2 string))))
((string-match "^\\([^,]+\\), +\\(.*\\)$" string)
;; Reverse order of comma-separated fields
(format "%s %s" (match-string 1 string) (match-string 2 string))
((and (not no-comma-swap)
(string-match "^\\([^,]+\\), +\\(.*\\)$" string))
;; Reverse order of comma-separated fields to handle:
;; From: "Galbraith, Peter" <>
;; but don't this for a name string extracted from the passwd file
;; with mh-alias-passwd-gecos-comma-separator-flag set to nil.
(format "%s %s" (match-string 2 string) (match-string 1 string))))
(format "%s %s" (match-string 2 string) (match-string 1 string))
;; Output string, with spaces replaced by dots.
(mh-alias-canonicalize-suggestion string))))
(defun mh-alias-canonicalize-suggestion (string)
"Process STRING to replace spacess by periods.
First all spaces are replaced by periods. Then every run of consecutive periods
are replaced with a single period. Finally the string is converted to lower
"Process STRING to replace spaces by periods.
First all spaces and commas are replaced by periods. Then every run of
consecutive periods are replaced with a single period. Finally the string
is converted to lower case."
(insert string)
;; Replace spaces with periods
(goto-char (point-min))
(replace-regexp " +" ".")
(while (re-search-forward " +" nil t)
(replace-match "." nil nil))
;; Replace commas with periods
(goto-char (point-min))
(while (re-search-forward ",+" nil t)
(replace-match "." nil nil))
;; Replace consecutive periods with a single period
(goto-char (point-min))
(replace-regexp "\\.\\.+" ".")
(while (re-search-forward "\\.\\.+" nil t)
(replace-match "." nil nil))
;; Convert to lower case
(downcase-region (point-min) (point-max))
;; Whew! all done...
......@@ -617,6 +643,63 @@ already has an alias."
(mh-alias-add-alias nil address)
(message "No email address found under point."))))
(defun mh-alias-apropos (regexp)
"Show all aliases that match REGEXP either in name or content."
(interactive "sAlias regexp: ")
(if mh-alias-local-users
(let ((matches "")(group-matches "")(passwd-matches))
(message "Reading MH aliases...")
(mh-exec-cmd-quiet t "ali" "-nolist" "-nouser")
(message "Reading MH aliases...done. Parsing...")
(while (re-search-forward regexp nil t)
((looking-at "^[ \t]") ;Continuation line
(setq group-matches
(concat group-matches
(or (re-search-backward "^[^ \t]" nil t)
(if (re-search-forward "^[^ \t]" nil t)
(forward-char -1))
(setq matches
(concat matches
(buffer-substring (point)(progn (end-of-line)(point)))
(message "Reading MH aliases...done. Parsing...done.")
(when mh-alias-local-users
"Reading MH aliases...done. Parsing...done. Passwd aliases...")
(setq passwd-matches
'(lambda (elem)
(if (or (string-match regexp (car elem))
(string-match regexp (cadr elem)))
(format "%s: %s\n" (car elem) (cadr elem))))
mh-alias-passwd-alist ""))
"Reading MH aliases...done. Parsing...done. 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*"
(if (not (string-equal "" matches))
(princ matches))
(when (not (string-equal group-matches ""))
(princ "\nGroup Aliases:\n\n")
(princ group-matches))
(when (not (string-equal passwd-matches ""))
(princ "\nLocal User Aliases:\n\n")
(princ passwd-matches))))))
(provide 'mh-alias)
;;; Local Variables:
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -72,18 +72,15 @@ digest are inserted into the folder after that message."
(message "Bursting digest...done")))