Fix up comment convention on the arch-tag lines.
[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 3;; Copyright (C) 1985, 1987, 1993, 1994, 2001, 2002, 2003, 2004,
2f043267 4;; 2005, 2006, 2007, 2008 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
ceaeecb0 13;; the Free Software Foundation; either version 3, 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
2b54af74
DN
112(declare-function rmail-update-summary "rmailsum" (&rest ignore))
113
fca97d87
RS
114;;; There are functions elsewhere in Emacs that use this function;
115;;; look at them before you change the calling method.
c889a41a 116;;;###autoload
ab59163d 117(defun rmail-output-to-rmail-file (file-name &optional count stay)
8f88558f 118 "Append the current message to an Rmail file named FILE-NAME.
119If the file does not exist, ask if it should be created.
120If file is being visited, the message is appended to the Emacs
121buffer visiting that file.
e155e60e
RS
122If the file exists and is not an Rmail file, the message is
123appended in inbox format, the same way `rmail-output' does it.
dba3adb0 124
dc591caf 125The default file name comes from `rmail-default-rmail-file',
42255e72
RS
126which is updated to the name you use in this command.
127
6c6bf1bb 128A prefix argument COUNT says to output that many consecutive messages,
ab59163d
DL
129starting with the current one. Deleted messages are skipped and don't count.
130
6c6bf1bb
EZ
131If the optional argument STAY is non-nil, then leave the last filed
132message up instead of moving forward to the next non-deleted message."
b25b6c34 133 (interactive
a51568bd
RS
134 (list (rmail-output-read-rmail-file-name)
135 (prefix-numeric-value current-prefix-arg)))
37c0ad58 136 (or count (setq count 1))
3bfd7edb
JB
137 (setq file-name
138 (expand-file-name file-name
fdc9edf2 139 (file-name-directory rmail-default-rmail-file)))
ecf1f700 140 (if (and (file-readable-p file-name) (not (mail-file-babyl-p file-name)))
dba3adb0 141 (rmail-output file-name count)
dba3adb0
RS
142 (rmail-maybe-set-message-counters)
143 (setq file-name (abbreviate-file-name file-name))
80abd2a8 144 (or (find-buffer-visiting file-name)
dba3adb0
RS
145 (file-exists-p file-name)
146 (if (yes-or-no-p
147 (concat "\"" file-name "\" does not exist, create it? "))
148 (let ((file-buffer (create-file-buffer file-name)))
8f88558f 149 (save-excursion
dba3adb0
RS
150 (set-buffer file-buffer)
151 (rmail-insert-rmail-file-header)
40beecee
EZ
152 (let ((require-final-newline nil)
153 (coding-system-for-write
154 (or rmail-file-coding-system
155 'emacs-mule-unix)))
dba3adb0
RS
156 (write-region (point-min) (point-max) file-name t 1)))
157 (kill-buffer file-buffer))
158 (error "Output file does not exist")))
159 (while (> count 0)
160 (let (redelete)
161 (unwind-protect
162 (progn
c96b208d 163 (set-buffer rmail-buffer)
953262f7
RS
164 ;; Temporarily turn off Deleted attribute.
165 ;; Do this outside the save-restriction, since it would
166 ;; shift the place in the buffer where the visible text starts.
167 (if (rmail-message-deleted-p rmail-current-message)
168 (progn (setq redelete t)
169 (rmail-set-attribute "deleted" nil)))
dba3adb0
RS
170 (save-restriction
171 (widen)
dba3adb0
RS
172 ;; Decide whether to append to a file or to an Emacs buffer.
173 (save-excursion
80abd2a8 174 (let ((buf (find-buffer-visiting file-name))
dba3adb0
RS
175 (cur (current-buffer))
176 (beg (1+ (rmail-msgbeg rmail-current-message)))
839ab81f
RS
177 (end (1+ (rmail-msgend rmail-current-message)))
178 (coding-system-for-write
179 (or rmail-file-coding-system
180 'emacs-mule-unix)))
dba3adb0 181 (if (not buf)
ab041f09
RS
182 ;; Output to a file.
183 (if rmail-fields-not-to-output
184 ;; Delete some fields while we output.
185 (let ((obuf (current-buffer)))
186 (set-buffer (get-buffer-create " rmail-out-temp"))
187 (insert-buffer-substring obuf beg end)
188 (rmail-delete-unwanted-fields)
189 (append-to-file (point-min) (point-max) file-name)
190 (set-buffer obuf)
191 (kill-buffer (get-buffer " rmail-out-temp")))
192 (append-to-file beg end file-name))
dba3adb0
RS
193 (if (eq buf (current-buffer))
194 (error "Can't output message to same file it's already in"))
195 ;; File has been visited, in buffer BUF.
196 (set-buffer buf)
197 (let ((buffer-read-only nil)
198 (msg (and (boundp 'rmail-current-message)
199 rmail-current-message)))
200 ;; If MSG is non-nil, buffer is in RMAIL mode.
201 (if msg
202 (progn
913126a6
RS
203 ;; Turn on auto save mode, if it's off in this
204 ;; buffer but enabled by default.
205 (and (not buffer-auto-save-file-name)
206 auto-save-default
207 (auto-save-mode t))
dba3adb0
RS
208 (rmail-maybe-set-message-counters)
209 (widen)
210 (narrow-to-region (point-max) (point-max))
211 (insert-buffer-substring cur beg end)
212 (goto-char (point-min))
213 (widen)
214 (search-backward "\n\^_")
215 (narrow-to-region (point) (point-max))
ab041f09 216 (rmail-delete-unwanted-fields)
dba3adb0 217 (rmail-count-new-messages t)
953262f7
RS
218 (if (rmail-summary-exists)
219 (rmail-select-summary
220 (rmail-update-summary)))
dba3adb0 221 (rmail-show-message msg))
839ab81f
RS
222 ;; Output file not in rmail mode => just insert at the end.
223 (narrow-to-region (point-min) (1+ (buffer-size)))
224 (goto-char (point-max))
225 (insert-buffer-substring cur beg end)
226 (rmail-delete-unwanted-fields)))))))
dba3adb0
RS
227 (rmail-set-attribute "filed" t))
228 (if redelete (rmail-set-attribute "deleted" t))))
229 (setq count (1- count))
230 (if rmail-delete-after-output
693ff613 231 (unless
ab59163d
DL
232 (if (and (= count 0) stay)
233 (rmail-delete-message)
234 (rmail-delete-forward))
235 (setq count 0))
dba3adb0 236 (if (> count 0)
693ff613 237 (unless
ab59163d
DL
238 (if (not stay) (rmail-next-undeleted-message 1))
239 (setq count 0)))))))
dba3adb0 240
c889a41a 241;;;###autoload
0a01a04e
RS
242(defcustom rmail-fields-not-to-output nil
243 "*Regexp describing fields to exclude when outputting a message to a file."
244 :type '(choice (const :tag "None" nil)
245 regexp)
246 :group 'rmail-output)
ab041f09
RS
247
248;; Delete from the buffer header fields we don't want output.
249;; NOT-RMAIL if t means this buffer does not have the full header
250;; and *** EOOH *** that a message in an Rmail file has.
251(defun rmail-delete-unwanted-fields (&optional not-rmail)
693ff613 252 (if rmail-fields-not-to-output
ab041f09
RS
253 (save-excursion
254 (goto-char (point-min))
255 ;; Find the end of the header.
256 (if (and (or not-rmail (search-forward "\n*** EOOH ***\n" nil t))
257 (search-forward "\n\n" nil t))
258 (let ((end (point-marker)))
259 (goto-char (point-min))
260 (while (re-search-forward rmail-fields-not-to-output end t)
261 (beginning-of-line)
262 (delete-region (point)
263 (progn (forward-line 1) (point)))))))))
264
fca97d87
RS
265;;; There are functions elsewhere in Emacs that use this function;
266;;; look at them before you change the calling method.
c889a41a 267;;;###autoload
32ad8f1f 268(defun rmail-output (file-name &optional count noattribute from-gnus)
f9d75fab 269 "Append this message to system-inbox-format mail file named FILE-NAME.
6c6bf1bb 270A prefix argument COUNT says to output that many consecutive messages,
37c0ad58 271starting with the current one. Deleted messages are skipped and don't count.
6c6bf1bb 272When called from lisp code, COUNT may be omitted and defaults to 1.
6e446ebb 273
49e4a58a
RS
274If the pruned message header is shown on the current message, then
275messages will be appended with pruned headers; otherwise, messages
276will be appended with their original headers.
277
346af510 278The default file name comes from `rmail-default-file',
42255e72
RS
279which is updated to the name you use in this command.
280
6e446ebb 281The optional third argument NOATTRIBUTE, if non-nil, says not
32ad8f1f
RS
282to set the `filed' attribute, and not to display a message.
283
284The optional fourth argument FROM-GNUS is set when called from GNUS."
8f88558f 285 (interactive
a51568bd
RS
286 (list (rmail-output-read-file-name)
287 (prefix-numeric-value current-prefix-arg)))
37c0ad58 288 (or count (setq count 1))
3bfd7edb
JB
289 (setq file-name
290 (expand-file-name file-name
fdc9edf2
RS
291 (and rmail-default-file
292 (file-name-directory rmail-default-file))))
ecf1f700 293 (if (and (file-readable-p file-name) (mail-file-babyl-p file-name))
dba3adb0 294 (rmail-output-to-rmail-file file-name count)
c96b208d 295 (set-buffer rmail-buffer)
49e4a58a
RS
296 (let ((orig-count count)
297 (rmailbuf (current-buffer))
298 (case-fold-search t)
299 (tembuf (get-buffer-create " rmail-output"))
300 (original-headers-p
32ad8f1f 301 (and (not from-gnus)
693ff613 302 (save-excursion
32ad8f1f
RS
303 (save-restriction
304 (narrow-to-region (rmail-msgbeg rmail-current-message) (point-max))
305 (goto-char (point-min))
306 (forward-line 1)
307 (= (following-char) ?0)))))
49e4a58a 308 header-beginning
693ff613 309 mail-from mime-version content-type)
49e4a58a 310 (while (> count 0)
fca97d87
RS
311 ;; Preserve the Mail-From and MIME-Version fields
312 ;; even if they have been pruned.
32ad8f1f 313 (or from-gnus
fca97d87
RS
314 (save-excursion
315 (save-restriction
316 (widen)
317 (goto-char (rmail-msgbeg rmail-current-message))
318 (setq header-beginning (point))
319 (search-forward "\n*** EOOH ***\n")
320 (narrow-to-region header-beginning (point))
693ff613
FP
321 (setq mail-from (mail-fetch-field "Mail-From"))
322 (unless rmail-enable-mime
323 (setq mime-version (mail-fetch-field "MIME-Version")
324 content-type (mail-fetch-field "Content-type"))))))
dba3adb0
RS
325 (save-excursion
326 (set-buffer tembuf)
327 (erase-buffer)
328 (insert-buffer-substring rmailbuf)
3427b085
GM
329 (when rmail-enable-mime
330 (if original-headers-p
331 (delete-region (goto-char (point-min))
332 (if (search-forward "\n*** EOOH ***\n")
333 (match-end 0)))
334 (goto-char (point-min))
335 (forward-line 2)
336 (delete-region (point-min)(point))
337 (search-forward "\n*** EOOH ***\n")
338 (delete-region (match-beginning 0)
339 (if (search-forward "\n\n")
340 (1- (match-end 0)))))
341 (setq buffer-file-coding-system (or rmail-file-coding-system
342 'raw-text)))
ab041f09 343 (rmail-delete-unwanted-fields t)
b054090e 344 (or (bolp) (insert "\n"))
dba3adb0 345 (goto-char (point-min))
49e4a58a
RS
346 (if mail-from
347 (insert mail-from "\n")
348 (insert "From "
349 (mail-strip-quoted-names (or (mail-fetch-field "from")
350 (mail-fetch-field "really-from")
351 (mail-fetch-field "sender")
352 "unknown"))
353 " " (current-time-string) "\n"))
5ef9bccd
EZ
354 (when mime-version
355 (insert "MIME-Version: " mime-version)
356 ;; Some malformed MIME messages set content-type to nil.
357 (when content-type
358 (insert "\nContent-type: " content-type "\n")))
dba3adb0
RS
359 ;; ``Quote'' "\nFrom " as "\n>From "
360 ;; (note that this isn't really quoting, as there is no requirement
361 ;; that "\n[>]+From " be quoted in the same transparent way.)
c4613e62
KH
362 (let ((case-fold-search nil))
363 (while (search-forward "\nFrom " nil t)
364 (forward-char -5)
365 (insert ?>)))
6e446ebb
RS
366 (write-region (point-min) (point-max) file-name t
367 (if noattribute 'nomsg)))
49e4a58a
RS
368 (or noattribute
369 (if (equal major-mode 'rmail-mode)
370 (rmail-set-attribute "filed" t)))
371 (setq count (1- count))
32ad8f1f
RS
372 (or from-gnus
373 (let ((next-message-p
374 (if rmail-delete-after-output
375 (rmail-delete-forward)
376 (if (> count 0)
377 (rmail-next-undeleted-message 1))))
378 (num-appended (- orig-count count)))
379 (if (and next-message-p original-headers-p)
380 (rmail-toggle-header))
381 (if (and (> count 0) (not next-message-p))
693ff613 382 (progn
26b4a51d 383 (error "%s"
32ad8f1f
RS
384 (save-excursion
385 (set-buffer rmailbuf)
386 (format "Only %d message%s appended" num-appended
387 (if (= num-appended 1) "" "s"))))
388 (setq count 0))))))
49e4a58a 389 (kill-buffer tembuf))))
c88ab9ce 390
d607b17d 391;;;###autoload
a24de134 392(defun rmail-output-body-to-file (file-name)
d607b17d
RS
393 "Write this message body to the file FILE-NAME.
394FILE-NAME defaults, interactively, from the Subject field of the message."
395 (interactive
396 (let ((default-file
d1cad408
RS
397 (or (mail-fetch-field "Subject")
398 rmail-default-body-file)))
399 (list (setq rmail-default-body-file
400 (read-file-name
401 "Output message body to file: "
402 (and default-file (file-name-directory default-file))
403 default-file
404 nil default-file)))))
405 (setq file-name
406 (expand-file-name file-name
407 (and rmail-default-body-file
408 (file-name-directory rmail-default-body-file))))
d607b17d
RS
409 (save-excursion
410 (goto-char (point-min))
411 (search-forward "\n\n")
a24de134 412 (and (file-exists-p file-name)
2c1459a0 413 (not (y-or-n-p (format "File %s exists; overwrite? " file-name)))
a24de134 414 (error "Operation aborted"))
d607b17d
RS
415 (write-region (point) (point-max) file-name)
416 (if (equal major-mode 'rmail-mode)
417 (rmail-set-attribute "stored" t)))
418 (if rmail-delete-after-output
419 (rmail-delete-forward)))
420
cbee283d 421;; arch-tag: 447117c6-1a9a-4b88-aa43-3101b043e3a4
c88ab9ce 422;;; rmailout.el ends here