Sync to HEAD
[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
924df208 3;; Copyright (C) 1993, 1995, 2001, 02, 2003 Free Software Foundation, Inc.
a1b4049d
BW
4
5;; Author: Bill Wohler <wohler@newt.com>
6;; Maintainer: Bill Wohler <wohler@newt.com>
7;; Keywords: mail
8;; See: mh-e.el
c26cf6c8 9
60370d40 10;; This file is part of GNU Emacs.
c26cf6c8 11
9b7bc076 12;; GNU Emacs is free software; you can redistribute it and/or modify
c26cf6c8
RS
13;; it under the terms of the GNU General Public License as published by
14;; the Free Software Foundation; either version 2, or (at your option)
15;; any later version.
16
9b7bc076 17;; GNU Emacs is distributed in the hope that it will be useful,
c26cf6c8
RS
18;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20;; GNU General Public License for more details.
21
22;; You should have received a copy of the GNU General Public License
b578f267
EN
23;; along with GNU Emacs; see the file COPYING. If not, write to the
24;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
25;; Boston, MA 02111-1307, USA.
c26cf6c8
RS
26
27;;; Commentary:
28
bdcfe844
BW
29;; Internal support for MH-E package.
30;; Putting these functions in a separate file lets MH-E start up faster,
b578f267 31;; since less Lisp code needs to be loaded all at once.
c26cf6c8 32
847b8219
KH
33;;; Change Log:
34
c26cf6c8
RS
35;;; Code:
36
c26cf6c8
RS
37(require 'mh-e)
38
c3d9274a 39;;; Customization
847b8219 40
c26cf6c8
RS
41(defvar mh-sortm-args nil
42 "Extra arguments to have \\[mh-sort-folder] pass to the \"sortm\" command.
847b8219
KH
43The arguments are passed to sortm if \\[mh-sort-folder] is given a
44prefix argument. Normally default arguments to sortm are specified in the
45MH profile.
c26cf6c8
RS
46For example, '(\"-nolimit\" \"-textfield\" \"subject\") is a useful setting.")
47
847b8219
KH
48(defvar mh-note-copied "C"
49 "String whose first character is used to notate copied messages.")
50
51(defvar mh-note-printed "P"
52 "String whose first character is used to notate printed messages.")
53
bdcfe844 54;;; Functions
847b8219 55
c3d9274a 56;;;###mh-autoload
c26cf6c8
RS
57(defun mh-burst-digest ()
58 "Burst apart the current message, which should be a digest.
847b8219 59The message is replaced by its table of contents and the messages from the
c26cf6c8
RS
60digest are inserted into the folder after that message."
61 (interactive)
62 (let ((digest (mh-get-msg-num t)))
63 (mh-process-or-undo-commands mh-current-folder)
c3d9274a 64 (mh-set-folder-modified-p t) ; lock folder while bursting
c26cf6c8
RS
65 (message "Bursting digest...")
66 (mh-exec-cmd "burst" mh-current-folder digest "-inplace")
847b8219
KH
67 (with-mh-folder-updating (t)
68 (beginning-of-line)
69 (delete-region (point) (point-max)))
70 (mh-regenerate-headers (format "%d-last" digest) t)
71 (mh-goto-cur-msg)
c26cf6c8
RS
72 (message "Bursting digest...done")))
73
c3d9274a 74;;;###mh-autoload
847b8219 75(defun mh-copy-msg (msg-or-seq folder)
a1b4049d 76 "Copy the specified MSG-OR-SEQ to another FOLDER without deleting them.
924df208
BW
77Default is the displayed message.
78If optional prefix argument is provided, then prompt for the message sequence.
79If variable `transient-mark-mode' is non-nil and the mark is active, then the
80selected region is copied.
81In a program, MSG-OR-SEQ can be a message number, a list of message numbers, a
82region in a cons cell, or a sequence."
83 (interactive (list (mh-interactive-msg-or-seq "Copy")
c3d9274a 84 (mh-prompt-for-folder "Copy to" "" t)))
924df208
BW
85 (let ((msg-list (let ((result ()))
86 (mh-iterate-on-msg-or-seq msg msg-or-seq
87 (mh-notate nil mh-note-copied mh-cmd-note)
88 (push msg result))
89 result)))
3d7ca223 90 (mh-exec-cmd "refile" (mh-coalesce-msg-list msg-list)
924df208 91 "-link" "-src" mh-current-folder folder)))
c26cf6c8 92
c3d9274a 93;;;###mh-autoload
c26cf6c8 94(defun mh-kill-folder ()
a1b4049d
BW
95 "Remove the current folder and all included messages.
96Removes all of the messages (files) within the specified current folder,
3d7ca223 97and then removes the folder (directory) itself."
c26cf6c8 98 (interactive)
3d7ca223 99 (if (or mh-index-data
924df208 100 (yes-or-no-p (format "Remove folder %s (and all included messages)? "
3d7ca223
BW
101 mh-current-folder)))
102 (let ((folder mh-current-folder)
103 (window-config mh-previous-window-config))
c3d9274a 104 (mh-set-folder-modified-p t) ; lock folder to kill it
3d7ca223 105 (mh-exec-cmd-daemon "rmf" 'mh-rmf-daemon folder)
bdcfe844
BW
106 (when (boundp 'mh-speed-folder-map)
107 (mh-speed-invalidate-map folder))
3d7ca223 108 (mh-remove-from-sub-folders-cache folder)
c3d9274a 109 (mh-set-folder-modified-p nil) ; so kill-buffer doesn't complain
3d7ca223 110 (if (and mh-show-buffer (get-buffer mh-show-buffer))
c3d9274a
BW
111 (kill-buffer mh-show-buffer))
112 (if (get-buffer folder)
3d7ca223
BW
113 (kill-buffer folder))
114 (when window-config
115 (set-window-configuration window-config))
116 (message "Folder %s removed" folder))
c3d9274a 117 (message "Folder not removed")))
c26cf6c8 118
3d7ca223
BW
119(defun mh-rmf-daemon (process output)
120 "The rmf PROCESS puts OUTPUT in temporary buffer.
121Display the results only if something went wrong."
122 (set-buffer (get-buffer-create mh-temp-buffer))
123 (insert-before-markers output)
124 (when (save-excursion
125 (beginning-of-buffer)
126 (re-search-forward "^rmf: " (point-max) t))
127 (display-buffer mh-temp-buffer)))
128
bdcfe844
BW
129;; Avoid compiler warning...
130(defvar view-exit-action)
c26cf6c8 131
c3d9274a 132;;;###mh-autoload
c26cf6c8
RS
133(defun mh-list-folders ()
134 "List mail folders."
135 (interactive)
3d7ca223 136 (let ((temp-buffer mh-folders-buffer))
a1b4049d
BW
137 (with-output-to-temp-buffer temp-buffer
138 (save-excursion
c3d9274a
BW
139 (set-buffer temp-buffer)
140 (erase-buffer)
141 (message "Listing folders...")
142 (mh-exec-cmd-output "folders" t (if mh-recursive-folders-flag
143 "-recurse"
144 "-norecurse"))
145 (goto-char (point-min))
146 (view-mode 1)
147 (setq view-exit-action 'kill-buffer)
148 (message "Listing folders...done")))))
149
150;;;###mh-autoload
c26cf6c8
RS
151(defun mh-pack-folder (range)
152 "Renumber the messages of a folder to be 1..n.
bdcfe844
BW
153First, offer to execute any outstanding commands for the current folder. If
154optional prefix argument provided, prompt for the RANGE of messages to display
155after packing. Otherwise, show the entire folder."
c26cf6c8 156 (interactive (list (if current-prefix-arg
c3d9274a
BW
157 (mh-read-msg-range mh-current-folder t)
158 '("all"))))
159 (let ((threaded-flag (memq 'unthread mh-view-ops)))
160 (mh-pack-folder-1 range)
161 (mh-goto-cur-msg)
162 (when mh-index-data
163 (mh-index-update-maps mh-current-folder))
164 (cond (threaded-flag (mh-toggle-threads))
165 (mh-index-data (mh-index-insert-folder-headers))))
c26cf6c8
RS
166 (message "Packing folder...done"))
167
c26cf6c8 168(defun mh-pack-folder-1 (range)
bdcfe844
BW
169 "Close and pack the current folder.
170Display the given RANGE of messages after packing. If RANGE is nil, show the
171entire folder."
c26cf6c8
RS
172 (mh-process-or-undo-commands mh-current-folder)
173 (message "Packing folder...")
c3d9274a 174 (mh-set-folder-modified-p t) ; lock folder while packing
c26cf6c8 175 (save-excursion
847b8219 176 (mh-exec-cmd-quiet t "folder" mh-current-folder "-pack"
c3d9274a 177 "-norecurse" "-fast"))
bdcfe844 178 (mh-reset-threads-and-narrowing)
c26cf6c8
RS
179 (mh-regenerate-headers range))
180
c3d9274a 181;;;###mh-autoload
c26cf6c8
RS
182(defun mh-pipe-msg (command include-headers)
183 "Pipe the current message through the given shell COMMAND.
184If INCLUDE-HEADERS (prefix argument) is provided, send the entire message.
185Otherwise just send the message's body without the headers."
186 (interactive
187 (list (read-string "Shell command on message: ") current-prefix-arg))
847b8219 188 (let ((msg-file-to-pipe (mh-msg-filename (mh-get-msg-num t)))
c3d9274a 189 (message-directory default-directory))
c26cf6c8 190 (save-excursion
847b8219 191 (set-buffer (get-buffer-create mh-temp-buffer))
c26cf6c8 192 (erase-buffer)
847b8219 193 (insert-file-contents msg-file-to-pipe)
c26cf6c8
RS
194 (goto-char (point-min))
195 (if (not include-headers) (search-forward "\n\n"))
847b8219 196 (let ((default-directory message-directory))
c3d9274a 197 (shell-command-on-region (point) (point-max) command nil)))))
c26cf6c8 198
c3d9274a 199;;;###mh-autoload
c26cf6c8
RS
200(defun mh-page-digest ()
201 "Advance displayed message to next digested message."
202 (interactive)
203 (mh-in-show-buffer (mh-show-buffer)
204 ;; Go to top of screen (in case user moved point).
205 (move-to-window-line 0)
206 (let ((case-fold-search nil))
207 ;; Search for blank line and then for From:
208 (or (and (search-forward "\n\n" nil t)
c3d9274a
BW
209 (re-search-forward "^From:" nil t))
210 (error "No more messages in digest")))
c26cf6c8
RS
211 ;; Go back to previous blank line, then forward to the first non-blank.
212 (search-backward "\n\n" nil t)
213 (forward-line 2)
214 (mh-recenter 0)))
215
c3d9274a 216;;;###mh-autoload
c26cf6c8
RS
217(defun mh-page-digest-backwards ()
218 "Back up displayed message to previous digested message."
219 (interactive)
220 (mh-in-show-buffer (mh-show-buffer)
221 ;; Go to top of screen (in case user moved point).
222 (move-to-window-line 0)
223 (let ((case-fold-search nil))
224 (beginning-of-line)
225 (or (and (search-backward "\n\n" nil t)
c3d9274a
BW
226 (re-search-backward "^From:" nil t))
227 (error "No previous message in digest")))
c26cf6c8
RS
228 ;; Go back to previous blank line, then forward to the first non-blank.
229 (if (search-backward "\n\n" nil t)
c3d9274a 230 (forward-line 2))
c26cf6c8
RS
231 (mh-recenter 0)))
232
c3d9274a 233;;;###mh-autoload
c26cf6c8 234(defun mh-print-msg (msg-or-seq)
924df208
BW
235 "Print MSG-OR-SEQ on printer.
236Default is the displayed message.
237If optional prefix argument is provided, then prompt for the message sequence.
238If variable `transient-mark-mode' is non-nil and the mark is active, then the
239selected region is printed.
240In a program, MSG-OR-SEQ can be a message number, a list of message numbers, a
241region in a cons cell, or a sequence.
242
a1b4049d 243The variable `mh-lpr-command-format' is used to generate the print command.
bdcfe844 244The messages are formatted by mhl. See the variable `mhl-formfile'."
924df208
BW
245 (interactive (list (mh-interactive-msg-or-seq "Print")))
246 (message "Printing...")
247 (let (msgs)
248 ;; Gather message numbers and add them to "printed" sequence.
249 (mh-iterate-on-msg-or-seq msg msg-or-seq
250 (mh-add-msgs-to-seq msg 'printed t)
251 (mh-notate nil mh-note-printed mh-cmd-note)
252 (push msg msgs))
253 (setq msgs (nreverse msgs))
254 ;; Print scan listing if we have more than one message.
255 (if (> (length msgs) 1)
256 (let* ((msgs-string
257 (mapconcat 'identity (mh-list-to-string
258 (mh-coalesce-msg-list msgs)) " "))
259 (lpr-command
260 (format mh-lpr-command-format
261 (cond ((listp msg-or-seq)
262 (format "Folder: %s, Messages: %s"
263 mh-current-folder msgs-string))
264 ((symbolp msg-or-seq)
265 (format "Folder: %s, Sequence: %s"
266 mh-current-folder msg-or-seq)))))
267 (scan-command
268 (format "scan %s | %s" msgs-string lpr-command)))
269 (if mh-print-background-flag
270 (mh-exec-cmd-daemon shell-file-name nil "-c" scan-command)
271 (call-process shell-file-name nil nil nil "-c" scan-command))))
272 ;; Print the messages
273 (dolist (msg msgs)
274 (let* ((mhl-command (format "%s %s %s"
275 (expand-file-name "mhl" mh-lib-progs)
276 (if mhl-formfile
277 (format " -form %s" mhl-formfile)
278 "")
279 (mh-msg-filename msg)))
280 (lpr-command
281 (format mh-lpr-command-format
282 (format "%s/%s" mh-current-folder msg)))
283 (print-command
284 (format "%s | %s" mhl-command lpr-command)))
285 (if mh-print-background-flag
286 (mh-exec-cmd-daemon shell-file-name nil "-c" print-command)
287 (call-process shell-file-name nil nil nil "-c" print-command)))))
288 (message "Printing...done"))
c26cf6c8 289
c3d9274a 290;;;###mh-autoload
847b8219 291(defun mh-sort-folder (&optional extra-args)
c26cf6c8
RS
292 "Sort the messages in the current folder by date.
293Calls the MH program sortm to do the work.
bdcfe844
BW
294The arguments in the list `mh-sortm-args' are passed to sortm if the optional
295argument EXTRA-ARGS is given."
c26cf6c8
RS
296 (interactive "P")
297 (mh-process-or-undo-commands mh-current-folder)
298 (setq mh-next-direction 'forward)
c3d9274a 299 (mh-set-folder-modified-p t) ; lock folder while sorting
c26cf6c8 300 (message "Sorting folder...")
c3d9274a
BW
301 (let ((threaded-flag (memq 'unthread mh-view-ops)))
302 (mh-exec-cmd "sortm" mh-current-folder (if extra-args mh-sortm-args))
303 (when mh-index-data
304 (mh-index-update-maps mh-current-folder))
305 (message "Sorting folder...done")
c3d9274a
BW
306 (mh-scan-folder mh-current-folder "all")
307 (cond (threaded-flag (mh-toggle-threads))
308 (mh-index-data (mh-index-insert-folder-headers)))))
309
310;;;###mh-autoload
c26cf6c8 311(defun mh-undo-folder (&rest ignore)
a1b4049d
BW
312 "Undo all pending deletes and refiles in current folder.
313Argument IGNORE is deprecated."
c26cf6c8 314 (interactive)
bdcfe844 315 (cond ((or mh-do-not-confirm-flag
c3d9274a
BW
316 (yes-or-no-p "Undo all commands in folder? "))
317 (setq mh-delete-list nil
318 mh-refile-list nil
319 mh-seq-list nil
320 mh-next-direction 'forward)
321 (with-mh-folder-updating (nil)
322 (mh-unmark-all-headers t)))
323 (t
324 (message "Commands not undone.")
924df208
BW
325 ;; Remove by 2003-06-30 if nothing seems amiss. XXX
326 ;; (sit-for 2)
327 )))
c3d9274a
BW
328
329;;;###mh-autoload
847b8219 330(defun mh-store-msg (directory)
c26cf6c8
RS
331 "Store the file(s) contained in the current message into DIRECTORY.
332The message can contain a shar file or uuencoded file.
333Default directory is the last directory used, or initially the value of
a1b4049d 334`mh-store-default-directory' or the current directory."
c3d9274a
BW
335 (interactive (list (let ((udir (or mh-store-default-directory
336 default-directory)))
337 (read-file-name "Store message in directory: "
338 udir udir nil))))
847b8219 339 (let ((msg-file-to-store (mh-msg-filename (mh-get-msg-num t))))
c26cf6c8 340 (save-excursion
847b8219 341 (set-buffer (get-buffer-create mh-temp-buffer))
c26cf6c8 342 (erase-buffer)
847b8219
KH
343 (insert-file-contents msg-file-to-store)
344 (mh-store-buffer directory))))
c26cf6c8 345
c3d9274a 346;;;###mh-autoload
847b8219 347(defun mh-store-buffer (directory)
c26cf6c8
RS
348 "Store the file(s) contained in the current buffer into DIRECTORY.
349The buffer can contain a shar file or uuencoded file.
350Default directory is the last directory used, or initially the value of
351`mh-store-default-directory' or the current directory."
bdcfe844 352 (interactive (list (let ((udir (or mh-store-default-directory
c3d9274a
BW
353 default-directory)))
354 (read-file-name "Store buffer in directory: "
355 udir udir nil))))
847b8219 356 (let ((store-directory (expand-file-name directory))
c3d9274a
BW
357 (sh-start (save-excursion
358 (goto-char (point-min))
359 (if (re-search-forward
360 "^#![ \t]*/bin/sh\\|^#\\|^: " nil t)
361 (progn
362 ;; The "cut here" pattern was removed from above
363 ;; because it seemed to hurt more than help.
364 ;; But keep this to make it easier to put it back.
365 (if (looking-at "^[^a-z0-9\"]*cut here\\b")
366 (forward-line 1))
367 (beginning-of-line)
368 (if (looking-at "^[#:]....+\n\\( ?\n\\)?end$")
369 nil ;most likely end of a uuencode
370 (point))))))
c3d9274a 371 (command "sh")
924df208
BW
372 (uudecode-filename "(unknown filename)")
373 log-begin)
847b8219 374 (if (not sh-start)
c3d9274a
BW
375 (save-excursion
376 (goto-char (point-min))
377 (if (re-search-forward "^begin [0-7]+ " nil t)
378 (setq uudecode-filename
379 (buffer-substring (point)
380 (progn (end-of-line) (point)))))))
c26cf6c8 381 (save-excursion
924df208
BW
382 (set-buffer (get-buffer-create mh-log-buffer))
383 (setq log-begin (mh-truncate-log-buffer))
c26cf6c8 384 (if (not (file-directory-p store-directory))
c3d9274a
BW
385 (progn
386 (insert "mkdir " directory "\n")
924df208 387 (call-process "mkdir" nil mh-log-buffer t store-directory)))
847b8219
KH
388 (insert "cd " directory "\n")
389 (setq mh-store-default-directory directory)
390 (if (not sh-start)
c3d9274a
BW
391 (progn
392 (setq command "uudecode")
393 (insert uudecode-filename " being uudecoded...\n"))))
924df208
BW
394 (set-window-start (display-buffer mh-log-buffer) log-begin) ;watch progress
395 (let ((default-directory (file-name-as-directory store-directory)))
396 (if (equal (call-process-region sh-start (point-max) command
397 nil mh-log-buffer t)
398 0)
399 (save-excursion
400 (set-buffer mh-log-buffer)
401 (insert "\n(mh-store finished)\n"))
402 (error "Error occurred during execution of %s" command)))))
bdcfe844
BW
403
404\f
405
406;;; Help Functions
407
924df208 408;;;###mh-autoload
bdcfe844
BW
409(defun mh-ephem-message (string)
410 "Display STRING in the minibuffer momentarily."
411 (message "%s" string)
412 (sit-for 5)
413 (message ""))
414
c3d9274a 415;;;###mh-autoload
bdcfe844
BW
416(defun mh-help ()
417 "Display cheat sheet for the MH-Folder commands in minibuffer."
418 (interactive)
419 (mh-ephem-message
420 (substitute-command-keys
421 (mapconcat 'identity (cdr (assoc nil mh-help-messages)) ""))))
a1506d29 422
c3d9274a 423;;;###mh-autoload
bdcfe844
BW
424(defun mh-prefix-help ()
425 "Display cheat sheet for the commands of the current prefix in minibuffer."
426 (interactive)
427 ;; We got here because the user pressed a `?', but he pressed a prefix key
428 ;; before that. Since the the key vector starts at index 0, the index of the
429 ;; last keystroke is length-1 and thus the second to last keystroke is at
430 ;; length-2. We use that information to obtain a suitable prefix character
431 ;; from the recent keys.
432 (let* ((keys (recent-keys))
c3d9274a 433 (prefix-char (elt keys (- (length keys) 2))))
bdcfe844
BW
434 (mh-ephem-message
435 (substitute-command-keys
436 (mapconcat 'identity (cdr (assoc prefix-char mh-help-messages)) "")))))
437
438(provide 'mh-funcs)
439
440;;; Local Variables:
c3d9274a 441;;; indent-tabs-mode: nil
bdcfe844
BW
442;;; sentence-end-double-space: nil
443;;; End:
444
6b61353c 445;;; arch-tag: 1936c4f1-4843-438e-bc4b-a63bb75a7762
60370d40 446;;; mh-funcs.el ends here