home *** CD-ROM | disk | FTP | other *** search
- ;;; Saving and piping messages under VM
- ;;; Copyright (C) 1989, 1990 Kyle E. Jones
- ;;;
- ;;; This program 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 1, or (at your option)
- ;;; any later version.
- ;;;
- ;;; This program 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 this program; if not, write to the Free Software
- ;;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
- ;; (match-data) returns the match data as MARKERS, often corrupting
- ;; it in the process due to buffer narrowing, and the fact that buffers are
- ;; indexed from 1 while strings are indexed from 0. :-(
- (defun vm-match-data ()
- (let ((index '(9 8 7 6 5 4 3 2 1 0))
- (list))
- (while index
- (setq list (cons (match-beginning (car index))
- (cons (match-end (car index)) list))
- index (cdr index)))
- list ))
-
- (defun vm-auto-select-folder (mp auto-folder-alist)
- (condition-case ()
- (catch 'match
- (let (header alist tuple-list)
- (setq alist auto-folder-alist)
- (while alist
- (setq header (vm-get-header-contents (car mp) (car (car alist))))
- (if (null header)
- ()
- (setq tuple-list (cdr (car alist)))
- (while tuple-list
- (if (let ((case-fold-search vm-auto-folder-case-fold-search))
- (string-match (car (car tuple-list)) header))
- ;; Don't waste time eval'ing an atom.
- (if (atom (cdr (car tuple-list)))
- (throw 'match (cdr (car tuple-list)))
- (let* ((match-data (vm-match-data))
- (buf (get-buffer-create " *VM scratch*"))
- (result))
- ;; Set up a buffer that matches our cached
- ;; match data.
- (save-excursion
- (set-buffer buf)
- (widen)
- (erase-buffer)
- (insert header)
- ;; It appears that get-buffer-create clobbers the
- ;; match-data.
- ;;
- ;; The match data is off by one because we matched
- ;; a string and Emacs indexes strings from 0 and
- ;; buffers from 1.
- ;;
- ;; Also store-match-data only accepts MARKERS!!
- ;; AUGHGHGH!!
- (store-match-data
- (mapcar
- (function (lambda (n) (and n (vm-marker n))))
- (mapcar
- (function (lambda (n) (and n (1+ n))))
- match-data)))
- (setq result (eval (cdr (car tuple-list))))
- (throw 'match (if (listp result)
- (vm-auto-select-folder mp result)
- result ))))))
- (setq tuple-list (cdr tuple-list))))
- (setq alist (cdr alist)))
- nil ))
- (error nil)))
-
- (defun vm-auto-archive-messages (&optional arg)
- "Save all unfiled messages that auto-match a folder via vm-auto-folder-alist
- to their appropriate folders. Deleted message are not saved.
-
- Prefix arg means to ask user for confirmation before saving each message."
- (interactive "P")
- (vm-select-folder-buffer)
- (vm-check-for-killed-summary)
- (vm-error-if-folder-empty)
- (let ((auto-folder)
- (archived 0))
- ;; Need separate (let ...) so vm-message-pointer can revert back
- ;; in time for (vm-update-summary-and-mode-line).
- ;; vm-last-save-folder is tucked away here since archives shouldn't affect
- ;; its value.
- (let ((vm-message-pointer vm-message-list)
- (vm-last-save-folder vm-last-save-folder)
- (vm-move-after-deleting))
- (while vm-message-pointer
- (and (not (vm-filed-flag (car vm-message-pointer)))
- ;; don't archive deleted messages
- (not (vm-deleted-flag (car vm-message-pointer)))
- (setq auto-folder (vm-auto-select-folder vm-message-pointer
- vm-auto-folder-alist))
- (or (null arg)
- (y-or-n-p
- (format "Save message %s in folder %s? "
- (vm-number-of (car vm-message-pointer))
- auto-folder)))
- (progn (vm-save-message auto-folder)
- (and vm-delete-after-archiving (vm-delete-message 1))
- (vm-increment archived)))
- (setq vm-message-pointer (cdr vm-message-pointer))))
- (if (zerop archived)
- (message "No messages archived")
- (message "%d message%s archived" archived (if (= 1 archived) "" "s"))
- (vm-update-summary-and-mode-line))))
-
- (defun vm-save-message (folder &optional count)
- "Save the current message to a mail folder.
- If the folder already exists, the message will be appended to it.
- Prefix arg COUNT means save the next COUNT messages. A negative COUNT means
- save the previous COUNT messages.
-
- When invoked on marked messages (via vm-next-command-uses-marks),
- all marked message in the current folder are saved; other messages are
- ignored.
-
- The saved messages are flagged as `filed'."
- (interactive
- (list
- ;; protect value of last-command
- (let ((last-command last-command))
- (vm-follow-summary-cursor)
- (let ((default (save-excursion
- (vm-select-folder-buffer)
- (vm-check-for-killed-summary)
- (or (vm-auto-select-folder vm-message-pointer
- vm-auto-folder-alist)
- vm-last-save-folder)))
- (dir (or vm-folder-directory default-directory)))
- (if default
- (read-file-name (format "Save in folder: (default %s) "
- default)
- dir default nil )
- (read-file-name "Save in folder: " dir nil nil))))
- (prefix-numeric-value current-prefix-arg)))
- (let (unexpanded-folder)
- (setq unexpanded-folder folder)
- (vm-select-folder-buffer)
- (vm-check-for-killed-summary)
- (vm-error-if-folder-empty)
- (or count (setq count 1))
- ;; Expand the filename, forcing relative paths to resolve
- ;; into the folder directory.
- (let ((default-directory
- (expand-file-name (or vm-folder-directory default-directory))))
- (setq folder (expand-file-name folder)))
- ;; Confirm new folders, if the user requested this.
- (if (and vm-confirm-new-folders (interactive-p)
- (not (file-exists-p folder))
- (or (not vm-visit-when-saving) (not (get-file-buffer folder)))
- (not (y-or-n-p (format "%s does not exist, save there anyway? "
- folder))))
- (error "Save aborted"))
- ;; Check and see if we are currently visiting the folder
- ;; that the user wants to save to.
- (if (and (not vm-visit-when-saving) (get-file-buffer folder))
- (error "Folder %s is being visited, cannot save." folder))
- (let ((mlist (vm-select-marked-or-prefixed-messages count))
- folder-buffer)
- (cond ((eq vm-visit-when-saving t)
- (setq folder-buffer (or (get-file-buffer folder)
- (find-file-noselect folder))))
- (vm-visit-when-saving
- (setq folder-buffer (get-file-buffer folder))))
- (save-excursion
- (while mlist
- (set-buffer (marker-buffer (vm-start-of (car mlist))))
- (vm-save-restriction
- (widen)
- ;; if message is deleted then we have to restuff with
- ;; the delete flag suppressed before we save.
- (if (or (vm-modflag-of (car mlist)) (vm-deleted-flag (car mlist)))
- (vm-stuff-attributes (car mlist) t))
- (if (null folder-buffer)
- (write-region (vm-start-of (car mlist))
- (vm-end-of (car mlist))
- folder t 'quiet)
- (let ((start (vm-start-of (car mlist)))
- (end (vm-end-of (car mlist))))
- (save-excursion
- (set-buffer folder-buffer)
- (let (buffer-read-only)
- (vm-save-restriction
- (widen)
- (save-excursion
- (goto-char (point-max))
- (insert-buffer-substring
- (marker-buffer (vm-start-of (car mlist)))
- start end))
- ;; vars should exist and be local
- ;; but they may have strange values,
- ;; so check the major-mode.
- (cond ((eq major-mode 'vm-mode)
- (vm-increment vm-messages-not-on-disk)
- (vm-set-buffer-modified-p (buffer-modified-p))
- (vm-clear-modification-flag-undos))))))))
- (if (null (vm-filed-flag (car mlist)))
- (vm-set-filed-flag (car mlist) t))
- (setq mlist (cdr mlist)))))
- (if folder-buffer
- (progn
- (save-excursion
- (set-buffer folder-buffer)
- (if (eq major-mode 'vm-mode)
- (let (buffer-read-only)
- (vm-check-for-killed-summary)
- (vm-assimilate-new-messages)
- (vm-update-summary-and-mode-line))))
- (message "Message%s saved to buffer %s" (if (/= 1 count) "s" "")
- (buffer-name folder-buffer)))
- (message "Message%s saved to %s" (if (/= 1 count) "s" "") folder)))
- (setq vm-last-save-folder unexpanded-folder)
- (if vm-delete-after-saving
- (vm-delete-message count))
- (vm-update-summary-and-mode-line)))
-
- (defun vm-save-message-sans-headers (file &optional count)
- "Save the current message to a file, without its header section.
- If the file already exists, the message will be appended to it.
- Prefix arg COUNT means save the next COUNT messages. A negative COUNT means
- save the previous COUNT.
-
- When invoked on marked messages (via vm-next-command-uses-marks),
- all marked messages in the current folder are saved; other messages are
- ignored.
-
- The saved messages are flagged as `written'.
-
- This command should NOT be used to save message to mail folders; use
- vm-save-message instead (normally bound to `s')."
- (interactive
- ;; protect value of last-command
- (let ((last-command last-command))
- (vm-follow-summary-cursor)
- (list
- (read-file-name "Write text to file: " nil nil nil)
- (prefix-numeric-value current-prefix-arg))))
- (vm-select-folder-buffer)
- (vm-check-for-killed-summary)
- (vm-error-if-folder-empty)
- (or count (setq count 1))
- (setq file (expand-file-name file))
- ;; Check and see if we are currently visiting the file
- ;; that the user wants to save to.
- (if (and (not vm-visit-when-saving) (get-file-buffer file))
- (error "File %s is being visited, cannot save." file))
- (let ((mlist (vm-select-marked-or-prefixed-messages count))
- file-buffer)
- (cond ((eq vm-visit-when-saving t)
- (setq file-buffer (or (get-file-buffer file)
- (find-file-noselect file))))
- (vm-visit-when-saving
- (setq file-buffer (get-file-buffer file))))
- (save-excursion
- (while mlist
- (set-buffer (marker-buffer (vm-start-of (car mlist))))
- (vm-save-restriction
- (widen)
- (if (null file-buffer)
- (write-region (vm-text-of (car mlist))
- (vm-text-end-of (car mlist))
- file t 'quiet)
- (let ((start (vm-text-of (car mlist)))
- (end (vm-text-end-of (car mlist))))
- (save-excursion
- (set-buffer file-buffer)
- (save-excursion
- (let (buffer-read-only)
- (vm-save-restriction
- (widen)
- (save-excursion
- (goto-char (point-max))
- (insert-buffer-substring
- (marker-buffer (vm-start-of (car mlist)))
- start end))))))))
- (if (null (vm-written-flag (car mlist)))
- (vm-set-written-flag (car mlist) t))
- (setq mlist (cdr mlist)))))
- (if file-buffer
- (message "Message%s written to buffer %s" (if (/= 1 count) "s" "")
- (buffer-name file-buffer))
- (message "Message%s written to %s" (if (/= 1 count) "s" "") file)))
- (vm-update-summary-and-mode-line))
-
- (defun vm-pipe-message-to-command (command prefix-arg)
- "Run shell command with the some or all of the current message as input.
- By default the entire message is used.
- With one \\[universal-argument] the text portion of the message is used.
- With two \\[universal-argument]'s the header portion of the message is used.
-
- When invoked on marked messages (via vm-next-command-uses-marks),
- each marked message is successively piped to the shell command,
- one message per command invocation.
-
- Output, if any, is displayed. The message is not altered."
- (interactive
- ;; protect value of last-command
- (let ((last-command last-command))
- (vm-follow-summary-cursor)
- (list (read-string "Pipe to command: " vm-last-pipe-command)
- current-prefix-arg)))
- (vm-select-folder-buffer)
- (vm-check-for-killed-summary)
- (vm-error-if-folder-empty)
- (setq vm-last-pipe-command command)
- (let ((buffer (get-buffer-create "*Shell Command Output*"))
- (pop-up-windows (and pop-up-windows (eq vm-mutable-windows t)))
- ;; prefix arg doesn't have "normal" meaning here, so only call
- ;; vm-select-marked-or-prefixed-messages if we're using marks.
- (mlist (if (eq last-command 'vm-next-command-uses-marks)
- (vm-select-marked-or-prefixed-messages 0)
- (list (car vm-message-pointer)))))
- (save-excursion (set-buffer buffer) (erase-buffer))
- (save-excursion
- (while mlist
- (set-buffer (marker-buffer (vm-start-of (car mlist))))
- (save-restriction
- (widen)
- (goto-char (vm-start-of (car mlist)))
- (forward-line)
- (cond ((equal prefix-arg nil)
- (narrow-to-region (point) (vm-text-end-of (car mlist))))
- ((equal prefix-arg '(4))
- (narrow-to-region (vm-text-of (car mlist))
- (vm-text-end-of (car mlist))))
- ((equal prefix-arg '(16))
- (narrow-to-region (point) (vm-text-of (car mlist))))
- (t (narrow-to-region (point) (vm-text-end-of (car mlist)))))
- (let ((pop-up-windows (and pop-up-windows (eq vm-mutable-windows t))))
- (call-process-region (point-min) (point-max)
- (or shell-file-name "sh")
- nil buffer nil "-c" command)))
- (setq mlist (cdr mlist)))
- (set-buffer buffer)
- (if (not (zerop (buffer-size)))
- (display-buffer buffer)))))
-