Fix comment typo.
[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
b1fc2b50 11;; GNU Emacs is free software: you can redistribute it and/or modify
8f88558f 12;; it under the terms of the GNU General Public License as published by
b1fc2b50
GM
13;; the Free Software Foundation, either version 3 of the License, or
14;; (at your option) any later version.
8f88558f 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
b1fc2b50 22;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
8f88558f 23
55535639
PJ
24;;; Commentary:
25
4f4b8eff 26;;; Code:
8f88558f 27
e5074474 28(require 'rmail)
a51568bd 29(provide 'rmailout)
e5074474 30
9259d36e 31;;;###autoload
0a01a04e 32(defcustom rmail-output-file-alist nil
0f91ee7e 33 "*Alist matching regexps to suggested output Rmail files.
dba3adb0 34This is a list of elements of the form (REGEXP . NAME-EXP).
9fb6ed08 35The suggestion is taken if REGEXP matches anywhere in the message buffer.
dba3adb0
RS
36NAME-EXP may be a string constant giving the file name to use,
37or more generally it may be any kind of expression that returns
0a01a04e
RS
38a file name as a string."
39 :type '(repeat (cons regexp
40 (choice :value ""
41 (string :tag "File Name")
42 sexp)))
43 :group 'rmail-output)
0f91ee7e 44
a51568bd
RS
45(defun rmail-output-read-rmail-file-name ()
46 "Read the file name to use for `rmail-output-to-rmail-file'.
47Set `rmail-default-rmail-file' to this name as well as returning it."
48 (let ((default-file
49 (let (answer tail)
50 (setq tail rmail-output-file-alist)
51 ;; Suggest a file based on a pattern match.
52 (while (and tail (not answer))
53 (save-excursion
c96b208d 54 (set-buffer rmail-buffer)
a51568bd
RS
55 (goto-char (point-min))
56 (if (re-search-forward (car (car tail)) nil t)
57 (setq answer (eval (cdr (car tail)))))
58 (setq tail (cdr tail))))
59 ;; If no suggestions, use same file as last time.
60 (expand-file-name (or answer rmail-default-rmail-file)))))
61 (let ((read-file
62 (expand-file-name
63 (read-file-name
5b76833f 64 (concat "Output message to Rmail file (default "
a51568bd 65 (file-name-nondirectory default-file)
5b76833f 66 "): ")
a51568bd
RS
67 (file-name-directory default-file)
68 (abbreviate-file-name default-file))
69 (file-name-directory default-file))))
70 ;; If the user enters just a directory,
71 ;; use the name within that directory chosen by the default.
72 (setq rmail-default-rmail-file
73 (if (file-directory-p read-file)
74 (expand-file-name (file-name-nondirectory default-file)
75 read-file)
76 read-file)))))
77
78(defun rmail-output-read-file-name ()
79 "Read the file name to use for `rmail-output'.
80Set `rmail-default-file' to this name as well as returning it."
81 (let ((default-file
82 (let (answer tail)
83 (setq tail rmail-output-file-alist)
84 ;; Suggest a file based on a pattern match.
85 (while (and tail (not answer))
86 (save-excursion
87 (goto-char (point-min))
88 (if (re-search-forward (car (car tail)) nil t)
89 (setq answer (eval (cdr (car tail)))))
90 (setq tail (cdr tail))))
91 ;; If no suggestion, use same file as last time.
92 (or answer rmail-default-file))))
93 (let ((read-file
94 (expand-file-name
95 (read-file-name
5b76833f 96 (concat "Output message to Unix mail file (default "
a51568bd 97 (file-name-nondirectory default-file)
5b76833f 98 "): ")
a51568bd
RS
99 (file-name-directory default-file)
100 (abbreviate-file-name default-file))
101 (file-name-directory default-file))))
102 (setq rmail-default-file
103 (if (file-directory-p read-file)
104 (expand-file-name (file-name-nondirectory default-file)
105 read-file)
106 (expand-file-name
107 (or read-file (file-name-nondirectory default-file))
108 (file-name-directory default-file)))))))
109
2b54af74
DN
110(declare-function rmail-update-summary "rmailsum" (&rest ignore))
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
6c6bf1bb 126A prefix argument COUNT says to output that many consecutive messages,
ab59163d
DL
127starting with the current one. Deleted messages are skipped and don't count.
128
6c6bf1bb
EZ
129If the optional argument STAY is non-nil, then leave the last filed
130message 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.
6c6bf1bb 268A prefix argument COUNT says to output that many consecutive messages,
37c0ad58 269starting with the current one. Deleted messages are skipped and don't count.
6c6bf1bb 270When called from lisp code, COUNT may be omitted and defaults to 1.
6e446ebb 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"))
5ef9bccd
EZ
352 (when mime-version
353 (insert "MIME-Version: " mime-version)
354 ;; Some malformed MIME messages set content-type to nil.
355 (when content-type
356 (insert "\nContent-type: " content-type "\n")))
dba3adb0
RS
357 ;; ``Quote'' "\nFrom " as "\n>From "
358 ;; (note that this isn't really quoting, as there is no requirement
359 ;; that "\n[>]+From " be quoted in the same transparent way.)
c4613e62
KH
360 (let ((case-fold-search nil))
361 (while (search-forward "\nFrom " nil t)
362 (forward-char -5)
363 (insert ?>)))
6e446ebb
RS
364 (write-region (point-min) (point-max) file-name t
365 (if noattribute 'nomsg)))
49e4a58a
RS
366 (or noattribute
367 (if (equal major-mode 'rmail-mode)
368 (rmail-set-attribute "filed" t)))
369 (setq count (1- count))
32ad8f1f
RS
370 (or from-gnus
371 (let ((next-message-p
372 (if rmail-delete-after-output
373 (rmail-delete-forward)
374 (if (> count 0)
375 (rmail-next-undeleted-message 1))))
376 (num-appended (- orig-count count)))
377 (if (and next-message-p original-headers-p)
378 (rmail-toggle-header))
379 (if (and (> count 0) (not next-message-p))
693ff613 380 (progn
26b4a51d 381 (error "%s"
32ad8f1f
RS
382 (save-excursion
383 (set-buffer rmailbuf)
384 (format "Only %d message%s appended" num-appended
385 (if (= num-appended 1) "" "s"))))
386 (setq count 0))))))
49e4a58a 387 (kill-buffer tembuf))))
c88ab9ce 388
d607b17d 389;;;###autoload
a24de134 390(defun rmail-output-body-to-file (file-name)
d607b17d
RS
391 "Write this message body to the file FILE-NAME.
392FILE-NAME defaults, interactively, from the Subject field of the message."
393 (interactive
394 (let ((default-file
d1cad408
RS
395 (or (mail-fetch-field "Subject")
396 rmail-default-body-file)))
397 (list (setq rmail-default-body-file
398 (read-file-name
399 "Output message body to file: "
400 (and default-file (file-name-directory default-file))
401 default-file
402 nil default-file)))))
403 (setq file-name
404 (expand-file-name file-name
405 (and rmail-default-body-file
406 (file-name-directory rmail-default-body-file))))
d607b17d
RS
407 (save-excursion
408 (goto-char (point-min))
409 (search-forward "\n\n")
a24de134 410 (and (file-exists-p file-name)
2c1459a0 411 (not (y-or-n-p (format "File %s exists; overwrite? " file-name)))
a24de134 412 (error "Operation aborted"))
d607b17d
RS
413 (write-region (point) (point-max) file-name)
414 (if (equal major-mode 'rmail-mode)
415 (rmail-set-attribute "stored" t)))
416 (if rmail-delete-after-output
417 (rmail-delete-forward)))
418
cbee283d 419;; arch-tag: 447117c6-1a9a-4b88-aa43-3101b043e3a4
c88ab9ce 420;;; rmailout.el ends here