49120
|
1 ;;; mh-alias.el --- MH-E mail alias completion and expansion
|
|
2 ;;
|
|
3 ;; Copyright (C) 1994, 1995, 1996, 1997, 2001, 2002 Free Software Foundation, Inc.
|
|
4
|
|
5 ;; Author: Peter S. Galbraith <psg@debian.org>
|
|
6 ;; Maintainer: Bill Wohler <wohler@newt.com>
|
|
7 ;; Keywords: mail
|
|
8 ;; See: mh-e.el
|
|
9
|
|
10 ;; This file is part of GNU Emacs.
|
|
11
|
|
12 ;; GNU Emacs is free software; you can redistribute it and/or modify
|
|
13 ;; it under the terms of the GNU General Public License as published by
|
|
14 ;; the Free Software Foundation; either version 2, or (at your option)
|
|
15 ;; any later version.
|
|
16
|
|
17 ;; GNU Emacs is distributed in the hope that it will be useful,
|
|
18 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
20 ;; GNU General Public License for more details.
|
|
21
|
|
22 ;; You should have received a copy of the GNU General Public License
|
|
23 ;; along with GNU Emacs; see the file COPYING. If not, write to the
|
|
24 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
25 ;; Boston, MA 02111-1307, USA.
|
|
26
|
|
27 ;;; Commentary:
|
|
28
|
|
29 ;; [To be deleted when documented in MH-E manual.]
|
|
30 ;;
|
|
31 ;; This module provides mail alias completion when entering addresses.
|
|
32 ;;
|
|
33 ;; Use the TAB key to complete aliases (and optionally local usernames) when
|
|
34 ;; initially composing a message in the To: and Cc: minibuffer prompts. You
|
|
35 ;; may enter multiple addressees separated with a comma (but do *not* add any
|
|
36 ;; space after the comma).
|
|
37 ;;
|
|
38 ;; In the header of a message draft, use "M-TAB (mh-letter-complete)" to
|
|
39 ;; complete aliases. This is useful when you want to add an addressee as an
|
|
40 ;; afterthought when creating a message, or when adding an additional
|
|
41 ;; addressee to a reply.
|
|
42 ;;
|
|
43 ;; By default, completion is case-insensitive. This can be changed by
|
|
44 ;; customizing the variable `mh-alias-completion-ignore-case-flag'. This is
|
|
45 ;; useful, for example, to differentiate between people aliases in lowercase
|
|
46 ;; such as:
|
|
47 ;;
|
|
48 ;; p.galbraith: Peter Galbraith <GalbraithP@dfo-mpo.gc.ca>
|
|
49 ;;
|
|
50 ;; and lists in uppercase such as:
|
|
51 ;;
|
|
52 ;; MH-E: MH-E mailing list <mh-e-devel@lists.sourceforge.net>
|
|
53 ;;
|
|
54 ;; Note that this variable affects minibuffer completion only. If you have an
|
|
55 ;; alias for P.Galbraith and type in p.galbraith at the prompt, it will still
|
|
56 ;; be expanded in the letter buffer because MH is case-insensitive.
|
|
57 ;;
|
|
58 ;; When you press ", (mh-alias-minibuffer-confirm-address)" after an alias in
|
|
59 ;; the minibuffer, the expansion for the previous mail alias appears briefly.
|
|
60 ;; To inhibit this, customize the variable `mh-alias-flash-on-comma'.
|
|
61 ;;
|
|
62 ;; The addresses and aliases entered in the minibuffer are added to the
|
|
63 ;; message draft. To expand the aliases before they are added to the draft,
|
|
64 ;; customize the variable `mh-alias-expand-aliases-flag'.
|
|
65 ;;
|
|
66 ;; Completion is also performed on usernames extracted from the /etc/passwd
|
|
67 ;; file. This can be a handy tool on a machine where you and co-workers
|
|
68 ;; exchange messages, but should probably be disabled on a system with
|
|
69 ;; thousands of users you don't know. This is done by customizing the
|
|
70 ;; variable `mh-alias-local-users'. This variable also takes a string which
|
|
71 ;; is executed to generate the password file. For example, you'd use "ypcat
|
|
72 ;; passwd" for NIS.
|
|
73 ;;
|
|
74 ;; Aliases are loaded the first time you send mail and get the "To:" prompt
|
|
75 ;; and whenever a source of aliases changes. Sources of system aliases are
|
|
76 ;; defined in the customization variable `mh-alias-system-aliases' and
|
|
77 ;; include:
|
|
78 ;;
|
|
79 ;; /etc/nmh/MailAliases
|
|
80 ;; /usr/lib/mh/MailAliases
|
|
81 ;; /etc/passwd
|
|
82 ;;
|
|
83 ;; Sources of personal aliases are read from the files listed in your MH
|
|
84 ;; profile component Aliasfile. Multiple files are separated by white space
|
|
85 ;; and are relative to your mail directory.
|
|
86 ;;
|
|
87 ;; Alias Insertions
|
|
88 ;; ~~~~~~~~~~~~~~~~
|
|
89 ;; There are commands to insert new aliases into your alias file(s) (defined
|
|
90 ;; by the `Aliasfile' component in the .mh_profile file or by the variable
|
|
91 ;; `mh-alias-insert-file'). In particular, there is a tool-bar icon to grab
|
|
92 ;; an alias from the From line of the current message.
|
|
93
|
|
94 ;;; Code:
|
|
95
|
|
96 (require 'mh-e)
|
|
97 (load "cmr" t t) ; Non-fatal dependency for
|
|
98 ; completing-read-multiple.
|
|
99 (eval-when-compile (defvar mail-abbrev-syntax-table))
|
|
100
|
|
101 ;;; Autoloads
|
|
102 (autoload 'mail-abbrev-complete-alias "mailabbrev")
|
|
103 (autoload 'multi-prompt "multi-prompt")
|
|
104
|
|
105 (defvar mh-alias-alist nil
|
|
106 "Alist of MH aliases.")
|
|
107 (defvar mh-alias-blind-alist nil
|
|
108 "Alist of MH aliases that are blind lists.")
|
|
109 (defvar mh-alias-passwd-alist nil
|
|
110 "Alist of aliases extracted from passwd file and their expansions.")
|
|
111 (defvar mh-alias-tstamp nil
|
|
112 "Time aliases were last loaded.")
|
|
113 (defvar mh-alias-read-address-map nil)
|
|
114 (if mh-alias-read-address-map
|
|
115 ()
|
|
116 (setq mh-alias-read-address-map
|
|
117 (copy-keymap minibuffer-local-completion-map))
|
|
118 (if mh-alias-flash-on-comma
|
|
119 (define-key mh-alias-read-address-map
|
|
120 "," 'mh-alias-minibuffer-confirm-address))
|
|
121 (define-key mh-alias-read-address-map " " 'self-insert-command))
|
|
122
|
|
123
|
|
124 ;;; Alias Loading
|
|
125
|
|
126 (defun mh-alias-tstamp (arg)
|
|
127 "Check whether alias files have been modified.
|
|
128 Return t if any file listed in the MH profile component Aliasfile has been
|
|
129 modified since the timestamp.
|
|
130 If ARG is non-nil, set timestamp with the current time."
|
|
131 (if arg
|
|
132 (let ((time (current-time)))
|
|
133 (setq mh-alias-tstamp (list (nth 0 time) (nth 1 time))))
|
|
134 (let ((stamp))
|
|
135 (car (memq t (mapcar
|
|
136 (function
|
|
137 (lambda (file)
|
|
138 (when (and file (file-exists-p file))
|
|
139 (setq stamp (nth 5 (file-attributes file)))
|
|
140 (or (> (car stamp) (car mh-alias-tstamp))
|
|
141 (and (= (car stamp) (car mh-alias-tstamp))
|
|
142 (> (cadr stamp) (cadr mh-alias-tstamp)))))))
|
|
143 (mh-alias-filenames t)))))))
|
|
144
|
|
145 (defun mh-alias-filenames (arg)
|
|
146 "Return list of filenames that contain aliases.
|
|
147 The filenames come from the MH profile component Aliasfile and are expanded.
|
|
148 If ARG is non-nil, filenames listed in `mh-alias-system-aliases' are appended."
|
|
149 (or mh-progs (mh-find-path))
|
|
150 (save-excursion
|
|
151 (let* ((filename (mh-profile-component "Aliasfile"))
|
|
152 (filelist (and filename (split-string filename "[ \t]+")))
|
|
153 (userlist
|
|
154 (mapcar
|
|
155 (function
|
|
156 (lambda (file)
|
|
157 (if (and mh-user-path file
|
|
158 (file-exists-p (expand-file-name file mh-user-path)))
|
|
159 (expand-file-name file mh-user-path))))
|
|
160 filelist)))
|
|
161 (if arg
|
|
162 (if (stringp mh-alias-system-aliases)
|
|
163 (append userlist (list mh-alias-system-aliases))
|
|
164 (append userlist mh-alias-system-aliases))
|
|
165 userlist))))
|
|
166
|
|
167 (defun mh-alias-local-users ()
|
|
168 "Return an alist of local users from /etc/passwd."
|
|
169 (let (passwd-alist)
|
|
170 (save-excursion
|
|
171 (set-buffer (get-buffer-create mh-temp-buffer))
|
|
172 (erase-buffer)
|
|
173 (cond
|
|
174 ((eq mh-alias-local-users t)
|
|
175 (if (file-readable-p "/etc/passwd")
|
|
176 (insert-file-contents "/etc/passwd")))
|
|
177 ((stringp mh-alias-local-users)
|
|
178 (insert mh-alias-local-users "\n")
|
|
179 (shell-command-on-region (point-min)(point-max) mh-alias-local-users t)
|
|
180 (goto-char (point-min))))
|
|
181 (while (< (point) (point-max))
|
|
182 (cond
|
|
183 ((looking-at "\\([^:]*\\):[^:]*:\\([^:]*\\):[^:]*:\\([^:,]*\\)[:,]")
|
|
184 (when (> (string-to-int (match-string 2)) 200)
|
|
185 (let* ((username (match-string 1))
|
|
186 (gecos-name (match-string 3))
|
|
187 (realname
|
|
188 (if (string-match "&" gecos-name)
|
|
189 (concat
|
|
190 (substring gecos-name 0 (match-beginning 0))
|
|
191 (capitalize username)
|
|
192 (substring gecos-name (match-end 0)))
|
|
193 gecos-name)))
|
|
194 (setq passwd-alist
|
|
195 (cons (list username
|
|
196 (if (string-equal "" realname)
|
|
197 (concat "<" username ">")
|
|
198 (concat realname " <" username ">")))
|
|
199 passwd-alist))))))
|
|
200 (forward-line 1)))
|
|
201 passwd-alist))
|
|
202
|
|
203 ;;;###mh-autoload
|
|
204 (defun mh-alias-reload ()
|
|
205 "Load MH aliases into `mh-alias-alist'."
|
|
206 (interactive)
|
|
207 (save-excursion
|
|
208 (message "Loading MH aliases...")
|
|
209 (mh-alias-tstamp t)
|
|
210 (mh-exec-cmd-quiet t "ali" "-nolist" "-nouser")
|
|
211 (setq mh-alias-alist nil)
|
|
212 (setq mh-alias-blind-alist nil)
|
|
213 (while (< (point) (point-max))
|
|
214 (cond
|
|
215 ((looking-at "^[ \t]")) ;Continuation line
|
|
216 ((looking-at "\\(.+\\): .+: .*$") ; A new -blind- MH alias
|
|
217 (when (not (assoc-ignore-case (match-string 1) mh-alias-blind-alist))
|
|
218 (setq mh-alias-blind-alist
|
|
219 (cons (list (match-string 1)) mh-alias-blind-alist))
|
|
220 (setq mh-alias-alist (cons (list (match-string 1)) mh-alias-alist))))
|
|
221 ((looking-at "\\(.+\\): .*$") ; A new MH alias
|
|
222 (when (not (assoc-ignore-case (match-string 1) mh-alias-alist))
|
|
223 (setq mh-alias-alist
|
|
224 (cons (list (match-string 1)) mh-alias-alist)))))
|
|
225 (forward-line 1)))
|
|
226 (when mh-alias-local-users
|
|
227 (setq mh-alias-passwd-alist (mh-alias-local-users))
|
|
228 ;; Update aliases with local users, but leave existing aliases alone.
|
|
229 (let ((local-users mh-alias-passwd-alist)
|
|
230 user)
|
|
231 (while local-users
|
|
232 (setq user (car local-users))
|
|
233 (if (not (assoc-ignore-case (car user) mh-alias-alist))
|
|
234 (setq mh-alias-alist (append mh-alias-alist (list user))))
|
|
235 (setq local-users (cdr local-users)))))
|
|
236 (message "Loading MH aliases...done"))
|
|
237
|
|
238 (defun mh-alias-reload-maybe ()
|
|
239 "Load new MH aliases."
|
|
240 (if (or (not mh-alias-alist) ; Doesn't exist, so create it.
|
|
241 (mh-alias-tstamp nil)) ; Out of date, so recreate it.
|
|
242 (mh-alias-reload)))
|
|
243
|
|
244
|
|
245 ;;; Alias Expansion
|
|
246
|
|
247 (defun mh-alias-ali (alias &optional user)
|
|
248 "Return ali expansion for ALIAS.
|
|
249 ALIAS must be a string for a single alias.
|
|
250 If USER is t, then assume ALIAS is an address and call ali -user.
|
|
251 ali returns the string unchanged if not defined. The same is done here."
|
|
252 (save-excursion
|
|
253 (let ((user-arg (if user "-user" "-nouser")))
|
|
254 (mh-exec-cmd-quiet t "ali" user-arg "-nolist" alias))
|
|
255 (goto-char (point-max))
|
|
256 (if (looking-at "^$") (delete-backward-char 1))
|
|
257 (buffer-substring (point-min)(point-max))))
|
|
258
|
|
259 (defun mh-alias-expand (alias)
|
|
260 "Return expansion for ALIAS.
|
|
261 Blind aliases or users from /etc/passwd are not expanded."
|
|
262 (cond
|
|
263 ((assoc-ignore-case alias mh-alias-blind-alist)
|
|
264 alias) ; Don't expand a blind alias
|
|
265 ((assoc-ignore-case alias mh-alias-passwd-alist)
|
|
266 (cadr (assoc-ignore-case alias mh-alias-passwd-alist)))
|
|
267 (t
|
|
268 (mh-alias-ali alias))))
|
|
269
|
|
270 ;;;###mh-autoload
|
|
271 (defun mh-read-address (prompt)
|
|
272 "Read an address from the minibuffer with PROMPT."
|
|
273 (mh-alias-reload-maybe)
|
|
274 (if (not mh-alias-alist) ; If still no aliases, just prompt
|
|
275 (read-string prompt)
|
|
276 (let* ((minibuffer-local-completion-map mh-alias-read-address-map)
|
|
277 (completion-ignore-case mh-alias-completion-ignore-case-flag)
|
|
278 (the-answer
|
|
279 (or (cond
|
|
280 ((fboundp 'completing-read-multiple)
|
|
281 (completing-read-multiple prompt mh-alias-alist nil nil))
|
|
282 ((featurep 'multi-prompt)
|
|
283 (multi-prompt "," nil prompt mh-alias-alist nil nil))
|
|
284 (t
|
|
285 (split-string
|
|
286 (completing-read "To: " mh-alias-alist nil nil)
|
|
287 ","))))))
|
|
288 (if (not mh-alias-expand-aliases-flag)
|
|
289 (mapconcat 'identity the-answer ", ")
|
|
290 ;; Loop over all elements, checking if in passwd aliast or blind first
|
|
291 (mapconcat 'mh-alias-expand the-answer ",\n ")))))
|
|
292
|
|
293 ;;;###mh-autoload
|
|
294 (defun mh-alias-minibuffer-confirm-address ()
|
|
295 "Display the alias expansion if `mh-alias-flash-on-comma' is non-nil."
|
|
296 (interactive)
|
|
297 (if (not mh-alias-flash-on-comma)
|
|
298 ()
|
|
299 (save-excursion
|
|
300 (let* ((case-fold-search t)
|
|
301 (the-name (buffer-substring
|
|
302 (progn (skip-chars-backward " \t")(point))
|
|
303 ;; This moves over to previous comma, if any
|
|
304 (progn (or (and (not (= 0 (skip-chars-backward "^,")))
|
|
305 ;; the skips over leading whitespace
|
|
306 (skip-chars-forward " "))
|
|
307 ;; no comma, then to beginning of word
|
|
308 (skip-chars-backward "^ \t"))
|
|
309 ;; In Emacs21, the beginning of the prompt
|
|
310 ;; line is accessible, which wasn't the case
|
|
311 ;; in emacs20. Skip over it.
|
|
312 (if (looking-at "^[^ \t]+:")
|
|
313 (skip-chars-forward "^ \t"))
|
|
314 (skip-chars-forward " ")
|
|
315 (point)))))
|
|
316 (if (assoc-ignore-case the-name mh-alias-alist)
|
|
317 (message "%s -> %s" the-name (mh-alias-expand the-name))
|
|
318 ;; Check if if was a single word likely to be an alias
|
|
319 (if (and (equal mh-alias-flash-on-comma 1)
|
|
320 (not (string-match " " the-name)))
|
|
321 (message "No alias for %s" the-name))))))
|
|
322 (self-insert-command 1))
|
|
323
|
|
324 ;;;###mh-autoload
|
|
325 (defun mh-alias-letter-expand-alias ()
|
|
326 "Expand mail alias before point."
|
|
327 (mh-alias-reload-maybe)
|
|
328 (let ((mail-abbrevs mh-alias-alist))
|
|
329 (mail-abbrev-complete-alias))
|
|
330 (when mh-alias-expand-aliases-flag
|
|
331 (let* ((end (point))
|
|
332 (syntax-table (syntax-table))
|
|
333 (beg (unwind-protect
|
|
334 (save-excursion
|
|
335 (set-syntax-table mail-abbrev-syntax-table)
|
|
336 (backward-word 1)
|
|
337 (point))
|
|
338 (set-syntax-table syntax-table)))
|
|
339 (alias (buffer-substring beg end))
|
|
340 (expansion (mh-alias-expand alias)))
|
|
341 (delete-region beg end)
|
|
342 (insert expansion))))
|
|
343
|
|
344 ;;; Adding addresses to alias file.
|
|
345
|
|
346 (defun mh-alias-suggest-alias (string)
|
|
347 "Suggest an alias for STRING."
|
|
348 (cond
|
|
349 ((string-match "^\\sw+$" string)
|
|
350 ;; One word -> downcase it.
|
|
351 (downcase string))
|
|
352 ((string-match "^\\(\\sw+\\)\\s-+\\(\\sw+\\)$" string)
|
|
353 ;; Two words -> first.last
|
|
354 (downcase
|
|
355 (format "%s.%s" (match-string 1 string) (match-string 2 string))))
|
|
356 ((string-match "^\\([-a-zA-Z0-9._]+\\)@[-a-zA-z0-9_]+\\.+[a-zA-Z0-9]+$"
|
|
357 string)
|
|
358 ;; email only -> downcase username
|
|
359 (downcase (match-string 1 string)))
|
|
360 ((string-match "^\"\\(.*\\)\".*" string)
|
|
361 ;; "Some name" <somename@foo.bar> -> recurse -> "Some name"
|
|
362 (mh-alias-suggest-alias (match-string 1 string)))
|
|
363 ((string-match "^\\(.*\\) +<.*>$" string)
|
|
364 ;; Some name <somename@foo.bar> -> recurse -> Some name
|
|
365 (mh-alias-suggest-alias (match-string 1 string)))
|
|
366 ((string-match (concat mh-address-mail-regexp " +(\\(.*\\))$") string)
|
|
367 ;; somename@foo.bar (Some name) -> recurse -> Some name
|
|
368 (mh-alias-suggest-alias (match-string 1 string)))
|
|
369 ((string-match "^\\(Dr\\|Prof\\)\\.? +\\(.*\\)" string)
|
|
370 ;; Strip out title
|
|
371 (mh-alias-suggest-alias (match-string 2 string)))
|
|
372 ((string-match "^\\(.*\\), +\\(Jr\\.?\\|II+\\)$" string)
|
|
373 ;; Strip out tails with comma
|
|
374 (mh-alias-suggest-alias (match-string 1 string)))
|
|
375 ((string-match "^\\(.*\\) +\\(Jr\\.?\\|II+\\)$" string)
|
|
376 ;; Strip out tails
|
|
377 (mh-alias-suggest-alias (match-string 1 string)))
|
|
378 ((string-match "^\\(\\sw+\\) +[A-Z]\\.? +\\(.*\\)$" string)
|
|
379 ;; Strip out initials
|
|
380 (mh-alias-suggest-alias
|
|
381 (format "%s %s" (match-string 1 string) (match-string 2 string))))
|
|
382 ((string-match "^\\([^,]+\\), +\\(.*\\)$" string)
|
|
383 ;; Reverse order of comma-separated fields
|
|
384 (mh-alias-suggest-alias
|
|
385 (format "%s %s" (match-string 2 string) (match-string 1 string))))
|
|
386 (t
|
|
387 ;; Output string, with spaces replaced by dots.
|
|
388 (downcase (replace-regexp-in-string
|
|
389 "\\.\\.+" "."
|
|
390 (replace-regexp-in-string " +" "." string))))))
|
|
391
|
|
392 (defun mh-alias-which-file-has-alias (alias file-list)
|
|
393 "Return the name of writable file which defines ALIAS from list FILE-LIST."
|
|
394 (save-excursion
|
|
395 (set-buffer (get-buffer-create mh-temp-buffer))
|
|
396 (let ((the-list file-list)
|
|
397 (found))
|
|
398 (while the-list
|
|
399 (erase-buffer)
|
|
400 (when (file-writable-p (car file-list))
|
|
401 (insert-file-contents (car file-list))
|
|
402 (if (re-search-forward (concat "^" (regexp-quote alias) ":"))
|
|
403 (setq found (car file-list)
|
|
404 the-list nil)
|
|
405 (setq the-list (cdr the-list)))))
|
|
406 found)))
|
|
407
|
|
408 (defun mh-alias-insert-file (&optional alias)
|
|
409 "Return the alias file to write a new entry for ALIAS in.
|
|
410 Use variable `mh-alias-insert-file' if non-nil, else use AliasFile component
|
|
411 value.
|
|
412 If ALIAS is specified and it already exists, try to return the file that
|
|
413 contains it."
|
|
414 (cond
|
|
415 ((and mh-alias-insert-file (listp mh-alias-insert-file))
|
|
416 (if (not (elt mh-alias-insert-file 1)) ; Only one entry, use it
|
|
417 (car mh-alias-insert-file)
|
|
418 (if (or (not alias)
|
|
419 (string-equal alias (mh-alias-ali alias))) ;alias doesn't exist
|
|
420 (completing-read "Alias file [press Tab]: "
|
|
421 (mapcar 'list mh-alias-insert-file) nil t)
|
|
422 (or (mh-alias-which-file-has-alias alias mh-alias-insert-file)
|
|
423 (completing-read "Alias file [press Tab]: "
|
|
424 (mapcar 'list mh-alias-insert-file) nil t)))))
|
|
425 ((and mh-alias-insert-file (stringp mh-alias-insert-file))
|
|
426 mh-alias-insert-file)
|
|
427 (t
|
|
428 ;; writable ones returned from (mh-alias-filenames):
|
|
429 (let ((autolist (delq nil (mapcar (lambda (file)
|
|
430 (if (and (file-writable-p file)
|
|
431 (not (string-equal
|
|
432 file "/etc/passwd")))
|
|
433 file))
|
|
434 (mh-alias-filenames t)))))
|
|
435 (cond
|
|
436 ((not autolist)
|
|
437 (error "No writable alias file.
|
|
438 Set `mh-alias-insert-file' or set AliasFile in your .mh_profile file"))
|
|
439 ((not (elt autolist 1)) ; Only one entry, use it
|
|
440 (car autolist))
|
|
441 ((or (not alias)
|
|
442 (string-equal alias (mh-alias-ali alias))) ;alias doesn't exist
|
|
443 (completing-read "Alias file [press Tab]: "
|
|
444 (mapcar 'list autolist) nil t))
|
|
445 (t
|
|
446 (or (mh-alias-which-file-has-alias alias autolist)
|
|
447 (completing-read "Alias file [press Tab]: "
|
|
448 (mapcar 'list autolist) nil t))))))))
|
|
449
|
|
450 (defun mh-alias-address-to-alias (address)
|
|
451 "Return the ADDRESS alias if defined, or nil."
|
|
452 (let* ((aliases (mh-alias-ali address t)))
|
|
453 (if (string-equal aliases address)
|
|
454 nil ; ali returned same string -> no.
|
|
455 ;; For the comma-separated aliases reyurned by ali, check that one of
|
|
456 ;; them doesn't expand into a list. e.g. we do have an individual
|
|
457 ;; alias for that adress.
|
|
458 (car (delq nil (mapcar
|
|
459 (function
|
|
460 (lambda (alias)
|
|
461 (let ((recurse (mh-alias-ali alias nil)))
|
|
462 (if (string-match ".*,.*" recurse)
|
|
463 nil
|
|
464 alias))))
|
|
465 (split-string aliases ", +")))))))
|
|
466
|
|
467 ;;;###mh-autoload
|
|
468 (defun mh-alias-from-has-no-alias-p ()
|
|
469 "Return t is From has no current alias set."
|
|
470 (mh-alias-reload-maybe)
|
|
471 (save-excursion
|
|
472 (if (not (mh-folder-line-matches-show-buffer-p))
|
|
473 nil ;No corresponding show buffer
|
|
474 (if (eq major-mode 'mh-folder-mode)
|
|
475 (set-buffer mh-show-buffer))
|
|
476 (not (mh-alias-address-to-alias (mh-extract-from-header-value))))))
|
|
477
|
|
478 (defun mh-alias-add-alias-to-file (alias address &optional file)
|
|
479 "Add ALIAS for ADDRESS in alias FILE without alias check or prompts.
|
|
480 Prompt for alias file if not provided and there is more than one candidate.
|
|
481 If ALIAS matches exactly, prompt to [i]nsert before old value or [a]ppend
|
|
482 after it."
|
|
483 (if (not file)
|
|
484 (setq file (mh-alias-insert-file alias)))
|
|
485 (save-excursion
|
|
486 (set-buffer (find-file-noselect file))
|
|
487 (goto-char (point-min))
|
|
488 (let ((alias-search (concat alias ":"))
|
|
489 (letter)
|
|
490 (here (point))
|
|
491 (case-fold-search t))
|
|
492 (cond
|
|
493 ;; Search for exact match (if we had the same alias before)
|
|
494 ((re-search-forward
|
|
495 (concat "^" (regexp-quote alias-search) " *\\(.*\\)") nil t)
|
|
496 (let ((answer (read-string
|
|
497 (format "Exists for %s; [i]nsert, [a]ppend: "
|
|
498 (match-string 1))))
|
|
499 (case-fold-search t))
|
|
500 (cond ((string-match "^i" answer))
|
|
501 ((string-match "^a" answer)
|
|
502 (forward-line 1))
|
|
503 (t
|
|
504 error "Quitting."))))
|
|
505 ;; No, so sort-in at the right place
|
|
506 ;; search for "^alias", then "^alia", etc.
|
|
507 ((eq mh-alias-insertion-location 'sorted)
|
|
508 (setq letter (substring alias-search -1)
|
|
509 alias-search (substring alias-search 0 -1))
|
|
510 (while (and (not (equal alias-search ""))
|
|
511 (not (re-search-forward
|
|
512 (concat "^" (regexp-quote alias-search)) nil t)))
|
|
513 (setq letter (substring alias-search -1)
|
|
514 alias-search (substring alias-search 0 -1)))
|
|
515 ;; Next, move forward to sort alphabetically for following letters
|
|
516 (beginning-of-line)
|
|
517 (while (re-search-forward
|
|
518 (concat "^" (regexp-quote alias-search) "[a-" letter "]")
|
|
519 nil t)
|
|
520 (forward-line 1)))
|
|
521 ((eq mh-alias-insertion-location 'bottom)
|
|
522 (goto-char (point-max)))
|
|
523 ((eq mh-alias-insertion-location 'top)
|
|
524 (goto-char (point-min)))))
|
|
525 (beginning-of-line)
|
|
526 (insert (format "%s: %s\n" alias address))
|
|
527 (save-buffer)))
|
|
528
|
|
529 ;;;###mh-autoload
|
|
530 (defun mh-alias-add-alias (alias address)
|
|
531 "*Add ALIAS for ADDRESS in personal alias file.
|
|
532 Prompts for confirmation if the address already has an alias.
|
|
533 If the alias is already is use, `mh-alias-add-alias-to-file' will prompt."
|
|
534 (interactive "P\nP")
|
|
535 (mh-alias-reload-maybe)
|
|
536 (setq alias (completing-read "Alias: " mh-alias-alist nil nil alias))
|
|
537 (setq address (read-string "Address: " address))
|
|
538 (let ((address-alias (mh-alias-address-to-alias address))
|
|
539 (alias-address (mh-alias-expand alias)))
|
|
540 (if (string-equal alias-address alias)
|
|
541 (setq alias-address nil))
|
|
542 (cond
|
|
543 ((and (equal alias address-alias)
|
|
544 (equal address alias-address))
|
|
545 (message "Already defined as: %s" alias-address))
|
|
546 (address-alias
|
|
547 (if (y-or-n-p (format "Address has alias %s; set new one? "
|
|
548 address-alias))
|
|
549 (mh-alias-add-alias-to-file alias address)))
|
|
550 (t
|
|
551 (mh-alias-add-alias-to-file alias address)))))
|
|
552
|
|
553 ;;;###mh-autoload
|
|
554 (defun mh-alias-grab-from-field ()
|
|
555 "*Add ALIAS for ADDRESS in personal alias file.
|
|
556 Prompts for confirmation if the alias is already in use or if the address
|
|
557 already has an alias."
|
|
558 (interactive)
|
|
559 (mh-alias-reload-maybe)
|
|
560 (save-excursion
|
|
561 (cond
|
|
562 ((mh-folder-line-matches-show-buffer-p)
|
|
563 (set-buffer mh-show-buffer))
|
|
564 ((and (eq major-mode 'mh-folder-mode)
|
|
565 (mh-get-msg-num nil))
|
|
566 (set-buffer (get-buffer-create mh-temp-buffer))
|
|
567 (insert-file-contents (mh-msg-filename (mh-get-msg-num t))))
|
|
568 ((eq major-mode 'mh-folder-mode)
|
|
569 (error "Cursor not pointing to a message")))
|
|
570 (let* ((address (mh-extract-from-header-value))
|
|
571 (alias (mh-alias-suggest-alias address)))
|
|
572 (mh-alias-add-alias alias address))))
|
|
573
|
|
574 ;;;###mh-autoload
|
|
575 (defun mh-alias-add-address-under-point ()
|
|
576 "Insert an alias for email address under point."
|
|
577 (interactive)
|
|
578 (let ((address (mh-goto-address-find-address-at-point)))
|
|
579 (if address
|
|
580 (mh-alias-add-alias nil address)
|
|
581 (message "No email address found under point."))))
|
|
582
|
|
583 (provide 'mh-alias)
|
|
584
|
|
585 ;;; Local Variables:
|
|
586 ;;; indent-tabs-mode: nil
|
|
587 ;;; sentence-end-double-space: nil
|
|
588 ;;; End:
|
|
589
|
|
590 ;;; mh-alias.el ends here
|