diff -udNr vm-7.14/Makefile vm-7.14-filter/Makefile
--- vm-7.14/Makefile	2003-03-20 10:50:02.000000000 -0800
+++ vm-7.14-filter/Makefile	2003-04-14 15:07:25.000000000 -0700
@@ -57,7 +57,8 @@
     vm-save.elc \
     vm-search.elc vm-sort.elc vm-summary.elc vm-startup.elc vm-thread.elc \
     vm-toolbar.elc vm-undo.elc \
-    vm-user.elc vm-vars.elc vm-virtual.elc vm-window.elc
+    vm-user.elc vm-vars.elc vm-virtual.elc vm-window.elc \
+    vm-filter.elc
 
 SOURCES = \
     vm-version.el \
@@ -68,7 +69,8 @@
     vm-mouse.el vm-page.el vm-pop.el vm-reply.el vm-save.el \
     vm-search.el vm-sort.el vm-startup.el vm-summary.el vm-thread.el \
     vm-toolbar.el vm-undo.el \
-    vm-user.el vm-vars.el vm-virtual.el vm-window.el
+    vm-user.el vm-vars.el vm-virtual.el vm-window.el \
+    vm-filter.el
 
 UTILS = qp-decode qp-encode base64-decode base64-encode
 
@@ -134,7 +136,7 @@
 	cp $(UTILS) $(BINDIR)
 
 clean:
-	rm -f $(UTILS) vm.info vm.info-* vm-autoload.el vm-autoload.elc $(OBJECTS) tapestry.elc
+	rm -f $(UTILS) vm.info vm.info-* vm-autoload.el vm-autoload.elc $(OBJECTS) tapestry.elc vm.el vm.elc
 
 vm.info:	vm.texinfo
 	@echo "making vm.info..."
@@ -166,6 +168,10 @@
 	@echo compiling vm-edit.el...
 	@$(EMACS) $(BATCHFLAGS) $(PRELOADS) -f batch-byte-compile vm-edit.el
 
+vm-filter.elc:	vm-filter.el $(CORE)
+	@echo compiling vm-filter.el...
+	@$(EMACS) $(BATCHFLAGS) $(PRELOADS) -f batch-byte-compile vm-filter.el
+
 vm-folder.elc:	vm-folder.el $(CORE)
 	@echo compiling vm-folder.el...
 	@$(EMACS) $(BATCHFLAGS) $(PRELOADS) -f batch-byte-compile vm-folder.el
