comparison lisp/mh-e/mh-alias.el @ 50702:7dd3d5eae9c7

Upgraded to MH-E version 7.3. See etc/MH-E-NEWS and lisp/mh-e/ChangeLog for details.
author Bill Wohler <wohler@newt.com>
date Fri, 25 Apr 2003 05:52:00 +0000
parents 0d8b17d428b5
children 695cf19ef79e
comparison
equal deleted inserted replaced
50701:cb5f0a5d5b36 50702:7dd3d5eae9c7
1 ;;; mh-alias.el --- MH-E mail alias completion and expansion 1 ;;; mh-alias.el --- MH-E mail alias completion and expansion
2 ;; 2 ;;
3 ;; Copyright (C) 1994, 1995, 1996, 1997, 2001, 2002 Free Software Foundation, Inc. 3 ;; Copyright (C) 1994, 95, 96, 1997,
4 ;; 2001, 02, 2003 Free Software Foundation, Inc.
4 5
5 ;; Author: Peter S. Galbraith <psg@debian.org> 6 ;; Author: Peter S. Galbraith <psg@debian.org>
6 ;; Maintainer: Bill Wohler <wohler@newt.com> 7 ;; Maintainer: Bill Wohler <wohler@newt.com>
7 ;; Keywords: mail 8 ;; Keywords: mail
8 ;; See: mh-e.el 9 ;; See: mh-e.el
91 ;; `mh-alias-insert-file'). In particular, there is a tool-bar icon to grab 92 ;; `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 ;; an alias from the From line of the current message.
93 94
94 ;;; Change Log: 95 ;;; Change Log:
95 96
96 ;; $Id: mh-alias.el,v 1.2 2003/02/03 20:55:30 wohler Exp $
97
98 ;;; Code: 97 ;;; Code:
99 98
100 (require 'mh-e) 99 (require 'mh-e)
101 (load "cmr" t t) ; Non-fatal dependency for 100 (load "cmr" t t) ; Non-fatal dependency for
102 ; completing-read-multiple. 101 ; completing-read-multiple.
103 (eval-when-compile (defvar mail-abbrev-syntax-table)) 102 (eval-when-compile (defvar mail-abbrev-syntax-table))
104 103
105 ;;; Autoloads 104 ;;; Autoloads
106 (autoload 'mail-abbrev-complete-alias "mailabbrev") 105 (eval-when (compile load eval)
107 (autoload 'multi-prompt "multi-prompt") 106 (ignore-errors
108 107 (require 'mailabbrev)
109 (defvar mh-alias-alist nil 108 (require 'multi-prompt)))
109
110 (defvar mh-alias-alist 'not-read
110 "Alist of MH aliases.") 111 "Alist of MH aliases.")
111 (defvar mh-alias-blind-alist nil 112 (defvar mh-alias-blind-alist nil
112 "Alist of MH aliases that are blind lists.") 113 "Alist of MH aliases that are blind lists.")
113 (defvar mh-alias-passwd-alist nil 114 (defvar mh-alias-passwd-alist nil
114 "Alist of aliases extracted from passwd file and their expansions.") 115 "Alist of aliases extracted from passwd file and their expansions.")
178 ((eq mh-alias-local-users t) 179 ((eq mh-alias-local-users t)
179 (if (file-readable-p "/etc/passwd") 180 (if (file-readable-p "/etc/passwd")
180 (insert-file-contents "/etc/passwd"))) 181 (insert-file-contents "/etc/passwd")))
181 ((stringp mh-alias-local-users) 182 ((stringp mh-alias-local-users)
182 (insert mh-alias-local-users "\n") 183 (insert mh-alias-local-users "\n")
183 (shell-command-on-region (point-min)(point-max) mh-alias-local-users t) 184 (shell-command-on-region (point-min) (point-max) mh-alias-local-users t)
184 (goto-char (point-min)))) 185 (goto-char (point-min))))
185 (while (< (point) (point-max)) 186 (while (< (point) (point-max))
186 (cond 187 (cond
187 ((looking-at "\\([^:]*\\):[^:]*:\\([^:]*\\):[^:]*:\\([^:,]*\\)[:,]") 188 ((looking-at "\\([^:]*\\):[^:]*:\\([^:]*\\):[^:]*:\\([^:,]*\\)[:,]")
188 (when (> (string-to-int (match-string 2)) 200) 189 (when (> (string-to-int (match-string 2)) 200)
239 (setq local-users (cdr local-users))))) 240 (setq local-users (cdr local-users)))))
240 (message "Loading MH aliases...done")) 241 (message "Loading MH aliases...done"))
241 242
242 (defun mh-alias-reload-maybe () 243 (defun mh-alias-reload-maybe ()
243 "Load new MH aliases." 244 "Load new MH aliases."
244 (if (or (not mh-alias-alist) ; Doesn't exist, so create it. 245 (if (or (eq mh-alias-alist 'not-read) ; Doesn't exist, so create it.
245 (mh-alias-tstamp nil)) ; Out of date, so recreate it. 246 (mh-alias-tstamp nil)) ; Out of date, so recreate it.
246 (mh-alias-reload))) 247 (mh-alias-reload)))
247 248
248 249
249 ;;; Alias Expansion 250 ;;; Alias Expansion
251 (defun mh-alias-ali (alias &optional user) 252 (defun mh-alias-ali (alias &optional user)
252 "Return ali expansion for ALIAS. 253 "Return ali expansion for ALIAS.
253 ALIAS must be a string for a single alias. 254 ALIAS must be a string for a single alias.
254 If USER is t, then assume ALIAS is an address and call ali -user. 255 If USER is t, then assume ALIAS is an address and call ali -user.
255 ali returns the string unchanged if not defined. The same is done here." 256 ali returns the string unchanged if not defined. The same is done here."
256 (save-excursion 257 (condition-case err
257 (let ((user-arg (if user "-user" "-nouser"))) 258 (save-excursion
258 (mh-exec-cmd-quiet t "ali" user-arg "-nolist" alias)) 259 (let ((user-arg (if user "-user" "-nouser")))
259 (goto-char (point-max)) 260 (mh-exec-cmd-quiet t "ali" user-arg "-nolist" alias))
260 (if (looking-at "^$") (delete-backward-char 1)) 261 (goto-char (point-max))
261 (buffer-substring (point-min)(point-max)))) 262 (if (looking-at "^$") (delete-backward-char 1))
263 (buffer-substring (point-min)(point-max)))
264 (error (progn
265 (message (error-message-string err))
266 alias))))
262 267
263 (defun mh-alias-expand (alias) 268 (defun mh-alias-expand (alias)
264 "Return expansion for ALIAS. 269 "Return expansion for ALIAS.
265 Blind aliases or users from /etc/passwd are not expanded." 270 Blind aliases or users from /etc/passwd are not expanded."
266 (cond 271 (cond
278 (if (not mh-alias-alist) ; If still no aliases, just prompt 283 (if (not mh-alias-alist) ; If still no aliases, just prompt
279 (read-string prompt) 284 (read-string prompt)
280 (let* ((minibuffer-local-completion-map mh-alias-read-address-map) 285 (let* ((minibuffer-local-completion-map mh-alias-read-address-map)
281 (completion-ignore-case mh-alias-completion-ignore-case-flag) 286 (completion-ignore-case mh-alias-completion-ignore-case-flag)
282 (the-answer 287 (the-answer
283 (or (cond 288 (cond ((fboundp 'completing-read-multiple)
284 ((fboundp 'completing-read-multiple) 289 (mh-funcall-if-exists
285 (completing-read-multiple prompt mh-alias-alist nil nil)) 290 completing-read-multiple prompt mh-alias-alist nil nil))
286 ((featurep 'multi-prompt) 291 ((featurep 'multi-prompt)
287 (multi-prompt "," nil prompt mh-alias-alist nil nil)) 292 (mh-funcall-if-exists
288 (t 293 multi-prompt "," nil prompt mh-alias-alist nil nil))
289 (split-string 294 (t (split-string
290 (completing-read prompt mh-alias-alist nil nil) 295 (completing-read prompt mh-alias-alist nil nil) ",")))))
291 ","))))))
292 (if (not mh-alias-expand-aliases-flag) 296 (if (not mh-alias-expand-aliases-flag)
293 (mapconcat 'identity the-answer ", ") 297 (mapconcat 'identity the-answer ", ")
294 ;; Loop over all elements, checking if in passwd aliast or blind first 298 ;; Loop over all elements, checking if in passwd aliast or blind first
295 (mapconcat 'mh-alias-expand the-answer ",\n "))))) 299 (mapconcat 'mh-alias-expand the-answer ",\n ")))))
296 300
323 (if (and (equal mh-alias-flash-on-comma 1) 327 (if (and (equal mh-alias-flash-on-comma 1)
324 (not (string-match " " the-name))) 328 (not (string-match " " the-name)))
325 (message "No alias for %s" the-name)))))) 329 (message "No alias for %s" the-name))))))
326 (self-insert-command 1)) 330 (self-insert-command 1))
327 331
332 (mh-do-in-xemacs (defvar mail-abbrevs))
333
328 ;;;###mh-autoload 334 ;;;###mh-autoload
329 (defun mh-alias-letter-expand-alias () 335 (defun mh-alias-letter-expand-alias ()
330 "Expand mail alias before point." 336 "Expand mail alias before point."
331 (mh-alias-reload-maybe) 337 (mh-alias-reload-maybe)
332 (let ((mail-abbrevs mh-alias-alist)) 338 (let ((mail-abbrevs mh-alias-alist))
333 (mail-abbrev-complete-alias)) 339 (mh-funcall-if-exists mail-abbrev-complete-alias))
334 (when mh-alias-expand-aliases-flag 340 (when mh-alias-expand-aliases-flag
335 (let* ((end (point)) 341 (let* ((end (point))
336 (syntax-table (syntax-table)) 342 (syntax-table (syntax-table))
337 (beg (unwind-protect 343 (beg (unwind-protect
338 (save-excursion 344 (save-excursion
348 ;;; Adding addresses to alias file. 354 ;;; Adding addresses to alias file.
349 355
350 (defun mh-alias-suggest-alias (string) 356 (defun mh-alias-suggest-alias (string)
351 "Suggest an alias for STRING." 357 "Suggest an alias for STRING."
352 (cond 358 (cond
359 ((string-match "^<\\(.*\\)>$" string)
360 ;; <somename@foo.bar> -> recurse, stripping brackets.
361 (mh-alias-suggest-alias (match-string 1 string)))
353 ((string-match "^\\sw+$" string) 362 ((string-match "^\\sw+$" string)
354 ;; One word -> downcase it. 363 ;; One word -> downcase it.
355 (downcase string)) 364 (downcase string))
356 ((string-match "^\\(\\sw+\\)\\s-+\\(\\sw+\\)$" string) 365 ((string-match "^\\(\\sw+\\)\\s-+\\(\\sw+\\)$" string)
357 ;; Two words -> first.last 366 ;; Two words -> first.last
387 ;; Reverse order of comma-separated fields 396 ;; Reverse order of comma-separated fields
388 (mh-alias-suggest-alias 397 (mh-alias-suggest-alias
389 (format "%s %s" (match-string 2 string) (match-string 1 string)))) 398 (format "%s %s" (match-string 2 string) (match-string 1 string))))
390 (t 399 (t
391 ;; Output string, with spaces replaced by dots. 400 ;; Output string, with spaces replaced by dots.
392 (downcase (replace-regexp-in-string 401 (mh-alias-canonicalize-suggestion string))))
393 "\\.\\.+" "." 402
394 (replace-regexp-in-string " +" "." string)))))) 403 (defun mh-alias-canonicalize-suggestion (string)
404 "Process STRING to replace spacess by periods.
405 First all spaces are replaced by periods. Then every run of consecutive periods
406 are replaced with a single period. Finally the string is converted to lower
407 case."
408 (with-temp-buffer
409 (insert string)
410 ;; Replace spaces with periods
411 (goto-char (point-min))
412 (replace-regexp " +" ".")
413 ;; Replace consecutive periods with a single period
414 (goto-char (point-min))
415 (replace-regexp "\\.\\.+" ".")
416 ;; Convert to lower case
417 (downcase-region (point-min) (point-max))
418 ;; Whew! all done...
419 (buffer-string)))
395 420
396 (defun mh-alias-which-file-has-alias (alias file-list) 421 (defun mh-alias-which-file-has-alias (alias file-list)
397 "Return the name of writable file which defines ALIAS from list FILE-LIST." 422 "Return the name of writable file which defines ALIAS from list FILE-LIST."
398 (save-excursion 423 (save-excursion
399 (set-buffer (get-buffer-create mh-temp-buffer)) 424 (set-buffer (get-buffer-create mh-temp-buffer))
401 (found)) 426 (found))
402 (while the-list 427 (while the-list
403 (erase-buffer) 428 (erase-buffer)
404 (when (file-writable-p (car file-list)) 429 (when (file-writable-p (car file-list))
405 (insert-file-contents (car file-list)) 430 (insert-file-contents (car file-list))
406 (if (re-search-forward (concat "^" (regexp-quote alias) ":")) 431 (if (re-search-forward (concat "^" (regexp-quote alias) ":") nil t)
407 (setq found (car file-list) 432 (setq found (car file-list)
408 the-list nil) 433 the-list nil)
409 (setq the-list (cdr the-list))))) 434 (setq the-list (cdr the-list)))))
410 found))) 435 found)))
411 436
468 alias)))) 493 alias))))
469 (split-string aliases ", +"))))))) 494 (split-string aliases ", +")))))))
470 495
471 ;;;###mh-autoload 496 ;;;###mh-autoload
472 (defun mh-alias-from-has-no-alias-p () 497 (defun mh-alias-from-has-no-alias-p ()
473 "Return t is From has no current alias set." 498 "Return t is From has no current alias set.
499 In the exceptional situation where there isn't a From header in the message the
500 function returns nil."
474 (mh-alias-reload-maybe) 501 (mh-alias-reload-maybe)
475 (save-excursion 502 (save-excursion
476 (if (not (mh-folder-line-matches-show-buffer-p)) 503 (if (not (mh-folder-line-matches-show-buffer-p))
477 nil ;No corresponding show buffer 504 nil ;No corresponding show buffer
478 (if (eq major-mode 'mh-folder-mode) 505 (if (eq major-mode 'mh-folder-mode)
479 (set-buffer mh-show-buffer)) 506 (set-buffer mh-show-buffer))
480 (not (mh-alias-address-to-alias (mh-extract-from-header-value)))))) 507 (let ((from-header (mh-extract-from-header-value)))
508 (and from-header
509 (not (mh-alias-address-to-alias from-header)))))))
481 510
482 (defun mh-alias-add-alias-to-file (alias address &optional file) 511 (defun mh-alias-add-alias-to-file (alias address &optional file)
483 "Add ALIAS for ADDRESS in alias FILE without alias check or prompts. 512 "Add ALIAS for ADDRESS in alias FILE without alias check or prompts.
484 Prompt for alias file if not provided and there is more than one candidate. 513 Prompt for alias file if not provided and there is more than one candidate.
485 If ALIAS matches exactly, prompt to [i]nsert before old value or [a]ppend 514 If ALIAS matches exactly, prompt to [i]nsert before old value or [a]ppend
489 (save-excursion 518 (save-excursion
490 (set-buffer (find-file-noselect file)) 519 (set-buffer (find-file-noselect file))
491 (goto-char (point-min)) 520 (goto-char (point-min))
492 (let ((alias-search (concat alias ":")) 521 (let ((alias-search (concat alias ":"))
493 (letter) 522 (letter)
494 (here (point))
495 (case-fold-search t)) 523 (case-fold-search t))
496 (cond 524 (cond
497 ;; Search for exact match (if we had the same alias before) 525 ;; Search for exact match (if we had the same alias before)
498 ((re-search-forward 526 ((re-search-forward
499 (concat "^" (regexp-quote alias-search) " *\\(.*\\)") nil t) 527 (concat "^" (regexp-quote alias-search) " *\\(.*\\)") nil t)
536 Prompts for confirmation if the address already has an alias. 564 Prompts for confirmation if the address already has an alias.
537 If the alias is already is use, `mh-alias-add-alias-to-file' will prompt." 565 If the alias is already is use, `mh-alias-add-alias-to-file' will prompt."
538 (interactive "P\nP") 566 (interactive "P\nP")
539 (mh-alias-reload-maybe) 567 (mh-alias-reload-maybe)
540 (setq alias (completing-read "Alias: " mh-alias-alist nil nil alias)) 568 (setq alias (completing-read "Alias: " mh-alias-alist nil nil alias))
569 (if (and address (string-match "^<\\(.*\\)>$" address))
570 (setq address (match-string 1 address)))
541 (setq address (read-string "Address: " address)) 571 (setq address (read-string "Address: " address))
572 (if (string-match "^<\\(.*\\)>$" address)
573 (setq address (match-string 1 address)))
542 (let ((address-alias (mh-alias-address-to-alias address)) 574 (let ((address-alias (mh-alias-address-to-alias address))
543 (alias-address (mh-alias-expand alias))) 575 (alias-address (mh-alias-expand alias)))
544 (if (string-equal alias-address alias) 576 (if (string-equal alias-address alias)
545 (setq alias-address nil)) 577 (setq alias-address nil))
546 (cond 578 (cond
569 (mh-get-msg-num nil)) 601 (mh-get-msg-num nil))
570 (set-buffer (get-buffer-create mh-temp-buffer)) 602 (set-buffer (get-buffer-create mh-temp-buffer))
571 (insert-file-contents (mh-msg-filename (mh-get-msg-num t)))) 603 (insert-file-contents (mh-msg-filename (mh-get-msg-num t))))
572 ((eq major-mode 'mh-folder-mode) 604 ((eq major-mode 'mh-folder-mode)
573 (error "Cursor not pointing to a message"))) 605 (error "Cursor not pointing to a message")))
574 (let* ((address (mh-extract-from-header-value)) 606 (let* ((address (or (mh-extract-from-header-value)
607 (error "Message has no From: header")))
575 (alias (mh-alias-suggest-alias address))) 608 (alias (mh-alias-suggest-alias address)))
576 (mh-alias-add-alias alias address)))) 609 (mh-alias-add-alias alias address))))
577 610
578 ;;;###mh-autoload 611 ;;;###mh-autoload
579 (defun mh-alias-add-address-under-point () 612 (defun mh-alias-add-address-under-point ()