|
|
1.1 root 1: ;; "RMAIL" mail reader for Emacs.
2: ;; Copyright (C) 1985 Free Software Foundation, Inc.
3:
4: ;; This file is part of GNU Emacs.
5:
6: ;; GNU Emacs is distributed in the hope that it will be useful,
7: ;; but WITHOUT ANY WARRANTY. No author or distributor
8: ;; accepts responsibility to anyone for the consequences of using it
9: ;; or for whether it serves any particular purpose or works at all,
10: ;; unless he says so in writing. Refer to the GNU Emacs General Public
11: ;; License for full details.
12:
13: ;; Everyone is granted permission to copy, modify and redistribute
14: ;; GNU Emacs, but only under the conditions described in the
15: ;; GNU Emacs General Public License. A copy of this license is
16: ;; supposed to have been given to you along with GNU Emacs so you
17: ;; can know your rights and responsibilities. It should be in a
18: ;; file named COPYING. Among other things, the copyright notice
19: ;; and this notice must be preserved on all copies.
20:
21:
22: ;; summary things
23:
24: (defun rmail-summary ()
25: "Display a summary of all messages, one line per message."
26: (interactive)
27: (rmail-new-summary "All" nil))
28:
29: (defun rmail-summary-by-labels (labels)
30: "Display a summary of all messages with one or more LABELS.
31: LABELS should be a string containing the desired labels, separated by commas."
32: (interactive "sLabels to summarize by: ")
33: (if (string= labels "")
34: (setq labels (or rmail-last-multi-labels
35: (error "No label specified"))))
36: (setq rmail-last-multi-labels labels)
37: (rmail-new-summary (concat "labels " labels)
38: 'rmail-message-labels-p
39: (concat ", \\(" (mail-comma-list-regexp labels) "\\),")))
40:
41: (defun rmail-summary-by-recipients (recipients &optional primary-only)
42: "Display a summary of all messages with the given RECIPIENTS.
43: Normally checks the To, From and Cc fields of headers;
44: but if PRIMARY-ONLY is non-nil (prefix arg given),
45: only look in the To and From fields.
46: RECIPIENTS is a string of names separated by commas."
47: (interactive "sRecipients to summarize by: \nP")
48: (rmail-new-summary
49: (concat "recipients " recipients)
50: 'rmail-message-recipients-p
51: (mail-comma-list-regexp recipients) primary-only))
52:
53: (defun rmail-message-recipients-p (msg recipients &optional primary-only)
54: (save-restriction
55: (goto-char (rmail-msgbeg msg))
56: (search-forward "\n*** EOOH ***\n")
57: (narrow-to-region (point) (progn (search-forward "\n\n") (point)))
58: (or (string-match recipients (or (mail-fetch-field "To") ""))
59: (string-match recipients (or (mail-fetch-field "From") ""))
60: (if (not primary-only)
61: (string-match recipients (or (mail-fetch-field "Cc") ""))))))
62:
63: (defun rmail-new-summary (description function &rest args)
64: "Create a summary of selected messages.
65: DESCRIPTION makes part of the mode line of the summary buffer.
66: For each message, FUNCTION is applied to the message number and ARGS...
67: and if the result is non-nil, that message is included.
68: nil for FUNCTION means all messages."
69: (message "Computing summary lines...")
70: (or (and rmail-summary-buffer
71: (buffer-name rmail-summary-buffer))
72: (setq rmail-summary-buffer
73: (generate-new-buffer (concat (buffer-name) "-summary"))))
74: (let ((summary-msgs ())
75: (new-summary-line-count 0))
76: (let ((msgnum 1)
77: (buffer-read-only nil))
78: (save-restriction
79: (save-excursion
80: (widen)
81: (goto-char (point-min))
82: (while (>= rmail-total-messages msgnum)
83: (if (or (null function)
84: (apply function (cons msgnum args)))
85: (setq summary-msgs
86: (cons (rmail-make-summary-line msgnum)
87: summary-msgs)))
88: (setq msgnum (1+ msgnum))))))
89: (let ((sbuf rmail-summary-buffer)
90: (rbuf (current-buffer))
91: (total rmail-total-messages)
92: (mesg rmail-current-message))
93: (pop-to-buffer sbuf)
94: (let ((buffer-read-only nil))
95: (erase-buffer)
96: (cond (summary-msgs
97: (princ (nreverse summary-msgs) sbuf)
98: (delete-char -1)
99: (subst-char-in-region 1 2 ?\( ?\ ))))
100: (setq buffer-read-only t)
101: (goto-char (point-min))
102: (rmail-summary-mode)
103: (make-local-variable 'minor-mode-alist)
104: (setq minor-mode-alist (list ": " description))
105: (setq rmail-buffer rbuf
106: rmail-total-messages total)
107: (rmail-summary-goto-msg mesg t)))
108: (message "Computing summary lines...done"))
109:
110: (defun rmail-make-summary-line (msg)
111: (let ((line (or (aref rmail-summary-vector (1- msg))
112: (progn
113: (setq new-summary-line-count
114: (1+ new-summary-line-count))
115: (if (zerop (% new-summary-line-count 10))
116: (message "Computing summary lines...%d"
117: new-summary-line-count))
118: (rmail-make-summary-line-1 msg)))))
119: ;; Fix up the part of the summary that says "deleted" or "unseen".
120: (aset line 4
121: (if (rmail-message-deleted-p msg) ?\D
122: (if (= ?0 (char-after (+ 3 (rmail-msgbeg msg))))
123: ?\- ?\ )))
124: line))
125:
126: (defun rmail-make-summary-line-1 (msg)
127: (goto-char (rmail-msgbeg msg))
128: (let* ((lim (save-excursion (forward-line 2) (point)))
129: pos
130: (labels
131: (progn
132: (forward-char 3)
133: (concat
134: ; (if (save-excursion (re-search-forward ",answered," lim t))
135: ; "*" "")
136: ; (if (save-excursion (re-search-forward ",filed," lim t))
137: ; "!" "")
138: (if (progn (search-forward ",,") (eolp))
139: ""
140: (concat "{"
141: (buffer-substring (point)
142: (progn (end-of-line) (point)))
143: "} ")))))
144: (line
145: (progn
146: (forward-line 1)
147: (if (looking-at "Summary-line: ")
148: (progn
149: (goto-char (match-end 0))
150: (setq line
151: (buffer-substring (point)
152: (progn (forward-line 1) (point)))))))))
153: ;; Obsolete status lines lacking a # should be flushed.
154: (and line
155: (not (string-match "#" line))
156: (progn
157: (delete-region (point)
158: (progn (forward-line -1) (point)))
159: (setq line nil)))
160: ;; If we didn't get a valid status line from the message,
161: ;; make a new one and put it in the message.
162: (or line
163: (let* ((case-fold-search t)
164: (next (rmail-msgend msg))
165: (beg (if (progn (goto-char (rmail-msgbeg msg))
166: (search-forward "\n*** EOOH ***\n" next t))
167: (point)
168: (forward-line 1)
169: (point)))
170: (end (progn (search-forward "\n\n" nil t) (point))))
171: (save-restriction
172: (narrow-to-region beg end)
173: (goto-char beg)
174: (setq line (rmail-make-basic-summary-line)))
175: (goto-char (rmail-msgbeg msg))
176: (forward-line 2)
177: (insert "Summary-line: " line)))
178: (setq pos (string-match "#" line))
179: (aset rmail-summary-vector (1- msg)
180: (concat (format "%4d " msg)
181: (substring line 0 pos)
182: labels
183: (substring line (1+ pos))))))
184:
185: (defun rmail-make-basic-summary-line ()
186: (goto-char (point-min))
187: (concat (save-excursion
188: (if (not (re-search-forward "^Date:" nil t))
189: " "
190: (cond ((re-search-forward "\\([^0-9:]\\)\\([0-3]?[0-9]\\)\\([- \t_]+\\)\\([adfjmnos][aceopu][bcglnprtvy]\\)"
191: (save-excursion (end-of-line) (point)) t)
192: (format "%2d-%3s"
193: (string-to-int (buffer-substring
194: (match-beginning 2)
195: (match-end 2)))
196: (buffer-substring
197: (match-beginning 4) (match-end 4))))
198: ((re-search-forward "\\([^a-z]\\)\\([adfjmnos][acepou][bcglnprtvy]\\)\\([-a-z \t_]*\\)\\([0-9][0-9]?\\)"
199: (save-excursion (end-of-line) (point)) t)
200: (format "%2d-%3s"
201: (string-to-int (buffer-substring
202: (match-beginning 4)
203: (match-end 4)))
204: (buffer-substring
205: (match-beginning 2) (match-end 2))))
206: (t "??????"))))
207: " "
208: (save-excursion
209: (if (not (re-search-forward "^From:[ \t]*" nil t))
210: " "
211: (let* ((from (mail-strip-quoted-names
212: (buffer-substring
213: (1- (point))
214: (progn (end-of-line)
215: (skip-chars-backward " \t")
216: (point)))))
217: len mch lo)
218: (if (string-match (concat "^"
219: (regexp-quote (user-login-name))
220: "\\($\\|@\\)")
221: from)
222: (save-excursion
223: (goto-char (point-min))
224: (if (not (re-search-forward "^To:[ \t]*" nil t))
225: nil
226: (setq from
227: (concat "to: "
228: (mail-strip-quoted-names
229: (buffer-substring
230: (point)
231: (progn (end-of-line)
232: (skip-chars-backward " \t")
233: (point)))))))))
234: (setq len (length from))
235: (setq mch (string-match "[@%]" from))
236: (format "%25s"
237: (if (or (not mch) (<= len 25))
238: (substring from (max 0 (- len 25)))
239: (substring from
240: (setq lo (cond ((< (- mch 9) 0) 0)
241: ((< len (+ mch 16))
242: (- len 25))
243: (t (- mch 9))))
244: (min len (+ lo 25))))))))
245: " #"
246: (if (re-search-forward "^Subject:" nil t)
247: (progn (skip-chars-forward " \t")
248: (buffer-substring (point)
249: (progn (end-of-line)
250: (point))))
251: (re-search-forward "[\n][\n]+" nil t)
252: (buffer-substring (point) (progn (end-of-line) (point))))
253: "\n"))
254:
255: (defun rmail-summary-next-all (&optional number)
256: (interactive "p")
257: (forward-line (if number number 1))
258: (rmail-summary-goto-msg))
259:
260: (defun rmail-summary-previous-all (&optional number)
261: (interactive "p")
262: (forward-line (- (if number number 1)))
263: (rmail-summary-goto-msg))
264:
265: (defun rmail-summary-next-msg (&optional number)
266: (interactive "p")
267: (forward-line 0)
268: (and (> number 0) (forward-line 1))
269: (let ((count (if (< number 0) (- number) number))
270: (search (if (> number 0) 're-search-forward 're-search-backward))
271: end)
272: (while (and (> count 0) (funcall search "^.....[^D]" nil t))
273: (setq count (1- count)))
274: (rmail-summary-goto-msg)))
275:
276: (defun rmail-summary-previous-msg (&optional number)
277: (interactive "p")
278: (rmail-summary-next-msg (- (if number number 1))))
279:
280: (defun rmail-summary-delete-forward ()
281: (interactive)
282: (let (end)
283: (rmail-summary-goto-msg)
284: (pop-to-buffer rmail-buffer)
285: (rmail-delete-message)
286: (pop-to-buffer rmail-summary-buffer)
287: (let ((buffer-read-only nil))
288: (skip-chars-forward " ")
289: (skip-chars-forward "[0-9]")
290: (delete-char 1)
291: (insert "D"))
292: (rmail-summary-next-msg 1)))
293:
294: (defun rmail-summary-undelete ()
295: (interactive)
296: (let ((buffer-read-only nil))
297: (end-of-line)
298: (cond ((re-search-backward "\\(^ *[0-9]*\\)\\(D\\)" nil t)
299: (replace-match "\\1 ")
300: (rmail-summary-goto-msg)
301: (pop-to-buffer rmail-buffer)
302: (and (rmail-message-deleted-p rmail-current-message)
303: (rmail-undelete-previous-message))
304: (pop-to-buffer rmail-summary-buffer))
305: (t
306: (rmail-summary-goto-msg)))))
307:
308: ;; Rmail Summary mode is suitable only for specially formatted data.
309: (put 'rmail-summary-mode 'mode-class 'special)
310:
311: (defun rmail-summary-mode ()
312: "Major mode in effect in Rmail summary buffer.
313: A subset of the Rmail mode commands are supported in this mode.
314: As commands are issued in the summary buffer the corresponding
315: mail message is displayed in the rmail buffer.
316:
317: n Move to next undeleted message, or arg messages.
318: p Move to previous undeleted message, or arg messages.
319: C-n Move to next, or forward arg messages.
320: C-p Move to previous, or previous arg messages.
321: j Jump to the message at the cursor location.
322: d Delete the message at the cursor location and move to next message.
323: u Undelete this or previous deleted message.
324: q Quit Rmail.
325: x Exit and kill the summary window.
326: space Scroll message in other window forward.
327: delete Scroll message backward.
328:
329: Entering this mode calls value of hook variable rmail-summary-mode-hook."
330: (interactive)
331: (kill-all-local-variables)
332: (make-local-variable 'rmail-buffer)
333: (make-local-variable 'rmail-total-messages)
334: (setq major-mode 'rmail-summary-mode)
335: (setq mode-name "RMAIL Summary")
336: (use-local-map rmail-summary-mode-map)
337: (setq truncate-lines t)
338: (setq buffer-read-only t)
339: (set-syntax-table text-mode-syntax-table)
340: (run-hooks 'rmail-summary-mode-hook))
341:
342: (defun rmail-summary-goto-msg (&optional n nowarn)
343: (interactive "P")
344: (if (consp n) (setq n (prefix-numeric-value n)))
345: (if (eobp) (forward-line -1))
346: (beginning-of-line)
347: (let ((buf rmail-buffer)
348: (cur (point))
349: (curmsg (string-to-int
350: (buffer-substring (point)
351: (min (point-max) (+ 5 (point)))))))
352: (if (not n)
353: (setq n curmsg)
354: (if (< n 1)
355: (progn (message "No preceding message")
356: (setq n 1)))
357: (if (> n rmail-total-messages)
358: (progn (message "No following message")
359: (goto-char (point-max))
360: (rmail-summary-goto-msg)))
361: (goto-char (point-min))
362: (if (not (re-search-forward (concat "^ *" (int-to-string n)) nil t))
363: (progn (or nowarn (message "Message %d not found" n))
364: (setq n curmsg)
365: (goto-char cur))))
366: (beginning-of-line)
367: (skip-chars-forward " ")
368: (skip-chars-forward "0-9")
369: (save-excursion (if (= (following-char) ?-)
370: (let ((buffer-read-only nil))
371: (delete-char 1)
372: (insert " "))))
373: (beginning-of-line)
374: (pop-to-buffer buf)
375: (rmail-show-message n)
376: (pop-to-buffer rmail-summary-buffer)))
377:
378: (defvar rmail-summary-mode-map nil)
379:
380: (if rmail-summary-mode-map
381: nil
382: (setq rmail-summary-mode-map (make-keymap))
383: (suppress-keymap rmail-summary-mode-map)
384: (define-key rmail-summary-mode-map "j" 'rmail-summary-goto-msg)
385: (define-key rmail-summary-mode-map "n" 'rmail-summary-next-msg)
386: (define-key rmail-summary-mode-map "p" 'rmail-summary-previous-msg)
387: (define-key rmail-summary-mode-map "\C-n" 'rmail-summary-next-all)
388: (define-key rmail-summary-mode-map "\C-p" 'rmail-summary-previous-all)
389: (define-key rmail-summary-mode-map " " 'rmail-summary-scroll-msg-up)
390: (define-key rmail-summary-mode-map "q" 'rmail-summary-quit)
391: (define-key rmail-summary-mode-map "u" 'rmail-summary-undelete)
392: (define-key rmail-summary-mode-map "x" 'rmail-summary-exit)
393: (define-key rmail-summary-mode-map "\177" 'rmail-summary-scroll-msg-down)
394: (define-key rmail-summary-mode-map "d" 'rmail-summary-delete-forward))
395:
396: (defun rmail-summary-scroll-msg-up (&optional dist)
397: "Scroll other window forward."
398: (interactive "P")
399: (scroll-other-window dist))
400:
401: (defun rmail-summary-scroll-msg-down (&optional dist)
402: "Scroll other window backward."
403: (interactive "P")
404: (scroll-other-window
405: (cond ((eq dist '-) nil)
406: ((null dist) '-)
407: (t (- (prefix-numeric-value dist))))))
408:
409: (defun rmail-summary-quit ()
410: "Quit out of rmail and rmail summary."
411: (interactive)
412: (rmail-summary-exit)
413: (rmail-quit))
414:
415: (defun rmail-summary-exit ()
416: "Exit rmail summary, remaining within rmail."
417: (interactive)
418: (bury-buffer (current-buffer))
419: (if (get-buffer-window rmail-buffer)
420: ;; Select the window with rmail in it, then delete this window.
421: (select-window (prog1
422: (get-buffer-window rmail-buffer)
423: (delete-window (selected-window))))
424: ;; Switch to the rmail buffer in this window.
425: (switch-to-buffer rmail-buffer)))
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.