aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lisp/mail/pmailout.el416
1 files changed, 292 insertions, 124 deletions
diff --git a/lisp/mail/pmailout.el b/lisp/mail/pmailout.el
index d551e13481d..f24030e3517 100644
--- a/lisp/mail/pmailout.el
+++ b/lisp/mail/pmailout.el
@@ -1,4 +1,4 @@
1;;; pmailout.el --- "PMAIL" mail reader for Emacs: output message to a file. 1;;; pmailout.el --- "PMAIL" mail reader for Emacs: output message to a file
2 2
3;; Copyright (C) 1985, 1987, 1993, 1994, 2001, 2002, 2003, 2004, 3;; Copyright (C) 1985, 1987, 1993, 1994, 2001, 2002, 2003, 2004,
4;; 2005, 2006, 2007, 2008 Free Software Foundation, Inc. 4;; 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
@@ -25,12 +25,9 @@
25 25
26;;; Code: 26;;; Code:
27 27
28(require 'pmail)
28(provide 'pmailout) 29(provide 'pmailout)
29 30
30(eval-when-compile
31 (require 'pmail)
32 (require 'pmaildesc))
33
34;;;###autoload 31;;;###autoload
35(defcustom pmail-output-file-alist nil 32(defcustom pmail-output-file-alist nil
36 "*Alist matching regexps to suggested output Pmail files. 33 "*Alist matching regexps to suggested output Pmail files.
@@ -45,40 +42,70 @@ a file name as a string."
45 sexp))) 42 sexp)))
46 :group 'pmail-output) 43 :group 'pmail-output)
47 44
48;;;###autoload 45(defun pmail-output-read-pmail-file-name ()
49(defcustom pmail-fields-not-to-output nil 46 "Read the file name to use for `pmail-output-to-pmail-file'.
50 "*Regexp describing fields to exclude when outputting a message to a file." 47Set `pmail-default-pmail-file' to this name as well as returning it."
51 :type '(choice (const :tag "None" nil) 48 (let ((default-file
52 regexp) 49 (let (answer tail)
53 :group 'pmail-output) 50 (setq tail pmail-output-file-alist)
51 ;; Suggest a file based on a pattern match.
52 (while (and tail (not answer))
53 (save-excursion
54 (set-buffer pmail-buffer)
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 pmail-default-pmail-file)))))
61 (let ((read-file
62 (expand-file-name
63 (read-file-name
64 (concat "Output message to Pmail file (default "
65 (file-name-nondirectory default-file)
66 "): ")
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 pmail-default-pmail-file
73 (if (file-directory-p read-file)
74 (expand-file-name (file-name-nondirectory default-file)
75 read-file)
76 read-file)))))
54 77
55(defun pmail-output-read-file-name () 78(defun pmail-output-read-file-name ()
56 "Read the file name to use for `pmail-output'. 79 "Read the file name to use for `pmail-output'.
57Set `pmail-default-file' to this name as well as returning it." 80Set `pmail-default-file' to this name as well as returning it."
58 (let* ((default-file 81 (let ((default-file
59 (with-current-buffer pmail-buffer 82 (let (answer tail)
60 (expand-file-name 83 (setq tail pmail-output-file-alist)
61 (or (catch 'answer 84 ;; Suggest a file based on a pattern match.
62 (dolist (i pmail-output-file-alist) 85 (while (and tail (not answer))
63 (goto-char (point-min)) 86 (save-excursion
64 (when (re-search-forward (car i) nil t) 87 (goto-char (point-min))
65 (throw 'answer (eval (cdr i)))))) 88 (if (re-search-forward (car (car tail)) nil t)
66 pmail-default-file)))) 89 (setq answer (eval (cdr (car tail)))))
67 (read-file 90 (setq tail (cdr tail))))
68 (expand-file-name 91 ;; If no suggestion, use same file as last time.
69 (read-file-name 92 (or answer pmail-default-file))))
70 (concat "Output message to Pmail (mbox) file: (default " 93 (let ((read-file
71 (file-name-nondirectory default-file) "): ") 94 (expand-file-name
72 (file-name-directory default-file) 95 (read-file-name
73 (abbreviate-file-name default-file)) 96 (concat "Output message to Unix mail file (default "
74 (file-name-directory default-file)))) 97 (file-name-nondirectory default-file)
75 (setq pmail-default-file 98 "): ")
76 (if (file-directory-p read-file) 99 (file-name-directory default-file)
100 (abbreviate-file-name default-file))
101 (file-name-directory default-file))))
102 (setq pmail-default-file
103 (if (file-directory-p read-file)
104 (expand-file-name (file-name-nondirectory default-file)
105 read-file)
77 (expand-file-name 106 (expand-file-name
78 (file-name-nondirectory default-file) read-file) 107 (or read-file (file-name-nondirectory default-file))
79 (expand-file-name 108 (file-name-directory default-file)))))))
80 (or read-file (file-name-nondirectory default-file))
81 (file-name-directory default-file))))))
82 109
83(declare-function pmail-update-summary "pmailsum" (&rest ignore)) 110(declare-function pmail-update-summary "pmailsum" (&rest ignore))
84 111
@@ -86,7 +113,7 @@ Set `pmail-default-file' to this name as well as returning it."
86;;; look at them before you change the calling method. 113;;; look at them before you change the calling method.
87;;;###autoload 114;;;###autoload
88(defun pmail-output-to-pmail-file (file-name &optional count stay) 115(defun pmail-output-to-pmail-file (file-name &optional count stay)
89 "Append the current message to an Pmail (mbox) file named FILE-NAME. 116 "Append the current message to an Pmail file named FILE-NAME.
90If the file does not exist, ask if it should be created. 117If the file does not exist, ask if it should be created.
91If file is being visited, the message is appended to the Emacs 118If file is being visited, the message is appended to the Emacs
92buffer visiting that file. 119buffer visiting that file.
@@ -101,35 +128,137 @@ starting with the current one. Deleted messages are skipped and don't count.
101 128
102If the optional argument STAY is non-nil, then leave the last filed 129If the optional argument STAY is non-nil, then leave the last filed
103message up instead of moving forward to the next non-deleted message." 130message up instead of moving forward to the next non-deleted message."
104 (interactive (list (pmail-output-read-file-name) 131 (interactive
105 (prefix-numeric-value current-prefix-arg))) 132 (list (pmail-output-read-pmail-file-name)
106 ;; Use the 'pmail-output function to perform the output. 133 (prefix-numeric-value current-prefix-arg)))
107 (pmail-output file-name count nil nil) 134 (or count (setq count 1))
108 ;; Deal with the next message 135 (setq file-name
109 (if pmail-delete-after-output 136 (expand-file-name file-name
110 (unless (if (and (= count 0) stay) 137 (file-name-directory pmail-default-pmail-file)))
138 (if (and (file-readable-p file-name) (not (mail-file-babyl-p file-name)))
139 (pmail-output file-name count)
140 (pmail-maybe-set-message-counters)
141 (setq file-name (abbreviate-file-name file-name))
142 (or (find-buffer-visiting file-name)
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)))
147 (save-excursion
148 (set-buffer file-buffer)
149 (pmail-insert-pmail-file-header)
150 (let ((require-final-newline nil)
151 (coding-system-for-write
152 (or pmail-file-coding-system
153 'emacs-mule-unix)))
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
161 (set-buffer pmail-buffer)
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 (pmail-message-deleted-p pmail-current-message)
166 (progn (setq redelete t)
167 (pmail-set-attribute "deleted" nil)))
168 (save-restriction
169 (widen)
170 ;; Decide whether to append to a file or to an Emacs buffer.
171 (save-excursion
172 (let ((buf (find-buffer-visiting file-name))
173 (cur (current-buffer))
174 (beg (1+ (pmail-msgbeg pmail-current-message)))
175 (end (1+ (pmail-msgend pmail-current-message)))
176 (coding-system-for-write
177 (or pmail-file-coding-system
178 'emacs-mule-unix)))
179 (if (not buf)
180 ;; Output to a file.
181 (if pmail-fields-not-to-output
182 ;; Delete some fields while we output.
183 (let ((obuf (current-buffer)))
184 (set-buffer (get-buffer-create " pmail-out-temp"))
185 (insert-buffer-substring obuf beg end)
186 (pmail-delete-unwanted-fields)
187 (append-to-file (point-min) (point-max) file-name)
188 (set-buffer obuf)
189 (kill-buffer (get-buffer " pmail-out-temp")))
190 (append-to-file beg end file-name))
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 'pmail-current-message)
197 pmail-current-message)))
198 ;; If MSG is non-nil, buffer is in PMAIL mode.
199 (if msg
200 (progn
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))
206 (pmail-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))
214 (pmail-delete-unwanted-fields)
215 (pmail-count-new-messages t)
216 (if (pmail-summary-exists)
217 (pmail-select-summary
218 (pmail-update-summary)))
219 (pmail-show-message msg))
220 ;; Output file not in pmail 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 (pmail-delete-unwanted-fields)))))))
225 (pmail-set-attribute "filed" t))
226 (if redelete (pmail-set-attribute "deleted" t))))
227 (setq count (1- count))
228 (if pmail-delete-after-output
229 (unless
230 (if (and (= count 0) stay)
111 (pmail-delete-message) 231 (pmail-delete-message)
112 (pmail-delete-forward)) 232 (pmail-delete-forward))
113 (setq count 0)) 233 (setq count 0))
114 (when (> count 0) 234 (if (> count 0)
115 (unless (when (not stay) 235 (unless
116 (pmail-next-undeleted-message 1)) 236 (if (not stay) (pmail-next-undeleted-message 1))
117 (setq count 0))))) 237 (setq count 0)))))))
118 238
119(defun pmail-delete-unwanted-fields () 239;;;###autoload
120 "Delete from the buffer header fields we don't want output." 240(defcustom pmail-fields-not-to-output nil
121 (when pmail-fields-not-to-output 241 "*Regexp describing fields to exclude when outputting a message to a file."
122 (save-excursion 242 :type '(choice (const :tag "None" nil)
123 (let ((limit (pmail-header-get-limit)) 243 regexp)
124 (inhibit-point-motion-hooks t) 244 :group 'pmail-output)
125 start) 245
246;; Delete from the buffer header fields we don't want output.
247;; NOT-PMAIL if t means this buffer does not have the full header
248;; and *** EOOH *** that a message in an Pmail file has.
249(defun pmail-delete-unwanted-fields (&optional not-pmail)
250 (if pmail-fields-not-to-output
251 (save-excursion
126 (goto-char (point-min)) 252 (goto-char (point-min))
127 (while (re-search-forward pmail-fields-not-to-output limit t) 253 ;; Find the end of the header.
128 (forward-line 0) 254 (if (and (or not-pmail (search-forward "\n*** EOOH ***\n" nil t))
129 (setq start (point)) 255 (search-forward "\n\n" nil t))
130 (while (progn (forward-line 1) (looking-at "[ \t]+")) 256 (let ((end (point-marker)))
131 (goto-char (line-end-position))) 257 (goto-char (point-min))
132 (delete-region start (point))))))) 258 (while (re-search-forward pmail-fields-not-to-output end t)
259 (beginning-of-line)
260 (delete-region (point)
261 (progn (forward-line 1) (point)))))))))
133 262
134;;; There are functions elsewhere in Emacs that use this function; 263;;; There are functions elsewhere in Emacs that use this function;
135;;; look at them before you change the calling method. 264;;; look at them before you change the calling method.
@@ -160,71 +289,111 @@ The optional fourth argument FROM-GNUS is set when called from GNUS."
160 (and pmail-default-file 289 (and pmail-default-file
161 (file-name-directory pmail-default-file)))) 290 (file-name-directory pmail-default-file))))
162 (if (and (file-readable-p file-name) (mail-file-babyl-p file-name)) 291 (if (and (file-readable-p file-name) (mail-file-babyl-p file-name))
163 (error "BABYL output not supported.") 292 (pmail-output-to-pmail-file file-name count)
164 (with-current-buffer pmail-buffer 293 (set-buffer pmail-buffer)
165 (let ((orig-count count) 294 (let ((orig-count count)
166 (pmailbuf (current-buffer)) 295 (pmailbuf (current-buffer))
167 (destbuf (find-buffer-visiting file-name)) 296 (case-fold-search t)
168 (case-fold-search t)) 297 (tembuf (get-buffer-create " pmail-output"))
169 (while (> count 0) 298 (original-headers-p
170 (with-temp-buffer 299 (and (not from-gnus)
171 (insert-buffer-substring pmailbuf) 300 (save-excursion
172 ;; ensure we can write without barfing on exotic characters 301 (save-restriction
173 (setq buffer-file-coding-system 302 (narrow-to-region (pmail-msgbeg pmail-current-message) (point-max))
174 (or pmail-file-coding-system 'raw-text)) 303 (goto-char (point-min))
175 ;; prune junk headers 304 (forward-line 1)
176 (pmail-delete-unwanted-fields) 305 (= (following-char) ?0)))))
177 (if (not destbuf) 306 header-beginning
178 ;; The destination file is not being visited, just write 307 mail-from mime-version content-type)
179 ;; out the processed message. 308 (while (> count 0)
180 (write-region (point-min) (point-max) file-name 309 ;; Preserve the Mail-From and MIME-Version fields
181 t (when noattribute 'nomsg)) 310 ;; even if they have been pruned.
182 ;; The destination file is being visited. Update it. 311 (or from-gnus
183 (let ((msg-string (buffer-string))) 312 (save-excursion
184 (with-current-buffer destbuf 313 (save-restriction
185 ;; Determine if the destination file is an Pmail file. 314 (widen)
186 (let ((buffer-read-only nil) 315 (goto-char (pmail-msgbeg pmail-current-message))
187 (dest-current-message 316 (setq header-beginning (point))
188 (and (boundp 'pmail-current-message) 317 (search-forward "\n*** EOOH ***\n")
189 pmail-current-message))) 318 (narrow-to-region header-beginning (point))
190 (if dest-current-message 319 (setq mail-from (mail-fetch-field "Mail-From"))
191 ;; The buffer is an Pmail buffer. Append the 320 (unless pmail-enable-mime
192 ;; message. 321 (setq mime-version (mail-fetch-field "MIME-Version")
193 (progn 322 content-type (mail-fetch-field "Content-type"))))))
194 (widen) 323 (save-excursion
195 (narrow-to-region (point-max) (point-max)) 324 (set-buffer tembuf)
196 (insert msg-string) 325 (erase-buffer)
197 (insert "\n") 326 (insert-buffer-substring pmailbuf)
198 (pmail-process-new-messages) 327 (when pmail-enable-mime
199 (pmail-show-message dest-current-message)) 328 (if original-headers-p
200 ;; The destination file is not an Pmail file, just 329 (delete-region (goto-char (point-min))
201 ;; insert at the end. 330 (if (search-forward "\n*** EOOH ***\n")
202 (goto-char (point-max)) 331 (match-end 0)))
203 (insert msg-string))))))) 332 (goto-char (point-min))
204 (unless noattribute 333 (forward-line 2)
205 (when (equal major-mode 'pmail-mode) 334 (delete-region (point-min)(point))
206 (pmail-set-attribute "filed" t) 335 (search-forward "\n*** EOOH ***\n")
207 (pmail-header-hide-headers))) 336 (delete-region (match-beginning 0)
208 (setq count (1- count)) 337 (if (search-forward "\n\n")
209 (unless from-gnus 338 (1- (match-end 0)))))
339 (setq buffer-file-coding-system (or pmail-file-coding-system
340 'raw-text)))
341 (pmail-delete-unwanted-fields t)
342 (or (bolp) (insert "\n"))
343 (goto-char (point-min))
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"))
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")))
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.)
360 (let ((case-fold-search nil))
361 (while (search-forward "\nFrom " nil t)
362 (forward-char -5)
363 (insert ?>)))
364 (write-region (point-min) (point-max) file-name t
365 (if noattribute 'nomsg)))
366 (or noattribute
367 (if (equal major-mode 'pmail-mode)
368 (pmail-set-attribute "filed" t)))
369 (setq count (1- count))
370 (or from-gnus
210 (let ((next-message-p 371 (let ((next-message-p
211 (if pmail-delete-after-output 372 (if pmail-delete-after-output
212 (pmail-delete-forward) 373 (pmail-delete-forward)
213 (when (> count 0) 374 (if (> count 0)
214 (pmail-next-undeleted-message 1)))) 375 (pmail-next-undeleted-message 1))))
215 (num-appended (- orig-count count))) 376 (num-appended (- orig-count count)))
216 (when (and (> count 0) (not next-message-p)) 377 (if (and next-message-p original-headers-p)
217 (error (format "Only %d message%s appended" num-appended 378 (pmail-toggle-header))
218 (if (= num-appended 1) "" "s"))) 379 (if (and (> count 0) (not next-message-p))
219 (setq count 0))))))))) 380 (progn
381 (error "%s"
382 (save-excursion
383 (set-buffer pmailbuf)
384 (format "Only %d message%s appended" num-appended
385 (if (= num-appended 1) "" "s"))))
386 (setq count 0))))))
387 (kill-buffer tembuf))))
220 388
221;;;###autoload 389;;;###autoload
222(defun pmail-output-body-to-file (file-name) 390(defun pmail-output-body-to-file (file-name)
223 "Write this message body to the file FILE-NAME. 391 "Write this message body to the file FILE-NAME.
224FILE-NAME defaults, interactively, from the Subject field of the message." 392FILE-NAME defaults, interactively, from the Subject field of the message."
225 (interactive 393 (interactive
226 (let ((default-file (or (mail-fetch-field "Subject") 394 (let ((default-file
227 pmail-default-body-file))) 395 (or (mail-fetch-field "Subject")
396 pmail-default-body-file)))
228 (list (setq pmail-default-body-file 397 (list (setq pmail-default-body-file
229 (read-file-name 398 (read-file-name
230 "Output message body to file: " 399 "Output message body to file: "
@@ -232,21 +401,20 @@ FILE-NAME defaults, interactively, from the Subject field of the message."
232 default-file 401 default-file
233 nil default-file))))) 402 nil default-file)))))
234 (setq file-name 403 (setq file-name
235 (expand-file-name 404 (expand-file-name file-name
236 file-name 405 (and pmail-default-body-file
237 (and pmail-default-body-file 406 (file-name-directory pmail-default-body-file))))
238 (file-name-directory pmail-default-body-file))))
239 (save-excursion 407 (save-excursion
240 (goto-char (point-min)) 408 (goto-char (point-min))
241 (search-forward "\n\n") 409 (search-forward "\n\n")
242 (and (file-exists-p file-name) 410 (and (file-exists-p file-name)
243 (not (y-or-n-p (message "File %s exists; overwrite? " file-name))) 411 (not (y-or-n-p (format "File %s exists; overwrite? " file-name)))
244 (error "Operation aborted")) 412 (error "Operation aborted"))
245 (write-region (point) (point-max) file-name) 413 (write-region (point) (point-max) file-name)
246 (when (equal major-mode 'pmail-mode) 414 (if (equal major-mode 'pmail-mode)
247 (pmail-desc-set-attribute pmail-current-message pmail-desc-stored-index t))) 415 (pmail-set-attribute "stored" t)))
248 (when pmail-delete-after-output 416 (if pmail-delete-after-output
249 (pmail-delete-forward))) 417 (pmail-delete-forward)))
250 418
251;; Local Variables: 419;; Local Variables:
252;; change-log-default-name: "ChangeLog.pmail" 420;; change-log-default-name: "ChangeLog.pmail"