Mercurial > emacs
diff lisp/mh-e/mh-funcs.el @ 49459:06b77df47802
* mh-e: Created directory. ChangeLog will appear in a week when we
release version 7.2.
* lisp/mail/mh-alias.el, lisp/mail/mh-comp.el,
lisp/mail/mh-customize.el, lisp/mail/mh-e.el, lisp/mail/mh-funcs.el,
lisp/mail/mh-identity.el, lisp/mail/mh-index.el,
lisp/mail/mh-loaddefs.el, lisp/mail/mh-mime.el, lisp/mail/mh-pick.el,
lisp/mail/mh-seq.el, lisp/mail/mh-speed.el, lisp/mail/mh-utils.el,
lisp/mail/mh-xemacs-compat.el: Moved to mh-e. Note that reply2.pbm and
reply2.xpm, which were created by the MH-E package, were left in mail
since they can probably be used by other mail packages.
* makefile.w32-in (WINS): Added mh-e.
* makefile.nt (WINS): Added mh-e.
author | Bill Wohler <wohler@newt.com> |
---|---|
date | Sun, 26 Jan 2003 02:38:37 +0000 |
parents | |
children | b35587af8747 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lisp/mh-e/mh-funcs.el Sun Jan 26 02:38:37 2003 +0000 @@ -0,0 +1,436 @@ +;;; mh-funcs.el --- MH-E functions not everyone will use right away + +;; Copyright (C) 1993, 1995, 2001, 2002 Free Software Foundation, Inc. + +;; Author: Bill Wohler <wohler@newt.com> +;; Maintainer: Bill Wohler <wohler@newt.com> +;; Keywords: mail +;; See: mh-e.el + +;; This file is part of GNU Emacs. + +;; GNU Emacs is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation; either version 2, or (at your option) +;; any later version. + +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs; see the file COPYING. If not, write to the +;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, +;; Boston, MA 02111-1307, USA. + +;;; Commentary: + +;; Internal support for MH-E package. +;; Putting these functions in a separate file lets MH-E start up faster, +;; since less Lisp code needs to be loaded all at once. + +;;; Change Log: + +;; $Id: mh-funcs.el,v 1.9 2003/01/08 23:21:16 wohler Exp $ + +;;; Code: + +(require 'mh-e) + +;;; Customization + +(defvar mh-sortm-args nil + "Extra arguments to have \\[mh-sort-folder] pass to the \"sortm\" command. +The arguments are passed to sortm if \\[mh-sort-folder] is given a +prefix argument. Normally default arguments to sortm are specified in the +MH profile. +For example, '(\"-nolimit\" \"-textfield\" \"subject\") is a useful setting.") + +(defvar mh-note-copied "C" + "String whose first character is used to notate copied messages.") + +(defvar mh-note-printed "P" + "String whose first character is used to notate printed messages.") + +;;; Functions + +;;;###mh-autoload +(defun mh-burst-digest () + "Burst apart the current message, which should be a digest. +The message is replaced by its table of contents and the messages from the +digest are inserted into the folder after that message." + (interactive) + (let ((digest (mh-get-msg-num t))) + (mh-process-or-undo-commands mh-current-folder) + (mh-set-folder-modified-p t) ; lock folder while bursting + (message "Bursting digest...") + (mh-exec-cmd "burst" mh-current-folder digest "-inplace") + (with-mh-folder-updating (t) + (beginning-of-line) + (delete-region (point) (point-max))) + (mh-regenerate-headers (format "%d-last" digest) t) + (mh-goto-cur-msg) + (message "Bursting digest...done"))) + +;;;###mh-autoload +(defun mh-copy-msg (msg-or-seq folder) + "Copy the specified MSG-OR-SEQ to another FOLDER without deleting them. +Default is the displayed message. If optional prefix argument is provided, +then prompt for the message sequence." + (interactive (list (cond + ((mh-mark-active-p t) + (mh-region-to-msg-list (region-beginning) (region-end))) + (current-prefix-arg + (mh-read-seq-default "Copy" t)) + (t + (mh-get-msg-num t))) + (mh-prompt-for-folder "Copy to" "" t))) + (mh-exec-cmd "refile" + (cond ((numberp msg-or-seq) msg-or-seq) + ((listp msg-or-seq) msg-or-seq) + (t (mh-coalesce-msg-list (mh-seq-to-msgs msg-or-seq)))) + "-link" "-src" mh-current-folder folder) + (if (numberp msg-or-seq) + (mh-notate msg-or-seq mh-note-copied mh-cmd-note) + (mh-notate-seq msg-or-seq mh-note-copied mh-cmd-note))) + +;;;###mh-autoload +(defun mh-kill-folder () + "Remove the current folder and all included messages. +Removes all of the messages (files) within the specified current folder, +and then removes the folder (directory) itself. +The value of `mh-folder-list-change-hook' is a list of functions to be called, +with no arguments, after the folders has been removed." + (interactive) + (if (yes-or-no-p (format "Remove folder %s (and all included messages)?" + mh-current-folder)) + (let ((folder mh-current-folder)) + (if (null mh-folder-list) + (mh-set-folder-list)) + (mh-set-folder-modified-p t) ; lock folder to kill it + (mh-exec-cmd-daemon "rmf" folder) + (setq mh-folder-list + (delq (assoc folder mh-folder-list) mh-folder-list)) + (when (boundp 'mh-speed-folder-map) + (mh-speed-invalidate-map folder)) + (run-hooks 'mh-folder-list-change-hook) + (message "Folder %s removed" folder) + (mh-set-folder-modified-p nil) ; so kill-buffer doesn't complain + (if (get-buffer mh-show-buffer) + (kill-buffer mh-show-buffer)) + (if (get-buffer folder) + (kill-buffer folder))) + (message "Folder not removed"))) + +;; Avoid compiler warning... +(defvar view-exit-action) + +;;;###mh-autoload +(defun mh-list-folders () + "List mail folders." + (interactive) + (let ((temp-buffer mh-temp-folders-buffer)) + (with-output-to-temp-buffer temp-buffer + (save-excursion + (set-buffer temp-buffer) + (erase-buffer) + (message "Listing folders...") + (mh-exec-cmd-output "folders" t (if mh-recursive-folders-flag + "-recurse" + "-norecurse")) + (goto-char (point-min)) + (view-mode 1) + (setq view-exit-action 'kill-buffer) + (message "Listing folders...done"))))) + +;;;###mh-autoload +(defun mh-pack-folder (range) + "Renumber the messages of a folder to be 1..n. +First, offer to execute any outstanding commands for the current folder. If +optional prefix argument provided, prompt for the RANGE of messages to display +after packing. Otherwise, show the entire folder." + (interactive (list (if current-prefix-arg + (mh-read-msg-range mh-current-folder t) + '("all")))) + (let ((threaded-flag (memq 'unthread mh-view-ops))) + (mh-pack-folder-1 range) + (mh-goto-cur-msg) + (when mh-index-data + (mh-index-update-maps mh-current-folder)) + (cond (threaded-flag (mh-toggle-threads)) + (mh-index-data (mh-index-insert-folder-headers)))) + (message "Packing folder...done")) + +(defun mh-pack-folder-1 (range) + "Close and pack the current folder. +Display the given RANGE of messages after packing. If RANGE is nil, show the +entire folder." + (mh-process-or-undo-commands mh-current-folder) + (message "Packing folder...") + (mh-set-folder-modified-p t) ; lock folder while packing + (save-excursion + (mh-exec-cmd-quiet t "folder" mh-current-folder "-pack" + "-norecurse" "-fast")) + (mh-reset-threads-and-narrowing) + (mh-regenerate-headers range)) + +;;;###mh-autoload +(defun mh-pipe-msg (command include-headers) + "Pipe the current message through the given shell COMMAND. +If INCLUDE-HEADERS (prefix argument) is provided, send the entire message. +Otherwise just send the message's body without the headers." + (interactive + (list (read-string "Shell command on message: ") current-prefix-arg)) + (let ((msg-file-to-pipe (mh-msg-filename (mh-get-msg-num t))) + (message-directory default-directory)) + (save-excursion + (set-buffer (get-buffer-create mh-temp-buffer)) + (erase-buffer) + (insert-file-contents msg-file-to-pipe) + (goto-char (point-min)) + (if (not include-headers) (search-forward "\n\n")) + (let ((default-directory message-directory)) + (shell-command-on-region (point) (point-max) command nil))))) + +;;;###mh-autoload +(defun mh-page-digest () + "Advance displayed message to next digested message." + (interactive) + (mh-in-show-buffer (mh-show-buffer) + ;; Go to top of screen (in case user moved point). + (move-to-window-line 0) + (let ((case-fold-search nil)) + ;; Search for blank line and then for From: + (or (and (search-forward "\n\n" nil t) + (re-search-forward "^From:" nil t)) + (error "No more messages in digest"))) + ;; Go back to previous blank line, then forward to the first non-blank. + (search-backward "\n\n" nil t) + (forward-line 2) + (mh-recenter 0))) + +;;;###mh-autoload +(defun mh-page-digest-backwards () + "Back up displayed message to previous digested message." + (interactive) + (mh-in-show-buffer (mh-show-buffer) + ;; Go to top of screen (in case user moved point). + (move-to-window-line 0) + (let ((case-fold-search nil)) + (beginning-of-line) + (or (and (search-backward "\n\n" nil t) + (re-search-backward "^From:" nil t)) + (error "No previous message in digest"))) + ;; Go back to previous blank line, then forward to the first non-blank. + (if (search-backward "\n\n" nil t) + (forward-line 2)) + (mh-recenter 0))) + +;;;###mh-autoload +(defun mh-print-msg (msg-or-seq) + "Print MSG-OR-SEQ (default: displayed message) on printer. +If optional prefix argument provided, then prompt for the message sequence. +The variable `mh-lpr-command-format' is used to generate the print command. +The messages are formatted by mhl. See the variable `mhl-formfile'." + (interactive (list (if current-prefix-arg + (reverse (mh-seq-to-msgs + (mh-read-seq-default "Print" t))) + (mh-get-msg-num t)))) + (if (numberp msg-or-seq) + (message "Printing message...") + (message "Printing sequence...")) + (let ((print-command + (if (numberp msg-or-seq) + (format "%s -nobell -clear %s %s | %s" + (expand-file-name "mhl" mh-lib-progs) + (mh-msg-filename msg-or-seq) + (if (stringp mhl-formfile) + (format "-form %s" mhl-formfile) + "") + (format mh-lpr-command-format + (if (numberp msg-or-seq) + (format "%s/%d" mh-current-folder + msg-or-seq) + (format "Sequence from %s" mh-current-folder)))) + (format "(scan -clear %s ; %s -nobell -clear %s %s) | %s" + (mapconcat (function (lambda (msg) msg)) msg-or-seq " ") + (expand-file-name "mhl" mh-lib-progs) + (if (stringp mhl-formfile) + (format "-form %s" mhl-formfile) + "") + (mh-msg-filenames msg-or-seq) + (format mh-lpr-command-format + (if (numberp msg-or-seq) + (format "%s/%d" mh-current-folder + msg-or-seq) + (format "Sequence from %s" + mh-current-folder))))))) + (if mh-print-background-flag + (mh-exec-cmd-daemon shell-file-name "-c" print-command) + (call-process shell-file-name nil nil nil "-c" print-command)) + (if (numberp msg-or-seq) + (mh-notate msg-or-seq mh-note-printed mh-cmd-note) + (mh-notate-seq msg-or-seq mh-note-printed mh-cmd-note)) + (mh-add-msgs-to-seq msg-or-seq 'printed t) + (if (numberp msg-or-seq) + (message "Printing message...done") + (message "Printing sequence...done")))) + +(defun mh-msg-filenames (msgs &optional folder) + "Return a list of file names for MSGS in FOLDER (default current folder)." + (mapconcat (function (lambda (msg) (mh-msg-filename msg folder))) msgs " ")) + +;;;###mh-autoload +(defun mh-sort-folder (&optional extra-args) + "Sort the messages in the current folder by date. +Calls the MH program sortm to do the work. +The arguments in the list `mh-sortm-args' are passed to sortm if the optional +argument EXTRA-ARGS is given." + (interactive "P") + (mh-process-or-undo-commands mh-current-folder) + (setq mh-next-direction 'forward) + (mh-set-folder-modified-p t) ; lock folder while sorting + (message "Sorting folder...") + (let ((threaded-flag (memq 'unthread mh-view-ops))) + (mh-exec-cmd "sortm" mh-current-folder (if extra-args mh-sortm-args)) + (when mh-index-data + (mh-index-update-maps mh-current-folder)) + (message "Sorting folder...done") + (mh-reset-threads-and-narrowing) + (mh-scan-folder mh-current-folder "all") + (cond (threaded-flag (mh-toggle-threads)) + (mh-index-data (mh-index-insert-folder-headers))))) + +;;;###mh-autoload +(defun mh-undo-folder (&rest ignore) + "Undo all pending deletes and refiles in current folder. +Argument IGNORE is deprecated." + (interactive) + (cond ((or mh-do-not-confirm-flag + (yes-or-no-p "Undo all commands in folder? ")) + (setq mh-delete-list nil + mh-refile-list nil + mh-seq-list nil + mh-next-direction 'forward) + (with-mh-folder-updating (nil) + (mh-unmark-all-headers t))) + (t + (message "Commands not undone.") + (sit-for 2)))) + +;;;###mh-autoload +(defun mh-store-msg (directory) + "Store the file(s) contained in the current message into DIRECTORY. +The message can contain a shar file or uuencoded file. +Default directory is the last directory used, or initially the value of +`mh-store-default-directory' or the current directory." + (interactive (list (let ((udir (or mh-store-default-directory + default-directory))) + (read-file-name "Store message in directory: " + udir udir nil)))) + (let ((msg-file-to-store (mh-msg-filename (mh-get-msg-num t)))) + (save-excursion + (set-buffer (get-buffer-create mh-temp-buffer)) + (erase-buffer) + (insert-file-contents msg-file-to-store) + (mh-store-buffer directory)))) + +;;;###mh-autoload +(defun mh-store-buffer (directory) + "Store the file(s) contained in the current buffer into DIRECTORY. +The buffer can contain a shar file or uuencoded file. +Default directory is the last directory used, or initially the value of +`mh-store-default-directory' or the current directory." + (interactive (list (let ((udir (or mh-store-default-directory + default-directory))) + (read-file-name "Store buffer in directory: " + udir udir nil)))) + (let ((store-directory (expand-file-name directory)) + (sh-start (save-excursion + (goto-char (point-min)) + (if (re-search-forward + "^#![ \t]*/bin/sh\\|^#\\|^: " nil t) + (progn + ;; The "cut here" pattern was removed from above + ;; because it seemed to hurt more than help. + ;; But keep this to make it easier to put it back. + (if (looking-at "^[^a-z0-9\"]*cut here\\b") + (forward-line 1)) + (beginning-of-line) + (if (looking-at "^[#:]....+\n\\( ?\n\\)?end$") + nil ;most likely end of a uuencode + (point)))))) + (log-buffer (get-buffer-create "*Store Output*")) + (command "sh") + (uudecode-filename "(unknown filename)")) + (if (not sh-start) + (save-excursion + (goto-char (point-min)) + (if (re-search-forward "^begin [0-7]+ " nil t) + (setq uudecode-filename + (buffer-substring (point) + (progn (end-of-line) (point))))))) + (save-excursion + (set-buffer log-buffer) + (erase-buffer) + (if (not (file-directory-p store-directory)) + (progn + (insert "mkdir " directory "\n") + (call-process "mkdir" nil log-buffer t store-directory))) + (insert "cd " directory "\n") + (setq mh-store-default-directory directory) + (if (not sh-start) + (progn + (setq command "uudecode") + (insert uudecode-filename " being uudecoded...\n")))) + (set-window-start (display-buffer log-buffer) 0) ;watch progress + (let (value) + (let ((default-directory (file-name-as-directory store-directory))) + (setq value (call-process-region sh-start (point-max) command + nil log-buffer t))) + (set-buffer log-buffer) + (mh-handle-process-error command value)) + (insert "\n(mh-store finished)\n"))) + + + +;;; Help Functions + +(defun mh-ephem-message (string) + "Display STRING in the minibuffer momentarily." + (message "%s" string) + (sit-for 5) + (message "")) + +;;;###mh-autoload +(defun mh-help () + "Display cheat sheet for the MH-Folder commands in minibuffer." + (interactive) + (mh-ephem-message + (substitute-command-keys + (mapconcat 'identity (cdr (assoc nil mh-help-messages)) "")))) + +;;;###mh-autoload +(defun mh-prefix-help () + "Display cheat sheet for the commands of the current prefix in minibuffer." + (interactive) + ;; We got here because the user pressed a `?', but he pressed a prefix key + ;; before that. Since the the key vector starts at index 0, the index of the + ;; last keystroke is length-1 and thus the second to last keystroke is at + ;; length-2. We use that information to obtain a suitable prefix character + ;; from the recent keys. + (let* ((keys (recent-keys)) + (prefix-char (elt keys (- (length keys) 2)))) + (mh-ephem-message + (substitute-command-keys + (mapconcat 'identity (cdr (assoc prefix-char mh-help-messages)) ""))))) + +(provide 'mh-funcs) + +;;; Local Variables: +;;; indent-tabs-mode: nil +;;; sentence-end-double-space: nil +;;; End: + +;;; mh-funcs.el ends here