* message.el (message-expand-group): Pass the common
[bpt/emacs.git] / lisp / mail / rmailout.el
CommitLineData
55535639 1;;; rmailout.el --- "RMAIL" mail reader for Emacs: output message to a file
c88ab9ce 2
e84b4b86
TTN
3;; Copyright (C) 1985, 1987, 1993, 1994, 2001, 2002, 2003, 2004,
4;; 2005 Free Software Foundation, Inc.
9750e079 5
4f4b8eff 6;; Maintainer: FSF
d7b4d18f 7;; Keywords: mail
4f4b8eff 8
8f88558f 9;; This file is part of GNU Emacs.
10
11;; GNU Emacs is free software; you can redistribute it and/or modify
12;; it under the terms of the GNU General Public License as published by
e5167999 13;; the Free Software Foundation; either version 2, or (at your option)
8f88558f 14;; any later version.
15
16;; GNU Emacs 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.
20
21;; You should have received a copy of the GNU General Public License
b578f267 22;; along with GNU Emacs; see the file COPYING. If not, write to the
3a35cf56
LK
23;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
24;; Boston, MA 02110-1301, USA.
8f88558f 25
55535639
PJ
26;;; Commentary:
27
4f4b8eff 28;;; Code:
8f88558f 29
e5074474 30(require 'rmail)
a51568bd 31(provide 'rmailout)
e5074474 32
9259d36e 33;;;###autoload
0a01a04e 34(defcustom rmail-output-file-alist nil
0f91ee7e 35 "*Alist matching regexps to suggested output Rmail files.
dba3adb0 36This is a list of elements of the form (REGEXP . NAME-EXP).
9fb6ed08 37The suggestion is taken if REGEXP matches anywhere in the message buffer.
dba3adb0
RS
38NAME-EXP may be a string constant giving the file name to use,
39or more generally it may be any kind of expression that returns
0a01a04e
RS
40a file name as a string."
41 :type '(repeat (cons regexp
42 (choice :value ""
43 (string :tag "File Name")
44 sexp)))
45 :group 'rmail-output)
0f91ee7e 46
a51568bd
RS
47(defun rmail-output-read-rmail-file-name ()
48 "Read the file name to use for `rmail-output-to-rmail-file'.
49Set `rmail-default-rmail-file' to this name as well as returning it."
50 (let ((default-file
51 (let (answer tail)
52 (setq tail rmail-output-file-alist)
53 ;; Suggest a file based on a pattern match.
54 (while (and tail (not answer))
55 (save-excursion
c96b208d 56 (set-buffer rmail-buffer)
a51568bd
RS
57 (goto-char (point-min))
58 (if (re-search-forward (car (car tail)) nil t)
59 (setq answer (eval (cdr (car tail)))))
60 (setq tail (cdr tail))))
61 ;; If no suggestions, use same file as last time.
62 (expand-file-name (or answer rmail-default-rmail-file)))))
63 (let ((read-file
64 (expand-file-name
65 (read-file-name
5b76833f 66 (concat "Output message to Rmail file (default "
a51568bd 67 (file-name-nondirectory default-file)
5b76833f 68 "): ")
a51568bd
RS
69 (file-name-directory default-file)
70 (abbreviate-file-name default-file))
71 (file-name-directory default-file))))
72 ;; If the user enters just a directory,
73 ;; use the name within that directory chosen by the default.
74 (setq rmail-default-rmail-file
75 (if (file-directory-p read-file)
76 (expand-file-name (file-name-nondirectory default-file)
77 read-file)
78 read-file)))))
79
80(defun rmail-output-read-file-name ()
81 "Read the file name to use for `rmail-output'.
82Set `rmail-default-file' to this name as well as returning it."
83 (let ((default-file
84 (let (answer tail)
85 (setq tail rmail-output-file-alist)
86 ;; Suggest a file based on a pattern match.
87 (while (and tail (not answer))
88 (save-excursion
89 (goto-char (point-min))
90 (if (re-search-forward (car (car tail)) nil t)
91 (setq answer (eval (cdr (car tail)))))
92 (setq tail (cdr tail))))
93 ;; If no suggestion, use same file as last time.
94 (or answer rmail-default-file))))
95 (let ((read-file
96 (expand-file-name
97 (read-file-name
5b76833f 98 (concat "Output message to Unix mail file (default "
a51568bd 99 (file-name-nondirectory default-file)
5b76833f 100 "): ")
a51568bd
RS
101 (file-name-directory default-file)
102 (abbreviate-file-name default-file))
103 (file-name-directory default-file))))
104 (setq rmail-default-file
105 (if (file-directory-p read-file)
106 (expand-file-name (file-name-nondirectory default-file)
107 read-file)
108 (expand-file-name
109 (or read-file (file-name-nondirectory default-file))
110 (file-name-directory default-file)))))))
111
fca97d87
RS
112;;; There are functions elsewhere in Emacs that use this function;
113;;; look at them before you change the calling method.
c889a41a 114;;;###autoload
ab59163d 115(defun rmail-output-to-rmail-file (file-name &optional count stay)
8f88558f 116 "Append the current message to an Rmail file named FILE-NAME.
117If the file does not exist, ask if it should be created.
118If file is being visited, the message is appended to the Emacs
119buffer visiting that file.
e155e60e
RS
120If the file exists and is not an Rmail file, the message is
121appended in inbox format, the same way `rmail-output' does it.
dba3adb0 122
dc591caf 123The default file name comes from `rmail-default-rmail-file',
42255e72
RS
124which is updated to the name you use in this command.
125
8f88558f 126A prefix argument N says to output N consecutive messages
ab59163d
DL
127starting with the current one. Deleted messages are skipped and don't count.
128
129If optional argument STAY is non-nil, then leave the last filed
130mesasge up instead of moving forward to the next non-deleted message."
b25b6c34 131 (interactive
a51568bd
RS
132 (list (rmail-output-read-rmail-file-name)
133 (prefix-numeric-value current-prefix-arg)))
37c0ad58 134 (or count (setq count 1))
3bfd7edb
JB
135 (setq file-name
136 (expand-file-name file-name
fdc9edf2 137 (file-name-directory rmail-default-rmail-file)))
ecf1f700 138 (if (and (file-readable-p file-name) (not (mail-file-babyl-p file-name)))
dba3adb0 139 (rmail-output file-name count)
dba3adb0
RS
140 (rmail-maybe-set-message-counters)
141 (setq file-name (abbreviate-file-name file-name))
80abd2a8 142 (or (find-buffer-visiting file-name)
dba3adb0
RS
143 (file-exists-p file-name)
144 (if (yes-or-no-p
145 (concat "\"" file-name "\" does not exist, create it? "))
146 (let ((file-buffer (create-file-buffer file-name)))
8f88558f 147 (save-excursion
dba3adb0
RS
148 (set-buffer file-buffer)
149 (rmail-insert-rmail-file-header)
40beecee
EZ
150 (let ((require-final-newline nil)
151 (coding-system-for-write
152 (or rmail-file-coding-system
153 'emacs-mule-unix)))
dba3adb0
RS
154 (write-region (point-min) (point-max) file-name t 1)))
155 (kill-buffer file-buffer))
156 (error "Output file does not exist")))
157 (while (> count 0)
158 (let (redelete)
159 (unwind-protect
160 (progn
c96b208d 161 (set-buffer rmail-buffer)
953262f7
RS
162 ;; Temporarily turn off Deleted attribute.
163 ;; Do this outside the save-restriction, since it would
164 ;; shift the place in the buffer where the visible text starts.
165 (if (rmail-message-deleted-p rmail-current-message)
166 (progn (setq redelete t)
167 (rmail-set-attribute "deleted" nil)))
dba3adb0
RS
168 (save-restriction
169 (widen)
dba3adb0
RS
170 ;; Decide whether to append to a file or to an Emacs buffer.
171 (save-excursion
80abd2a8 172 (let ((buf (find-buffer-visiting file-name))
dba3adb0
RS
173 (cur (current-buffer))
174 (beg (1+ (rmail-msgbeg rmail-current-message)))
839ab81f
RS
175 (end (1+ (rmail-msgend rmail-current-message)))
176 (coding-system-for-write
177 (or rmail-file-coding-system
178 'emacs-mule-unix)))
dba3adb0 179 (if (not buf)
ab041f09
RS
180 ;; Output to a file.
181 (if rmail-fields-not-to-output
182 ;; Delete some fields while we output.
183 (let ((obuf (current-buffer)))
184 (set-buffer (get-buffer-create " rmail-out-temp"))
185 (insert-buffer-substring obuf beg end)
186 (rmail-delete-unwanted-fields)
187 (append-to-file (point-min) (point-max) file-name)
188 (set-buffer obuf)
189 (kill-buffer (get-buffer " rmail-out-temp")))
190 (append-to-file beg end file-name))
dba3adb0
RS
191 (if (eq buf (current-buffer))
192 (error "Can't output message to same file it's already in"))
193 ;; File has been visited, in buffer BUF.
194 (set-buffer buf)
195 (let ((buffer-read-only nil)
196 (msg (and (boundp 'rmail-current-message)
197 rmail-current-message)))
198 ;; If MSG is non-nil, buffer is in RMAIL mode.
199 (if msg
200 (progn
913126a6
RS
201 ;; Turn on auto save mode, if it's off in this
202 ;; buffer but enabled by default.
203 (and (not buffer-auto-save-file-name)
204 auto-save-default
205 (auto-save-mode t))
dba3adb0
RS
206 (rmail-maybe-set-message-counters)
207 (widen)
208 (narrow-to-region (point-max) (point-max))
209 (insert-buffer-substring cur beg end)
210 (goto-char (point-min))
211 (widen)
212 (search-backward "\n\^_")
213 (narrow-to-region (point) (point-max))
ab041f09 214 (rmail-delete-unwanted-fields)
dba3adb0 215 (rmail-count-new-messages t)
953262f7
RS
216 (if (rmail-summary-exists)
217 (rmail-select-summary
218 (rmail-update-summary)))
dba3adb0 219 (rmail-show-message msg))
839ab81f
RS
220 ;; Output file not in rmail mode => just insert at the end.
221 (narrow-to-region (point-min) (1+ (buffer-size)))
222 (goto-char (point-max))
223 (insert-buffer-substring cur beg end)
224 (rmail-delete-unwanted-fields)))))))
dba3adb0
RS
225 (rmail-set-attribute "filed" t))
226 (if redelete (rmail-set-attribute "deleted" t))))
227 (setq count (1- count))
228 (if rmail-delete-after-output
693ff613 229 (unless
ab59163d
DL
230 (if (and (= count 0) stay)
231 (rmail-delete-message)
232 (rmail-delete-forward))
233 (setq count 0))
dba3adb0 234 (if (> count 0)
693ff613 235 (unless
ab59163d
DL
236 (if (not stay) (rmail-next-undeleted-message 1))
237 (setq count 0)))))))
dba3adb0 238
c889a41a 239;;;###autoload
0a01a04e
RS
240(defcustom rmail-fields-not-to-output nil
241 "*Regexp describing fields to exclude when outputting a message to a file."
242 :type '(choice (const :tag "None" nil)
243 regexp)
244 :group 'rmail-output)
ab041f09
RS
245
246;; Delete from the buffer header fields we don't want output.
247;; NOT-RMAIL if t means this buffer does not have the full header
248;; and *** EOOH *** that a message in an Rmail file has.
249(defun rmail-delete-unwanted-fields (&optional not-rmail)
693ff613 250 (if rmail-fields-not-to-output
ab041f09
RS
251 (save-excursion
252 (goto-char (point-min))
253 ;; Find the end of the header.
254 (if (and (or not-rmail (search-forward "\n*** EOOH ***\n" nil t))
255 (search-forward "\n\n" nil t))
256 (let ((end (point-marker)))
257 (goto-char (point-min))
258 (while (re-search-forward rmail-fields-not-to-output end t)
259 (beginning-of-line)
260 (delete-region (point)
261 (progn (forward-line 1) (point)))))))))
262
fca97d87
RS
263;;; There are functions elsewhere in Emacs that use this function;
264;;; look at them before you change the calling method.
c889a41a 265;;;###autoload
32ad8f1f 266(defun rmail-output (file-name &optional count noattribute from-gnus)
f9d75fab 267 "Append this message to system-inbox-format mail file named FILE-NAME.
8f88558f 268A prefix argument N says to output N consecutive messages
37c0ad58 269starting with the current one. Deleted messages are skipped and don't count.
6e446ebb
RS
270When called from lisp code, N may be omitted.
271
49e4a58a
RS
272If the pruned message header is shown on the current message, then
273messages will be appended with pruned headers; otherwise, messages
274will be appended with their original headers.
275
346af510 276The default file name comes from `rmail-default-file',
42255e72
RS
277which is updated to the name you use in this command.
278
6e446ebb 279The optional third argument NOATTRIBUTE, if non-nil, says not
32ad8f1f
RS
280to set the `filed' attribute, and not to display a message.
281
282The optional fourth argument FROM-GNUS is set when called from GNUS."
8f88558f 283 (interactive
a51568bd
RS
284 (list (rmail-output-read-file-name)
285 (prefix-numeric-value current-prefix-arg)))
37c0ad58 286 (or count (setq count 1))
3bfd7edb
JB
287 (setq file-name
288 (expand-file-name file-name
fdc9edf2
RS
289 (and rmail-default-file
290 (file-name-directory rmail-default-file))))
ecf1f700 291 (if (and (file-readable-p file-name) (mail-file-babyl-p file-name))
dba3adb0 292 (rmail-output-to-rmail-file file-name count)
c96b208d 293 (set-buffer rmail-buffer)
49e4a58a
RS
294 (let ((orig-count count)
295 (rmailbuf (current-buffer))
296 (case-fold-search t)
297 (tembuf (get-buffer-create " rmail-output"))
298 (original-headers-p
32ad8f1f 299 (and (not from-gnus)
693ff613 300 (save-excursion
32ad8f1f
RS
301 (save-restriction
302 (narrow-to-region (rmail-msgbeg rmail-current-message) (point-max))
303 (goto-char (point-min))
304 (forward-line 1)
305 (= (following-char) ?0)))))
49e4a58a 306 header-beginning
693ff613 307 mail-from mime-version content-type)
49e4a58a 308 (while (> count 0)
fca97d87
RS
309 ;; Preserve the Mail-From and MIME-Version fields
310 ;; even if they have been pruned.
32ad8f1f 311 (or from-gnus
fca97d87
RS
312 (save-excursion
313 (save-restriction
314 (widen)
315 (goto-char (rmail-msgbeg rmail-current-message))
316 (setq header-beginning (point))
317 (search-forward "\n*** EOOH ***\n")
318 (narrow-to-region header-beginning (point))
693ff613
FP
319 (setq mail-from (mail-fetch-field "Mail-From"))
320 (unless rmail-enable-mime
321 (setq mime-version (mail-fetch-field "MIME-Version")
322 content-type (mail-fetch-field "Content-type"))))))
dba3adb0
RS
323 (save-excursion
324 (set-buffer tembuf)
325 (erase-buffer)
326 (insert-buffer-substring rmailbuf)
3427b085
GM
327 (when rmail-enable-mime
328 (if original-headers-p
329 (delete-region (goto-char (point-min))
330 (if (search-forward "\n*** EOOH ***\n")
331 (match-end 0)))
332 (goto-char (point-min))
333 (forward-line 2)
334 (delete-region (point-min)(point))
335 (search-forward "\n*** EOOH ***\n")
336 (delete-region (match-beginning 0)
337 (if (search-forward "\n\n")
338 (1- (match-end 0)))))
339 (setq buffer-file-coding-system (or rmail-file-coding-system
340 'raw-text)))
ab041f09 341 (rmail-delete-unwanted-fields t)
b054090e 342 (or (bolp) (insert "\n"))
dba3adb0 343 (goto-char (point-min))
49e4a58a
RS
344 (if mail-from
345 (insert mail-from "\n")
346 (insert "From "
347 (mail-strip-quoted-names (or (mail-fetch-field "from")
348 (mail-fetch-field "really-from")
349 (mail-fetch-field "sender")
350 "unknown"))
351 " " (current-time-string) "\n"))
fca97d87 352 (if mime-version
693ff613
FP
353 (insert "MIME-Version: " mime-version
354 "\nContent-type: " content-type "\n"))
dba3adb0
RS
355 ;; ``Quote'' "\nFrom " as "\n>From "
356 ;; (note that this isn't really quoting, as there is no requirement
357 ;; that "\n[>]+From " be quoted in the same transparent way.)
c4613e62
KH
358 (let ((case-fold-search nil))
359 (while (search-forward "\nFrom " nil t)
360 (forward-char -5)
361 (insert ?>)))
6e446ebb
RS
362 (write-region (point-min) (point-max) file-name t
363 (if noattribute 'nomsg)))
49e4a58a
RS
364 (or noattribute
365 (if (equal major-mode 'rmail-mode)
366 (rmail-set-attribute "filed" t)))
367 (setq count (1- count))
32ad8f1f
RS
368 (or from-gnus
369 (let ((next-message-p
370 (if rmail-delete-after-output
371 (rmail-delete-forward)
372 (if (> count 0)
373 (rmail-next-undeleted-message 1))))
374 (num-appended (- orig-count count)))
375 (if (and next-message-p original-headers-p)
376 (rmail-toggle-header))
377 (if (and (> count 0) (not next-message-p))
693ff613 378 (progn
32ad8f1f
RS
379 (error
380 (save-excursion
381 (set-buffer rmailbuf)
382 (format "Only %d message%s appended" num-appended
383 (if (= num-appended 1) "" "s"))))
384 (setq count 0))))))
49e4a58a 385 (kill-buffer tembuf))))
c88ab9ce 386
d607b17d 387;;;###autoload
a24de134 388(defun rmail-output-body-to-file (file-name)
d607b17d
RS
389 "Write this message body to the file FILE-NAME.
390FILE-NAME defaults, interactively, from the Subject field of the message."
391 (interactive
392 (let ((default-file
d1cad408
RS
393 (or (mail-fetch-field "Subject")
394 rmail-default-body-file)))
395 (list (setq rmail-default-body-file
396 (read-file-name
397 "Output message body to file: "
398 (and default-file (file-name-directory default-file))
399 default-file
400 nil default-file)))))
401 (setq file-name
402 (expand-file-name file-name
403 (and rmail-default-body-file
404 (file-name-directory rmail-default-body-file))))
d607b17d
RS
405 (save-excursion
406 (goto-char (point-min))
407 (search-forward "\n\n")
a24de134
RS
408 (and (file-exists-p file-name)
409 (not (y-or-n-p (message "File %s exists; overwrite? " file-name)))
410 (error "Operation aborted"))
d607b17d
RS
411 (write-region (point) (point-max) file-name)
412 (if (equal major-mode 'rmail-mode)
413 (rmail-set-attribute "stored" t)))
414 (if rmail-delete-after-output
415 (rmail-delete-forward)))
416
ab5796a9 417;;; arch-tag: 447117c6-1a9a-4b88-aa43-3101b043e3a4
c88ab9ce 418;;; rmailout.el ends here