diff lisp/vm/vm-virtual.el @ 0:376386a54a3c r19-14

Import from CVS: tag r19-14
author cvs
date Mon, 13 Aug 2007 08:45:50 +0200
parents
children 859a2309aef8
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lisp/vm/vm-virtual.el	Mon Aug 13 08:45:50 2007 +0200
@@ -0,0 +1,642 @@
+;;; Virtual folders for VM
+;;; Copyright (C) 1990, 1993, 1994, 1995 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.
+
+(provide 'vm-virtual)
+
+;; This function builds the virtual message list.
+;;
+;; If the new-messages argument is nil, the message list is
+;; derived from the folders listed in the virtual folder
+;; definition and selected by the various selectors.  The
+;; resulting message list is assigned to vm-message-list.
+;;
+;; If new-messages is non-nil then it is a list of messages to be
+;; tried against the selector parts of the virtual folder
+;; definition.  Matching messages are added to
+;; vm-message-list, instead of replacing it.
+;;
+;; The messages in new-messages must all be in the same real folder.
+(defun vm-build-virtual-message-list (new-messages)
+  (let ((clauses (cdr vm-virtual-folder-definition))
+
+	(message-set (make-vector 311 0))
+	(vbuffer (current-buffer))
+	(mirrored vm-virtual-mirror)
+	(case-fold-search t)
+	(tail-cons (vm-last vm-message-list))
+	(new-message-list nil)
+
+	;; selectors
+	(any 'vm-vs-any)
+	(and 'vm-vs-and)
+	(or 'vm-vs-or)
+	(not 'vm-vs-not)
+	(header 'vm-vs-header)
+	(label 'vm-vs-label)
+	(text 'vm-vs-text)
+	(recipient 'vm-vs-recipient)
+	(author 'vm-vs-author)
+	(subject 'vm-vs-subject)
+	(sent-before 'vm-vs-sent-before)
+	(sent-after 'vm-vs-sent-after)
+	(more-chars-than 'vm-vs-more-chars-than)
+	(less-chars-than 'vm-vs-less-chars-than)
+	(more-lines-than 'vm-vs-more-lines-than)
+	(less-lines-than 'vm-vs-less-lines-than)
+	(new 'vm-vs-new)
+	(unread 'vm-vs-unread)
+	(read 'vm-vs-read)
+	(deleted 'vm-vs-deleted)
+	(replied 'vm-vs-replied)
+	(forwarded 'vm-vs-forwarded)
+	(filed 'vm-vs-filed)
+	(written 'vm-vs-written)
+	(edited 'vm-vs-edited)
+	(marked 'vm-vs-marked)
+
+	virtual location-vector
+	message mp folders folder
+	selectors sel-list selector arglist i
+	real-buffers-used)
+    ;; Since there is at most one virtual message in the folder
+    ;; buffer of a virtual folder, the location data vector (and
+    ;; the markers in it) of all virtual messages in a virtual
+    ;; folder is shared.  We initialize the vector here if it
+    ;; hasn't been created already.
+    (if vm-message-list
+	(setq location-vector (vm-location-data-of (car vm-message-pointer)))
+      (setq i 0
+	    location-vector (make-vector vm-location-data-vector-length nil))
+      (while (< i vm-location-data-vector-length)
+	(aset location-vector i (vm-marker nil))
+	(vm-increment i)))
+    ;; To keep track of the messages in a virtual folder to
+    ;; prevent duplicates we create and maintain a set that
+    ;; contain all the real messages.
+    (setq mp vm-message-list)
+    (while mp
+      (intern (vm-message-id-number-of (vm-real-message-of (car mp)))
+	      message-set)
+      (setq mp (cdr mp)))
+    ;; now select the messages
+    (save-excursion
+      (while clauses
+	(setq folders (car (car clauses))
+	      selectors (cdr (car clauses)))
+	(while folders
+	  (setq folder (car folders))
+	  (and (stringp folder)
+	       (setq folder (expand-file-name folder vm-folder-directory)))
+	  (and (listp folder)
+	       (setq folder (eval folder)))
+	  (cond
+	   ((null folder)
+	    ;; folder was a s-expr which returned nil
+	    ;; skip it
+	    nil )
+	   ((and (stringp folder) (file-directory-p folder))
+	    (setq folders (nconc folders
+				 (vm-delete-backup-file-names
+				  (vm-delete-auto-save-file-names
+				   (vm-delete-directory-file-names
+				    (directory-files folder t nil)))))))
+	   ((or (null new-messages)
+		;; If we're assimilating messages into an
+		;; existing virtual folder, only allow selectors
+		;; that would be normally applied to this folder.
+		(and (bufferp folder)
+		     (eq (vm-buffer-of (car new-messages)) folder))
+		(and (stringp folder)
+		     (eq (vm-buffer-of (car new-messages))
+			 ;; letter bomb protection
+			 ;; set inhibit-local-variables to t for v18 Emacses
+			 ;; set enable-local-variables to nil for newer Emacses
+			 (let ((inhibit-local-variables t)
+			       (enable-local-variables nil))
+			   (find-file-noselect folder)))))
+	    (set-buffer (or (and (bufferp folder) folder)
+			    (vm-get-file-buffer folder)
+			    (find-file-noselect folder)))
+	    (if (eq major-mode 'vm-virtual-mode)
+		(setq virtual t
+		      real-buffers-used
+		      (append vm-real-buffers real-buffers-used))
+	      (setq virtual nil)
+	      (if (not (memq (current-buffer) real-buffers-used))
+		  (setq real-buffers-used (cons (current-buffer)
+						real-buffers-used)))
+	      (if (not (eq major-mode 'vm-mode))
+		  (vm-mode)))
+	    ;; change (sexpr) into ("/file" "/file2" ...)
+	    ;; this assumes that there will never be (sexpr sexpr2)
+	    ;; in a virtual folder spec.
+	    (if (bufferp folder)
+		(if virtual
+		    (setcar (car clauses)
+			    (delq nil
+				  (mapcar 'buffer-file-name vm-real-buffers)))
+		  (if buffer-file-name
+		      (setcar (car clauses) (list buffer-file-name)))))
+	    ;; if new-messages non-nil use it instead of the
+	    ;; whole message list
+	    (setq mp (or new-messages vm-message-list))
+	    (while mp
+	      (if (and (not (intern-soft
+			     (vm-message-id-number-of
+			      (vm-real-message-of (car mp)))
+			     message-set))
+		       (if virtual
+			   (save-excursion
+			     (set-buffer
+			      (vm-buffer-of
+			       (vm-real-message-of
+				(car mp))))
+			     (apply 'vm-vs-or (vm-real-message-of (car mp))
+				    selectors))
+			 (apply 'vm-vs-or (car mp) selectors)))
+		  (progn
+		    (intern
+		     (vm-message-id-number-of
+		      (vm-real-message-of (car mp)))
+		      message-set)
+		    (setq message (copy-sequence
+				   (vm-real-message-of (car mp))))
+		    (if mirrored
+			()
+		      (vm-set-mirror-data-of
+		       message
+		       (make-vector vm-mirror-data-vector-length nil))
+		      (vm-set-virtual-messages-sym-of
+		       message (make-symbol "<v>"))
+		      (vm-set-virtual-messages-of message nil)
+		      (vm-set-attributes-of
+		       message
+		       (make-vector vm-attributes-vector-length nil)))
+		    (vm-set-location-data-of message location-vector)
+		    (vm-set-softdata-of
+		     message
+		     (make-vector vm-softdata-vector-length nil))
+		    (vm-set-real-message-sym-of
+		     message
+		     (vm-real-message-sym-of (car mp)))
+		    (vm-set-message-type-of message vm-folder-type)
+		    (vm-set-message-id-number-of message
+						 vm-message-id-number)
+		    (vm-increment vm-message-id-number)
+		    (vm-set-buffer-of message vbuffer)
+		    (vm-set-reverse-link-sym-of message (make-symbol "<--"))
+		    (vm-set-reverse-link-of message tail-cons)
+		    (if (null tail-cons)
+			(setq new-message-list (list message)
+			      tail-cons new-message-list)
+		      (setcdr tail-cons (list message))
+		      (if (null new-message-list)
+			  (setq new-message-list (cdr tail-cons)))
+		      (setq tail-cons (cdr tail-cons)))))
+	      (setq mp (cdr mp)))))
+	  (setq folders (cdr folders)))
+	(setq clauses (cdr clauses))))
+; this doesn't need to work currently, but it might someday
+;    (if virtual
+;	(setq real-buffers-used (vm-delete-duplicates real-buffers-used)))
+    (vm-increment vm-modification-counter)
+    ;; Until this point the user doesn't really have a virtual
+    ;; folder, as the virtual messages haven't been linked to the
+    ;; real messages, virtual buffers to the real buffers, and no
+    ;; message list has been installed.
+    ;;
+    ;; Now we tie it all together, with this section of code being
+    ;; uninterruptible.
+    (let ((inhibit-quit t))
+      (if (null vm-real-buffers)
+	  (setq vm-real-buffers real-buffers-used))
+      (save-excursion
+	(while real-buffers-used
+	  (set-buffer (car real-buffers-used))
+	  (if (not (memq vbuffer vm-virtual-buffers))
+	      (setq vm-virtual-buffers (cons vbuffer vm-virtual-buffers)))
+	  (setq real-buffers-used (cdr real-buffers-used))))
+      (setq mp new-message-list)
+      (while mp
+	(vm-set-virtual-messages-of
+	 (vm-real-message-of (car mp))
+	 (cons (car mp) (vm-virtual-messages-of (car mp))))
+	(setq mp (cdr mp)))
+      (if vm-message-list
+	  (progn
+	    (vm-set-summary-redo-start-point new-message-list)
+	    (vm-set-numbering-redo-start-point new-message-list))
+	(vm-set-summary-redo-start-point t)
+	(vm-set-numbering-redo-start-point t)
+	(setq vm-message-list new-message-list)))))
+
+(defun vm-create-virtual-folder (selector &optional arg read-only)
+  "Create a new virtual folder from messages in the current folder.
+The messages will be chosen by applying the selector you specify,
+which is normally read from the minibuffer.
+
+Prefix arg means the new virtual folder should be visited read only."
+  (interactive
+   (let ((last-command last-command)
+	 (this-command this-command)
+	 (prefix current-prefix-arg))
+     (vm-select-folder-buffer)
+     (nconc (vm-read-virtual-selector "Create virtual folder of messages: ")
+	    (list prefix))))
+  (vm-select-folder-buffer)
+  (vm-check-for-killed-summary)
+  (vm-error-if-folder-empty)
+  (let (vm-virtual-folder-alist name)
+    (if arg
+	(setq name (format "%s %s %s" (buffer-name) selector arg))
+      (setq name (format "%s %s" (buffer-name) selector)))
+    (setq vm-virtual-folder-alist
+	  (list
+	   (list name
+		 (list (list (list 'get-buffer (buffer-name)))
+		       (if arg (list selector arg) (list selector))))))
+    (vm-visit-virtual-folder name read-only)))
+
+(defun vm-apply-virtual-folder (name &optional read-only)
+  "Apply the selectors of a named virtual folder to the current folder
+and create a virtual folder containing the selected messages.
+
+Prefix arg means the new virtual folder should be visited read only."
+  (interactive
+   (let ((last-command last-command)
+	 (this-command this-command))
+     (list
+      (completing-read "Apply this virtual folder's selectors: "
+		       vm-virtual-folder-alist nil t)
+      current-prefix-arg)))
+  (vm-select-folder-buffer)
+  (vm-check-for-killed-summary)
+  (vm-error-if-folder-empty)
+  (let ((vfolder (assoc name vm-virtual-folder-alist))
+	clauses vm-virtual-folder-alist)
+    (or vfolder (error "No such virtual folder, %s" name))
+    (setq vfolder (vm-copy vfolder))
+    (setq clauses (cdr vfolder))
+    (while clauses
+      (setcar (car clauses) (list (list 'get-buffer (buffer-name))))
+      (setq clauses (cdr clauses)))
+    (setcar vfolder (format "%s/%s" (buffer-name) (car vfolder)))
+    (setq vm-virtual-folder-alist (list vfolder))
+    (vm-visit-virtual-folder (car vfolder) read-only)))
+
+(defun vm-toggle-virtual-mirror ()
+  (interactive)
+  (vm-select-folder-buffer)
+  (vm-check-for-killed-summary)
+  (if (not (eq major-mode 'vm-virtual-mode))
+      (error "This is not a virtual folder."))
+  (let ((mp vm-message-list)
+	(inhibit-quit t)
+	modified undo-list)
+    (setq undo-list vm-saved-undo-record-list
+	  vm-saved-undo-record-list vm-undo-record-list
+	  vm-undo-record-list undo-list
+	  vm-undo-record-pointer undo-list)
+    (setq modified vm-saved-buffer-modified-p
+	  vm-saved-buffer-modified-p (buffer-modified-p))
+    (set-buffer-modified-p modified)
+    (if vm-virtual-mirror
+	(while mp
+	  (vm-set-attributes-of
+	   (car mp) (or (vm-saved-virtual-attributes-of (car mp))
+			(make-vector vm-attributes-vector-length nil)))
+	  (vm-set-mirror-data-of
+	   (car mp) (or (vm-saved-virtual-mirror-data-of (car mp))
+			(make-vector vm-mirror-data-vector-length nil)))
+	  (vm-mark-for-summary-update (car mp) t)
+	  (setq mp (cdr mp)))
+      (while mp
+	;; mark for summary update _before_ we set this message to
+	;; be mirrored.  this will prevent the real message and
+	;; the other messages that will share attributes with
+	;; this message from having their summaries
+	;; updated... they don't need it.
+	(vm-mark-for-summary-update (car mp) t)
+	(vm-set-saved-virtual-attributes-of
+	 (car mp) (vm-attributes-of (car mp)))
+	(vm-set-saved-virtual-mirror-data-of
+	 (car mp) (vm-mirror-data-of (car mp)))
+	(vm-set-attributes-of
+	 (car mp) (vm-attributes-of (vm-real-message-of (car mp))))
+	(vm-set-mirror-data-of
+	 (car mp) (vm-mirror-data-of (vm-real-message-of (car mp))))
+	(setq mp (cdr mp))))
+    (setq vm-virtual-mirror (not vm-virtual-mirror))
+    (vm-increment vm-modification-counter))
+  (vm-update-summary-and-mode-line)
+  (message "Virtual folder now %s the underlying real folder%s."
+	   (if vm-virtual-mirror "mirrors" "does not mirror")
+	   (if (cdr vm-real-buffers) "s" "")))
+
+(defun vm-virtual-help ()
+  (interactive)
+  (vm-display nil nil '(vm-virtual-help) '(vm-virtual-help))
+  (message "VV = visit, VA = apply selectors, VC = create, VM = toggle virtual mirror"))
+
+(defun vm-delete-directory-file-names (list)
+  (vm-delete 'file-directory-p list))
+
+(defun vm-delete-backup-file-names (list)
+  (vm-delete 'backup-file-name-p list))
+
+(defun vm-delete-auto-save-file-names (list)
+  (vm-delete 'auto-save-file-name-p list))
+
+(defun vm-vs-or (m &rest selectors)
+  (let ((result nil) selector arglist)
+    (while selectors
+      (setq selector (car (car selectors))
+	    arglist (cdr (car selectors))
+	    result (apply (symbol-value selector) m arglist)
+	    selectors (if result nil (cdr selectors))))
+    result ))
+
+(defun vm-vs-and (m &rest selectors)
+  (let ((result t) selector arglist)
+    (while selectors
+      (setq selector (car (car selectors))
+	    arglist (cdr (car selectors))
+	    result (apply (symbol-value selector) m arglist)
+	    selectors (if (null result) nil (cdr selectors))))
+    result ))
+
+(defun vm-vs-not (m arg)
+  (let ((selector (car arg))
+	(arglist (cdr arg)))
+    (not (apply (symbol-value selector) m arglist))))
+
+(defun vm-vs-any (m) t)
+
+(defun vm-vs-author (m arg)
+  (or (string-match arg (vm-su-full-name m))
+      (string-match arg (vm-su-from m))))
+
+(defun vm-vs-recipient (m arg)
+  (or (string-match arg (vm-su-to m))
+      (string-match arg (vm-su-to-names m))))
+
+(defun vm-vs-subject (m arg)
+  (string-match arg (vm-su-subject m)))
+
+(defun vm-vs-sent-before (m arg)
+  (string< (vm-so-sortable-datestring m) (vm-timezone-make-date-sortable arg)))
+
+(defun vm-vs-sent-after (m arg)
+  (string< (vm-timezone-make-date-sortable arg) (vm-so-sortable-datestring m)))
+
+(defun vm-vs-header (m arg)
+  (save-excursion
+    (save-restriction
+      (widen)
+      (goto-char (vm-headers-of m))
+      (re-search-forward arg (vm-text-of m) t))))
+
+(defun vm-vs-label (m arg)
+  (vm-member arg (vm-labels-of m)))
+
+(defun vm-vs-text (m arg)
+  (save-excursion
+    (save-restriction
+      (widen)
+      (goto-char (vm-text-of m))
+      (re-search-forward arg (vm-text-end-of m) t))))
+
+(defun vm-vs-more-chars-than (m arg)
+  (> (string-to-int (vm-su-byte-count m)) arg))
+
+(defun vm-vs-less-chars-than (m arg)
+  (< (string-to-int (vm-su-byte-count m)) arg))
+
+(defun vm-vs-more-lines-than (m arg)
+  (> (string-to-int (vm-su-line-count m)) arg))
+
+(defun vm-vs-less-lines-than (m arg)
+  (< (string-to-int (vm-su-line-count m)) arg))
+
+(defun vm-vs-new (m) (vm-new-flag m))
+(defun vm-vs-unread (m) (vm-unread-flag m))
+(defun vm-vs-read (m) (not (or (vm-new-flag m) (vm-unread-flag m))))
+(defun vm-vs-deleted (m) (vm-deleted-flag m))
+(defun vm-vs-replied (m) (vm-replied-flag m))
+(defun vm-vs-forwarded (m) (vm-forwarded-flag m))
+(defun vm-vs-filed (m) (vm-filed-flag m))
+(defun vm-vs-written (m) (vm-written-flag m))
+(defun vm-vs-marked (m) (vm-mark-of m))
+(defun vm-vs-edited (m) (vm-edited-flag m))
+
+(put 'header 'vm-virtual-selector-clause "with header matching")
+(put 'label 'vm-virtual-selector-clause "with label of")
+(put 'text 'vm-virtual-selector-clause "with text matching")
+(put 'recipient 'vm-virtual-selector-clause "with recipient matching")
+(put 'author 'vm-virtual-selector-clause "with author matching")
+(put 'subject 'vm-virtual-selector-clause "with subject matching")
+(put 'sent-before 'vm-virtual-selector-clause "sent before")
+(put 'sent-after 'vm-virtual-selector-clause "sent after")
+(put 'more-chars-than 'vm-virtual-selector-clause
+     "with more characters than")
+(put 'less-chars-than 'vm-virtual-selector-clause
+     "with less characters than")
+(put 'more-lines-than 'vm-virtual-selector-clause "with more lines than")
+(put 'less-lines-than 'vm-virtual-selector-clause "with less lines than")
+
+(defun vm-read-virtual-selector (prompt)
+  (let (selector (arg nil))
+    (setq selector
+	  (vm-read-string prompt vm-supported-interactive-virtual-selectors)
+	  selector (intern selector))
+    (if (memq selector '(header label text recipient
+			 author subject
+			 sent-before sent-after
+			 more-chars-than more-lines-than
+			 less-chars-than less-lines-than))
+	(progn
+	  (setq prompt (concat (substring prompt 0 -2) " "
+			       (get selector 'vm-virtual-selector-clause)
+			       ": "))
+	  (cond ((memq selector '(more-chars-than more-lines-than
+			          less-chars-than less-lines-than))
+		 (setq arg (vm-read-number prompt)))
+		((eq selector 'label)
+		 (let ((vm-completion-auto-correct nil)
+		       (completion-ignore-case t))
+		   (setq arg (downcase
+			      (vm-read-string
+			       prompt
+			       (vm-obarray-to-string-list
+				vm-label-obarray)
+			       nil)))))
+		(t (setq arg (read-string prompt))))))
+    (list selector arg)))
+
+;; clear away links between real and virtual folders when
+;; a vm-quit is performed in either type folder.
+(defun vm-virtual-quit ()
+  (save-excursion
+    (cond ((eq major-mode 'vm-virtual-mode)
+	   ;; don't trust blindly, user might have killed some of
+	   ;; these buffers.
+	   (setq vm-real-buffers (vm-delete 'buffer-name vm-real-buffers t))
+	   (let ((bp vm-real-buffers)
+		 (mp vm-message-list)
+		 (b (current-buffer))
+		 ;; lock out interrupts here
+		 (inhibit-quit t))
+	     (while bp
+	       (set-buffer (car bp))
+	       (setq vm-virtual-buffers (delq b vm-virtual-buffers)
+		     bp (cdr bp)))
+	     (while mp
+	       (vm-set-virtual-messages-of
+		(vm-real-message-of (car mp))
+		(delq (car mp) (vm-virtual-messages-of
+				(vm-real-message-of (car mp)))))
+	       (setq mp (cdr mp)))))
+	  ((eq major-mode 'vm-mode)
+	   ;; don't trust blindly, user might have killed some of
+	   ;; these buffers.
+	   (setq vm-virtual-buffers
+		 (vm-delete 'buffer-name vm-virtual-buffers t))
+	   (let ((bp vm-virtual-buffers)
+		 (mp vm-message-list)
+		 vmp
+		 (b (current-buffer))
+		 ;; lock out interrupts here
+		 (inhibit-quit t))
+	     (while mp
+	       (setq vmp (vm-virtual-messages-of (car mp)))
+	       (while vmp
+		 ;; we'll clear these messages from the virtual
+		 ;; folder by looking for messages that have a "Q"
+		 ;; id number associated with them.
+		 (vm-set-message-id-number-of (car vmp) "Q")
+		 (setq vmp (cdr vmp)))
+	       (vm-set-virtual-messages-of (car mp) nil)
+	       (setq mp (cdr mp)))
+	     (while bp
+	       (set-buffer (car bp))
+	       (setq vm-real-buffers (delq b vm-real-buffers))
+	       ;; set the message pointer to a new value if it is
+	       ;; now invalid.
+	       (setq vmp vm-message-pointer)
+	       (while (and vm-message-pointer
+			   (equal "Q" (vm-message-id-number-of
+				       (car vm-message-pointer))))
+		 (setq vm-message-pointer
+		       (cdr vm-message-pointer)))
+	       ;; if there were no good messages ahead, try going
+	       ;; backward.
+	       (if (null vm-message-pointer)
+		   (progn
+		     (setq vm-message-pointer vmp)
+		     (while (and vm-message-pointer
+				 (equal "Q" (vm-message-id-number-of
+					     (car vm-message-pointer))))
+		       (setq vm-message-pointer
+			     (vm-reverse-link-of (car vm-message-pointer))))))
+	       ;; expunge the virtual messages associated with
+	       ;; real messages that are going away.
+	       (setq vm-message-list
+		     (vm-delete (function
+				 (lambda (m)
+				   (equal "Q" (vm-message-id-number-of m))))
+				vm-message-list nil))
+	       (if (null vm-message-pointer)
+		   (setq vm-message-pointer vm-message-list))
+	       ;; same for vm-last-message-pointer
+	       (if (null vm-last-message-pointer)
+		   (setq vm-last-message-pointer nil))
+	       (vm-clear-virtual-quit-invalidated-undos)
+	       (vm-reverse-link-messages)
+	       (vm-set-numbering-redo-start-point t)
+	       (vm-set-summary-redo-start-point t)
+	       (if vm-message-pointer
+		   (vm-preview-current-message)
+		 (vm-update-summary-and-mode-line))
+	       (setq bp (cdr bp))))))))
+
+(defun vm-virtual-save-folder (prefix)
+  (save-excursion
+    ;; don't trust blindly, user might have killed some of
+    ;; these buffers.
+    (setq vm-real-buffers (vm-delete 'buffer-name vm-real-buffers t))
+    (let ((bp vm-real-buffers))
+      (while bp
+	(set-buffer (car bp))
+	(vm-save-folder prefix)
+	(setq bp (cdr bp)))))
+  (vm-set-buffer-modified-p nil)
+  (vm-clear-modification-flag-undos)
+  (vm-update-summary-and-mode-line))
+
+(defun vm-virtual-get-new-mail ()
+  (save-excursion
+    ;; don't trust blindly, user might have killed some of
+    ;; these buffers.
+    (setq vm-real-buffers (vm-delete 'buffer-name vm-real-buffers t))
+    (let ((bp vm-real-buffers))
+      (while bp
+	(set-buffer (car bp))
+	(condition-case error-data
+	    (vm-get-new-mail)
+	  (folder-read-only
+	   (message "Folder is read only: %s"
+		    (or buffer-file-name (buffer-name)))
+	   (sit-for 1))
+	  (unrecognized-folder-type
+	   (message "Folder type is unrecognized: %s"
+		    (or buffer-file-name (buffer-name)))
+	   (sit-for 1)))
+	(setq bp (cdr bp)))))
+  (vm-emit-totals-blurb))
+
+(defun vm-make-virtual-copy (m)
+  (widen)
+  (let ((virtual-buffer (current-buffer))
+	(real-m (vm-real-message-of m))
+	(buffer-read-only nil)
+	(modified (buffer-modified-p)))
+    (unwind-protect
+	(save-excursion
+	  (set-buffer (vm-buffer-of real-m))
+	  (save-restriction
+	    (widen)
+	    ;; must reference this now so that headers will be in
+	    ;; their final position before the message is copied.
+	    ;; otherwise the vheader offset computed below will be wrong.
+	    (vm-vheaders-of real-m)
+	    (copy-to-buffer virtual-buffer (vm-start-of real-m)
+			    (vm-end-of real-m))))
+      (set-buffer-modified-p modified))
+    (set-marker (vm-start-of m) (point-min))
+    (set-marker (vm-headers-of m) (+ (vm-start-of m)
+				     (- (vm-headers-of real-m)
+					(vm-start-of real-m))))
+    (set-marker (vm-vheaders-of m) (+ (vm-start-of m)
+				      (- (vm-vheaders-of real-m)
+					 (vm-start-of real-m))))
+    (set-marker (vm-text-of m) (+ (vm-start-of m) (- (vm-text-of real-m)
+						     (vm-start-of real-m))))
+    (set-marker (vm-text-end-of m) (+ (vm-start-of m)
+				      (- (vm-text-end-of real-m)
+					 (vm-start-of real-m))))
+    (set-marker (vm-end-of m) (+ (vm-start-of m) (- (vm-end-of real-m)
+						    (vm-start-of real-m))))))