Update FSF's address.
[bpt/emacs.git] / lisp / mail / mh-funcs.el
CommitLineData
c26cf6c8 1;;; mh-funcs --- mh-e functions not everyone will use right away
b4b1e78a 2;; Time-stamp: <95/08/19 16:44:06 gildea>
c26cf6c8 3
847b8219 4;; Copyright (C) 1993, 1995 Free Software Foundation, Inc.
c26cf6c8 5
b4b1e78a 6;; This file is part of mh-e, part of GNU Emacs.
c26cf6c8 7
9b7bc076 8;; GNU Emacs is free software; you can redistribute it and/or modify
c26cf6c8
RS
9;; it under the terms of the GNU General Public License as published by
10;; the Free Software Foundation; either version 2, or (at your option)
11;; any later version.
12
9b7bc076 13;; GNU Emacs is distributed in the hope that it will be useful,
c26cf6c8
RS
14;; but WITHOUT ANY WARRANTY; without even the implied warranty of
15;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16;; GNU General Public License for more details.
17
18;; You should have received a copy of the GNU General Public License
b578f267
EN
19;; along with GNU Emacs; see the file COPYING. If not, write to the
20;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21;; Boston, MA 02111-1307, USA.
c26cf6c8
RS
22
23;;; Commentary:
24
b578f267
EN
25;; Internal support for mh-e package.
26;; Putting these functions in a separate file lets mh-e start up faster,
27;; since less Lisp code needs to be loaded all at once.
c26cf6c8 28
847b8219
KH
29;;; Change Log:
30
b578f267 31;; $Id: mh-funcs.el,v 1.4 1995/11/03 02:29:34 kwzh Exp erik $
847b8219 32
c26cf6c8
RS
33;;; Code:
34
35(provide 'mh-funcs)
36(require 'mh-e)
37
847b8219
KH
38;;; customization
39
c26cf6c8
RS
40(defvar mh-sortm-args nil
41 "Extra arguments to have \\[mh-sort-folder] pass to the \"sortm\" command.
847b8219
KH
42The arguments are passed to sortm if \\[mh-sort-folder] is given a
43prefix argument. Normally default arguments to sortm are specified in the
44MH profile.
c26cf6c8
RS
45For example, '(\"-nolimit\" \"-textfield\" \"subject\") is a useful setting.")
46
847b8219
KH
47(defvar mh-note-copied "C"
48 "String whose first character is used to notate copied messages.")
49
50(defvar mh-note-printed "P"
51 "String whose first character is used to notate printed messages.")
52
53;;; functions
54
c26cf6c8
RS
55(defun mh-burst-digest ()
56 "Burst apart the current message, which should be a digest.
847b8219 57The message is replaced by its table of contents and the messages from the
c26cf6c8
RS
58digest are inserted into the folder after that message."
59 (interactive)
60 (let ((digest (mh-get-msg-num t)))
61 (mh-process-or-undo-commands mh-current-folder)
62 (mh-set-folder-modified-p t) ; lock folder while bursting
63 (message "Bursting digest...")
64 (mh-exec-cmd "burst" mh-current-folder digest "-inplace")
847b8219
KH
65 (with-mh-folder-updating (t)
66 (beginning-of-line)
67 (delete-region (point) (point-max)))
68 (mh-regenerate-headers (format "%d-last" digest) t)
69 (mh-goto-cur-msg)
c26cf6c8
RS
70 (message "Bursting digest...done")))
71
72
847b8219
KH
73(defun mh-copy-msg (msg-or-seq folder)
74 "Copy the specified MESSAGE(s) to another FOLDER without deleting them.
c26cf6c8
RS
75Default is the displayed message. If optional prefix argument is
76provided, then prompt for the message sequence."
847b8219 77 (interactive (list (if current-prefix-arg
c26cf6c8 78 (mh-read-seq-default "Copy" t)
847b8219
KH
79 (mh-get-msg-num t))
80 (mh-prompt-for-folder "Copy to" "" t)))
81 (mh-exec-cmd "refile" msg-or-seq "-link" "-src" mh-current-folder folder)
c26cf6c8 82 (if (numberp msg-or-seq)
847b8219
KH
83 (mh-notate msg-or-seq mh-note-copied mh-cmd-note)
84 (mh-notate-seq msg-or-seq mh-note-copied mh-cmd-note)))
c26cf6c8
RS
85
86(defun mh-kill-folder ()
87 "Remove the current folder."
88 (interactive)
89 (if (or mh-do-not-confirm
90 (yes-or-no-p (format "Remove folder %s? " mh-current-folder)))
91 (let ((folder mh-current-folder))
92 (if (null mh-folder-list)
93 (mh-set-folder-list))
94 (mh-set-folder-modified-p t) ; lock folder to kill it
95 (mh-exec-cmd-daemon "rmf" folder)
96 (setq mh-folder-list
97 (delq (assoc folder mh-folder-list) mh-folder-list))
847b8219 98 (run-hooks 'mh-folder-list-change-hook)
c26cf6c8
RS
99 (message "Folder %s removed" folder)
100 (mh-set-folder-modified-p nil) ; so kill-buffer doesn't complain
101 (if (get-buffer mh-show-buffer)
102 (kill-buffer mh-show-buffer))
103 (kill-buffer folder))
104 (message "Folder not removed")))
105
106
107(defun mh-list-folders ()
108 "List mail folders."
109 (interactive)
847b8219 110 (with-output-to-temp-buffer mh-temp-buffer
c26cf6c8 111 (save-excursion
847b8219 112 (switch-to-buffer mh-temp-buffer)
c26cf6c8
RS
113 (erase-buffer)
114 (message "Listing folders...")
115 (mh-exec-cmd-output "folders" t (if mh-recursive-folders
116 "-recurse"
117 "-norecurse"))
118 (goto-char (point-min))
119 (message "Listing folders...done"))))
120
121
122(defun mh-pack-folder (range)
123 "Renumber the messages of a folder to be 1..n.
124First, offer to execute any outstanding commands for the current folder.
847b8219 125If optional prefix argument provided, prompt for the RANGE of messages
c26cf6c8
RS
126to display after packing. Otherwise, show the entire folder."
127 (interactive (list (if current-prefix-arg
128 (mh-read-msg-range
129 "Range to scan after packing [all]? ")
130 "all")))
131 (mh-pack-folder-1 range)
132 (mh-goto-cur-msg)
133 (message "Packing folder...done"))
134
135
136(defun mh-pack-folder-1 (range)
137 ;; Close and pack the current folder.
138 (mh-process-or-undo-commands mh-current-folder)
139 (message "Packing folder...")
140 (mh-set-folder-modified-p t) ; lock folder while packing
141 (save-excursion
847b8219
KH
142 (mh-exec-cmd-quiet t "folder" mh-current-folder "-pack"
143 "-norecurse" "-fast"))
c26cf6c8
RS
144 (mh-regenerate-headers range))
145
146
147(defun mh-pipe-msg (command include-headers)
148 "Pipe the current message through the given shell COMMAND.
149If INCLUDE-HEADERS (prefix argument) is provided, send the entire message.
150Otherwise just send the message's body without the headers."
151 (interactive
152 (list (read-string "Shell command on message: ") current-prefix-arg))
847b8219
KH
153 (let ((msg-file-to-pipe (mh-msg-filename (mh-get-msg-num t)))
154 (message-directory default-directory))
c26cf6c8 155 (save-excursion
847b8219 156 (set-buffer (get-buffer-create mh-temp-buffer))
c26cf6c8 157 (erase-buffer)
847b8219 158 (insert-file-contents msg-file-to-pipe)
c26cf6c8
RS
159 (goto-char (point-min))
160 (if (not include-headers) (search-forward "\n\n"))
847b8219
KH
161 (let ((default-directory message-directory))
162 (shell-command-on-region (point) (point-max) command nil)))))
c26cf6c8
RS
163
164
165(defun mh-page-digest ()
166 "Advance displayed message to next digested message."
167 (interactive)
168 (mh-in-show-buffer (mh-show-buffer)
169 ;; Go to top of screen (in case user moved point).
170 (move-to-window-line 0)
171 (let ((case-fold-search nil))
172 ;; Search for blank line and then for From:
173 (or (and (search-forward "\n\n" nil t)
847b8219 174 (re-search-forward "^From:" nil t))
c26cf6c8
RS
175 (error "No more messages in digest")))
176 ;; Go back to previous blank line, then forward to the first non-blank.
177 (search-backward "\n\n" nil t)
178 (forward-line 2)
179 (mh-recenter 0)))
180
181
182(defun mh-page-digest-backwards ()
183 "Back up displayed message to previous digested message."
184 (interactive)
185 (mh-in-show-buffer (mh-show-buffer)
186 ;; Go to top of screen (in case user moved point).
187 (move-to-window-line 0)
188 (let ((case-fold-search nil))
189 (beginning-of-line)
190 (or (and (search-backward "\n\n" nil t)
847b8219 191 (re-search-backward "^From:" nil t))
c26cf6c8
RS
192 (error "No previous message in digest")))
193 ;; Go back to previous blank line, then forward to the first non-blank.
194 (if (search-backward "\n\n" nil t)
195 (forward-line 2))
196 (mh-recenter 0)))
197
198
199(defun mh-print-msg (msg-or-seq)
200 "Print MESSAGE(s) (default: displayed message) on printer.
201If optional prefix argument provided, then prompt for the message sequence.
202The variable mh-lpr-command-format is used to generate the print command.
203The messages are formatted by mhl. See the variable mhl-formfile."
204 (interactive (list (if current-prefix-arg
205 (reverse (mh-seq-to-msgs
206 (mh-read-seq-default "Print" t)))
207 (mh-get-msg-num t))))
208 (if (numberp msg-or-seq)
209 (message "Printing message...")
210 (message "Printing sequence..."))
211 (let ((print-command
212 (if (numberp msg-or-seq)
213 (format "%s -nobell -clear %s %s | %s"
214 (expand-file-name "mhl" mh-lib)
215 (mh-msg-filename msg-or-seq)
216 (if (stringp mhl-formfile)
217 (format "-form %s" mhl-formfile)
218 "")
219 (format mh-lpr-command-format
220 (if (numberp msg-or-seq)
221 (format "%s/%d" mh-current-folder
222 msg-or-seq)
223 (format "Sequence from %s" mh-current-folder))))
224 (format "(scan -clear %s ; %s -nobell -clear %s %s) | %s"
225 (mapconcat (function (lambda (msg) msg)) msg-or-seq " ")
226 (expand-file-name "mhl" mh-lib)
227 (if (stringp mhl-formfile)
228 (format "-form %s" mhl-formfile)
229 "")
230 (mh-msg-filenames msg-or-seq)
231 (format mh-lpr-command-format
232 (if (numberp msg-or-seq)
233 (format "%s/%d" mh-current-folder
234 msg-or-seq)
235 (format "Sequence from %s"
236 mh-current-folder)))))))
237 (if mh-print-background
238 (mh-exec-cmd-daemon shell-file-name "-c" print-command)
239 (call-process shell-file-name nil nil nil "-c" print-command))
240 (if (numberp msg-or-seq)
847b8219
KH
241 (mh-notate msg-or-seq mh-note-printed mh-cmd-note)
242 (mh-notate-seq msg-or-seq mh-note-printed mh-cmd-note))
c26cf6c8
RS
243 (mh-add-msgs-to-seq msg-or-seq 'printed t)
244 (if (numberp msg-or-seq)
245 (message "Printing message...done")
246 (message "Printing sequence...done"))))
247
248
249(defun mh-msg-filenames (msgs &optional folder)
250 ;; Return a list of file names for MSGS in FOLDER (default current folder).
251 (mapconcat (function (lambda (msg) (mh-msg-filename msg folder))) msgs " "))
252
253
847b8219 254(defun mh-sort-folder (&optional extra-args)
c26cf6c8
RS
255 "Sort the messages in the current folder by date.
256Calls the MH program sortm to do the work.
257The arguments in the list mh-sortm-args are passed to sortm
847b8219 258if this function is passed an argument."
c26cf6c8
RS
259 (interactive "P")
260 (mh-process-or-undo-commands mh-current-folder)
261 (setq mh-next-direction 'forward)
262 (mh-set-folder-modified-p t) ; lock folder while sorting
263 (message "Sorting folder...")
847b8219 264 (mh-exec-cmd "sortm" mh-current-folder (if extra-args mh-sortm-args))
c26cf6c8
RS
265 (message "Sorting folder...done")
266 (mh-scan-folder mh-current-folder "all"))
267
268
269(defun mh-undo-folder (&rest ignore)
847b8219 270 "Undo all pending deletes and refiles in current folder."
c26cf6c8
RS
271 (interactive)
272 (cond ((or mh-do-not-confirm
273 (yes-or-no-p "Undo all commands in folder? "))
274 (setq mh-delete-list nil
275 mh-refile-list nil
276 mh-seq-list nil
277 mh-next-direction 'forward)
278 (with-mh-folder-updating (nil)
279 (mh-unmark-all-headers t)))
280 (t
281 (message "Commands not undone.")
282 (sit-for 2))))
283
284
847b8219 285(defun mh-store-msg (directory)
c26cf6c8
RS
286 "Store the file(s) contained in the current message into DIRECTORY.
287The message can contain a shar file or uuencoded file.
288Default directory is the last directory used, or initially the value of
289mh-store-default-directory or the current directory."
290 (interactive (list (let ((udir (or mh-store-default-directory default-directory)))
291 (read-file-name "Store message in directory: "
292 udir udir nil))))
847b8219 293 (let ((msg-file-to-store (mh-msg-filename (mh-get-msg-num t))))
c26cf6c8 294 (save-excursion
847b8219 295 (set-buffer (get-buffer-create mh-temp-buffer))
c26cf6c8 296 (erase-buffer)
847b8219
KH
297 (insert-file-contents msg-file-to-store)
298 (mh-store-buffer directory))))
c26cf6c8 299
847b8219 300(defun mh-store-buffer (directory)
c26cf6c8
RS
301 "Store the file(s) contained in the current buffer into DIRECTORY.
302The buffer can contain a shar file or uuencoded file.
303Default directory is the last directory used, or initially the value of
304`mh-store-default-directory' or the current directory."
305 (interactive (list (let ((udir (or mh-store-default-directory default-directory)))
847b8219
KH
306 (read-file-name "Store buffer in directory: "
307 udir udir nil))))
308 (let ((store-directory (expand-file-name directory))
309 (sh-start (save-excursion
310 (goto-char (point-min))
311 (if (re-search-forward
312 "^#![ \t]*/bin/sh\\|^#\\|^: " nil t)
313 (progn
314 ;; The "cut here" pattern was removed from above
315 ;; because it seemed to hurt more than help.
316 ;; But keep this to make it easier to put it back.
317 (if (looking-at "^[^a-z0-9\"]*cut here\\b")
c26cf6c8 318 (forward-line 1))
847b8219
KH
319 (beginning-of-line)
320 (if (looking-at "^[#:]....+\n\\( ?\n\\)?end$")
321 nil ;most likely end of a uuencode
322 (point))))))
c26cf6c8 323 (log-buffer (get-buffer-create "*Store Output*"))
847b8219
KH
324 (command "sh")
325 (uudecode-filename "(unknown filename)"))
326 (if (not sh-start)
327 (save-excursion
328 (goto-char (point-min))
329 (if (re-search-forward "^begin [0-7]+ " nil t)
330 (setq uudecode-filename
331 (buffer-substring (point)
332 (progn (end-of-line) (point)))))))
c26cf6c8
RS
333 (save-excursion
334 (set-buffer log-buffer)
335 (erase-buffer)
336 (if (not (file-directory-p store-directory))
337 (progn
847b8219 338 (insert "mkdir " directory "\n")
c26cf6c8 339 (call-process "mkdir" nil log-buffer t store-directory)))
847b8219
KH
340 (insert "cd " directory "\n")
341 (setq mh-store-default-directory directory)
342 (if (not sh-start)
c26cf6c8
RS
343 (progn
344 (setq command "uudecode")
847b8219 345 (insert uudecode-filename " being uudecoded...\n"))))
c26cf6c8 346 (set-window-start (display-buffer log-buffer) 0) ;watch progress
847b8219
KH
347 (let (value)
348 (let ((default-directory (file-name-as-directory store-directory)))
349 (setq value (call-process-region sh-start (point-max) command
350 nil log-buffer t)))
351 (set-buffer log-buffer)
352 (mh-handle-process-error command value))
c26cf6c8
RS
353 (insert "\n(mh-store finished)\n")))
354