diff -udNr vm-7.14/vm-filter.el vm-7.14-filter/vm-filter.el
--- vm-7.14/vm-filter.el	1969-12-31 16:00:00.000000000 -0800
+++ vm-7.14-filter/vm-filter.el	2003-04-14 15:19:55.000000000 -0700
@@ -0,0 +1,383 @@
+;;; Email filtering for VM
+;;; Copyright (C) 2002 Adam Milazzo
+;;; Thanks to Paul Graham for the statistics algorithms
+;;;
+;;; 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-filter)
+
+(defvar vm-filter-changed nil)
+
+(defun vm-filter-remove-duplicates(lst)
+  "Remove duplicate items from a list."
+  (let ((slst (sort lst #'string<))
+        (ret nil)
+        (olp nil))
+    (while slst
+      (if (not (equal (car slst) (car olp)))
+        (setq ret (cons (car slst) ret)))
+      (setq olp slst slst (cdr slst)))
+    ret))
+
+(defun vm-filter-default(buf)
+  "The default filter function. Returns non-nil if the message is spam.
+   First, it checks the From: header against the whitelist. If the source
+   email doesn't pass the whitelist, it then uses the pseudo-bayesian stuff
+   (`vm-filter-probability-of-spam-buffer') to calculate a probability and
+   then returns non-nil if the probability is >= `vm-filter-default-spam-threshold'"
+  (not (or
+    (and vm-filter-whitelist
+      (save-excursion
+        (set-buffer buf)
+        (beginning-of-buffer)
+        (or case-fold-search (setq case-fold-search t))
+        (and 
+          (or (re-search-forward "^From:.*?< *\\(.*?\\) *> *$" nil t)
+              (re-search-forward "^From: *\\(.*?\\) *$" nil t))
+          (member
+            (downcase (buffer-substring-no-properties (match-beginning 1) (match-end 1)))
+            vm-filter-whitelist))))
+    (< (vm-filter-probability-of-spam-buffer buf) vm-filter-default-spam-threshold))))
+
+(defun vm-filter-split-words(buf)
+  "Split a buffer into a list of lowercase words. It ignores HTML
+   comments and words that are too big or too small."
+  (save-excursion
+    (set-buffer buf)
+    (beginning-of-buffer)
+    (while (re-search-forward "\<!--.*?--\>" nil t)
+      (replace-match "" nil nil))
+    (let* ((lst (split-string (downcase (buffer-string)) "[^a-zA-Z0-9\-'$]")) (olp nil) (lp lst) (n 0))
+      (while lp
+        (setq n (length (car lp)))
+        (if (or (< n 2) (> n 20))
+          (if olp
+            (setq lp (setcdr olp (cdr lp)))
+            (setq lst (cdr lst) lp lst))
+          (setq olp lp lp (cdr lp))))
+      lst)))
+
+(defun vm-filter-count-words(hash lst)
+  "For each word in the list 'lst', increment the corresponding
+   value in the hash. The hash contains counts of words."
+  (let ((v nil))
+    (while lst
+      (if (setq v (gethash (car lst) hash))
+        (puthash (car lst) (1+ v) hash)
+        (puthash (car lst) 1 hash))
+      (setq lst (cdr lst)))))
+
+(defun vm-filter-word-probability(word)
+  "Calculates the spamminess of a word -- used to create
+   vm-filter-word-hash."
+  (let ((g (float (gethash word vm-filter-good-hash 0)))
+        (b (float (gethash word vm-filter-bad-hash 0)))
+        (ngood (max 1 vm-filter-num-good)) (nbad (max 1 vm-filter-num-bad)))
+    (unless (< (+ g b) 5)
+      (max .01
+        (min .99
+          (/ (min 1 (/ b nbad))
+             (+ (min 1 (/ g ngood))
+                (min 1 (/ b nbad)))))))))
+
+(defun vm-filter-dump-hash(hash)
+  "Dump the contents of a hash to a new buffer."
+  (let ((lst nil) (buf (generate-new-buffer "HASH DUMP")))
+    (switch-to-buffer buf)
+    (maphash #'(lambda(k v) (setq lst (cons k lst))) hash)
+    (mapcar #'(lambda(w) (insert (format "%s - %s\n" w (gethash w hash))))
+            (sort lst #'(lambda(a b) (> (gethash a hash) (gethash b hash)))))))
+
+(defun vm-filter-dump-word-hash()
+  (interactive)
+  (vm-filter-dump-hash vm-filter-word-hash))
+  
+(defun vm-filter-dump-good-hash()
+  (interactive)
+  (vm-filter-dump-hash vm-filter-good-hash))
+  
+(defun vm-filter-dump-bad-hash()
+  (interactive)
+  (vm-filter-dump-hash vm-filter-bad-hash))
+ 
+(defun vm-filter-reset-hashes()
+  "Set/reset hashes to an empty, initialized state."
+  (setq vm-filter-good-hash (make-hash-table :test 'equal :size 4096))
+  (setq vm-filter-bad-hash  (make-hash-table :test 'equal :size 4096))
+  (setq vm-filter-word-hash (make-hash-table :test 'equal :size 4096)))
+
+(defun vm-filter-write-hash(hash buf)
+  "Write a hash to a buffer."
+  (print (hash-table-count hash) buf)
+  (maphash #'(lambda(k v) (progn (print k buf) (print v buf))) hash))
+
+(defun vm-filter-read-hash(hash file)
+  "Read a hash (written by `vm-filter-write-hash') from a buffer."
+  (let ((num 0) (k nil) (v nil))
+    (setq num (read file))
+    (while (> num 0)
+      (setq k (read file) v (read file))
+      (puthash k v hash)
+      (setq num (1- num)))))
+
+(defun vm-filter-save()
+  "Save the filter if it has changed."
+  (interactive)
+  (if vm-filter-changed
+    (with-temp-message "Saving email filter data..."
+    (with-temp-buffer
+      (let ((buf (current-buffer)))
+        (print vm-filter-num-good buf)
+        (print vm-filter-num-bad buf)
+        (vm-filter-write-hash vm-filter-good-hash buf)
+        (vm-filter-write-hash vm-filter-bad-hash buf)
+        (condition-case nil
+          (write-file vm-filter-file)
+          (error (error (format "Unable to write to file %s" vm-filter-file))))
+        (setq vm-filter-changed nil))))))
+
+(defun vm-filter-maybe-save()
+  "If the filter is valid and has changed, save it."
+  (interactive)
+  (and vm-filter-good-hash vm-filter-bad-hash vm-filter-changed (vm-filter-save)))
+
+(defun vm-filter-load()
+  "Load the filter from the file specified by `vm-filter-file'."
+  (interactive)
+  (with-temp-message "Loading email filter data..."
+  (with-temp-buffer
+    (let ((buf (current-buffer)))
+      (vm-filter-reset-hashes)
+      (condition-case nil
+        (progn
+          (insert-file-contents-literally vm-filter-file)
+          (setq vm-filter-num-good (read buf))
+          (setq vm-filter-num-bad  (read buf))
+          (vm-filter-read-hash vm-filter-good-hash buf)
+          (vm-filter-read-hash vm-filter-bad-hash  buf))
+        (error nil))
+      (setq vm-filter-changed nil)))))
+
+(defun vm-filter-on-load()
+  "If the filter hasn't been initialized, load it from the file
+   and recalculate the word hash."
+  (interactive)
+  (if (or (not vm-filter-good-hash) (not vm-filter-bad-hash))
+    (progn
+      (vm-filter-load)
+      (vm-filter-recalc-word-hash))))
+
+(defun vm-filter-insert-into-word-hash(word dummy)
+  "Given a word, calculate its probability and insert it
+   into `vm-filter-word-hash'."
+  (or
+   (gethash word vm-filter-word-hash)
+   (let ((v (vm-filter-word-probability word)))
+     (and v (puthash word v vm-filter-word-hash)))))
+
+(defun vm-filter-recalc-word-hash()
+  "Recalculate the contents of `vm-filter-word-hash' using the
+   `vm-filter-good-hash' and `vm-filter-bad-hash' hashes. This
+   may take some time, so it's not done automatically when the
+   good/bad hashes change. This is the hash that is used to
+   compute the probability of a buffer containing spam."
+  (interactive)
+  (with-temp-message "Recalculating word hash..."
+    (setq vm-filter-word-hash (make-hash-table :test 'equal :size 4096))
+    (maphash 'vm-filter-insert-into-word-hash vm-filter-good-hash)
+    (maphash 'vm-filter-insert-into-word-hash vm-filter-bad-hash)))
+
+(defun vm-filter-comb-prob(probs)
+  "Combine a list of probilities (0.0 to 1.0) into a single value."
+  (let ((prod (apply #'* probs)))
+    (/ prod (+ prod (apply #'* (mapcar #'(lambda(x) (- 1 x))
+                                       probs))))))
+
+(defun vm-filter-find-interesting-probs(lst num)
+  "Find the 'num' most interesting words in 'lst' where
+   more interesting words are defined as having a probability
+   further from 0.5. It doesn't consider duplicate words more than once."
+  (let* ((prob (sort (mapcar #'(lambda(w) (gethash w vm-filter-word-hash vm-filter-default-spam-prob)) 
+                             (vm-filter-remove-duplicates lst))
+                     #'(lambda(x y) (> (abs (- x 0.5)) (abs (- y 0.5))))))
+         (lp prob) (olp lp))
+    (while (and lp (> num 0))
+      (setq olp lp lp (cdr lp) num (1- num)))
+    (if lp (setcdr olp nil))
+    prob))
+    
+(defun vm-filter-probability-of-spam-report(&optional buf)
+  "Find the most interesting words in the buffer (default to
+   current) -- the ones that would be used to determine
+   spamminess. Then present the words and their probabilities,
+   and a total spamminess factor."
+  (interactive)
+  (let
+    ((words
+      (let*
+          ((lst (vm-filter-remove-duplicates (vm-filter-split-words (or buf (current-buffer)))))
+           (num vm-filter-num-interesting-probs)
+           (hash vm-filter-word-hash)
+           (prob (sort lst #'(lambda(x y) (> (abs (- (gethash x hash 0.4) 0.5)) (abs (- (gethash y hash 0.4) 0.5))))))
+           (lp prob)
+           (olp lp))
+        (while (and lp (> num 0))
+          (setq olp lp lp (cdr lp) num (1- num)))
+        (if lp (setcdr olp nil))
+        prob))
+     (hash vm-filter-word-hash)
+     (obuf (generate-new-buffer (format "spam report for %s" (buffer-name buf)))))
+    (switch-to-buffer obuf)
+    (insert "Interesting words:\n")
+    (mapcar #'(lambda(w) (insert (format "%s\t\t%s\n" w (gethash w hash 0.4)))) words)
+    (insert (format "Overall Spam Probability: %s" 
+                    (vm-filter-comb-prob (mapcar #'(lambda(w) (gethash w hash 0.4)) words))))))
+
+
+(defun vm-filter-probability-of-spam-buffer(buf)
+  "Given a buffer, calculate and return the probability that
+   it's spam."
+  (vm-filter-comb-prob (vm-filter-find-interesting-probs (vm-filter-split-words buf) vm-filter-num-interesting-probs)))
+  
+(defun vm-filter-probability-of-spam()
+  "Print the probability of the current buffer being spam."
+  (interactive)
+  (message "Probability is %s%%" (* (vm-filter-probability-of-spam-buffer (current-buffer)) 100)))
+
+(defun vm-filter-learn(buf hash)
+  "Learn from the contents of buffer 'buf' and insert the
+   results into hash 'hash'. Then, mark the filter as changed."
+  (vm-filter-count-words hash (vm-filter-split-words buf))
+  (setq vm-filter-changed t))
+
+(defun vm-filter-learn-spam-default(&optional buf)
+  "Learn from spam by calling `vm-filter-learn' and increment
+   the number of seen spam emails."
+  (interactive)
+  (vm-filter-learn (or buf (current-buffer)) vm-filter-bad-hash)
+  (setq vm-filter-num-bad (1+ vm-filter-num-bad)))
+
+(defun vm-filter-learn-nonspam-default(&optional buf)
+  "Learn from nonspam by calling `vm-filter-learn' and increment
+   the number of seen nonspam emails."
+  (interactive)
+  (vm-filter-learn (or buf (current-buffer)) vm-filter-good-hash)
+  (setq vm-filter-num-good (1+ vm-filter-num-good)))
+
+(defun vm-get-temporary-buffer(message)
+  "Given a VM message, dump the full contents into a
+   temporary buffer and return the buffer."
+  (save-excursion
+    (vm-select-folder-buffer)
+    (save-restriction
+    (let ((buf (generate-new-buffer " temporary email filter")))
+      (widen)
+      (copy-to-buffer buf
+       (marker-position (vm-headers-of message))
+       (marker-position (vm-text-end-of message)))
+      buf))))
+
+(defun vm-filter-email()
+  "Scan the current list of emails and mark for deletion any that
+   are determined to be spam."
+  (interactive)
+  (if vm-filter-function
+    (with-temp-message "Filtering email for spam..."
+      (vm-error-if-folder-read-only)
+      (save-excursion
+        (vm-select-folder-buffer)
+        (vm-check-for-killed-summary)
+        (vm-error-if-folder-empty)
+        (save-restriction
+        (let ((mp vm-message-list) (total 0) (del 0) (buf (generate-new-buffer " temporary email filter")))
+          (widen)
+          (while (and mp (car mp))
+            (setq total (1+ total))
+            (copy-to-buffer buf
+             (marker-position (vm-headers-of (car mp)))
+             (marker-position (vm-text-end-of (car mp))))
+            (if (funcall vm-filter-function buf)
+              (progn
+                (setq del (1+ del))
+                (vm-set-deleted-flag (car mp) t)))
+            (setq mp (cdr mp)))
+          (kill-buffer buf)
+          (if (/= del 0) (vm-update-summary-and-mode-line))
+          (message "%d/%d mails filtered" del total)))))
+    (message "my-email-filter-function is nil -- no filtering performed")))
+
+(defun vm-delete-as-spam(arg)
+  "Mark the current message as spam (with `vm-mark-as-spam')
+   and then delete it."
+  (interactive "p")
+  (vm-mark-as-spam arg t)
+  (vm-delete-message arg)
+  (if (and vm-filter-move-after-mark (not vm-move-after-deleting)) (vm-next-message)))
+
+(defun vm-delete-as-nonspam(arg)
+  "Mark the current message as nonspam (with `vm-mark-as-nonspam')
+   and then delete it."
+  (interactive "p")
+  (vm-mark-as-nonspam arg t)
+  (vm-delete-message arg)
+  (if (and vm-filter-move-after-mark (not vm-move-after-deleting)) (vm-next-message)))
+
+(defun vm-undelete-as-nonspam(arg)
+  "Undelete the current message as nonspam."
+  (interactive "p")
+  (vm-mark-as-nonspam arg t)
+  (vm-undelete-message arg)
+  (if (and vm-filter-move-after-mark (not vm-move-after-undeleting)) (vm-next-message)))
+
+(defun vm-filter-mark(arg func nomove)
+  "Mark the current message as spam/nonspam depending on the
+   function passed. If `vm-filter-move-after-mark' is not nil,
+   move to the next message."
+  (vm-follow-summary-cursor)
+  (vm-select-folder-buffer)
+  (vm-check-for-killed-summary)
+  (vm-error-if-folder-empty)
+  (if (not (vm-mark-of (car vm-message-pointer)))
+    (let ((buf (vm-get-temporary-buffer (car vm-message-pointer))))
+      (funcall func buf)
+      (kill-buffer buf)
+      (vm-mark-message arg)))
+  (if (and (not nomove) vm-filter-move-after-mark) (vm-next-message)))
+
+(defun vm-mark-as-spam(arg &optional nomove)
+  "Mark the current message as spam by calling `vm-filter-mark'."
+  (interactive "p")
+  (vm-filter-mark arg vm-filter-learn-spam-function nomove))
+  
+(defun vm-mark-as-nonspam(arg &optional nomove)
+  "Mark the current message as nonspam by calling `vm-filter-mark'."
+  (interactive "p")
+  (vm-filter-mark arg vm-filter-learn-nonspam-function nomove))
+
+(defun vm-filter-prune-hash(hash min)
+  "Delete all items from a hash with a value less than 'min'."
+  (setq vm-filter-changed t)
+  (let ((lst nil))
+    (maphash #'(lambda(k v) (setq lst (cons k lst))) hash)
+    (while lst
+      (if (< (gethash (car lst) hash) min)
+        (remhash (car lst) hash))
+      (setq lst (cdr lst)))))
+
+(defun vm-filter-prune-hashes(min)
+  "Delete all filter items seen less than 'min' times."
+  (interactive)
+  (vm-filter-prune-hash vm-filter-good-hash min)
+  (vm-filter-prune-hash vm-filter-bad-hash  min))
diff -udNr vm-7.14/vm-startup.el vm-7.14-filter/vm-startup.el
--- vm-7.14/vm-startup.el	2003-03-27 20:30:14.000000000 -0800
+++ vm-7.14-filter/vm-startup.el	2003-04-14 15:07:25.000000000 -0700
@@ -17,6 +17,11 @@
 
 ;;(provide 'vm-startup)
 
+(require 'vm-filter)
+
+(add-hook 'vm-mode-hook 'vm-filter-on-load)
+(add-hook 'kill-emacs-hook 'vm-filter-maybe-save)
+  
 (defvar enable-multibyte-characters)
 
 ;;;###autoload
diff -udNr vm-7.14/vm-vars.el vm-7.14-filter/vm-vars.el
--- vm-7.14/vm-vars.el	2003-03-27 13:50:11.000000000 -0800
+++ vm-7.14-filter/vm-vars.el	2003-04-14 15:07:25.000000000 -0700
@@ -1833,6 +1833,7 @@
                      lines.  ARG should be a number.
    marked          - matches message if it is marked, as with `vm-mark-message'.
    new             - matches message if it is new.
+   nonspam         - matches messages determined to not be spam
    not             - matches message only if its selector argument
                      does NOT match the message.  Example:
                        (not (deleted))
@@ -1866,6 +1867,7 @@
                      default to the current date's value for that
                      part, with the exception of the hh:mm:ss
                      part which defaults to midnight.
+   spam            - messages that the filtering determines to be spam
    subject         - matches message if ARG matches any part of the message's
                      subject; ARG should be a regular expression.
    text            - matches message if ARG matches any part of the text
@@ -3876,6 +3878,13 @@
     (define-key map "\C-x\C-s" 'vm-save-buffer)
     (define-key map "\C-x\C-w" 'vm-write-file)
     (define-key map "\C-x\C-q" 'vm-toggle-read-only)
+    (define-key map "\C-f\C-f" 'vm-filter-email)
+    (define-key map "\C-f\C-r" 'vm-filter-recalc-word-hash)
+    (define-key map "\C-fs" 'vm-mark-as-spam)
+    (define-key map "\C-f\C-n" 'vm-mark-as-nonspam)
+    (define-key map "\C-f\C-s" 'vm-delete-as-spam)
+    (define-key map "\C-f\C-u" 'vm-undelete-as-nonspam)
+    (define-key map "\C-fn" 'vm-delete-as-nonspam)
     (define-key map "%" 'vm-change-folder-type)
     (define-key map "\M-C" 'vm-show-copying-restrictions)
     (define-key map "\M-W" 'vm-show-no-warranty)
@@ -4278,6 +4287,8 @@
     ("less-chars-than")
     ("more-lines-than")
     ("less-lines-than")
+    ("nonspam")
+    ("spam")
     ("new")
     ("unread")
     ("read")
@@ -4320,6 +4331,8 @@
     (less-chars-than . vm-vs-less-chars-than)
     (more-lines-than . vm-vs-more-lines-than)
     (less-lines-than . vm-vs-less-lines-than)
+    (nonspam . vm-vs-nonspam)
+    (spam . vm-vs-spam)
     (new . vm-vs-new)
     (unread . vm-vs-unread)
     (read . vm-vs-read)
@@ -4774,6 +4787,35 @@
 (defvar vm-stunnel-configuration-file nil)
 (defvar vm-fsfemacs-cached-scroll-bar-width nil)
 
+(defvar vm-filter-file "~/.vm-filter"
+  "The path to the file that will hold the spam digest for the filter.")
+(defvar vm-filter-function 'vm-filter-default
+  "The function used to determine whether a buffer is spam or not.")
+(defvar vm-filter-learn-spam-function 'vm-filter-learn-spam-default
+  "The function used to learn about spam, given a buffer.")
+(defvar vm-filter-learn-nonspam-function 'vm-filter-learn-nonspam-default
+  "The function used to learn about nonspam, given a buffer.")
+
+(defvar vm-filter-good-hash nil "The hash holding counts of good words.")
+(defvar vm-filter-bad-hash  nil "The hash holding counts of bad words.")
+(defvar vm-filter-word-hash nil "The hash holding word probabilities.")
+(defvar vm-filter-num-good  0 "The number of nonspam emails seen so far.")
+(defvar vm-filter-num-bad   0 "The number of spam emails seen so far.")
+(defvar vm-filter-whitelist nil "A list of lowercase email addresses from which email will always be allowed.")
+
+(defvar vm-filter-move-after-mark t
+  "If non-nil, move to the next message after marking an email
+   as spam/nonspam.")
+(defvar vm-filter-num-interesting-probs 15
+  "The number of words to consider when looking at a buffer in
+   an attempt to determine whether it's spam.")
+(defvar vm-filter-default-spam-prob 0.4
+  "The default probability assigned to words that are not found
+   in the word hash.")
+(defvar vm-filter-default-spam-threshold 0.9
+  "The probability at or above which an email will be considered
+   to be spam.")
+
 (cond (vm-faked-defcustom
        (fmakunbound 'defcustom)
        (fmakunbound 'defgroup)))
diff -udNr vm-7.14/vm-virtual.el vm-7.14-filter/vm-virtual.el
--- vm-7.14/vm-virtual.el	2003-03-20 10:56:30.000000000 -0800
+++ vm-7.14-filter/vm-virtual.el	2003-04-14 15:07:25.000000000 -0700
@@ -535,6 +535,22 @@
 (defun vm-vs-unmarked (m) (not (vm-mark-of m)))
 (defun vm-vs-unedited (m) (not (vm-edited-flag m)))
 
+(defun vm-vs-spam(m)
+  (and
+   vm-filter-function
+   (let ((buf (vm-get-temporary-buffer m)) (res nil))
+    (setq res (funcall vm-filter-function buf))
+    (kill-buffer buf)
+    res)))
+
+(defun vm-vs-nonspam(m)
+  (or
+   (not vm-filter-function)
+   (let ((buf (vm-get-temporary-buffer m)) (res nil))
+    (setq res (not (funcall vm-filter-function buf)))
+    (kill-buffer buf)
+    res)))
+
 (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")
