comparison lisp/mh-e/mh-alias.el @ 56406:d36b00b98db0

Upgraded to MH-E version 7.4.4. See etc/MH-E-NEWS and lisp/mh-e/ChangeLog for details.
author Bill Wohler <wohler@newt.com>
date Tue, 13 Jul 2004 03:06:25 +0000
parents 695cf19ef79e
children e9a6cbc8ca5e 97905c4f1a42
comparison
equal deleted inserted replaced
56405:10b68aa88abe 56406:d36b00b98db0
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, 95, 96, 1997, 3 ;; Copyright (C) 1994, 95, 96, 1997,
4 ;; 2001, 02, 2003 Free Software Foundation, Inc. 4 ;; 2001, 02, 03, 2004 Free Software Foundation, Inc.
5 5
6 ;; Author: Peter S. Galbraith <psg@debian.org> 6 ;; Author: Peter S. Galbraith <psg@debian.org>
7 ;; Maintainer: Bill Wohler <wohler@newt.com> 7 ;; Maintainer: Bill Wohler <wohler@newt.com>
8 ;; Keywords: mail 8 ;; Keywords: mail
9 ;; See: mh-e.el 9 ;; See: mh-e.el
125 "," 'mh-alias-minibuffer-confirm-address)) 125 "," 'mh-alias-minibuffer-confirm-address))
126 (define-key mh-alias-read-address-map " " 'self-insert-command)) 126 (define-key mh-alias-read-address-map " " 'self-insert-command))
127 127
128 128
129 ;;; Alias Loading 129 ;;; Alias Loading
130
131 (defmacro mh-assoc-ignore-case (key alist)
132 "Search for string KEY in ALIST.
133 This is a wrapper around `assoc-string' or `assoc-ignore-case'. Avoid
134 `assoc-ignore-case' which is now an obsolete function."
135 (cond ((fboundp 'assoc-string) `(assoc-string ,key ,alist t))
136 ((fboundp 'assoc-ignore-case) `(assoc-ignore-case ,key ,alist))
137 (t (error "The macro mh-assoc-ignore-case not implemented properly"))))
130 138
131 (defun mh-alias-tstamp (arg) 139 (defun mh-alias-tstamp (arg)
132 "Check whether alias files have been modified. 140 "Check whether alias files have been modified.
133 Return t if any file listed in the MH profile component Aliasfile has been 141 Return t if any file listed in the MH profile component Aliasfile has been
134 modified since the timestamp. 142 modified since the timestamp.
167 (if (stringp mh-alias-system-aliases) 175 (if (stringp mh-alias-system-aliases)
168 (append userlist (list mh-alias-system-aliases)) 176 (append userlist (list mh-alias-system-aliases))
169 (append userlist mh-alias-system-aliases)) 177 (append userlist mh-alias-system-aliases))
170 userlist)))) 178 userlist))))
171 179
180 (defun mh-alias-gecos-name (gecos-name username comma-separator)
181 "Return a usable address string from a GECOS-NAME and USERNAME.
182 Use only part of the GECOS-NAME up to the first comma if COMMA-SEPARATOR is
183 non-nil."
184 (let ((res gecos-name))
185 ;; Keep only string until first comma if COMMA-SEPARATOR is t.
186 (if (and comma-separator
187 (string-match "^\\([^,]+\\)," res))
188 (setq res (match-string 1 res)))
189 ;; Replace "&" with capitalized username
190 (if (string-match "&" res)
191 (setq res (mh-replace-in-string "&" (capitalize username) res)))
192 ;; Remove " character
193 (if (string-match "\"" res)
194 (setq res (mh-replace-in-string "\"" "" res)))
195 ;; If empty string, use username instead
196 (if (string-equal "" res)
197 (setq res username))
198 ;; Surround by quotes if doesn't consist of simple characters
199 (if (not (string-match "^[ a-zA-Z0-9-]+$" res))
200 (setq res (concat "\"" res "\"")))
201 res))
202
172 (defun mh-alias-local-users () 203 (defun mh-alias-local-users ()
173 "Return an alist of local users from /etc/passwd." 204 "Return an alist of local users from /etc/passwd."
174 (let (passwd-alist) 205 (let (passwd-alist)
175 (save-excursion 206 (save-excursion
176 (set-buffer (get-buffer-create mh-temp-buffer)) 207 (set-buffer (get-buffer-create mh-temp-buffer))
183 (insert mh-alias-local-users "\n") 214 (insert mh-alias-local-users "\n")
184 (shell-command-on-region (point-min) (point-max) mh-alias-local-users t) 215 (shell-command-on-region (point-min) (point-max) mh-alias-local-users t)
185 (goto-char (point-min)))) 216 (goto-char (point-min))))
186 (while (< (point) (point-max)) 217 (while (< (point) (point-max))
187 (cond 218 (cond
188 ((looking-at "\\([^:]*\\):[^:]*:\\([^:]*\\):[^:]*:\\([^:,]*\\)[:,]") 219 ((looking-at "\\([^:]*\\):[^:]*:\\([^:]*\\):[^:]*:\\([^:]*\\):")
189 (when (> (string-to-int (match-string 2)) 200) 220 (when (> (string-to-int (match-string 2)) 200)
190 (let* ((username (match-string 1)) 221 (let* ((username (match-string 1))
191 (gecos-name (match-string 3)) 222 (gecos-name (match-string 3))
192 (realname 223 (realname (mh-alias-gecos-name
193 (if (string-match "&" gecos-name) 224 gecos-name username
194 (concat 225 mh-alias-passwd-gecos-comma-separator-flag)))
195 (substring gecos-name 0 (match-beginning 0))
196 (capitalize username)
197 (substring gecos-name (match-end 0)))
198 gecos-name)))
199 (setq passwd-alist 226 (setq passwd-alist
200 (cons (list username 227 (cons
201 (if (string-equal "" realname) 228 (list (if mh-alias-local-users-prefix
202 (concat "<" username ">") 229 (concat mh-alias-local-users-prefix
203 (concat realname " <" username ">"))) 230 (mh-alias-suggest-alias realname t))
204 passwd-alist)))))) 231 username)
232 (if (string-equal username realname)
233 (concat "<" username ">")
234 (concat realname " <" username ">")))
235 passwd-alist))))))
205 (forward-line 1))) 236 (forward-line 1)))
206 passwd-alist)) 237 passwd-alist))
207 238
208 ;;;###mh-autoload 239 ;;;###mh-autoload
209 (defun mh-alias-reload () 240 (defun mh-alias-reload ()
217 (setq mh-alias-blind-alist nil) 248 (setq mh-alias-blind-alist nil)
218 (while (< (point) (point-max)) 249 (while (< (point) (point-max))
219 (cond 250 (cond
220 ((looking-at "^[ \t]")) ;Continuation line 251 ((looking-at "^[ \t]")) ;Continuation line
221 ((looking-at "\\(.+\\): .+: .*$") ; A new -blind- MH alias 252 ((looking-at "\\(.+\\): .+: .*$") ; A new -blind- MH alias
222 (when (not (assoc-ignore-case (match-string 1) mh-alias-blind-alist)) 253 (when (not (mh-assoc-ignore-case (match-string 1) mh-alias-blind-alist))
223 (setq mh-alias-blind-alist 254 (setq mh-alias-blind-alist
224 (cons (list (match-string 1)) mh-alias-blind-alist)) 255 (cons (list (match-string 1)) mh-alias-blind-alist))
225 (setq mh-alias-alist (cons (list (match-string 1)) mh-alias-alist)))) 256 (setq mh-alias-alist (cons (list (match-string 1)) mh-alias-alist))))
226 ((looking-at "\\(.+\\): .*$") ; A new MH alias 257 ((looking-at "\\(.+\\): .*$") ; A new MH alias
227 (when (not (assoc-ignore-case (match-string 1) mh-alias-alist)) 258 (when (not (mh-assoc-ignore-case (match-string 1) mh-alias-alist))
228 (setq mh-alias-alist 259 (setq mh-alias-alist
229 (cons (list (match-string 1)) mh-alias-alist))))) 260 (cons (list (match-string 1)) mh-alias-alist)))))
230 (forward-line 1))) 261 (forward-line 1)))
231 (when mh-alias-local-users 262 (when mh-alias-local-users
232 (setq mh-alias-passwd-alist (mh-alias-local-users)) 263 (setq mh-alias-passwd-alist (mh-alias-local-users))
233 ;; Update aliases with local users, but leave existing aliases alone. 264 ;; Update aliases with local users, but leave existing aliases alone.
234 (let ((local-users mh-alias-passwd-alist) 265 (let ((local-users mh-alias-passwd-alist)
235 user) 266 user)
236 (while local-users 267 (while local-users
237 (setq user (car local-users)) 268 (setq user (car local-users))
238 (if (not (assoc-ignore-case (car user) mh-alias-alist)) 269 (if (not (mh-assoc-ignore-case (car user) mh-alias-alist))
239 (setq mh-alias-alist (append mh-alias-alist (list user)))) 270 (setq mh-alias-alist (append mh-alias-alist (list user))))
240 (setq local-users (cdr local-users))))) 271 (setq local-users (cdr local-users)))))
241 (message "Loading MH aliases...done")) 272 (message "Loading MH aliases...done"))
242 273
274 ;;;###mh-autoload
243 (defun mh-alias-reload-maybe () 275 (defun mh-alias-reload-maybe ()
244 "Load new MH aliases." 276 "Load new MH aliases."
245 (if (or (eq mh-alias-alist 'not-read) ; Doesn't exist, so create it. 277 (if (or (eq mh-alias-alist 'not-read) ; Doesn't exist, so create it.
246 (mh-alias-tstamp nil)) ; Out of date, so recreate it. 278 (mh-alias-tstamp nil)) ; Out of date, so recreate it.
247 (mh-alias-reload))) 279 (mh-alias-reload)))
267 299
268 (defun mh-alias-expand (alias) 300 (defun mh-alias-expand (alias)
269 "Return expansion for ALIAS. 301 "Return expansion for ALIAS.
270 Blind aliases or users from /etc/passwd are not expanded." 302 Blind aliases or users from /etc/passwd are not expanded."
271 (cond 303 (cond
272 ((assoc-ignore-case alias mh-alias-blind-alist) 304 ((mh-assoc-ignore-case alias mh-alias-blind-alist)
273 alias) ; Don't expand a blind alias 305 alias) ; Don't expand a blind alias
274 ((assoc-ignore-case alias mh-alias-passwd-alist) 306 ((mh-assoc-ignore-case alias mh-alias-passwd-alist)
275 (cadr (assoc-ignore-case alias mh-alias-passwd-alist))) 307 (cadr (mh-assoc-ignore-case alias mh-alias-passwd-alist)))
276 (t 308 (t
277 (mh-alias-ali alias)))) 309 (mh-alias-ali alias))))
278 310
279 ;;;###mh-autoload 311 ;;;###mh-autoload
280 (defun mh-read-address (prompt) 312 (defun mh-read-address (prompt)
300 332
301 ;;;###mh-autoload 333 ;;;###mh-autoload
302 (defun mh-alias-minibuffer-confirm-address () 334 (defun mh-alias-minibuffer-confirm-address ()
303 "Display the alias expansion if `mh-alias-flash-on-comma' is non-nil." 335 "Display the alias expansion if `mh-alias-flash-on-comma' is non-nil."
304 (interactive) 336 (interactive)
305 (if (not mh-alias-flash-on-comma) 337 (when mh-alias-flash-on-comma
306 ()
307 (save-excursion 338 (save-excursion
308 (let* ((case-fold-search t) 339 (let* ((case-fold-search t)
309 (the-name (buffer-substring 340 (beg (mh-beginning-of-word))
310 (progn (skip-chars-backward " \t")(point)) 341 (the-name (buffer-substring-no-properties beg (point))))
311 ;; This moves over to previous comma, if any 342 (if (mh-assoc-ignore-case the-name mh-alias-alist)
312 (progn (or (and (not (= 0 (skip-chars-backward "^,")))
313 ;; the skips over leading whitespace
314 (skip-chars-forward " "))
315 ;; no comma, then to beginning of word
316 (skip-chars-backward "^ \t"))
317 ;; In Emacs21, the beginning of the prompt
318 ;; line is accessible, which wasn't the case
319 ;; in emacs20. Skip over it.
320 (if (looking-at "^[^ \t]+:")
321 (skip-chars-forward "^ \t"))
322 (skip-chars-forward " ")
323 (point)))))
324 (if (assoc-ignore-case the-name mh-alias-alist)
325 (message "%s -> %s" the-name (mh-alias-expand the-name)) 343 (message "%s -> %s" the-name (mh-alias-expand the-name))
326 ;; Check if if was a single word likely to be an alias 344 ;; Check if if was a single word likely to be an alias
327 (if (and (equal mh-alias-flash-on-comma 1) 345 (if (and (equal mh-alias-flash-on-comma 1)
328 (not (string-match " " the-name))) 346 (not (string-match " " the-name)))
329 (message "No alias for %s" the-name)))))) 347 (message "No alias for %s" the-name))))))
333 351
334 ;;;###mh-autoload 352 ;;;###mh-autoload
335 (defun mh-alias-letter-expand-alias () 353 (defun mh-alias-letter-expand-alias ()
336 "Expand mail alias before point." 354 "Expand mail alias before point."
337 (mh-alias-reload-maybe) 355 (mh-alias-reload-maybe)
338 (let ((mail-abbrevs mh-alias-alist)) 356 (let* ((end (point))
339 (mh-funcall-if-exists mail-abbrev-complete-alias)) 357 (begin (mh-beginning-of-word))
340 (when mh-alias-expand-aliases-flag 358 (input (buffer-substring-no-properties begin end)))
341 (let* ((end (point)) 359 (mh-complete-word input mh-alias-alist begin end)
342 (syntax-table (syntax-table)) 360 (when mh-alias-expand-aliases-flag
343 (beg (unwind-protect 361 (let* ((end (point))
344 (save-excursion 362 (expansion (mh-alias-expand (buffer-substring begin end))))
345 (set-syntax-table mail-abbrev-syntax-table) 363 (delete-region begin end)
346 (backward-word 1) 364 (insert expansion)))))
347 (point))
348 (set-syntax-table syntax-table)))
349 (alias (buffer-substring beg end))
350 (expansion (mh-alias-expand alias)))
351 (delete-region beg end)
352 (insert expansion))))
353 365
354 ;;; Adding addresses to alias file. 366 ;;; Adding addresses to alias file.
355 367
356 (defun mh-alias-suggest-alias (string) 368 (defun mh-alias-suggest-alias (string &optional no-comma-swap)
357 "Suggest an alias for STRING." 369 "Suggest an alias for STRING.
370 Don't reverse the order of strings separated by a comma if NO-COMMA-SWAP is
371 non-nil."
358 (cond 372 (cond
359 ((string-match "^<\\(.*\\)>$" string) 373 ((string-match "^<\\(.*\\)>$" string)
360 ;; <somename@foo.bar> -> recurse, stripping brackets. 374 ;; <somename@foo.bar> -> recurse, stripping brackets.
361 (mh-alias-suggest-alias (match-string 1 string))) 375 (mh-alias-suggest-alias (match-string 1 string) no-comma-swap))
362 ((string-match "^\\sw+$" string) 376 ((string-match "^\\sw+$" string)
363 ;; One word -> downcase it. 377 ;; One word -> downcase it.
364 (downcase string)) 378 (downcase string))
365 ((string-match "^\\(\\sw+\\)\\s-+\\(\\sw+\\)$" string) 379 ((string-match "^\\(\\sw+\\)\\s-+\\(\\sw+\\)$" string)
366 ;; Two words -> first.last 380 ;; Two words -> first.last
370 string) 384 string)
371 ;; email only -> downcase username 385 ;; email only -> downcase username
372 (downcase (match-string 1 string))) 386 (downcase (match-string 1 string)))
373 ((string-match "^\"\\(.*\\)\".*" string) 387 ((string-match "^\"\\(.*\\)\".*" string)
374 ;; "Some name" <somename@foo.bar> -> recurse -> "Some name" 388 ;; "Some name" <somename@foo.bar> -> recurse -> "Some name"
375 (mh-alias-suggest-alias (match-string 1 string))) 389 (mh-alias-suggest-alias (match-string 1 string) no-comma-swap))
376 ((string-match "^\\(.*\\) +<.*>$" string) 390 ((string-match "^\\(.*\\) +<.*>$" string)
377 ;; Some name <somename@foo.bar> -> recurse -> Some name 391 ;; Some name <somename@foo.bar> -> recurse -> Some name
378 (mh-alias-suggest-alias (match-string 1 string))) 392 (mh-alias-suggest-alias (match-string 1 string) no-comma-swap))
379 ((string-match (concat mh-address-mail-regexp " +(\\(.*\\))$") string) 393 ((string-match (concat mh-address-mail-regexp " +(\\(.*\\))$") string)
380 ;; somename@foo.bar (Some name) -> recurse -> Some name 394 ;; somename@foo.bar (Some name) -> recurse -> Some name
381 (mh-alias-suggest-alias (match-string 1 string))) 395 (mh-alias-suggest-alias (match-string 1 string) no-comma-swap))
382 ((string-match "^\\(Dr\\|Prof\\)\\.? +\\(.*\\)" string) 396 ((string-match "^\\(Dr\\|Prof\\)\\.? +\\(.*\\)" string)
383 ;; Strip out title 397 ;; Strip out title
384 (mh-alias-suggest-alias (match-string 2 string))) 398 (mh-alias-suggest-alias (match-string 2 string) no-comma-swap))
385 ((string-match "^\\(.*\\), +\\(Jr\\.?\\|II+\\)$" string) 399 ((string-match "^\\(.*\\), +\\(Jr\\.?\\|II+\\)$" string)
386 ;; Strip out tails with comma 400 ;; Strip out tails with comma
387 (mh-alias-suggest-alias (match-string 1 string))) 401 (mh-alias-suggest-alias (match-string 1 string) no-comma-swap))
388 ((string-match "^\\(.*\\) +\\(Jr\\.?\\|II+\\)$" string) 402 ((string-match "^\\(.*\\) +\\(Jr\\.?\\|II+\\)$" string)
389 ;; Strip out tails 403 ;; Strip out tails
390 (mh-alias-suggest-alias (match-string 1 string))) 404 (mh-alias-suggest-alias (match-string 1 string) no-comma-swap))
391 ((string-match "^\\(\\sw+\\) +[A-Z]\\.? +\\(.*\\)$" string) 405 ((string-match "^\\(\\sw+\\) +[A-Z]\\.? +\\(.*\\)$" string)
392 ;; Strip out initials 406 ;; Strip out initials
393 (mh-alias-suggest-alias 407 (mh-alias-suggest-alias
394 (format "%s %s" (match-string 1 string) (match-string 2 string)))) 408 (format "%s %s" (match-string 1 string) (match-string 2 string))
395 ((string-match "^\\([^,]+\\), +\\(.*\\)$" string) 409 no-comma-swap))
396 ;; Reverse order of comma-separated fields 410 ((and (not no-comma-swap)
411 (string-match "^\\([^,]+\\), +\\(.*\\)$" string))
412 ;; Reverse order of comma-separated fields to handle:
413 ;; From: "Galbraith, Peter" <psg@debian.org>
414 ;; but don't this for a name string extracted from the passwd file
415 ;; with mh-alias-passwd-gecos-comma-separator-flag set to nil.
397 (mh-alias-suggest-alias 416 (mh-alias-suggest-alias
398 (format "%s %s" (match-string 2 string) (match-string 1 string)))) 417 (format "%s %s" (match-string 2 string) (match-string 1 string))
418 no-comma-swap))
399 (t 419 (t
400 ;; Output string, with spaces replaced by dots. 420 ;; Output string, with spaces replaced by dots.
401 (mh-alias-canonicalize-suggestion string)))) 421 (mh-alias-canonicalize-suggestion string))))
402 422
403 (defun mh-alias-canonicalize-suggestion (string) 423 (defun mh-alias-canonicalize-suggestion (string)
404 "Process STRING to replace spacess by periods. 424 "Process STRING to replace spaces by periods.
405 First all spaces are replaced by periods. Then every run of consecutive periods 425 First all spaces and commas are replaced by periods. Then every run of
406 are replaced with a single period. Finally the string is converted to lower 426 consecutive periods are replaced with a single period. Finally the string
407 case." 427 is converted to lower case."
408 (with-temp-buffer 428 (with-temp-buffer
409 (insert string) 429 (insert string)
410 ;; Replace spaces with periods 430 ;; Replace spaces with periods
411 (goto-char (point-min)) 431 (goto-char (point-min))
412 (replace-regexp " +" ".") 432 (while (re-search-forward " +" nil t)
433 (replace-match "." nil nil))
434 ;; Replace commas with periods
435 (goto-char (point-min))
436 (while (re-search-forward ",+" nil t)
437 (replace-match "." nil nil))
413 ;; Replace consecutive periods with a single period 438 ;; Replace consecutive periods with a single period
414 (goto-char (point-min)) 439 (goto-char (point-min))
415 (replace-regexp "\\.\\.+" ".") 440 (while (re-search-forward "\\.\\.+" nil t)
441 (replace-match "." nil nil))
416 ;; Convert to lower case 442 ;; Convert to lower case
417 (downcase-region (point-min) (point-max)) 443 (downcase-region (point-min) (point-max))
418 ;; Whew! all done... 444 ;; Whew! all done...
419 (buffer-string))) 445 (buffer-string)))
420 446
615 (let ((address (mh-goto-address-find-address-at-point))) 641 (let ((address (mh-goto-address-find-address-at-point)))
616 (if address 642 (if address
617 (mh-alias-add-alias nil address) 643 (mh-alias-add-alias nil address)
618 (message "No email address found under point.")))) 644 (message "No email address found under point."))))
619 645
646 ;;;###mh-autoload
647 (defun mh-alias-apropos (regexp)
648 "Show all aliases that match REGEXP either in name or content."
649 (interactive "sAlias regexp: ")
650 (if mh-alias-local-users
651 (mh-alias-reload-maybe))
652 (let ((matches "")(group-matches "")(passwd-matches))
653 (save-excursion
654 (message "Reading MH aliases...")
655 (mh-exec-cmd-quiet t "ali" "-nolist" "-nouser")
656 (message "Reading MH aliases...done. Parsing...")
657 (while (re-search-forward regexp nil t)
658 (beginning-of-line)
659 (cond
660 ((looking-at "^[ \t]") ;Continuation line
661 (setq group-matches
662 (concat group-matches
663 (buffer-substring
664 (save-excursion
665 (or (re-search-backward "^[^ \t]" nil t)
666 (point)))
667 (progn
668 (if (re-search-forward "^[^ \t]" nil t)
669 (forward-char -1))
670 (point))))))
671 (t
672 (setq matches
673 (concat matches
674 (buffer-substring (point)(progn (end-of-line)(point)))
675 "\n")))))
676 (message "Reading MH aliases...done. Parsing...done.")
677 (when mh-alias-local-users
678 (message
679 "Reading MH aliases...done. Parsing...done. Passwd aliases...")
680 (setq passwd-matches
681 (mapconcat
682 '(lambda (elem)
683 (if (or (string-match regexp (car elem))
684 (string-match regexp (cadr elem)))
685 (format "%s: %s\n" (car elem) (cadr elem))))
686 mh-alias-passwd-alist ""))
687 (message
688 "Reading MH aliases...done. Parsing...done. Passwd aliases...done.")))
689 (if (and (string-equal "" matches)
690 (string-equal "" group-matches)
691 (string-equal "" passwd-matches))
692 (message "No matches")
693 (with-output-to-temp-buffer "*Help*"
694 (if (not (string-equal "" matches))
695 (princ matches))
696 (when (not (string-equal group-matches ""))
697 (princ "\nGroup Aliases:\n\n")
698 (princ group-matches))
699 (when (not (string-equal passwd-matches ""))
700 (princ "\nLocal User Aliases:\n\n")
701 (princ passwd-matches))))))
702
620 (provide 'mh-alias) 703 (provide 'mh-alias)
621 704
622 ;;; Local Variables: 705 ;;; Local Variables:
623 ;;; indent-tabs-mode: nil 706 ;;; indent-tabs-mode: nil
624 ;;; sentence-end-double-space: nil 707 ;;; sentence-end-double-space: nil