Switch to recommended form of GPLv3 permissions notice.
[bpt/emacs.git] / lisp / mh-e / mh-funcs.el
CommitLineData
bdcfe844 1;;; mh-funcs.el --- MH-E functions not everyone will use right away
c26cf6c8 2
e495eaec 3;; Copyright (C) 1993, 1995,
2f043267 4;; 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
a1b4049d
BW
5
6;; Author: Bill Wohler <wohler@newt.com>
7;; Maintainer: Bill Wohler <wohler@newt.com>
8;; Keywords: mail
9;; See: mh-e.el
c26cf6c8 10
60370d40 11;; This file is part of GNU Emacs.
c26cf6c8 12
5e809f55 13;; GNU Emacs is free software: you can redistribute it and/or modify
c26cf6c8 14;; it under the terms of the GNU General Public License as published by
5e809f55
GM
15;; the Free Software Foundation, either version 3 of the License, or
16;; (at your option) any later version.
c26cf6c8 17
9b7bc076 18;; GNU Emacs is distributed in the hope that it will be useful,
c26cf6c8
RS
19;; but WITHOUT ANY WARRANTY; without even the implied warranty of
20;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21;; GNU General Public License for more details.
22
23;; You should have received a copy of the GNU General Public License
5e809f55 24;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
c26cf6c8
RS
25
26;;; Commentary:
27
bdcfe844 28;; Putting these functions in a separate file lets MH-E start up faster,
b578f267 29;; since less Lisp code needs to be loaded all at once.
c26cf6c8 30
dda00b2c
BW
31;; Please add the functions in alphabetical order. If only one or two
32;; small support routines are needed, place them with the function;
33;; otherwise, create a separate section for them.
34
847b8219
KH
35;;; Change Log:
36
c26cf6c8
RS
37;;; Code:
38
c26cf6c8 39(require 'mh-e)
dda00b2c 40(require 'mh-scan)
847b8219 41
c3d9274a 42;;;###mh-autoload
c26cf6c8 43(defun mh-burst-digest ()
553fb735
BW
44 "Break up digest into separate messages\\<mh-folder-mode-map>.
45
2dcf34f9
BW
46This command uses the MH command \"burst\" to break out each
47message in the digest into its own message. Using this command,
48you can quickly delete unwanted messages, like this: Once the
49digest is split up, toggle out of MH-Folder Show mode with
50\\[mh-toggle-showing] so that the scan lines fill the screen and
51messages aren't displayed. Then use \\[mh-delete-msg] to quickly
52delete messages that you don't want to read (based on the
53\"Subject:\" header field). You can also burst the digest to
54reply directly to the people who posted the messages in the
55digest. One problem you may encounter is that the \"From:\"
56header fields are preceded with a \">\" so that your reply can't
57create the \"To:\" field correctly. In this case, you must
58correct the \"To:\" field yourself."
c26cf6c8
RS
59 (interactive)
60 (let ((digest (mh-get-msg-num t)))
61 (mh-process-or-undo-commands mh-current-folder)
c3d9274a 62 (mh-set-folder-modified-p t) ; lock folder while bursting
c26cf6c8
RS
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
c3d9274a 72;;;###mh-autoload
a66894d8 73(defun mh-copy-msg (range folder)
2be362c2
BW
74 "Copy RANGE to FOLDER\\<mh-folder-mode-map>.
75
2dcf34f9 76If you wish to copy a message to another folder, you can use this
4023e353 77command (see the \"-link\" argument to \"refile\"). Like the
2dcf34f9
BW
78command \\[mh-refile-msg], this command prompts you for the name
79of the target folder and you can specify a range. Note that
80unlike the command \\[mh-refile-msg], the copy takes place
81immediately. The original copy remains in the current folder.
a66894d8 82
2dcf34f9
BW
83Check the documentation of `mh-interactive-range' to see how
84RANGE is read in interactive use."
a66894d8 85 (interactive (list (mh-interactive-range "Copy")
c3d9274a 86 (mh-prompt-for-folder "Copy to" "" t)))
924df208 87 (let ((msg-list (let ((result ()))
a66894d8 88 (mh-iterate-on-range msg range
924df208
BW
89 (mh-notate nil mh-note-copied mh-cmd-note)
90 (push msg result))
91 result)))
3d7ca223 92 (mh-exec-cmd "refile" (mh-coalesce-msg-list msg-list)
924df208 93 "-link" "-src" mh-current-folder folder)))
c26cf6c8 94
c3d9274a 95;;;###mh-autoload
c26cf6c8 96(defun mh-kill-folder ()
d1699462
BW
97 "Remove folder.
98
2dcf34f9
BW
99Remove all of the messages (files) within the current folder, and
100then remove the folder (directory) itself.
d1699462 101
2dcf34f9
BW
102Run the abnormal hook `mh-kill-folder-suppress-prompt-hooks'. The
103hook functions are called with no arguments and should return a
104non-nil value to suppress the normal prompt when you remove a
105folder. This is useful for folders that are easily regenerated."
c26cf6c8 106 (interactive)
a66894d8 107 (if (or (run-hook-with-args-until-success
d1699462 108 'mh-kill-folder-suppress-prompt-hooks)
924df208 109 (yes-or-no-p (format "Remove folder %s (and all included messages)? "
3d7ca223
BW
110 mh-current-folder)))
111 (let ((folder mh-current-folder)
112 (window-config mh-previous-window-config))
c3d9274a 113 (mh-set-folder-modified-p t) ; lock folder to kill it
3d7ca223 114 (mh-exec-cmd-daemon "rmf" 'mh-rmf-daemon folder)
bdcfe844
BW
115 (when (boundp 'mh-speed-folder-map)
116 (mh-speed-invalidate-map folder))
3d7ca223 117 (mh-remove-from-sub-folders-cache folder)
c3d9274a 118 (mh-set-folder-modified-p nil) ; so kill-buffer doesn't complain
3d7ca223 119 (if (and mh-show-buffer (get-buffer mh-show-buffer))
c3d9274a
BW
120 (kill-buffer mh-show-buffer))
121 (if (get-buffer folder)
3d7ca223
BW
122 (kill-buffer folder))
123 (when window-config
124 (set-window-configuration window-config))
125 (message "Folder %s removed" folder))
c3d9274a 126 (message "Folder not removed")))
c26cf6c8 127
3d7ca223
BW
128(defun mh-rmf-daemon (process output)
129 "The rmf PROCESS puts OUTPUT in temporary buffer.
130Display the results only if something went wrong."
131 (set-buffer (get-buffer-create mh-temp-buffer))
132 (insert-before-markers output)
133 (when (save-excursion
e495eaec 134 (goto-char (point-min))
3d7ca223
BW
135 (re-search-forward "^rmf: " (point-max) t))
136 (display-buffer mh-temp-buffer)))
137
04f6a2d1 138;; Shush compiler.
73e6d1af 139(defvar view-exit-action)
c26cf6c8 140
c3d9274a 141;;;###mh-autoload
c26cf6c8
RS
142(defun mh-list-folders ()
143 "List mail folders."
144 (interactive)
3d7ca223 145 (let ((temp-buffer mh-folders-buffer))
a1b4049d
BW
146 (with-output-to-temp-buffer temp-buffer
147 (save-excursion
c3d9274a
BW
148 (set-buffer temp-buffer)
149 (erase-buffer)
150 (message "Listing folders...")
151 (mh-exec-cmd-output "folders" t (if mh-recursive-folders-flag
152 "-recurse"
153 "-norecurse"))
154 (goto-char (point-min))
d5dc8c56 155 (mh-view-mode-enter)
c3d9274a
BW
156 (setq view-exit-action 'kill-buffer)
157 (message "Listing folders...done")))))
158
159;;;###mh-autoload
c26cf6c8 160(defun mh-pack-folder (range)
2be362c2
BW
161 "Pack folder\\<mh-folder-mode-map>.
162
2dcf34f9
BW
163This command packs the folder, removing gaps from the numbering
164sequence. If you don't want to rescan the entire folder
165afterward, this command will accept a RANGE. Check the
166documentation of `mh-interactive-range' to see how RANGE is read
167in interactive use.
2be362c2 168
2dcf34f9
BW
169This command will ask if you want to process refiles or deletes
170first and then either run \\[mh-execute-commands] for you or undo
798b73dd 171the pending refiles and deletes."
c26cf6c8 172 (interactive (list (if current-prefix-arg
a66894d8
BW
173 (mh-read-range "Scan" mh-current-folder t nil t
174 mh-interpret-number-as-range-flag)
c3d9274a
BW
175 '("all"))))
176 (let ((threaded-flag (memq 'unthread mh-view-ops)))
177 (mh-pack-folder-1 range)
178 (mh-goto-cur-msg)
179 (when mh-index-data
180 (mh-index-update-maps mh-current-folder))
181 (cond (threaded-flag (mh-toggle-threads))
182 (mh-index-data (mh-index-insert-folder-headers))))
c26cf6c8
RS
183 (message "Packing folder...done"))
184
c26cf6c8 185(defun mh-pack-folder-1 (range)
bdcfe844 186 "Close and pack the current folder.
2be362c2
BW
187
188Display RANGE after packing, or the entire folder if RANGE is nil."
c26cf6c8
RS
189 (mh-process-or-undo-commands mh-current-folder)
190 (message "Packing folder...")
c3d9274a 191 (mh-set-folder-modified-p t) ; lock folder while packing
c26cf6c8 192 (save-excursion
847b8219 193 (mh-exec-cmd-quiet t "folder" mh-current-folder "-pack"
c3d9274a 194 "-norecurse" "-fast"))
bdcfe844 195 (mh-reset-threads-and-narrowing)
c26cf6c8
RS
196 (mh-regenerate-headers range))
197
c3d9274a 198;;;###mh-autoload
c26cf6c8 199(defun mh-page-digest ()
553fb735 200 "Display next message in digest."
c26cf6c8
RS
201 (interactive)
202 (mh-in-show-buffer (mh-show-buffer)
203 ;; Go to top of screen (in case user moved point).
204 (move-to-window-line 0)
205 (let ((case-fold-search nil))
206 ;; Search for blank line and then for From:
207 (or (and (search-forward "\n\n" nil t)
c3d9274a
BW
208 (re-search-forward "^From:" nil t))
209 (error "No more messages in digest")))
c26cf6c8
RS
210 ;; Go back to previous blank line, then forward to the first non-blank.
211 (search-backward "\n\n" nil t)
212 (forward-line 2)
213 (mh-recenter 0)))
214
c3d9274a 215;;;###mh-autoload
c26cf6c8 216(defun mh-page-digest-backwards ()
553fb735 217 "Display previous message in digest."
c26cf6c8
RS
218 (interactive)
219 (mh-in-show-buffer (mh-show-buffer)
220 ;; Go to top of screen (in case user moved point).
221 (move-to-window-line 0)
222 (let ((case-fold-search nil))
223 (beginning-of-line)
224 (or (and (search-backward "\n\n" nil t)
c3d9274a
BW
225 (re-search-backward "^From:" nil t))
226 (error "No previous message in digest")))
c26cf6c8
RS
227 ;; Go back to previous blank line, then forward to the first non-blank.
228 (if (search-backward "\n\n" nil t)
c3d9274a 229 (forward-line 2))
c26cf6c8
RS
230 (mh-recenter 0)))
231
dda00b2c
BW
232;;;###mh-autoload
233(defun mh-pipe-msg (command include-header)
234 "Pipe message through shell command COMMAND.
235
236You are prompted for the Unix command through which you wish to
237run your message. If you give a prefix argument INCLUDE-HEADER to
238this command, the message header is included in the text passed
239to the command."
240 (interactive
241 (list (read-string "Shell command on message: ") current-prefix-arg))
242 (let ((msg-file-to-pipe (mh-msg-filename (mh-get-msg-num t)))
243 (message-directory default-directory))
244 (save-excursion
245 (set-buffer (get-buffer-create mh-temp-buffer))
246 (erase-buffer)
247 (insert-file-contents msg-file-to-pipe)
248 (goto-char (point-min))
249 (if (not include-header) (search-forward "\n\n"))
250 (let ((default-directory message-directory))
251 (shell-command-on-region (point) (point-max) command nil)))))
252
c3d9274a 253;;;###mh-autoload
847b8219 254(defun mh-sort-folder (&optional extra-args)
af435184 255 "Sort folder.
2dcf34f9 256
af435184
BW
257By default, messages are sorted by date. The option
258`mh-sortm-args' holds extra arguments to pass on to the command
259\"sortm\" when a prefix argument EXTRA-ARGS is used."
c26cf6c8
RS
260 (interactive "P")
261 (mh-process-or-undo-commands mh-current-folder)
262 (setq mh-next-direction 'forward)
c3d9274a 263 (mh-set-folder-modified-p t) ; lock folder while sorting
c26cf6c8 264 (message "Sorting folder...")
c3d9274a
BW
265 (let ((threaded-flag (memq 'unthread mh-view-ops)))
266 (mh-exec-cmd "sortm" mh-current-folder (if extra-args mh-sortm-args))
267 (when mh-index-data
268 (mh-index-update-maps mh-current-folder))
269 (message "Sorting folder...done")
c3d9274a
BW
270 (mh-scan-folder mh-current-folder "all")
271 (cond (threaded-flag (mh-toggle-threads))
272 (mh-index-data (mh-index-insert-folder-headers)))))
273
c3d9274a 274;;;###mh-autoload
847b8219 275(defun mh-store-msg (directory)
5a4aad03 276 "Unpack message created with \"uudecode\" or \"shar\".
553fb735 277
2dcf34f9
BW
278The default DIRECTORY for extraction is the current directory;
279however, you have a chance to specify a different extraction
280directory. The next time you use this command, the default
281directory is the last directory you used. If you would like to
282change the initial default directory, customize the option
af435184
BW
283`mh-store-default-directory', change the value from \"Current\"
284to \"Directory\", and then enter the name of the directory for
285storing the content of these messages."
c3d9274a
BW
286 (interactive (list (let ((udir (or mh-store-default-directory
287 default-directory)))
288 (read-file-name "Store message in directory: "
289 udir udir nil))))
847b8219 290 (let ((msg-file-to-store (mh-msg-filename (mh-get-msg-num t))))
c26cf6c8 291 (save-excursion
847b8219 292 (set-buffer (get-buffer-create mh-temp-buffer))
c26cf6c8 293 (erase-buffer)
847b8219
KH
294 (insert-file-contents msg-file-to-store)
295 (mh-store-buffer directory))))
c26cf6c8 296
847b8219 297(defun mh-store-buffer (directory)
af435184 298 "Unpack buffer created with \"uudecode\" or \"shar\".
2dcf34f9 299
af435184 300See `mh-store-msg' for a description of DIRECTORY."
bdcfe844 301 (interactive (list (let ((udir (or mh-store-default-directory
c3d9274a
BW
302 default-directory)))
303 (read-file-name "Store buffer in directory: "
304 udir udir nil))))
847b8219 305 (let ((store-directory (expand-file-name directory))
c3d9274a
BW
306 (sh-start (save-excursion
307 (goto-char (point-min))
308 (if (re-search-forward
309 "^#![ \t]*/bin/sh\\|^#\\|^: " nil t)
310 (progn
311 ;; The "cut here" pattern was removed from above
312 ;; because it seemed to hurt more than help.
313 ;; But keep this to make it easier to put it back.
314 (if (looking-at "^[^a-z0-9\"]*cut here\\b")
315 (forward-line 1))
316 (beginning-of-line)
317 (if (looking-at "^[#:]....+\n\\( ?\n\\)?end$")
318 nil ;most likely end of a uuencode
319 (point))))))
c3d9274a 320 (command "sh")
924df208
BW
321 (uudecode-filename "(unknown filename)")
322 log-begin)
847b8219 323 (if (not sh-start)
c3d9274a
BW
324 (save-excursion
325 (goto-char (point-min))
326 (if (re-search-forward "^begin [0-7]+ " nil t)
327 (setq uudecode-filename
328 (buffer-substring (point)
329 (progn (end-of-line) (point)))))))
c26cf6c8 330 (save-excursion
924df208
BW
331 (set-buffer (get-buffer-create mh-log-buffer))
332 (setq log-begin (mh-truncate-log-buffer))
c26cf6c8 333 (if (not (file-directory-p store-directory))
c3d9274a
BW
334 (progn
335 (insert "mkdir " directory "\n")
924df208 336 (call-process "mkdir" nil mh-log-buffer t store-directory)))
847b8219
KH
337 (insert "cd " directory "\n")
338 (setq mh-store-default-directory directory)
339 (if (not sh-start)
c3d9274a
BW
340 (progn
341 (setq command "uudecode")
342 (insert uudecode-filename " being uudecoded...\n"))))
924df208
BW
343 (set-window-start (display-buffer mh-log-buffer) log-begin) ;watch progress
344 (let ((default-directory (file-name-as-directory store-directory)))
345 (if (equal (call-process-region sh-start (point-max) command
346 nil mh-log-buffer t)
347 0)
348 (save-excursion
349 (set-buffer mh-log-buffer)
350 (insert "\n(mh-store finished)\n"))
351 (error "Error occurred during execution of %s" command)))))
bdcfe844 352
c3d9274a 353;;;###mh-autoload
dda00b2c
BW
354(defun mh-undo-folder ()
355 "Undo all refiles and deletes in the current folder."
bdcfe844 356 (interactive)
dda00b2c
BW
357 (cond ((or mh-do-not-confirm-flag
358 (yes-or-no-p "Undo all commands in folder? "))
359 (setq mh-delete-list nil
360 mh-refile-list nil
361 mh-seq-list nil
362 mh-next-direction 'forward)
363 (with-mh-folder-updating (nil)
364 (mh-remove-all-notation)))
365 (t
366 (message "Commands not undone"))))
bdcfe844
BW
367
368(provide 'mh-funcs)
369
cee9f5c6
BW
370;; Local Variables:
371;; indent-tabs-mode: nil
372;; sentence-end-double-space: nil
373;; End:
bdcfe844 374
cee9f5c6 375;; arch-tag: 1936c4f1-4843-438e-bc4b-a63bb75a7762
60370d40 376;;; mh-funcs.el ends here