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