1 ;;; wl-address.el -- Tiny address management for Wanderlust.
3 ;; Copyright 1998,1999,2000 Yuuichi Teranishi <teranisi@gohome.org>
5 ;; Author: Yuuichi Teranishi <teranisi@gohome.org>
6 ;; Keywords: mail, net news
7 ;; Time-stamp: <2000-04-10 09:29:44 teranisi>
9 ;; This file is part of Wanderlust (Yet Another Message Interface on Emacsen).
11 ;; This program is free software; you can redistribute it and/or modify
12 ;; it under the terms of the GNU General Public License as published by
13 ;; the Free Software Foundation; either version 2, or (at your option)
16 ;; This program is distributed in the hope that it will be useful,
17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 ;; GNU General Public License for more details.
21 ;; You should have received a copy of the GNU General Public License
22 ;; along with GNU Emacs; see the file COPYING. If not, write to the
23 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24 ;; Boston, MA 02111-1307, USA.
36 (defvar wl-address-complete-header-regexp "^\\(To\\|From\\|Cc\\|Bcc\\|Mail-Followup-To\\|Reply-To\\|Return-Receipt-To\\):")
37 (defvar wl-newsgroups-complete-header-regexp "^\\(Newsgroups\\|Followup-To\\):")
38 (defvar wl-folder-complete-header-regexp "^\\(FCC\\):")
39 (defvar wl-address-list nil)
40 (defvar wl-address-completion-list nil)
41 (defvar wl-address-petname-hash nil)
43 (defun wl-complete-field-to ()
45 (let ((cl wl-address-completion-list))
47 (completing-read "To: " cl)
48 (read-string "To: "))))
50 (defun wl-complete-field-body-or-tab ()
52 (let ((case-fold-search t)
55 (if (wl-draft-on-field-p)
60 (goto-char (point-min))
61 (search-forward (concat "\n" mail-header-separator "\n") nil 0)
65 (while (and (looking-at "^[ \t]")
66 (not (= (point) (point-min))))
68 (cond ((looking-at wl-address-complete-header-regexp)
69 (setq completion-list wl-address-completion-list)
71 ((looking-at wl-folder-complete-header-regexp)
72 (setq completion-list wl-folder-entity-hashtb)
73 (setq skip-chars "^, "))
74 ((looking-at wl-newsgroups-complete-header-regexp)
75 (setq completion-list wl-folder-newsgroups-hashtb)))))
76 (wl-complete-field-body completion-list
77 epand-char skip-chars)
78 (indent-for-tab-command)))))
80 (defvar wl-completion-buf-name "*Completions*")
82 (defvar wl-complete-candidates nil)
84 (defun wl-complete-window-show (all)
85 (if (and (get-buffer-window wl-completion-buf-name)
86 (equal wl-complete-candidates all))
87 (let ((win (get-buffer-window wl-completion-buf-name)))
89 (set-buffer wl-completion-buf-name)
90 (if (pos-visible-in-window-p (point-max) win)
91 (set-window-start win 1)
92 (scroll-other-window))))
93 (message "Making completion list...")
94 (setq wl-complete-candidates all)
95 (with-output-to-temp-buffer
96 wl-completion-buf-name
97 (display-completion-list all))
98 (message "Making completion list... done")))
100 (defun wl-complete-window-delete ()
101 (let (comp-buf comp-win)
102 (if (setq comp-buf (get-buffer wl-completion-buf-name))
103 (if (setq comp-win (get-buffer-window comp-buf))
104 (delete-window comp-win)))))
106 (defun wl-complete-field ()
109 (start (save-excursion
110 (skip-chars-backward "_a-zA-Z0-9+@%.!\\-")
113 (pattern (buffer-substring start end))
114 (cl wl-draft-field-completion-list))
118 (let ((completion-ignore-case t))
119 (try-completion pattern cl)))
120 (cond ((eq completion t)
121 (let ((alias (assoc pattern cl)))
124 (delete-region start end)
126 ; (wl-highlight-message (point-min)(point-max) t)
128 (wl-complete-window-delete))
130 (message "Can't find completion for \"%s\"" pattern)
132 ((not (string= pattern completion))
133 (delete-region start end)
135 (wl-complete-window-delete)
136 (wl-highlight-message (point-min)(point-max) t))
138 (let ((list (all-completions pattern cl)))
139 (wl-complete-window-show list)))))))
141 (defun wl-complete-insert (start end pattern completion-list)
142 (let ((alias (and (consp completion-list)
143 (assoc pattern completion-list)))
147 (delete-region start end)
149 (if (setq comp-buf (get-buffer wl-completion-buf-name))
150 (if (setq comp-win (get-buffer-window comp-buf))
151 (delete-window comp-win)))))))
153 (defun wl-complete-field-body (completion-list &optional epand-char skip-chars)
156 (start (save-excursion
157 ; (skip-chars-backward "_a-zA-Z0-9+@%.!\\-")
158 (skip-chars-backward (or skip-chars
159 "_a-zA-Z0-9+@%.!\\-/"))
162 (pattern (buffer-substring start end))
163 (len (length pattern))
164 (cl completion-list))
167 (setq completion (try-completion pattern cl))
168 (cond ((eq completion t)
169 (wl-complete-insert start end pattern completion-list)
170 (wl-complete-window-delete)
171 (message "Sole completion"))
174 (char-equal (aref pattern (1- len)) epand-char)
175 (assoc (substring pattern 0 (1- len)) cl))
178 (substring pattern 0 (1- len))
181 (message "Can't find completion for \"%s\"" pattern)
183 ((not (string= pattern completion))
184 (delete-region start end)
187 (let ((list (sort (all-completions pattern cl) 'string<)))
188 (wl-complete-window-show list)))))))
190 (defvar wl-address-init-func 'wl-local-address-init)
192 (defun wl-address-init ()
193 (funcall wl-address-init-func))
195 (defun wl-local-address-init ()
196 (message "Updating addresses...")
197 (setq wl-address-list
198 (wl-address-make-address-list wl-address-file))
199 (setq wl-address-completion-list
200 (wl-address-make-completion-list wl-address-list))
201 (if (file-readable-p wl-alias-file)
202 (setq wl-address-completion-list
203 (append wl-address-completion-list
204 (wl-address-make-alist-from-alias-file wl-alias-file))))
205 (setq wl-address-petname-hash (elmo-make-hash))
209 (elmo-set-hash-val (downcase (car x))
211 wl-address-petname-hash)))
213 (message "Updating addresses...done."))
216 (defun wl-address-expand-aliases (alist nest-count)
217 (when (< nest-count 5)
218 (let (expn-str new-expn-str expn new-expn(n 0) (expanded nil))
219 (while (setq expn-str (cdr (nth n alist)))
220 (setq new-expn-str nil)
221 (while (string-match "^[ \t]*\\([^,]+\\)" expn-str)
222 (setq expn (elmo-match-string 1 expn-str))
223 (setq expn-str (wl-string-delete-match expn-str 0))
224 (if (string-match "^[ \t,]+" expn-str)
225 (setq expn-str (wl-string-delete-match expn-str 0)))
226 (if (string-match "[ \t,]+$" expn)
227 (setq expn (wl-string-delete-match expn 0)))
228 (setq new-expn (cdr (assoc expn alist)))
231 (setq new-expn-str (concat new-expn-str (and new-expn-str ", ")
232 (or new-expn expn))))
234 (setcdr (nth n alist) new-expn-str))
237 (wl-address-expand-aliases alist (1+ nest-count))))))
239 (defun wl-address-make-alist-from-alias-file (file)
241 (let ((case-fold-search t)
243 (insert-file-contents file)
244 (while (re-search-forward ",$" nil t)
247 (delete-backward-char 1))
248 (goto-char (point-min))
249 (while (re-search-forward "^\\([^#;\n][^:]+\\):[ \t]*\\(.*\\)$" nil t)
250 (setq alias (wl-match-buffer 1)
251 expn (wl-match-buffer 2))
252 (setq alist (cons (cons alias expn) alist)))
253 (wl-address-expand-aliases alist 0)
254 (nreverse alist) ; return value
257 (defun wl-address-make-address-list (path)
258 (if (and path (file-readable-p path))
261 (coding-system-for-read wl-cs-autoconv))
262 (insert-file-contents path)
263 (goto-char (point-min))
266 "^\\([^#\n][^ \t\n]+\\)[ \t]+\"\\(.*\\)\"[ \t]+\"\\(.*\\)\"[ \t]*.*$")
270 (list (wl-match-buffer 1)
272 (wl-match-buffer 3)))))
276 (defsubst wl-address-get-petname (str)
277 (let ((addr (downcase (wl-address-header-extract-address str))))
278 (or (elmo-get-hash-val addr wl-address-petname-hash)
281 (defsubst wl-address-make-completion-list (address-list)
282 (mapcar '(lambda (entity)
284 (concat (nth 2 entity) " <"(nth 0 entity)">"))) address-list))
286 (defsubst wl-address-user-mail-address-p (address)
287 "Judge whether ADDRESS is user's or not."
288 (member (downcase (wl-address-header-extract-address address))
289 (or (mapcar 'downcase wl-user-mail-address-list)
291 (wl-address-header-extract-address
294 (defsubst wl-address-header-extract-address (str)
295 "Extracts a real e-mail address from STR and returns it.
296 e.g. \"Mine Sakurai <m-sakura@ccs.mt.nec.co.jp>\"
297 -> \"m-sakura@ccs.mt.nec.co.jp\".
298 e.g. \"m-sakura@ccs.mt.nec.co.jp (Mine Sakurai)\"
299 -> \"m-sakura@ccs.mt.nec.co.jp\"."
300 (cond ((string-match ".*<\\([^>]*\\)>" str) ; .* to extract last <>
301 (wl-match-string 1 str))
302 ((string-match "\\([^ \t\n]*@[^ \t\n]*\\)" str)
303 (wl-match-string 1 str))
306 (defsubst wl-address-header-extract-realname (str)
307 "Extracts a real name from STR and returns it.
308 e.g. \"Mr. bar <hoge@foo.com>\"
310 (cond ((string-match "\\(.*[^ \t]\\)[ \t]*<[^>]*>" str)
311 (wl-match-string 1 str))
314 (defun wl-address-petname-delete (the-email)
315 "Delete petname in wl-address-file."
316 (let* ( (tmp-buf (get-buffer-create " *wl-petname-tmp*"))
317 (output-coding-system
318 (mime-charset-to-coding-system wl-mime-charset)))
320 (message "Deleting Petname...")
322 (insert-file-contents wl-address-file)
323 (delete-matching-lines (concat "^[ \t]*" the-email))
324 (write-region (point-min) (point-max)
325 wl-address-file nil 'no-msg)
326 (message "Deleting Petname...done")
327 (kill-buffer tmp-buf)))
330 (defun wl-address-petname-add-or-change (the-email
333 &optional change-petname)
334 "Add petname to wl-address-file, if not registerd.
335 If already registerd, change it."
336 (let (the-realname the-petname)
338 ;; setup output "petname"
339 ;; if null petname'd, let default-petname be the petname.
341 (read-from-minibuffer (format "Petname: ") default-petname))
342 (if (string= the-petname "")
343 (setq the-petname (or default-petname the-email)))
345 ;; setup output "realname"
347 (read-from-minibuffer (format "Real Name: ") default-realname))
348 ;; (if (string= the-realname "")
349 ;; (setq the-realname default-petname))
351 ;; writing to ~/.address
352 (let ( (tmp-buf (get-buffer-create " *wl-petname-tmp*"))
353 (output-coding-system (mime-charset-to-coding-system wl-mime-charset)))
355 (message "Adding Petname...")
357 (if (file-exists-p wl-address-file)
358 (insert-file-contents wl-address-file))
359 (if (not change-petname)
362 (goto-char (point-max))
363 (if (and (> (buffer-size) 0)
364 (not (eq (char-after (1- (point-max))) ?\n)))
367 (if (re-search-forward (concat "^[ \t]*" the-email) nil t)
368 (delete-region (save-excursion (beginning-of-line)
370 (save-excursion (end-of-line)
372 (insert (format "%s\t\"%s\"\t\"%s\"\n"
373 the-email the-petname the-realname))
374 (write-region (point-min) (point-max)
375 wl-address-file nil 'no-msg)
376 (message "Adding Petname...done")
377 (kill-buffer tmp-buf))))
379 (provide 'wl-address)
381 ;;; wl-address.el ends here