[gnu.emacs] VM 4.37

kjones@UUNET.UU.NET (Kyle Jones) (07/13/89)

This is the first of four messages containing the Emacs-Lisp source and
documentation for the VM (View Mail) mail reader, version 4.37.  There
are a number of improvements, nearly all of which were sparked by your
suggestions.  There are no new commands.  Here are the new variables:

	vm-circular-folders
	vm-confirm-new-folders
	vm-delete-after-saving
	vm-delete-empty-folders
	vm-follow-summary-cursor
	vm-forwarding-subject-format
	vm-in-reply-to-format
	vm-move-after-deleting
	vm-mutable-windows
	vm-preview-read-messages
	vm-strip-reply-headers

`M-x vm' starts VM.  Type a ? for help.  There's an Info document if you
care to go that route, but the help text should be enough for most.  The
manual can also be processed and printed with TeX.

MMDF users should (setq vm-folder-type 'mmdf) in their .emacs files.

Mouseketeers will be interested in the variable `vm-follow-summary-cursor'.

If you don't like VM changing your window configuration, `vm-mutable-windows'
will interest you.

Users who want compressed mail folders should (require 'crypt) and
(setq vm-visit-when-saving t) in their .emacs files.  If you don't have
crypt.el, you can get it from me.  N.B.: your primary inbox cannot be
compressed.

If you use Emacs' etc/fakemail, then make sure `vm-strip-reply-headers'
is set non-nil, otherwise your replies to messages may disappear without
a trace.

Users with multiple spool files, or spool files in strange locations
should set the variable `vm-spool-files'.

There are many other variables; all are listed in the on-line help and
in the manual.

VM requires GNU Emacs version 18.51 or beyond.

Scream at the peacocks.
-----------------------------
#!/bin/sh
# shar:	Shell Archiver  (v1.22)
#
# This is part 1 of a multipart archive                                    
# do not concatenate these parts, unpack them in order with /bin/sh        
#
#	Run the following text with /bin/sh to create:
#	  vm-delete.el
#	  vm-digest.el
#	  vm-group.el
#	  vm-license.el
#	  vm-reply.el
#	  vm-save.el
#	  vm-search.el
#	  vm-summary.el
#	  vm-undo.el
#	  vm.el
#	  vm.texinfo
#	  COPYING
#	  README
#
if test -r s2_seq_.tmp
then echo "Must unpack archives in sequence!"
     next=`cat s2_seq_.tmp`; echo "Please unpack part $next next"
     exit 1; fi
echo "x - extracting vm-delete.el (Text)"
sed 's/^X//' << 'SHAR_EOF' > vm-delete.el &&
X;;; Delete and expunge commands for VM.
X;;; Copyright (C) 1989 Kyle E. Jones
X;;;
X;;; This program is free software; you can redistribute it and/or modify
X;;; it under the terms of the GNU General Public License as published by
X;;; the Free Software Foundation; either version 1, or (at your option)
X;;; any later version.
X;;;
X;;; This program is distributed in the hope that it will be useful,
X;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
X;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
X;;; GNU General Public License for more details.
X;;;
X;;; You should have received a copy of the GNU General Public License
X;;; along with this program; if not, write to the Free Software
X;;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
X;;;
X;;; Send bug reports to kyle@cs.odu.edu.
X
X(require 'vm)
X
X(defun vm-delete-message (count)
X  "Mark the current message for deletion.
XWith a prefix arg mark the next COUNT messages for deletion.  A negative
Xarg means the previous COUNT messages are marked."
X  (interactive "p")
X  (if (interactive-p)
X      (vm-follow-summary-cursor))
X  (if vm-mail-buffer
X      (set-buffer vm-mail-buffer))
X  (vm-error-if-folder-empty)
X  (if (not (eq vm-circular-folders t))
X      (vm-check-count count))
X  (let ((direction (if (< count 0) 'backward 'forward))
X	(count (vm-abs count))
X	(oldmp vm-message-pointer)
X	(vm-message-pointer vm-message-pointer))
X    (while (not (zerop count))
X      (if (not (vm-deleted-flag (car vm-message-pointer)))
X	  (vm-set-deleted-flag (car vm-message-pointer) t))
X      (vm-decrement count)
X      (if (not (zerop count))
X	  (vm-move-message-pointer direction))))
X  (vm-update-summary-and-mode-line)
X  (if vm-move-after-deleting
X      (vm-next-message count)))
X
X(defun vm-undelete-message (count)
X  "Remove the deletion mark from the current message.
XWith a prefix arg unmark the next COUNT messages.  A negative arg means
Xthe previous COUNT messages are unmarked."
X  (interactive "p")
X  (if (interactive-p)
X      (vm-follow-summary-cursor))
X  (if vm-mail-buffer
X      (set-buffer vm-mail-buffer))
X  (vm-error-if-folder-empty)
X  (if (not (eq vm-circular-folders t))
X      (vm-check-count count))
X  (let ((direction (if (< count 0) 'backward 'forward))
X	(count (vm-abs count))
X	(oldmp vm-message-pointer)
X	(vm-message-pointer vm-message-pointer))
X    (while (not (zerop count))
X      (if (vm-deleted-flag (car vm-message-pointer))
X	  (vm-set-deleted-flag (car vm-message-pointer) nil))
X      (vm-decrement count)
X      (if (not (zerop count))
X	  (vm-move-message-pointer direction))))
X  (vm-update-summary-and-mode-line))
X
X(defun vm-kill-subject ()
X  "Mark all mesages with the same subject as the current message
X\(ignoring re:'s) for deletion."
X  (interactive)
X  (vm-follow-summary-cursor)
X  (if vm-mail-buffer
X      (set-buffer vm-mail-buffer))
X  (vm-error-if-folder-empty)
X  (let ((subject (vm-subject-of (car vm-message-pointer)))
X	(mp vm-message-list))
X    (if (string-match "^\\(re: *\\)+" subject)
X	(setq subject (substring subject (match-end 0))))
X    (setq subject (concat "^\\(re: *\\)*" (regexp-quote subject) " *$"))
X    (while mp
X      (if (and (not (vm-deleted-flag (car mp)))
X	       (string-match subject (vm-subject-of (car mp))))
X	  (vm-set-deleted-flag (car mp) t))
X      (setq mp (cdr mp))))
X  (vm-update-summary-and-mode-line))
X
X(defun vm-expunge-folder (&optional quitting shaddap)
X  "Expunge deleted messages, but don't save folder to disk or exit VM."
X  (interactive)
X  (if vm-mail-buffer
X      (set-buffer vm-mail-buffer))
X  (let ((inhibit-quit t))
X    (if (vm-gobble-deleted-messages)
X	(if (not quitting)
X	    (progn
X	      (if (not shaddap)
X		  (message "Deleted messages expunged."))
X	      (vm-number-messages)
X	      (if vm-summary-buffer
X		  (vm-do-summary))
X	      (if (and vm-message-pointer vm-summary-buffer)
X		  (vm-set-summary-pointer (car vm-message-pointer)))
X	      (if (null vm-message-pointer)
X		  (vm-next-message)
X		(if (null vm-system-state)
X		    (vm-preview-current-message)
X		  (vm-update-summary-and-mode-line)))))
X      (error "No messages are marked for deletion."))))
SHAR_EOF
chmod 0664 vm-delete.el || echo "restore of vm-delete.el fails"
echo "x - extracting vm-digest.el (Text)"
sed 's/^X//' << 'SHAR_EOF' > vm-digest.el &&
X;;; Support code for RFC934 digests
X;;; Copyright (C) 1989 Kyle E. Jones
X;;;
X;;; This program is free software; you can redistribute it and/or modify
X;;; it under the terms of the GNU General Public License as published by
X;;; the Free Software Foundation; either version 1, or (at your option)
X;;; any later version.
X;;;
X;;; This program is distributed in the hope that it will be useful,
X;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
X;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
X;;; GNU General Public License for more details.
X;;;
X;;; You should have received a copy of the GNU General Public License
X;;; along with this program; if not, write to the Free Software
X;;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
X
X(require 'vm)
X
X(defun vm-rfc934-char-stuff-region (start end)
X  (setq end (vm-marker end))
X  (save-excursion
X    (goto-char start)
X    (while (and (< (point) end) (re-search-forward "^-" end t))
X      (replace-match "- -" t t)))
X  (set-marker end nil))
X
X(defun vm-rfc934-char-unstuff-region (start end)
X  (setq end (vm-marker end))
X  (save-excursion
X    (goto-char start)
X    (while (and (< (point) end) (re-search-forward "^- "  end t))
X      (replace-match "" t t)
X      (forward-char)))
X  (set-marker end nil))
X
X(defun vm-digestify-region (start end)
X  (setq end (vm-marker end))
X  (let ((separator-regexp (if (eq vm-folder-type 'mmdf)
X			      "\n+\001\001\001\001\n\001\001\001\001"
X			    "\n+\nFrom .*")))
X    (save-excursion
X      (vm-rfc934-char-stuff-region start end)
X      (goto-char start)
X      (insert-before-markers "------- Start of digest -------\n")
X      (delete-region (point) (progn (forward-line) (point)))
X      (while (re-search-forward separator-regexp end t)
X	(replace-match "\n\n------------------------------\n" t nil))
X      (goto-char end)
X      (if (eq vm-folder-type 'mmdf)
X	  (delete-region (point) (progn (forward-line -1) (point))))
X      (insert-before-markers "------- End of digest -------\n")))
X  (set-marker end nil))
X
X(defun vm-burst-digest ()
X  "Burst the current message (a digest) into its individual messages.
XThe digest's messages are assimilated into the folder as new mail would be,
Xe.g. message grouping takes place and if you're not reading a message
Xyou will be moved to the first new or unread message."
X  (interactive)
X  (vm-follow-summary-cursor)
X  (if vm-mail-buffer
X      (set-buffer vm-mail-buffer))
X  (vm-error-if-folder-empty)
X  (let ((inhibit-quit t) start end reg-start leader trailer
X	(reg-end (vm-marker nil))
X	(text-start (vm-marker nil))
X	(buffer-read-only)
X	(old-buffer-modified-p (buffer-modified-p))
X	(m (car vm-message-pointer)))
X    (save-excursion
X      (vm-save-restriction
X       (condition-case ()
X	   (progn
X	     (widen)
X	     (goto-char (point-max))
X	     (setq start (point))
X	     (insert-buffer-substring (current-buffer)
X				      (vm-text-of (car vm-message-pointer))
X				      (vm-text-end-of
X				       (car vm-message-pointer)))
X	     (if (not
X		  (re-search-backward "\\(^-[^ ].*\n+\\|^-\n+\\)+" start t))
X		 (error "final EB not found")
X	       (setq end (point-marker))
X	       ;; Reverse searchs are odd.  The above expression simply
X	       ;; will not match  more than one message separator despite
X	       ;; the "1 or more" directive at the end.
X	       ;; This will have to suffice.
X	       (while
X		   (and
X		    (save-excursion
X		      (re-search-backward "\\(^-[^ ].*\n+\\|^-\n+\\)+" start t)
X		      (= end (match-end 0))))
X		 (set-marker end (match-beginning 0))
X		 (goto-char end))
X	       (skip-chars-backward "\n")
X	       (set-marker end (point))
X	       (delete-region end (point-max)))
X	     (goto-char start)
X	     (if (not (re-search-forward "^-[^ ]" end t))
X		 (error "start EB not found")
X	       (delete-region start (match-beginning 0)))
X	     ;; Concoct suitable separator strings for the future messages.
X	     (if (eq vm-folder-type 'mmdf)
X		 (setq leader "\001\001\001\001\n"
X		       trailer "\n\001\001\001\001\n")
X	       (setq leader (concat "From " (vm-from-of m) " "
X				    (current-time-string) "\n")
X		     trailer "\n\n"))
X	     (goto-char start)
X	     (while (re-search-forward
X		     "\\(\\(\n+\\)\\|\\(^\\)\\)\\(-[^ ].*\n+\\|-\n+\\)+"
X		     end 0)
X	       ;; delete EB
X	       (replace-match "" t t)
X	       ;; stuff separator
X	       (if (match-beginning 2)
X		   (insert trailer))
X	       (insert leader)
X	       ;; Delete attribute headers so message will appear
X	       ;; brand new to the user
X	       (setq reg-start (point))
X	       (save-excursion
X		 (search-forward "\n\n" nil 0)
X		 (set-marker text-start (point)))
X	       (if (re-search-forward vm-attributes-header-regexp text-start t)
X		   (delete-region (match-beginning 0) (match-end 0)))
X	       (if vm-berkeley-mail-compatibility
X		   (progn
X		     (goto-char reg-start)
X		     (if (re-search-forward vm-berkeley-mail-status-header-regexp
X					    text-start t)
X			 (delete-region (match-beginning 0) (match-end 0)))))
X	       ;; find end of message separator and unstuff the message
X	       (goto-char reg-start)
X	       (set-marker reg-end (if (re-search-forward "\n+-[^ ]" end 0)
X				       (match-beginning 0)
X				     (point)))
X	       (vm-rfc934-char-unstuff-region reg-start reg-end)
X	       (goto-char reg-end))
X	     (goto-char end)
X	     (insert trailer)
X	     (set-marker end nil)
X	     (set-marker reg-end nil)
X	     (vm-clear-modification-flag-undos))
X	 (error (and start (delete-region start (point-max)))
X		(set-buffer-modified-p old-buffer-modified-p)
X		(error "Malformed digest")))))
X    (if (vm-assimilate-new-messages)
X	(progn
X	  (vm-emit-totals-blurb)
X	  ;; If there's a current grouping, then the summary has already
X	  ;; been redone in vm-group-messages.
X	  (if (and vm-summary-buffer (not vm-current-grouping))
X	      (progn
X		(vm-do-summary)
X		(vm-emit-totals-blurb)))
X	  (vm-thoughtfully-select-message)
X	  (if vm-summary-buffer
X	      (vm-set-summary-pointer (car vm-message-pointer)))))))
SHAR_EOF
chmod 0664 vm-digest.el || echo "restore of vm-digest.el fails"
echo "x - extracting vm-group.el (Text)"
sed 's/^X//' << 'SHAR_EOF' > vm-group.el &&
X;;; Commands to rearrange (group) message presentation
X;;; Copyright (C) 1989 Kyle E. Jones
X;;;
X;;; This program is free software; you can redistribute it and/or modify
X;;; it under the terms of the GNU General Public License as published by
X;;; the Free Software Foundation; either version 1, or (at your option)
X;;; any later version.
X;;;
X;;; This program is distributed in the hope that it will be useful,
X;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
X;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
X;;; GNU General Public License for more details.
X;;;
X;;; You should have received a copy of the GNU General Public License
X;;; along with this program; if not, write to the Free Software
X;;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
X
X(require 'vm)
X
X(defun vm-group-by (group-function)
X  (let (start end end-prev mp mp-prev)
X    (setq start vm-message-list)
X    (while start
X      (setq end (cdr start)
X	    end-prev start
X	    mp end
X	    mp-prev start)
X      (while mp
X	(if (funcall group-function (car start) (car mp))
X	    (if (eq end mp)
X		(setq end-prev end end (cdr end)
X		      mp-prev mp mp (cdr mp))
X	      (setcdr mp-prev (cdr mp))
X	      (setcdr end-prev mp)
X	      (setcdr mp end)
X	      (setq end-prev (cdr end-prev)
X		    mp (cdr mp)))
X	  (setq mp-prev mp mp (cdr mp))))
X      (setq start end))))
X
X(defconst vm-group-by-subject-closure (cons t t))
X
X(defun vm-group-by-subject (m1 m2)
X  (let ((subject (vm-su-subject m1)))
X    (if (eq subject (car vm-group-by-subject-closure))
X	(setq subject (cdr vm-group-by-subject-closure))
X      (setcar vm-group-by-subject-closure subject)
X      (if (string-match "^\\(re: *\\)+" subject)
X	  (setq subject (substring subject (match-end 0))))
X      (setq subject (concat "^\\(re: *\\)*"
X			    (regexp-quote subject)
X			    " *$"))
X      (setcdr vm-group-by-subject-closure subject))
X    (string-match subject (vm-su-subject m2))))
X
X(defun vm-group-by-author (m1 m2)
X  (string= (vm-full-name-of m1) (vm-full-name-of m2)))
X
X(defun vm-group-by-date-sent (m1 m2)
X  (and (string= (vm-monthday-of m1) (vm-monthday-of m2))
X       (string= (vm-month-of m1) (vm-month-of m2))
X       (string= (vm-year-of m1) (vm-year-of m2))))
X
X(defun vm-revert-to-arrival-time-grouping ()
X  (let ((curr (car vm-message-pointer))
X	(last (car vm-last-message-pointer)))
X    (setq vm-message-list
X	  (sort vm-message-list
X		(function
X		 (lambda (p q) (< (vm-start-of p) (vm-start-of q))))))
X    (cond (curr
X	   (setq vm-message-pointer vm-message-list)
X	   (while (not (eq (car vm-message-pointer) curr))
X	     (setq vm-message-pointer (cdr vm-message-pointer)))))
X    (cond (last
X	   (setq vm-last-message-pointer vm-message-list)
X	   (while (not (eq (car vm-last-message-pointer) last))
X	     (setq vm-last-message-pointer (cdr vm-last-message-pointer)))))))
X
X(defun vm-group-messages (grouping)
X  "Group messages by the argument GROUPING.
XInteractively this argument is prompted for in the minibuffer,
Xwith completion."
X  (interactive
X   (list 
X    (completing-read
X     (format "Group messages by (default %s): "
X	     (or vm-group-by "arrival-time"))
X     vm-supported-groupings-alist 'identity t)))
X  (if vm-mail-buffer
X      (set-buffer vm-mail-buffer))
X  (if (equal grouping "")
X      (setq grouping vm-group-by))
X  (cond ((and grouping (not (stringp grouping)))
X	 (error "Unsupported grouping: %s" grouping))
X	((equal grouping "arrival-time")
X	 (setq grouping nil)))
X  (if grouping
X      (let ((group-function (intern (concat "vm-group-by-" grouping))))
X	(if (not (fboundp group-function))
X	    (error "Unsupported grouping: %s" grouping))
X	(vm-revert-to-arrival-time-grouping)
X	(message "Grouping messages by %s..." grouping)
X	(vm-group-by group-function)
X	(message "Grouping messages by %s... done" grouping)
X	(setq vm-current-grouping grouping)
X	(vm-number-messages))
X    (vm-revert-to-arrival-time-grouping)
X    (setq vm-current-grouping grouping)
X    (vm-number-messages)
X    (if (interactive-p)
X	(message "Reverted to arrival time grouping")))
X  (if vm-summary-buffer
X      (vm-do-summary))
X  (if vm-message-pointer
X      (progn
X	(vm-update-summary-and-mode-line)
X	(vm-set-summary-pointer (car vm-message-pointer)))))
SHAR_EOF
chmod 0664 vm-group.el || echo "restore of vm-group.el fails"
echo "x - extracting vm-license.el (Text)"
sed 's/^X//' << 'SHAR_EOF' > vm-license.el &&
X;;; Code to show VM's warranty and copying restrictions
X;;; Copyright (C) 1989 Kyle E. Jones
X;;;
X;;; This program is free software; you can redistribute it and/or modify
X;;; it under the terms of the GNU General Public License as published by
X;;; the Free Software Foundation; either version 1, or (at your option)
X;;; any later version.
X;;;
X;;; This program is distributed in the hope that it will be useful,
X;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
X;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
X;;; GNU General Public License for more details.
X;;;
X;;; You should have received a copy of the GNU General Public License
X;;; along with this program; if not, write to the Free Software
X;;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
X
X(require 'vm)
X
X(defconst vm-license-buffer-name "*GNU General Public License*")
X
X(defconst vm-license-string
X"		    GNU GENERAL PUBLIC LICENSE
X		     Version 1, February 1989
X
X Copyright (C) 1989 Free Software Foundation, Inc.
X                    675 Mass Ave, Cambridge, MA 02139, USA
X Everyone is permitted to copy and distribute verbatim copies
X of this license document, but changing it is not allowed.
X
X			    Preamble
X
X  The license agreements of most software companies try to keep users
Xat the mercy of those companies.  By contrast, our General Public
XLicense is intended to guarantee your freedom to share and change free
Xsoftware--to make sure the software is free for all its users.  The
XGeneral Public License applies to the Free Software Foundation's
Xsoftware and to any other program whose authors commit to using it.
XYou can use it for your programs, too.
X
X  When we speak of free software, we are referring to freedom, not
Xprice.  Specifically, the General Public License is designed to make
Xsure that you have the freedom to give away or sell copies of free
Xsoftware, that you receive source code or can get it if you want it,
Xthat you can change the software or use pieces of it in new free
Xprograms; and that you know you can do these things.
X
X  To protect your rights, we need to make restrictions that forbid
Xanyone to deny you these rights or to ask you to surrender the rights.
XThese restrictions translate to certain responsibilities for you if you
Xdistribute copies of the software, or if you modify it.
X
X  For example, if you distribute copies of a such a program, whether
Xgratis or for a fee, you must give the recipients all the rights that
Xyou have.  You must make sure that they, too, receive or can get the
Xsource code.  And you must tell them their rights.
X
X  We protect your rights with two steps: (1) copyright the software, and
X(2) offer you this license which gives you legal permission to copy,
Xdistribute and/or modify the software.
X
X  Also, for each author's protection and ours, we want to make certain
Xthat everyone understands that there is no warranty for this free
Xsoftware.  If the software is modified by someone else and passed on, we
Xwant its recipients to know that what they have is not the original, so
Xthat any problems introduced by others will not reflect on the original
Xauthors' reputations.
X
X  The precise terms and conditions for copying, distribution and
Xmodification follow.
X
X		    GNU GENERAL PUBLIC LICENSE
X   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
X
X  0. This License Agreement applies to any program or other work which
Xcontains a notice placed by the copyright holder saying it may be
Xdistributed under the terms of this General Public License.  The
X\"Program\", below, refers to any such program or work, and a \"work based
Xon the Program\" means either the Program or any work containing the
XProgram or a portion of it, either verbatim or with modifications.  Each
Xlicensee is addressed as \"you\".
X
X  1. You may copy and distribute verbatim copies of the Program's source
Xcode as you receive it, in any medium, provided that you conspicuously and
Xappropriately publish on each copy an appropriate copyright notice and
Xdisclaimer of warranty; keep intact all the notices that refer to this
XGeneral Public License and to the absence of any warranty; and give any
Xother recipients of the Program a copy of this General Public License
Xalong with the Program.  You may charge a fee for the physical act of
Xtransferring a copy.
X
X  2. You may modify your copy or copies of the Program or any portion of
Xit, and copy and distribute such modifications under the terms of Paragraph
X1 above, provided that you also do the following:
X
X    a) cause the modified files to carry prominent notices stating that
X    you changed the files and the date of any change; and
X
X    b) cause the whole of any work that you distribute or publish, that
X    in whole or in part contains the Program or any part thereof, either
X    with or without modifications, to be licensed at no charge to all
X    third parties under the terms of this General Public License (except
X    that you may choose to grant warranty protection to some or all
X    third parties, at your option).
X
X    c) If the modified program normally reads commands interactively when
X    run, you must cause it, when started running for such interactive use
X    in the simplest and most usual way, to print or display an
X    announcement including an appropriate copyright notice and a notice
X    that there is no warranty (or else, saying that you provide a
X    warranty) and that users may redistribute the program under these
X    conditions, and telling the user how to view a copy of this General
X    Public License.
X
X    d) You may charge a fee for the physical act of transferring a
X    copy, and you may at your option offer warranty protection in
X    exchange for a fee.
X
XMere aggregation of another independent work with the Program (or its
Xderivative) on a volume of a storage or distribution medium does not bring
Xthe other work under the scope of these terms.
X
X  3. You may copy and distribute the Program (or a portion or derivative of
Xit, under Paragraph 2) in object code or executable form under the terms of
XParagraphs 1 and 2 above provided that you also do one of the following:
X
X    a) accompany it with the complete corresponding machine-readable
X    source code, which must be distributed under the terms of
X    Paragraphs 1 and 2 above; or,
X
X    b) accompany it with a written offer, valid for at least three
X    years, to give any third party free (except for a nominal charge
X    for the cost of distribution) a complete machine-readable copy of the
X    corresponding source code, to be distributed under the terms of
X    Paragraphs 1 and 2 above; or,
X
X    c) accompany it with the information you received as to where the
X    corresponding source code may be obtained.  (This alternative is
X    allowed only for noncommercial distribution and only if you
X    received the program in object code or executable form alone.)
X
XSource code for a work means the preferred form of the work for making
Xmodifications to it.  For an executable file, complete source code means
Xall the source code for all modules it contains; but, as a special
Xexception, it need not include source code for modules which are standard
Xlibraries that accompany the operating system on which the executable
Xfile runs, or for standard header files or definitions files that
Xaccompany that operating system.
X
X  4. You may not copy, modify, sublicense, distribute or transfer the
XProgram except as expressly provided under this General Public License.
XAny attempt otherwise to copy, modify, sublicense, distribute or transfer
Xthe Program is void, and will automatically terminate your rights to use
Xthe Program under this License.  However, parties who have received
Xcopies, or rights to use copies, from you under this General Public
XLicense will not have their licenses terminated so long as such parties
Xremain in full compliance.
X
X  5. By copying, distributing or modifying the Program (or any work based
Xon the Program) you indicate your acceptance of this license to do so,
Xand all its terms and conditions.
X
X  6. Each time you redistribute the Program (or any work based on the
XProgram), the recipient automatically receives a license from the original
Xlicensor to copy, distribute or modify the Program subject to these
Xterms and conditions.  You may not impose any further restrictions on the
Xrecipients' exercise of the rights granted herein.
X
X  7. The Free Software Foundation may publish revised and/or new versions
Xof the General Public License from time to time.  Such new versions will
Xbe similar in spirit to the present version, but may differ in detail to
Xaddress new problems or concerns.
X
XEach version is given a distinguishing version number.  If the Program
Xspecifies a version number of the license which applies to it and \"any
Xlater version\", you have the option of following the terms and conditions
Xeither of that version or of any later version published by the Free
XSoftware Foundation.  If the Program does not specify a version number of
Xthe license, you may choose any version ever published by the Free Software
XFoundation.
X
X  8. If you wish to incorporate parts of the Program into other free
Xprograms whose distribution conditions are different, write to the author
Xto ask for permission.  For software which is copyrighted by the Free
XSoftware Foundation, write to the Free Software Foundation; we sometimes
Xmake exceptions for this.  Our decision will be guided by the two goals
Xof preserving the free status of all derivatives of our free software and
Xof promoting the sharing and reuse of software generally.
X
X			    NO WARRANTY
X
X  9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
XFOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
XOTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
XPROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
XOR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
XMERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
XTO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
XPROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
XREPAIR OR CORRECTION.
X
X  10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
XWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
XREDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
XINCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
XOUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
XTO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
XYOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
XPROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
XPOSSIBILITY OF SUCH DAMAGES.
X
X		     END OF TERMS AND CONDITIONS
X")
X
X(defun vm-show-copying-restrictions (&optional warranty)
X  "Display the GNU General Public License."
X  (interactive)
X  (if (get-buffer vm-license-buffer-name)
X      (progn
X	(if (get-buffer-window (get-buffer vm-license-buffer-name))
X	    (select-window (get-buffer-window
X			    (get-buffer vm-license-buffer-name)))
X	  (switch-to-buffer vm-license-buffer-name t))
X	(goto-char (point-min))
X	(if warranty
X	    (progn
X	      (search-forward "NO WARRANTY\n" nil t)
X	      (forward-line -1)
X	      (set-window-start (selected-window) (point)))))
X    (save-excursion
X      (switch-to-buffer (get-buffer-create vm-license-buffer-name) t)
X      (insert vm-license-string)
X      (goto-char (point-min))
X      (if warranty
X	  (progn
X	    (search-forward "NO WARRANTY\n" nil t)
X	    (forward-line -1)
X	    (set-window-start (selected-window) (point))))
X      (set-buffer-modified-p nil))
X    ;; This goes into a recursive edit!
X    (view-buffer vm-license-buffer-name)
X    (condition-case () (kill-buffer vm-license-buffer-name) (error nil))))
X
X(defun vm-show-no-warranty ()
X  "Display \"NO WARRANTY\" section of the GNU General Public License."
X  (interactive)
X  (vm-show-copying-restrictions t))
SHAR_EOF
chmod 0664 vm-license.el || echo "restore of vm-license.el fails"
echo "x - extracting vm-reply.el (Text)"
sed 's/^X//' << 'SHAR_EOF' > vm-reply.el &&
X;;; Mailing, forwarding, and replying commands for VM
X;;; Copyright (C) 1989 Kyle E. Jones
X;;;
X;;; This program is free software; you can redistribute it and/or modify
X;;; it under the terms of the GNU General Public License as published by
X;;; the Free Software Foundation; either version 1, or (at your option)
X;;; any later version.
X;;;
X;;; This program is distributed in the hope that it will be useful,
X;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
X;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
X;;; GNU General Public License for more details.
X;;;
X;;; You should have received a copy of the GNU General Public License
X;;; along with this program; if not, write to the Free Software
X;;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
X
X(require 'vm)
X
X(defun vm-do-reply (to-all include-text)
X  (vm-follow-summary-cursor)
X  (if vm-mail-buffer
X      (set-buffer vm-mail-buffer))
X  (vm-error-if-folder-empty)
X  (save-restriction
X    (widen)
X    (let ((mail-buffer (current-buffer))
X	  (text-start (vm-text-of (car vm-message-pointer)))
X	  (text-end (vm-text-end-of (car vm-message-pointer)))
X	  (mp vm-message-pointer)
X	  to cc subject message-id tmp)
X      (cond ((setq to (vm-get-header-contents (car mp) "Reply-To")))
X	    ((setq to (vm-get-header-contents (car mp) "From")))
X	    ((setq to (vm-grok-From_-author (car mp))))
X	    (t (error "Cannot find a From: or Reply-To: header in message")))
X      (setq subject (vm-get-header-contents (car mp) "Subject")
X	    message-id (and vm-in-reply-to-format
X			    (vm-sprintf 'vm-in-reply-to-format (car mp))))
X      (if to-all
X	  (progn
X	    (setq cc (vm-get-header-contents (car mp) "To"))
X	    (setq tmp (vm-get-header-contents (car mp) "Cc"))
X	    (if tmp
X		(if cc
X		    (setq cc (concat cc ",\n\t" tmp))
X		  (setq cc tmp)))))
X      (if vm-strip-reply-headers
X	  (let ((mail-use-rfc822 t))
X	    (require 'mail-utils)
X	    (and to (setq to (mail-strip-quoted-names to)))
X	    (and cc (setq to (mail-strip-quoted-names cc)))))
X      (if (mail nil to subject message-id cc)
X	  (progn
X	    (use-local-map (copy-keymap (current-local-map)))
X	    (local-set-key "\C-c\C-y" 'vm-yank-message)
X	    (local-set-key "\C-c\C-s" 'vm-mail-send)
X	    (local-set-key "\C-c\C-c" 'vm-mail-send-and-exit)
X	    (local-set-key "\C-c\C-v" vm-mode-map)
X	    (setq vm-mail-buffer mail-buffer
X		  vm-message-pointer mp)
X	    (cond (include-text
X		   (goto-char (point-max))
X		   (insert-buffer-substring mail-buffer text-start text-end)
X		   (goto-char (- (point) (- text-end text-start)))
X		   (save-excursion
X		     (if vm-included-text-attribution-format
X			 (insert (vm-sprintf
X				  'vm-included-text-attribution-format
X				  (car mp))))
X		     (while (and (re-search-forward "^" nil t) (not (eobp)))
X		       (replace-match vm-included-text-prefix t t))))))))))
X
X(defun vm-yank-message (n prefix)
X  "Yank message number N into the current buffer at point.
X
XThis command is meant to be used in VM created *mail* buffers; the
Xyanked message comes from the mail buffer containing the message you
Xare replying to, forwarding, or invoked VM's mail command from.
X
XAll message headers are yanked along with the text.  Point is left
Xbefore the inserted text, the mark after.  Any hook functions bound to
Xmail-yank-hooks are run, aftert inserting the text and setting point
Xand mark.
X
XPrefix arg means to ignore mail-yank-hooks, don't set the mark, prepend the
Xvalue of vm-included-text-prefix to every yanked line, and don't yank any
Xheaders other than those specified in vm-visible-headers."
X  (interactive
X   (list
X    (let (default (result 0) prompt)
X      (save-excursion
X	(if (and vm-mail-buffer (buffer-name vm-mail-buffer))
X	    (set-buffer vm-mail-buffer))
X	(setq default (and vm-message-pointer
X			   (vm-number-of (car vm-message-pointer)))
X	      prompt (if default
X			 (format "Yank message number: (default %s) "
X				 default)
X		       "Yank message number: "))
X	(while (zerop result)
X	  (setq result (read-string prompt))
X	  (and (string= result "") default (setq result default))
X	  (setq result (string-to-int result))))
X      result )
X    current-prefix-arg ))
X  (if (not (bufferp vm-mail-buffer))
X      (error "This is not a VM *mail* buffer."))
X  (if (null (buffer-name vm-mail-buffer))
X      (error "The mail buffer containing message %d has been killed." n))
X  (let ((b (current-buffer)) (start (point)) mp end)
X    (save-restriction
X      (widen)
X      (save-excursion
X	(set-buffer vm-mail-buffer)
X	(setq mp (nthcdr (1- n) vm-message-list))
X	(if (null mp)
X	    (error "No such message."))
X	(save-restriction
X	  (widen)
X	  (append-to-buffer b (if prefix
X				  (vm-vheaders-of (car mp))
X				(vm-start-of (car mp)))
X			    (vm-text-end-of (car mp)))
X	  (setq end (vm-marker (+ start (- (vm-text-end-of (car mp))
X					   (if prefix
X					       (vm-vheaders-of (car mp))
X					     (vm-start-of (car mp))))) b))))
X      (if prefix
X	  (save-excursion
X	    (while (and (< (point) end) (re-search-forward "^" end t))
X	      (replace-match vm-included-text-prefix t t)
X	      (forward-line)))
X	;; Delete UNIX From or MMDF ^A^A^A^A line
X	(delete-region (point) (progn (forward-line) (point)))
X	(push-mark end)
X	(run-hooks 'mail-yank-hooks)))))
X
X(defun vm-mail-send-and-exit (arg)
X  "Just like mail-send-and-exit except that VM marks the appropriate message
Xas having been replied to, if appropriate."
X  (interactive "P")
X  (let ((reply-buf (current-buffer)))
X    (mail-send-and-exit arg)
X    (save-excursion
X      (set-buffer reply-buf)
X      (vm-mark-replied))))
X
X(defun vm-mail-send ()
X  "Just like mail-send except that VM marks the appropriate message
Xas having been replied to, if appropriate."
X  (interactive)
X  (mail-send)
X  (vm-mark-replied))
X
X(defun vm-mark-replied ()
X  (if (and (bufferp vm-mail-buffer) (buffer-name vm-mail-buffer))
X      (save-excursion
X	(let ((mp vm-message-pointer))
X	  (set-buffer vm-mail-buffer)
X	  (cond ((and (memq (car mp) vm-message-list)
X		      (null (vm-replied-flag (car mp))))
X		 (vm-set-replied-flag (car mp) t)
X		 (vm-update-summary-and-mode-line)))))))
X
X(defun vm-reply ()
X  "Reply to the sender of the current message.
XYou will be deposited into a standard Emacs *mail* buffer to compose and
Xsend your message.  See the documentation for the function `mail' for
Xmore info.
X
XNote that the normal binding of C-c C-y in the *mail* buffer is
Xautomatically changed to vm-yank-message during a reply.  This allows
Xyou to yank any message from the current folder into a reply.
X
XNormal VM commands may be accessed in the reply buffer by prefixing them
Xwith C-c C-v."
X  (interactive)
X  (vm-do-reply nil nil))
X
X(defun vm-reply-include-text ()
X  "Reply to the sender (only) of the current message and include text
Xfrom the message.  See the documentation for function vm-reply for details."
X  (interactive)
X  (vm-do-reply nil t))
X
X(defun vm-followup ()
X  "Reply to all recipients of the current message.
XSee the documentation for the function vm-reply for details."
X  (interactive)
X  (vm-do-reply t nil))
X
X(defun vm-followup-include-text ()
X  "Reply to all recipients of the current message and include text from
Xthe message.  See the documentation for the function vm-reply for details."
X  (interactive)
X  (vm-do-reply t t))
X
X(defun vm-forward-message ()
X  "Forward the current message to one or more third parties.
XYou will be placed in a *mail* buffer as is usual with replies, but you
Xmust fill in the To: and Subject: headers manually." 
X  (interactive)
X  (vm-follow-summary-cursor)
X  (if vm-mail-buffer
X      (set-buffer vm-mail-buffer))
X  (vm-error-if-folder-empty)
X  (let ((b (current-buffer))
X	(m (car vm-message-pointer))
X	(start))
X    (save-restriction
X      (widen)
X      (cond ((mail nil nil (and vm-forwarding-subject-format
X				(vm-sprintf 'vm-forwarding-subject-format m)))
X	     (use-local-map (copy-keymap (current-local-map)))
X	     (local-set-key "\C-c\C-y" 'vm-yank-message)
X	     (local-set-key "\C-c\C-v" vm-mode-map)
X	     (setq vm-mail-buffer b)
X	     (goto-char (point-max))
X	     (insert "------- Start of forwarded message -------\n")
X	     (setq start (point))
X	     (insert-buffer-substring b
X				      (save-excursion
X					(set-buffer b)
X					(goto-char (vm-start-of m))
X					(forward-line 1)
X					(point))
X				      (vm-text-end-of m))
X	     (if vm-rfc934-forwarding
X		 (vm-rfc934-char-stuff-region start (point)))
X	     (insert "------- End of forwarded message -------\n")
X	     (goto-char (point-min))
X	     (end-of-line))))))
X
X(defun vm-mail ()
X  "Send a mail message from within VM."
X  (interactive)
X  (vm-follow-summary-cursor)
X  (if vm-mail-buffer
X      (set-buffer vm-mail-buffer))
X  (let ((mail-buffer (current-buffer)))
X    (cond ((mail)
X	   (use-local-map (copy-keymap (current-local-map)))
X	   (local-set-key "\C-c\C-y" 'vm-yank-message)
X	   (local-set-key "\C-c\C-v" vm-mode-map)
X	   (setq vm-mail-buffer mail-buffer)))))
X
X(defun vm-send-digest ()
X  "Send a digest of all messages in the current folder to recipients.
XYou will be placed in a *mail* buffer as is usual with replies, but you
Xmust fill in the To: and Subject: headers manually." 
X  (interactive)
X  (if vm-mail-buffer
X      (set-buffer vm-mail-buffer))
X  (vm-error-if-folder-empty)
X  (let ((b (current-buffer))
X	(start))
X    (save-restriction
X      (widen)
X      (cond
X       ((mail)
X	(use-local-map (copy-keymap (current-local-map)))
X	(local-set-key "\C-c\C-y" 'vm-yank-message)
X	(local-set-key "\C-c\C-v" vm-mode-map)
X	(setq vm-mail-buffer b)
X	(goto-char (point-max))
X	(setq start (point))
X	(insert-buffer-substring b)
X	(vm-digestify-region start (point))
X	(goto-char (point-min))
X	(end-of-line))))))
SHAR_EOF
chmod 0664 vm-reply.el || echo "restore of vm-reply.el fails"
echo "x - extracting vm-save.el (Text)"
sed 's/^X//' << 'SHAR_EOF' > vm-save.el &&
X;;; Saving and piping messages under VM
X;;; Copyright (C) 1989 Kyle E. Jones
X;;;
X;;; This program is free software; you can redistribute it and/or modify
X;;; it under the terms of the GNU General Public License as published by
X;;; the Free Software Foundation; either version 1, or (at your option)
X;;; any later version.
X;;;
X;;; This program is distributed in the hope that it will be useful,
X;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
X;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
X;;; GNU General Public License for more details.
X;;;
X;;; You should have received a copy of the GNU General Public License
X;;; along with this program; if not, write to the Free Software
X;;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
X
X(require 'vm)
X
X;; (match-data) returns the match data as MARKERS, often corrupting
X;; it in the process due to buffer narrowing, and the fact that buffers are
X;; indexed from 1 while strings are indexed from 0. :-(
X(defun vm-match-data ()
X  (delq nil
X	(apply 'nconc
X	       (mapcar (function
X			(lambda (n) (list (match-beginning n) (match-end n))))
X		       '(0 1 2 3 4 5 6 7 8 9)))))
X
X(defun vm-auto-select-folder (mp)
X  (condition-case ()
X      (catch 'match
X	(let (header alist tuple-list)
X	  (setq alist vm-auto-folder-alist)
X	  (while alist
X	    (setq header (vm-get-header-contents (car mp) (car (car alist))))
X	    (if (null header)
X		()
X	      (setq tuple-list (cdr (car alist)))
X	      (while tuple-list
X		(if (let (case-fold-search)
X		      (string-match (car (car tuple-list)) header))
X		    (let* ((match-data (vm-match-data))
X			   (buf (get-buffer-create " *VM scratch*")))
X		      ;; Set up a buffer that matches our cached
X		      ;; match data.
X		      (save-excursion
X			(set-buffer buf)
X			(widen)
X			(erase-buffer)
X			(insert header)
X			;; It appears that get-buffer-create clobbers the
X			;; match-data.
X			;;
X			;; The match data is off by one because we matched
X			;; a string and Emacs indexes strings from 0 and
X			;; buffers from 1.
X			;;
X			;; Also store-match-data only accepts MARKERS!!
X			;; AUGHGHGH!!
X			(store-match-data
X			 (mapcar (function (lambda (n) (vm-marker n)))
X				 (mapcar '1+ match-data)))
X			(throw 'match (eval (cdr (car tuple-list)))))))
X		(setq tuple-list (cdr tuple-list))))
X	    (setq alist (cdr alist)))
X	  nil ))
X    (error nil)))
X
X(defun vm-auto-archive-messages ()
X  "Save all unfiled messages that auto-match a folder via vm-auto-folder-alist
Xto their appropriate folders."
X  (interactive)
X  (if vm-mail-buffer
X      (set-buffer vm-mail-buffer))
X  (vm-error-if-folder-empty)
X  (let ((auto-folder)
X	(archived 0))
X    ;; Need separate (let ...) so vm-message-pointer can revert back
X    ;; in time for (vm-update-summary-and-mode-line).
X    ;; vm-last-save-folder is tucked away here since archives shouldn't affect
X    ;; its value.
X    (let ((vm-message-pointer vm-message-list)
X	  (vm-last-save-folder vm-last-save-folder)
X	  (vm-move-after-deleting))
X      (while vm-message-pointer
X	(and (not (vm-filed-flag (car vm-message-pointer)))
X	     (setq auto-folder (vm-auto-select-folder vm-message-pointer))
X	     (progn (vm-save-message auto-folder)
X		    (vm-increment archived)))
X	(setq vm-message-pointer (cdr vm-message-pointer))))
X    (if (zerop archived)
X	(message "No messages archived")
X      (message "%d message%s archived" archived (if (= 1 archived) "" "s"))
X      (vm-update-summary-and-mode-line))))
X
X;; unexpanded-folder is an old fashioned local variable.
X(defun vm-save-message (folder &optional count unexpanded-folder)
X  "Save the current message to a mail folder.
XPrefix arg COUNT means save the next COUNT messages.  A negative COUNT means
Xsave the previous COUNT.  If the folder already exists, the message
Xwill be appended to it.  The saved messages are marked as being filed."
X  (interactive
X   (list
X    (progn
X      (vm-follow-summary-cursor)
X      (let ((default (save-excursion
X		       (if vm-mail-buffer
X			   (set-buffer vm-mail-buffer))
X		       (or (vm-auto-select-folder vm-message-pointer)
X			   vm-last-save-folder)))
X	    (dir (or vm-folder-directory default-directory)))
X	(if default
X	    (read-file-name (format "Save in folder: (default %s) "
X				    default)
X			    dir default nil )
X	  (read-file-name "Save in folder: " dir nil nil))))
X    (prefix-numeric-value current-prefix-arg)))
X  (setq unexpanded-folder folder)
X  (if vm-mail-buffer
X      (set-buffer vm-mail-buffer))
X  (vm-error-if-folder-empty)
X  (or count (setq count 1))
X  (if (not (eq vm-circular-folders t))
X      (vm-check-count count))
X  ;; Expand the filename forcing relative paths to resolve
X  ;; into the folder directory.
X  (let ((default-directory (or vm-folder-directory default-directory)))
X    (setq folder (expand-file-name folder)))
X  ;; Confirm new folders, if the user requested this.
X  (if (and vm-confirm-new-folders (interactive-p) (not (file-exists-p folder))
X	   (not (y-or-n-p (format "%s does not exist, save there anyway? "
X				  folder))))
X      (error "Save aborted"))
X  (if (not vm-visit-when-saving)
X      ;; Check and see if we are currently visiting the folder
X      ;; that the user wants to save to.
X      (let ((blist (buffer-list)))
X	(while blist
X	  (if (equal (buffer-file-name (car blist)) folder)
X	      (error "Folder %s is being visited, cannot save." folder))
X	  (setq blist (cdr blist)))))
X  (let ((vm-message-pointer vm-message-pointer)
X	(direction (if (> count 0) 'forward 'backward))
X	(folder-buffer)
X	(mail-buffer (current-buffer))
X	(counter)
X	(count (vm-abs count)))
X    (setq counter count)
X    (if vm-visit-when-saving
X	(progn
X	  (setq folder-buffer (find-file-noselect folder))
X	  (if (eq folder-buffer mail-buffer)
X	      (error "This IS folder %s, you must save messages elsewhere."
X		     buffer-file-name))))
X    (save-restriction
X      (widen)
X      (while (not (zerop counter))
X	(if (not vm-visit-when-saving)
X	    (write-region (vm-start-of (car vm-message-pointer))
X			  (vm-end-of (car vm-message-pointer))
X			  folder t 'quiet)
X	  (let ((start (vm-start-of (car vm-message-pointer)))
X		(end (vm-end-of (car vm-message-pointer))))
X	    (save-excursion
X	      (set-buffer folder-buffer)
X	      (let (buffer-read-only)
X		(vm-save-restriction
X		 (widen)
X		 (goto-char (point-max))
X		 (insert-buffer-substring mail-buffer start end)
X		 (vm-increment vm-messages-not-on-disk)
X		 (vm-clear-modification-flag-undos))))))
X	(if (null (vm-filed-flag (car vm-message-pointer)))
X	    (vm-set-filed-flag (car vm-message-pointer) t))
X	(vm-decrement counter)
X	(if (not (zerop counter))
X	    (vm-move-message-pointer direction))))
X    (if vm-visit-when-saving
X	(progn
X	  (save-excursion
X	    (set-buffer folder-buffer)
X	    (let (buffer-read-only)
X	      (if (eq major-mode 'vm-mode)
X		  (progn
X		    (vm-assimilate-new-messages)
X		    ;; If there's a current grouping, then the summary
X		    ;; has already been redone in vm-group-messages.
X		    (if (and vm-summary-buffer (not vm-current-grouping))
X			(progn
X			  (vm-do-summary)
X			  (if (get-buffer-window vm-summary-buffer)
X			      (vm-set-summary-pointer
X			       (car vm-message-pointer)))))))))
X	  (message "Message%s saved to buffer %s" (if (/= 1 count) "s" "")
X		   (buffer-name folder-buffer)))
X      (message "Message%s saved to %s" (if (/= 1 count) "s" "") folder)))
X  (setq vm-last-save-folder unexpanded-folder)
X  (if vm-delete-after-saving
X      (vm-delete-message count))
X  (vm-update-summary-and-mode-line))
X
X(defun vm-save-message-sans-headers (file &optional count)
X  "Save the current message to a file minus its header section.
XPrefix arg COUNT means save the next COUNT messages.  A negative COUNT means
Xsave the previous COUNT.  If the file already exists, the message
Xwill be appended to it.  The saved messages are NOT marked as being filed,
Xbecause the filed attributes is meant to denote saving to mail folders and
Xthis command should NOT be used to do that.  Use vm-save-message instead
X\(normally bound to `s')."
X  (interactive
X   (progn
X     (vm-follow-summary-cursor)
X     (list
X      (read-file-name "Write text to file: " nil nil nil)
X      (prefix-numeric-value current-prefix-arg))))
X  (if vm-mail-buffer
X      (set-buffer vm-mail-buffer))
X  (vm-error-if-folder-empty)
X  (or count (setq count 1))
X  (if (not (eq vm-circular-folders t))
X      (vm-check-count count))
X  (setq file (expand-file-name file))
X  (if (not vm-visit-when-saving)
X      ;; Check and see if we are currently visiting the file
X      ;; that the user wants to save to.
X      (let ((blist (buffer-list)))
X	(while blist
X	  (if (equal (buffer-file-name (car blist)) file)
X	      (error "File %s is being visited, cannot save." file))
X	  (setq blist (cdr blist)))))
X  (let ((vm-message-pointer vm-message-pointer)
X	(direction (if (> count 0) 'forward 'backward))
X	(file-buffer)
X	(mail-buffer (current-buffer))
X	(counter)
X	(count (vm-abs count)))
X    (setq counter count)
X    (if vm-visit-when-saving
X	(progn
X	  (setq file-buffer (find-file-noselect file))
X	  (if (eq file-buffer mail-buffer)
X	      (error "This IS file %s, you must write messages elsewhere."
X		     buffer-file-name))))
X    (save-restriction
X      (widen)
X      (while (not (zerop counter))
X	(if (not vm-visit-when-saving)
X	    (write-region (vm-text-of (car vm-message-pointer))
X			  (vm-text-end-of (car vm-message-pointer))
X			  file t 'quiet)
X	  (let ((start (vm-text-of (car vm-message-pointer)))
X		(end (vm-text-end-of (car vm-message-pointer))))
X	    (save-excursion
X	      (set-buffer file-buffer)
X	      (save-excursion
X		(let (buffer-read-only)
X		  (vm-save-restriction
X		   (widen)
X		   (goto-char (point-max))
X		   (insert-buffer-substring mail-buffer start end)))))))
X	(vm-decrement counter)
X	(if (not (zerop counter))
X	    (vm-move-message-pointer direction))))
X    (if vm-visit-when-saving
X	(message "Message%s written to buffer %s" (if (/= 1 count) "s" "")
X		 (buffer-name file-buffer))
X      (message "Message%s written to %s" (if (/= 1 count) "s" "") file)))
X  (vm-update-summary-and-mode-line))
X
X(defun vm-pipe-message-to-command (command prefix-arg)
X  "Run shell command with the some or all of the current message as input.
XBy default the entire message is used.
XWith one \\[universal-argument] the text portion of the message is used.
XWith two \\[universal-argument]'s the header portion of the message is used.
X
XOutput is discarded.  The message is not altered."
X  (interactive
X   (progn
X     (vm-follow-summary-cursor)
X     (list (read-string "Pipe message to command: " vm-last-pipe-command)
X	   current-prefix-arg)))
X  (if vm-mail-buffer
X      (set-buffer vm-mail-buffer))
X  (vm-error-if-folder-empty)
X  (setq vm-last-pipe-command command)
X  (let ((buffer (get-buffer-create "*Shell Command Output*"))
X	(pop-up-windows (and pop-up-windows (eq vm-mutable-windows t))))
X    (save-excursion (set-buffer buffer) (erase-buffer))
X    (save-restriction
X      (widen)
X      (cond ((equal prefix-arg nil)
X	     (narrow-to-region (vm-start-of (car vm-message-pointer))
X			       (vm-end-of (car vm-message-pointer))))
X	    ((equal prefix-arg '(4))
X	     (narrow-to-region (vm-text-of (car vm-message-pointer))
X			       (vm-text-end-of (car vm-message-pointer))))
X	    ((equal prefix-arg '(16))
X	     (narrow-to-region (vm-start-of (car vm-message-pointer))
X			       (vm-text-of (car vm-message-pointer))))
X	    (t (narrow-to-region (vm-start-of (car vm-message-pointer))
X				 (vm-end-of (car vm-message-pointer)))))
X      (let ((pop-up-windows (and pop-up-windows (eq vm-mutable-windows t))))
X	(call-process-region (point-min) (point-max)
X			     (or shell-file-name "sh")
X			     nil buffer nil "-c" command)))
X    (set-buffer buffer)
X    (if (not (zerop (buffer-size)))
X	(display-buffer buffer))))
SHAR_EOF
chmod 0664 vm-save.el || echo "restore of vm-save.el fails"
echo "x - extracting vm-search.el (Text)"
sed 's/^X//' << 'SHAR_EOF' > vm-search.el &&
X;; Incremental search through a mail folder
X;; Copyright (C) 1985, 1986 Free Software Foundation, Inc.
X
X;; This file is part of GNU Emacs.
X
X;; GNU Emacs is distributed in the hope that it will be useful,
X;; but WITHOUT ANY WARRANTY.  No author or distributor
X;; accepts responsibility to anyone for the consequences of using it
X;; or for whether it serves any particular purpose or works at all,
X;; unless he says so in writing.  Refer to the GNU Emacs General Public
X;; License for full details.
X
X;; Everyone is granted permission to copy, modify and redistribute
X;; GNU Emacs, but only under the conditions described in the
X;; GNU Emacs General Public License.   A copy of this license is
X;; supposed to have been given to you along with GNU Emacs so you
X;; can know your rights and responsibilities.  It should be in a
X;; file named COPYING.  Among other things, the copyright notice
X;; and this notice must be preserved on all copies.
X
X
X;; Adapted for the VM mail reader, Kyle Jones, May 1989
X
X
X(require 'vm)
X
X;; This function does all the work of incremental search.
X;; The functions attached to ^R and ^S are trivial,
X;; merely calling this one, but they are always loaded by default
X;; whereas this file can optionally be autoloadable.
X;; This is the only entry point in this file.
X
X(defun vm-isearch (forward &optional regexp)
X  (let ((search-string "")
X	(search-message "")
X	(cmds nil)
X	(success t)
X	(wrapped nil)
X	(barrier (point))
X	adjusted
X	(invalid-regexp nil)
X	(slow-terminal-mode (and (<= (baud-rate) search-slow-speed)
X				 (> (window-height)
X				    (* 4 search-slow-window-lines))))
X	(other-end nil)    ;Start of last match if fwd, end if backwd.
X	(small-window nil)		;if t, using a small window
X	(found-point nil)		;to restore point from a small window
X	;; This is the window-start value found by the search.
X	(found-start nil)
X	(opoint (point))
X	(vm-ml-attributes-string vm-ml-attributes-string)
X	(vm-ml-message-number vm-ml-message-number)
X	(vm-message-pointer vm-message-pointer)
X	(inhibit-quit t))  ;Prevent ^G from quitting immediately.
X    (vm-isearch-push-state)
X    (save-window-excursion
X     (catch 'search-done
X       (while t
X	 (or (>= unread-command-char 0)
X	     (progn
X	       (or (input-pending-p)
X		   (vm-isearch-message))
X	       (if (and slow-terminal-mode
X			(not (or small-window (pos-visible-in-window-p))))
X		   (progn
X		     (setq small-window t)
X		     (setq found-point (point))
X		     (move-to-window-line 0)
X		     (let ((window-min-height 1))
X		       (split-window nil (if (< search-slow-window-lines 0)
X					     (1+ (- search-slow-window-lines))
X					   (- (window-height)
X					      (1+ search-slow-window-lines)))))
X		     (if (< search-slow-window-lines 0)
X			 (progn (vertical-motion (- 1 search-slow-window-lines))
X				(set-window-start (next-window) (point))
X				(set-window-hscroll (next-window)
X						    (window-hscroll))
X				(set-window-hscroll (selected-window) 0))
X		       (other-window 1))
X		     (goto-char found-point)))))
X	 (let ((char (if quit-flag
X			 ?\C-g
X		       (read-char))))
X	   (setq quit-flag nil adjusted nil)
X	   ;; Meta character means exit search.
X	   (cond ((and (>= char 128)
X		       search-exit-option)
X		  (setq unread-command-char char)
X		  (throw 'search-done t))
X		 ((eq char search-exit-char)
X		  ;; Esc means exit search normally.
X		  ;; Except, if first thing typed, it means do nonincremental
X		  (if (= 0 (length search-string))
X		      (vm-nonincremental-search forward regexp))
X		  (throw 'search-done t))
X		 ((= char ?\C-g)
X		  ;; ^G means the user tried to quit.
X		  (ding)
X		  (discard-input)
X		  (if success
X		      ;; If search is successful, move back to starting point
X		      ;; and really do quit.
X		      (progn (goto-char opoint)
X			     (signal 'quit nil))
X		    ;; If search is failing, rub out until it is once more
X		    ;;  successful.
X		    (while (not success) (vm-isearch-pop))))
X		 ((or (eq char search-repeat-char)
X		      (eq char search-reverse-char))
X		  (if (eq forward (eq char search-repeat-char))
X		      ;; C-s in forward or C-r in reverse.
X		      (if (equal search-string "")
X			  ;; If search string is empty, use last one.
X			  (setq search-string
X				(if regexp
X				    search-last-regexp search-last-string)
X				search-message
X				(mapconcat 'text-char-description
X					   search-string ""))
X			;; If already have what to search for, repeat it.
X			(or success
X			    (progn (goto-char (if forward (point-min) (point-max)))
X				   (setq wrapped t))))
X		    ;; C-s in reverse or C-r in forward, change direction.
X		    (setq forward (not forward)))
SHAR_EOF
echo "End of part 1"
echo "File vm-search.el is continued in part 2"
echo "2" > s2_seq_.tmp
exit 0

kjones@UUNET.UU.NET (Kyle Jones) (07/13/89)

#!/bin/sh
# this is part 2 of a multipart archive
# do not concatenate these parts, unpack them in order with /bin/sh
# file vm-search.el continued
#
CurArch=2
if test ! -r s2_seq_.tmp
then echo "Please unpack part 1 first!"
     exit 1; fi
( read Scheck
  if test "$Scheck" != $CurArch
  then echo "Please unpack part $Scheck next!"
       exit 1;
  else exit 0; fi
) < s2_seq_.tmp || exit 1
echo "x - Continuing file vm-search.el"
sed 's/^X//' << 'SHAR_EOF' >> vm-search.el
X		  (setq barrier (point)) ; For subsequent \| if regexp.
X		  (setq success t)
X		  (or (equal search-string "")
X		      (vm-isearch-search))
X		  (vm-isearch-push-state))
X		 ((= char search-delete-char)
X		  ;; Rubout means discard last input item and move point
X		  ;; back.  If buffer is empty, just beep.
X		  (if (null (cdr cmds))
X		      (ding)
X		    (vm-isearch-pop)))
X		 (t
X		  (cond ((or (eq char search-yank-word-char)
X			     (eq char search-yank-line-char))
X			 ;; ^W means gobble next word from buffer.
X			 ;; ^Y means gobble rest of line from buffer.
X			 (let ((word (save-excursion
X				       (and (not forward) other-end
X					    (goto-char other-end))
X				       (buffer-substring
X					(point)
X					(save-excursion
X					  (if (eq char search-yank-line-char)
X					      (end-of-line)
X					    (forward-word 1))
X					  (point))))))
X			   (setq search-string (concat search-string word)
X				 search-message
X				   (concat search-message
X					   (mapconcat 'text-char-description
X						      word "")))))
X			 ;; Any other control char =>
X			 ;;  unread it and exit the search normally.
X			 ((and search-exit-option
X			       (/= char search-quote-char)
X			       (or (= char ?\177)
X				   (and (< char ? ) (/= char ?\t) (/= char ?\r))))
X			  (setq unread-command-char char)
X			  (throw 'search-done t))
X			 (t
X			  ;; Any other character => add it to the
X			  ;;  search string and search.
X			  (cond ((= char search-quote-char)
X				 (setq char (read-quoted-char
X					     (vm-isearch-message t))))
X				((= char ?\r)
X				 ;; unix braindeath
X				 (setq char ?\n)))
X			  (setq search-string (concat search-string
X						      (char-to-string char))
X				search-message (concat search-message
X						       (text-char-description char)))))
X		  (if (and (not success)
X			   ;; unsuccessful regexp search may become
X			   ;;  successful by addition of characters which
X			   ;;  make search-string valid
X			   (not regexp))
X		      nil
X		    ;; If a regexp search may have been made more
X		    ;; liberal, retreat the search start.
X		    ;; Go back to place last successful search started
X		    ;; or to the last ^S/^R (barrier), whichever is nearer.
X		    (and regexp success cmds
X			 (cond ((memq char '(?* ??))
X				(setq adjusted t)
X				(let ((cs (nth (if forward
X						   5 ; other-end
X						 2) ; saved (point)
X					       (car (cdr cmds)))))
X				  ;; (car cmds) is after last search;
X				  ;; (car (cdr cmds)) is from before it.
X				  (setq cs (or cs barrier))
X				  (goto-char
X				   (if forward
X				       (max cs barrier)
X				     (min cs barrier)))))
X			       ((eq char ?\|)
X				(setq adjusted t)
X				(goto-char barrier))))
X		    ;; In reverse regexp search, adding a character at
X		    ;; the end may cause zero or many more chars to be
X		    ;; matched, in the string following point.
X		    ;; Allow all those possibiities without moving point as
X		    ;; long as the match does not extend past search origin.
X		    (if (and regexp (not forward) (not adjusted)
X			     (condition-case ()
X				 (looking-at search-string)
X			       (error nil))
X			     (<= (match-end 0) (min opoint barrier)))
X			(setq success t invalid-regexp nil
X			      other-end (match-end 0))
X		      ;; Not regexp, not reverse, or no match at point.
X		      (if (and other-end (not adjusted))
X			  (goto-char (if forward other-end
X				       (min opoint barrier (1+ other-end)))))
X		      (vm-isearch-search)))
X		  (vm-isearch-push-state))))))
X     (setq found-start (window-start (selected-window)))
X     (setq found-point (point)))
X    (if (> (length search-string) 0)
X	(if regexp
X	    (setq search-last-regexp search-string)
X	    (setq search-last-string search-string)))
X    (message "")
X    (if small-window
X	(goto-char found-point)
X      ;; Exiting the save-window-excursion clobbers this; restore it.
X      (set-window-start (selected-window) found-start t))))
X
X(defun vm-isearch-message (&optional c-q-hack ellipsis)
X  ;; If about to search, and previous search regexp was invalid,
X  ;; check that it still is.  If it is valid now,
X  ;; let the message we display while searching say that it is valid.
X  (and invalid-regexp ellipsis
X       (condition-case ()
X	   (progn (re-search-forward search-string (point) t)
X		  (setq invalid-regexp nil))
X	 (error nil)))
X  ;; If currently failing, display no ellipsis.
X  (or success (setq ellipsis nil))
X  (let ((m (concat (if success "" "failing ")
X		   (if wrapped "wrapped ")
X		   (if regexp "regexp " "")
X		   "VM I-search"
X		   (if forward ": " " backward: ")
X		   search-message
X		   (if c-q-hack "^Q" "")
X		   (if invalid-regexp
X		       (concat " [" invalid-regexp "]")
X		     ""))))
X    (aset m 0 (upcase (aref m 0)))
X    (let ((cursor-in-echo-area ellipsis))
X      (if c-q-hack m (message "%s" m)))))
X
X(defun vm-isearch-pop ()
X  (setq cmds (cdr cmds))
X  (let ((cmd (car cmds)))
X    (setq search-string (car cmd)
X	  search-message (car (cdr cmd))
X	  success (nth 3 cmd)
X	  forward (nth 4 cmd)
X	  other-end (nth 5 cmd)
X	  invalid-regexp (nth 6 cmd)
X	  wrapped (nth 7 cmd)
X	  barrier (nth 8 cmd)
X	  vm-ml-attributes-string (nth 9 cmd)
X	  vm-ml-message-number (nth 10 cmd)
X	  vm-message-pointer (nth 11 cmd))
X    (goto-char (car (cdr (cdr cmd))))
X    (vm-set-summary-pointer (car vm-message-pointer))))
X
X(defun vm-isearch-push-state ()
X  (setq cmds (cons (list search-string search-message (point)
X			 success forward other-end invalid-regexp
X			 wrapped barrier
X			 vm-ml-attributes-string vm-ml-message-number
X			 vm-message-pointer)
X		   cmds)))
X
X(defun vm-isearch-search ()
X  (vm-isearch-message nil t)
X  (condition-case lossage
X      (let ((inhibit-quit nil))
X	(if regexp (setq invalid-regexp nil))
X	(setq success
X	      (funcall
X	       (if regexp
X		   (if forward 're-search-forward 're-search-backward)
X		 (if forward 'search-forward 'search-backward))
X	       search-string nil t))
X	(if success
X	    (setq other-end
X		  (if forward (match-beginning 0) (match-end 0)))))
X    (quit (setq unread-command-char ?\C-g)
X	  (setq success nil))
X    (invalid-regexp (setq invalid-regexp (car (cdr lossage)))
X		    (if (string-match "\\`Premature \\|\\`Unmatched \\|\\`Invalid "
X				      invalid-regexp)
X			(setq invalid-regexp "incomplete input"))))
X  (if success
X      (vm-update-search-position)
X    ;; Ding if failed this time after succeeding last time.
X    (and (nth 3 (car cmds))
X	 (ding))
X    (goto-char (nth 2 (car cmds)))))
X
X;; This is called from incremental-search
X;; if the first input character is the exit character.
X;; The interactive-arg-reader uses free variables `forward' and `regexp'
X;; which are bound by `incremental-search'.
X
X;; We store the search string in `search-string'
X;; which has been bound already by `incremental-search'
X;; so that, when we exit, it is copied into `search-last-string'.
X
X(defun vm-nonincremental-search (forward regexp)
X  (let (message char function string inhibit-quit
X		(cursor-in-echo-area t))
X    ;; Prompt assuming not word search,
X    (setq message (if regexp 
X		      (if forward "VM Regexp search: "
X			"VM Regexp search backward: ")
X		    (if forward "VM Search: " "VM Search backward: ")))
X    (message "%s" message)
X    ;; Read 1 char and switch to word search if it is ^W.
X    (setq char (read-char))
X    (if (eq char search-yank-word-char)
X	(setq message (if forward "VM Word search: " "VM Word search backward: "))
X      ;; Otherwise let that 1 char be part of the search string.
X      (setq unread-command-char char))
X    (setq function
X	  (if (eq char search-yank-word-char)
X	      (if forward 'word-search-forward 'word-search-backward)
X	    (if regexp
X		(if forward 're-search-forward 're-search-backward)
X	      (if forward 'search-forward 'search-backward))))
X    ;; Read the search string with corrected prompt.
X    (setq string (read-string message))
X    ;; Empty means use default.
X    (if (= 0 (length string))
X	(setq string search-last-string)
X      ;; Set last search string now so it is set even if we fail.
X      (setq search-last-string string))
X    ;; Since we used the minibuffer, we should be available for redo.
X    (setq command-history (cons (list function string) command-history))
X    ;; Go ahead and search.
X    (funcall function string)))
X
X(defun vm-update-search-position (&optional record-change)
X  (if (and (>= (point) (vm-start-of (car vm-message-pointer)))
X	   (<= (point) (vm-end-of (car vm-message-pointer))))
X      nil
X    (let ((mp vm-message-list)
X	  (point (point)))
X      (while mp
X	(if (and (>= point (vm-start-of (car mp)))
X		 (<= point (vm-end-of (car mp))))
X	    (if record-change
X		(setq vm-last-message-pointer vm-message-pointer
X		      vm-message-pointer mp mp nil)
X	      (setq vm-message-pointer mp mp nil))
X	  (setq mp (cdr mp))))
X      (vm-update-summary-and-mode-line)
X      (vm-set-summary-pointer (car vm-message-pointer)))))
X
X(defun vm-isearch-forward ()
X  "Incrementally search forward through the current folder's messages.
XUsage is identical to the standard Emacs incremental search.
XWhen the search terminates the message containing point will be selected."
X  (interactive)
X  (vm-follow-summary-cursor)
X  (if vm-mail-buffer
X      (set-buffer vm-mail-buffer))
X  (if (null (get-buffer-window (current-buffer)))
X      (progn
X	(display-buffer (current-buffer))
X	(vm-proportion-windows)))
X  (vm-error-if-folder-empty)
X  (let ((clip-head (point-min))
X	(clip-tail (point-max))
X	(old-w (selected-window)))
X    (unwind-protect
X	(progn (select-window (get-buffer-window (current-buffer)))
X	       (widen)
X	       (vm-isearch t vm-search-using-regexps)
X	       (vm-update-search-position t)
X	       ;; vm-show-current-message only adjusts (point-max)
X	       (narrow-to-region
X		(if (< (point) (vm-vheaders-of (car vm-message-pointer)))
X		    (vm-start-of (car vm-message-pointer))
X		  (vm-vheaders-of (car vm-message-pointer)))
X		(point-max))
X	       (save-excursion
X		 (vm-show-current-message))
X	       (vm-howl-if-eom-visible)
X	       ;; make the clipping unwind a noop
X	       (setq clip-head (point-min))
X	       (setq clip-tail (point-max)))
X      (narrow-to-region clip-head clip-tail)
X      (select-window old-w))))
SHAR_EOF
echo "File vm-search.el is complete"
chmod 0664 vm-search.el || echo "restore of vm-search.el fails"
echo "x - extracting vm-summary.el (Text)"
sed 's/^X//' << 'SHAR_EOF' > vm-summary.el &&
X;;; Summary gathering and formatting routines for VM
X;;; Copyright (C) 1989 Kyle E. Jones
X;;;
X;;; This program is free software; you can redistribute it and/or modify
X;;; it under the terms of the GNU General Public License as published by
X;;; the Free Software Foundation; either version 1, or (at your option)
X;;; any later version.
X;;;
X;;; This program is distributed in the hope that it will be useful,
X;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
X;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
X;;; GNU General Public License for more details.
X;;;
X;;; You should have received a copy of the GNU General Public License
X;;; along with this program; if not, write to the Free Software
X;;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
X
X(require 'vm)
X
X(defun vm-summary-mode ()
X  "Major mode for VM folder summaries.
XThis major mode use the same keymap as vm-mode.  See the vm-mode documentation
Xfor a list of available commands."
X  (setq mode-name "VM Summary"
X	major-mode 'vm-summary-mode
X	mode-line-buffer-identification	'("VM " vm-version ": %b")
X	buffer-read-only t
X	overlay-arrow-string "->"
X	overlay-arrow-position nil
X	truncate-lines t)
X  (use-local-map vm-mode-map)
X  (save-excursion
X    (set-buffer vm-mail-buffer)
X    (vm-set-summary-pointer (car vm-message-pointer))))
X
X(put 'vm-summary-mode 'mode-class 'special)
X
X(defun vm-summarize (&optional dont-redo)
X  "Summarize the contents of the folder in a summary buffer. 
XThe format is as described by the variable vm-summary-format.  Generally
Xone line per message is most pleasing to the eye but this is not
Xmandatory."
X  (interactive "p")
X  (if vm-mail-buffer
X      (set-buffer vm-mail-buffer))
X  (vm-error-if-folder-empty)
X  (if (or (null vm-summary-buffer) (not dont-redo))
X      (let ((b (current-buffer))
X	    (inhibit-quit t))
X	(setq vm-summary-buffer
X	      (get-buffer-create (format "%s Summary" (buffer-name))))
X	(save-excursion
X	  (set-buffer vm-summary-buffer)
X	  (abbrev-mode 0)
X	  (auto-fill-mode 0)
X	  (setq vm-mail-buffer b))
X	(vm-do-summary)
X	(save-excursion
X	  (set-buffer vm-summary-buffer)
X	  (vm-summary-mode))))
X  (if vm-mutable-windows
X      (let ((pop-up-windows (and pop-up-windows (eq vm-mutable-windows t))))
X	(display-buffer vm-summary-buffer))
X    (switch-to-buffer vm-summary-buffer))
X  (if (eq vm-mutable-windows t)
X      (vm-proportion-windows))
X  (if vm-mail-buffer
X      (set-buffer vm-mail-buffer))
X  (vm-set-summary-pointer (car vm-message-pointer)))
X
X(defun vm-do-summary ()
X  (let ((mp vm-message-list)
X	(n 0)
X	;; Just for laughs, make the update interval variable.
X	(modulus (+ (% (vm-abs (random)) 7) 10))
X	summary)
X    (message "Generating summary...")
X    (save-excursion
X      (set-buffer vm-summary-buffer)
X      (let ((buffer-read-only nil))
X	(erase-buffer)
X	(while mp
X	  (set-buffer vm-mail-buffer)
X	  (setq summary (vm-sprintf 'vm-summary-format (car mp)))
X	  (set-buffer vm-summary-buffer)
X	  (vm-set-su-start-of (car mp) (point-marker))
X	  ;; the leading spaces are to make room for the overlay-arrow-string
X	  (insert "  " summary)
X	  (vm-set-su-end-of (car mp) (point-marker))
X	  (setq mp (cdr mp) n (1+ n))
X	  (if (zerop (% n modulus))
X	      (message "Generating summary... %d" n)))))
X    (message "Generating summary... done")))
X
X(defun vm-update-message-summary (mp)
X  (if vm-summary-buffer
X      (let ((summary (vm-sprintf 'vm-summary-format (car mp))))
X	(save-excursion
X	  (set-buffer vm-summary-buffer)
X	  (let ((inhibit-quit t) buffer-read-only)
X	    (goto-char (vm-su-start-of (car mp)))
X	    ;; We insert a char here and delete it later to avoid
X	    ;; markers clumping at the beginning of the summary,
X	    (insert "*")
X	    (delete-region (point) (vm-su-end-of (car mp)))
X	    (insert-before-markers "  " summary)
X	    (goto-char (vm-su-start-of (car mp)))
X	    (delete-char 1))))))
X
X(defun vm-set-summary-pointer (m)
X  (setq overlay-arrow-position (vm-su-start-of m))
X  (cond (vm-summary-buffer
X	 (let ((w (get-buffer-window vm-summary-buffer)))
X	   (save-excursion
X	     (set-buffer vm-summary-buffer)
X	     (goto-char overlay-arrow-position)
X	     (and w (set-window-point w overlay-arrow-position)))))))
X
X(defun vm-follow-summary-cursor ()
X  (and vm-follow-summary-cursor (eq major-mode 'vm-summary-mode)
X       (let ((point (point))
X	     message-pointer message-list)
X	 (save-excursion
X	   (set-buffer vm-mail-buffer)
X	   (setq message-pointer vm-message-pointer
X		 message-list vm-message-list))
X	 (if (or (null message-pointer)
X		 (and (>= point (vm-su-start-of (car message-pointer)))
X		      (< point (vm-su-end-of (car message-pointer)))))
X	     ()
X	   (if (< point (vm-su-start-of (car message-pointer)))
X	       (setq mp message-list)
X	     (setq mp (cdr message-pointer) message-pointer nil))
X	   (while (and (not (eq mp message-pointer))
X		       (>= point (vm-su-end-of (car mp))))
X	     (setq mp (cdr mp)))
X	   (if (not (eq mp message-pointer))
X	       (save-excursion
X		 (set-buffer vm-mail-buffer)
X		 (setq vm-last-message-pointer vm-message-pointer
X		       vm-message-pointer mp)
X		 (vm-set-summary-pointer (car vm-message-pointer))
X		 (vm-preview-current-message)
X		 ;; return non-nil so the caller will know the
X		 ;; a new message was selected.
X		 t ))))))
X
X(defun vm-sprintf (format-variable message)
X  (if (not (eq (get format-variable 'vm-compiled-format)
X	       (symbol-value format-variable)))
X      (vm-compile-format format-variable))
X  ;; The local variable name `vm-su-message' is mandatory here for
X  ;; the format s-expression to work.
X  (let ((vm-su-message message))
X    (eval (get format-variable 'vm-format-sexp))))
X
X(defun vm-compile-format (format-variable)
X  (let ((format (symbol-value format-variable))
X	sexp sexp-fmt conv-spec last-match-end case-fold-search)
X    (store-match-data nil)
X    (while (string-match
X"%\\(-\\)?\\([0-9]\\)*\\(\\.\\([0-9]+\\)\\)?\\([acdfFhilmnswyz%]\\)"
X	    format (match-end 0))
X      (setq conv-spec (aref format (match-beginning 5)))
X      (if (memq conv-spec '(?a ?c ?d ?f ?F ?h ?i ?l ?m ?n ?s ?w ?y ?z))
X	  (progn
X	    (cond ((= conv-spec ?a)
X		   (setq sexp (cons (list 'vm-su-attribute-indicators
X					  'vm-su-message) sexp)))
X		  ((= conv-spec ?c)
X		   (setq sexp (cons (list 'vm-su-byte-count
X					  'vm-su-message) sexp)))
X		  ((= conv-spec ?d)
X		   (setq sexp (cons (list 'vm-su-monthday
X					  'vm-su-message) sexp)))
X		  ((= conv-spec ?f)
X		   (setq sexp (cons (list 'vm-su-from
X					  'vm-su-message) sexp)))
X		  ((= conv-spec ?F)
X		   (setq sexp (cons (list 'vm-su-full-name
X					  'vm-su-message) sexp)))
X		  ((= conv-spec ?h)
X		   (setq sexp (cons (list 'vm-su-hour
X					  'vm-su-message) sexp)))
X		  ((= conv-spec ?i)
X		   (setq sexp (cons (list 'vm-su-message-id
X					  'vm-su-message) sexp)))
X		  ((= conv-spec ?l)
X		   (setq sexp (cons (list 'vm-su-line-count
X					  'vm-su-message) sexp)))
X		  ((= conv-spec ?m)
X		   (setq sexp (cons (list 'vm-su-month
X					  'vm-su-message) sexp)))
X		  ((= conv-spec ?n)
X		   (setq sexp (cons (list 'vm-su-message-number
X					  'vm-su-message) sexp)))
X		  ((= conv-spec ?s)
X		   (setq sexp (cons (list 'vm-su-subject
X					  'vm-su-message) sexp)))
X		  ((= conv-spec ?w)
X		   (setq sexp (cons (list 'vm-su-weekday
X					  'vm-su-message) sexp)))
X		  ((= conv-spec ?y)
X		   (setq sexp (cons (list 'vm-su-year
X					  'vm-su-message) sexp)))
X		  ((= conv-spec ?z)
X		   (setq sexp (cons (list 'vm-su-zone
X					  'vm-su-message) sexp))))
X	    (cond ((match-beginning 1)
X		   (setcar sexp
X			   (list 'vm-left-justify-string (car sexp)
X				 (string-to-int (substring format
X							   (match-beginning 2)
X							   (match-end 2))))))
X		  ((match-beginning 2)
X		   (setcar sexp
X			   (list 'vm-right-justify-string (car sexp)
X				 (string-to-int (substring format
X							   (match-beginning 2)
X							   (match-end 2)))))))
X	    (cond ((match-beginning 3)
X		   (setcar sexp
X			   (list 'vm-truncate-string (car sexp)
X				 (string-to-int (substring format
X							   (match-beginning 4)
X							   (match-end 4)))))))
X	    (setq sexp-fmt
X		  (cons "%s"
X			(cons (substring format
X					 (or last-match-end 0)
X					 (match-beginning 0))
X			      sexp-fmt))))
X	(setq sexp-fmt
X	      (cons "%%"
X		    (cons (substring format
X				     (or last-match-end 0)
X				     (match-beginning 0))
X			  sexp-fmt))))
X      (setq last-match-end (match-end 0)))
X    (setq sexp-fmt 
X	  (cons (substring format
X			   (or last-match-end 0)
X			   (length format))
X		sexp-fmt)
X	  sexp-fmt (apply 'concat (nreverse sexp-fmt))
X	  sexp (cons 'format (cons sexp-fmt (nreverse sexp))))
X    (put format-variable 'vm-format-sexp sexp)
X    (put format-variable 'vm-compiled-format format)))
X
X(defun vm-get-header-contents (message header-name)
X  (let (contents regexp)
X    (setq regexp (format vm-header-regexp-format header-name))
X    (save-excursion
X      (set-buffer (marker-buffer (vm-start-of message)))
X      (save-restriction
X	(widen)
X	(goto-char (vm-start-of message))
X	(while (re-search-forward regexp (vm-text-of message) t)
X	  (if contents
X	      (setq contents
X		    (concat
X		     contents ",\n\t"
X		     (buffer-substring (match-beginning 1) (match-end 1))))
X	    (setq contents
X		  (buffer-substring (match-beginning 1) (match-end 1)))))
X	contents))))
X
X(defun vm-left-justify-string (string width)
X  (if (>= (length string) width)
X      string
X    (concat string (make-string (- width (length string)) ?\ ))))
X
X(defun vm-right-justify-string (string width)
X  (if (>= (length string) width)
X      string
X    (concat (make-string (- width (length string)) ?\ ) string)))
X
X(defun vm-truncate-string (string width)
X  (if (<= (length string) width)
X      string
X    (substring string 0 width)))
X
X(defun vm-su-attribute-indicators (m)
X  (concat
X   (cond ((vm-deleted-flag m) "D")
X	 ((vm-new-flag m) "N")
X	 ((vm-unread-flag m) "U")
X	 (t " "))
X   (cond ((vm-filed-flag m) "F")
X	 (t " "))
X   (cond ((vm-replied-flag m) "R")
X	 (t " "))))
X
X(defun vm-su-byte-count (m)
X  (or (vm-byte-count-of m)
X      (vm-set-byte-count-of m (int-to-string
X			       (- (vm-text-end-of m) (vm-text-of m))))))
X
X(defun vm-su-weekday (m)
X  (or (vm-weekday-of m)
X      (progn (vm-su-do-date m) (vm-weekday-of m))))
X
X(defun vm-su-monthday (m)
X  (or (vm-monthday-of m)
X      (progn (vm-su-do-date m) (vm-monthday-of m))))
X
X(defun vm-su-month (m)
X  (or (vm-month-of m)
X      (progn (vm-su-do-date m) (vm-month-of m))))
X
X(defun vm-su-year (m)
X  (or (vm-year-of m)
X      (progn (vm-su-do-date m) (vm-year-of m))))
X
X(defun vm-su-hour (m)
X  (or (vm-hour-of m)
X      (progn (vm-su-do-date m) (vm-hour-of m))))
X
X(defun vm-su-zone (m)
X  (or (vm-zone-of m)
X      (progn (vm-su-do-date m) (vm-zone-of m))))
X
X;; Some yogurt-headed delivery agents don't even provide a Date: header.
X(defun vm-grok-From_-date (message)
X  ;; If this is MMDF, forget it.
X  (if (eq vm-folder-type 'mmdf)
X      nil
X    (save-excursion
X      (set-buffer (marker-buffer (vm-start-of message)))
X      (save-restriction
X	(widen)
X	(goto-char (vm-start-of message))
X	(if (looking-at "From [^ \t\n]+[ \t]+\\([^ \t\n].*\\)")
X	    (buffer-substring (match-beginning 1) (match-end 1)))))))
X
X(defun vm-su-do-date (m)
X  (let (date)
X    (setq date (or (vm-get-header-contents m "Date") (vm-grok-From_-date m)))
X    (cond
X     ((null date)
X      (vm-set-weekday-of m "")
X      (vm-set-monthday-of m "")
X      (vm-set-month-of m "")
X      (vm-set-year-of m "")
X      (vm-set-hour-of m "")
X      (vm-set-zone-of m ""))
X     ((string-match
X;; The date format recognized here is the one specified in RFC 822.
X;; Some slop is allowed e.g. dashes between the monthday, month and year
X;; because such malformed headers headers have been observed.
X"\\(\\([a-z][a-z][a-z]\\),\\)?[ \t\n]*\\([0-9][0-9]?\\)[ \t\n---]*\\([a-z][a-z][a-z]\\)[ \t\n---]*[0-9]*\\([0-9][0-9]\\)[ \t\n]*\\([0-9:]+\\)[ \t\n]*\\([a-z][a-z]?[a-z]?\\|[---+][0-9][0-9][0-9][0-9]\\)"
X       date)
X      (if (match-beginning 2)
X	  (vm-set-weekday-of m (substring date (match-beginning 2)
X					  (match-end 2)))
X	(vm-set-weekday-of m ""))
X      (vm-set-monthday-of m (substring date (match-beginning 3) (match-end 3)))
X      (vm-set-month-of m (substring date (match-beginning 4) (match-end 4)))
X      (vm-set-year-of m (substring date (match-beginning 5) (match-end 5)))
X      (vm-set-hour-of m (substring date (match-beginning 6) (match-end 6)))
X      (vm-set-zone-of m (substring date (match-beginning 7) (match-end 7))))
X     ((string-match
X;; UNIX ctime(3) format with slop allowed in the whitespace and we allow for
X;; the possibility of a timezone at the end.
X"\\([a-z][a-z][a-z]\\)[ \t\n]*\\([a-z][a-z][a-z]\\)[ \t\n]*\\([0-9][0-9]?\\)[ \t\n]*\\([0-9:]+\\)[ \t\n]*[0-9][0-9]\\([0-9][0-9]\\)[ \t\n]*\\([a-z][a-z]?[a-z]?\\|[---+][0-9][0-9][0-9][0-9]\\)?"
X       date)
X      (vm-set-weekday-of m (substring date (match-beginning 1) (match-end 1)))
X      (vm-set-month-of m (substring date (match-beginning 2) (match-end 2)))
X      (vm-set-monthday-of m (substring date (match-beginning 3) (match-end 3)))
X      (vm-set-hour-of m (substring date (match-beginning 4) (match-end 4)))
X      (vm-set-year-of m (substring date (match-beginning 5) (match-end 5)))
X      (if (match-beginning 6)
X	  (vm-set-zone-of m (substring date (match-beginning 6)
X				       (match-end 6)))))
X     (t
X      (vm-set-weekday-of m "")
X      (vm-set-monthday-of m "")
X      (vm-set-month-of m "")
X      (vm-set-year-of m "")
X      (vm-set-hour-of m "")
X      (vm-set-zone-of m "")))))
X
X(defun vm-su-full-name (m)
X  (or (vm-full-name-of m)
X      (progn (vm-su-do-author m) (vm-full-name-of m))))
X
X(defun vm-su-from (m)
X  (or (vm-from-of m)
X      (progn (vm-su-do-author m) (vm-from-of m))))
X
X;; Some yogurt-headed delivery agents don't even provide a From: header.
X(defun vm-grok-From_-author (message)
X  ;; If this is MMDF, forget it.
X  (if (eq vm-folder-type 'mmdf)
X      nil
X    (save-excursion
X      (set-buffer (marker-buffer (vm-start-of message)))
X      (save-restriction
X	(widen)
X	(goto-char (vm-start-of message))
X	(if (looking-at "From \\([^ \t\n]+\\)")
X	    (buffer-substring (match-beginning 1) (match-end 1)))))))
X
X(defun vm-su-do-author (m)
X  (let (full-name from)
X    (setq full-name (vm-get-header-contents m "Full-Name"))
X    (setq from (or (vm-get-header-contents m "From") (vm-grok-From_-author m)))
X    (cond ((null from)
X	   (setq from "???")
X	   (if (null full-name)
X	       (setq full-name "???")))
X	  ((string-match "^\\(\\([^<]+[^ \t\n]\\)[ \t\n]+\\)?<\\([^>]+\\)>"
X			 from)
X	   (if (and (match-beginning 2) (null full-name))
X	       (setq full-name
X		     (substring from (match-beginning 2) (match-end 2))))
X	   (setq from (substring from (match-beginning 3) (match-end 3))))
X	  ((string-match "[\000-\177]*(\\([^)]+\\))[\000-\177]*" from)
X	   (if (null full-name)
X	       (setq full-name (substring from (match-beginning 1)
X					  (match-end 1))))
X	   (setq from
X		 (concat
X		  (substring from (match-beginning 0) (1- (match-beginning 1)))
X		  (substring from (1+ (match-end 1)) (match-end 0))))))
X    ;; ewe ewe see pee...
X    (if (and vm-gargle-uucp (string-match
X"\\([^!@:.]+\\)\\(\\.[^!@:]+\\)?!\\([^!@: \t\n]+\\)\\(@\\([^!@:. \t\n]+\\)\\(.[^ \t\n]+\\)?\\)?[ \t\n]*$"
X			     from))
X	(setq from
X	      (concat
X	       (substring from (match-beginning 3) (match-end 3)) "@"
X	       (if (and (match-beginning 5) (match-beginning 2)
X			(not (match-beginning 6)))
X		   (concat (substring from (match-beginning 5) (match-end 5))
X			   ".")
X		 "")
X	       (substring from (match-beginning 1)
X			  (or (match-end 2) (match-end 1)))
X	       (if (match-end 2) "" ".UUCP"))))
X    (if (or (null full-name) (string-match "^[ \t\n]*$" full-name))
X	(setq full-name from))
X    (vm-set-full-name-of m full-name)
X    (vm-set-from-of m from)))
X
X(defun vm-su-message-id (m)
X  (or (vm-message-id-of m)
X      (vm-set-message-id-of m
X			    (or (vm-get-header-contents m "Message-Id")
X				""))))
X
X(defun vm-su-line-count (m)
X  (or (vm-line-count-of m)
X      (vm-set-line-count-of
X       m
X       (save-restriction
X	 (widen)
X	 (int-to-string
X	  (count-lines (vm-text-of m) (vm-text-end-of m)))))))
X
X(defun vm-su-message-number (m)
X  (vm-number-of m))
X
X(defun vm-su-subject (m)
X  (or (vm-subject-of m)
X      (vm-set-subject-of m
X			 (or (vm-get-header-contents m "Subject") ""))))
SHAR_EOF
chmod 0664 vm-summary.el || echo "restore of vm-summary.el fails"
echo "x - extracting vm-undo.el (Text)"
sed 's/^X//' << 'SHAR_EOF' > vm-undo.el &&
X;;; Commands to undo message attribute changes in VM
X;;; Copyright (C) 1989 Kyle E. Jones
X;;;
X;;; This program is free software; you can redistribute it and/or modify
X;;; it under the terms of the GNU General Public License as published by
X;;; the Free Software Foundation; either version 1, or (at your option)
X;;; any later version.
X;;;
X;;; This program is distributed in the hope that it will be useful,
X;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
X;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
X;;; GNU General Public License for more details.
X;;;
X;;; You should have received a copy of the GNU General Public License
X;;; along with this program; if not, write to the Free Software
X;;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
X
X(require 'vm)
X
X(defun vm-undo-boundary ()
X  (if (car vm-undo-record-list)
X      (setq vm-undo-record-list (cons nil vm-undo-record-list))))
X
X(defun vm-clear-expunge-invalidated-undos ()
X  (let ((udp vm-undo-record-list) udp-prev)
X    (while udp
X      (cond ((null (car udp))
X	     (setq udp-prev udp))
X	    ((and (not (eq (car (car udp)) 'set-buffer-modified-p))
X		  (vm-deleted-flag (car (cdr (car udp)))))
X	     (cond (udp-prev (setcdr udp-prev (cdr udp)))
X		   (t (setq vm-undo-record-list (cdr udp)))))
X	    (t (setq udp-prev udp)))
X      (setq udp (cdr udp)))
X    (vm-clear-modification-flag-undos))
X  (vm-squeeze-consecutive-undo-boundaries))
X	    
X(defun vm-clear-modification-flag-undos ()
X  (let ((udp vm-undo-record-list) udp-prev)
X    (while udp
X      (cond ((null (car udp))
X	     (setq udp-prev udp))
X	    ((eq (car (car udp)) 'set-buffer-modified-p)
X	     (cond (udp-prev (setcdr udp-prev (cdr udp)))
X		   (t (setq vm-undo-record-list (cdr udp)))))
X	    (t (setq udp-prev udp)))
X      (setq udp (cdr udp))))
X  (vm-squeeze-consecutive-undo-boundaries))
X
X;; squeeze out consecutive record separators left by the deletions
X(defun vm-squeeze-consecutive-undo-boundaries ()
X  (let ((udp vm-undo-record-list) udp-prev)
X    (while udp
X      (cond ((and (null (car udp)) udp-prev (null (car udp-prev)))
X	     (setcdr udp-prev (cdr udp)))
X	    (t (setq udp-prev udp)))
X      (setq udp (cdr udp)))
X    (if (equal '(nil) vm-undo-record-list)
X	(setq vm-undo-record-list nil))))
X	    
X(defun vm-undo-record (sexp)
X  (setq vm-undo-record-list (cons sexp vm-undo-record-list)))
X
X(defun vm-undo ()
X  "Undo last change to message attributes in the current folder.
XConsecutive invocations of this command cause sequentially earlier
Xchanges to be undone.  After an intervening command between undos,
Xthe undos themselves become undoable."
X  (interactive)
X  (if vm-mail-buffer
X      (set-buffer vm-mail-buffer))
X  (let ((inhibit-quit t))
X    (if (not (eq last-command 'vm-undo))
X	(setq vm-undo-record-pointer vm-undo-record-list))
X    (if (not vm-undo-record-pointer)
X	(error "No further VM undo information available"))
X    ;; skip current record boundary
X    (setq vm-undo-record-pointer (cdr vm-undo-record-pointer))
X    (while (car vm-undo-record-pointer)
X      (eval (car vm-undo-record-pointer))
X      (setq vm-undo-record-pointer (cdr vm-undo-record-pointer)))
X    (message "VM Undo!")
X    (vm-update-summary-and-mode-line)))
X
X(defun vm-set-new-flag (m flag)
X  (let ((inhibit-quit t))
X    (cond ((not (buffer-modified-p))
X	   (set-buffer-modified-p t)
X	   (vm-undo-record (list 'set-buffer-modified-p nil))))
X    (vm-undo-record (list 'vm-set-new-flag m (not flag)))
X    (vm-undo-boundary)
X    (aset (aref m 5) 0 flag)
X    (vm-mark-for-display-update m)))
X
X(defun vm-set-unread-flag (m flag)
X  (let ((inhibit-quit t))
X    (cond ((not (buffer-modified-p))
X	   (set-buffer-modified-p t)
X	   (vm-undo-record (list 'set-buffer-modified-p nil))))
X    (vm-undo-record (list 'vm-set-unread-flag m (not flag)))
X    (vm-undo-boundary)
X    (aset (aref m 5) 1 flag)
X    (vm-mark-for-display-update m)))
X
X(defun vm-set-deleted-flag (m flag)
X  (let ((inhibit-quit t))
X    (cond ((not (buffer-modified-p))
X	   (set-buffer-modified-p t)
X	   (vm-undo-record (list 'set-buffer-modified-p nil))))
X    (vm-undo-record (list 'vm-set-deleted-flag m (not flag)))
X    (vm-undo-boundary)
X    (aset (aref m 5) 2 flag)
X    (vm-mark-for-display-update m)))
X
X(defun vm-set-filed-flag (m flag)
X  (let ((inhibit-quit t))
X    (cond ((not (buffer-modified-p))
X	   (set-buffer-modified-p t)
X	   (vm-undo-record (list 'set-buffer-modified-p nil))))
X    (vm-undo-record (list 'vm-set-filed-flag m (not flag)))
X    (vm-undo-boundary)
X    (aset (aref m 5) 3 flag)
X    (vm-mark-for-display-update m)))
X
X(defun vm-set-replied-flag (m flag)
X  (let ((inhibit-quit t))
X    (cond ((not (buffer-modified-p))
X	   (set-buffer-modified-p t)
X	   (vm-undo-record (list 'set-buffer-modified-p nil))))
X    (vm-undo-record (list 'vm-set-replied-flag m (not flag)))
X    (vm-undo-boundary)
X    (aset (aref m 5) 4 flag)
X    (vm-mark-for-display-update m)))
SHAR_EOF
chmod 0664 vm-undo.el || echo "restore of vm-undo.el fails"
echo "x - extracting vm.el (Text)"
sed 's/^X//' << 'SHAR_EOF' > vm.el &&
X;;; UNIX style mail reader for GNU Emacs
X;;; Copyright (C) 1989 Kyle E. Jones
X;;;
X;;; This program is free software; you can redistribute it and/or modify
X;;; it under the terms of the GNU General Public License as published by
X;;; the Free Software Foundation; either version 1, or (at your option)
X;;; any later version.
X;;;
X;;; This program is distributed in the hope that it will be useful,
X;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
X;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
X;;; GNU General Public License for more details.
X;;;
X;;; You should have received a copy of the GNU General Public License
X;;; along with this program; if not, write to the Free Software
X;;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
X
X;; This is a set of Emacs-Lisp commands and support functions for
X;; reading mail.  While a mail reader (RMAIL) is distributed with GNU
X;; Emacs it converts a user's mailbox to BABYL format, a behavior I
X;; find quite unpalatable.
X;;
X;; VM is similar to RMAIL in that it scoops mail from the system mailbox
X;; into a primary inbox for reading, but the similarity ends there.
X;; VM does not reformat the mailbox beyond reordering the headers
X;; according to user preference, and adding a header used internally to
X;; store message attributes.
X;;
X;; Entry points to VM are the commands vm and vm-visit-folder.
X;;
X;; If autoloading then the lines:
X;;   (autoload 'vm "vm" nil t)
X;;   (autoload 'vm-visit-folder "vm" nil t)
X;; should appear in a user's .emacs or in default.el in the lisp
X;; directory of the Emacs distribution.
X;;
X;; VM requires Emacs' etc/movemail to work on your system.
X
X(provide 'vm)
X
X(defvar vm-primary-inbox "~/INBOX"
X  "*Mail is moved from the system mailbox to this file for reading.")
X
X(defvar vm-crash-box "~/INBOX.CRASH"
X  "*File in which to store mail temporarily while it is transferrred from
Xthe system mailbox to the primary inbox.  If the something happens
Xduring this mail transfer, any missing mail will be found in this file.
XVM will do crash recovery from this file automatically at startup, as
Xnecessary.")
X
X(defvar vm-spool-files nil
X  "*If non-nil this variable's value should be a list of strings naming files
Xthat VM will check for incoming mail instead of the where VM thinks your
Xsystem mailbox is.  This variable can be used to specify multiple spool files
Xor to point VM in the right direction if its notion of your system mailbox is
Xincorrect.")
X
X(defvar vm-visible-headers
X  '("From:" "Sender:" "To:" "Apparently-To:" "Cc:" "Subject:" "Date:")
X  "*List of headers that should be visible when VM first displays a message.
XThese should be listed in the order you wish them presented.
XRegular expressions are allowed.")
X
X(defvar vm-highlighted-header-regexp nil
X  "*Regular expression that matches the beginnings of headers that should
Xbe highlighted when a message is first presented.  For exmaple setting
Xthis variable to \"^From\\\\|^Subject\" causes the From: and Subject:
Xheaders to be highlighted.")
X
X(defvar vm-preview-lines 0
X  "*Non-nil value N causes VM to display the visible headers + N lines of text
Xfrom a message when it is first presented.  The message is not actually marked
Xas read until the message is exposed in its entirety.  Nil causes VM not to
Xpreview a message at all; it is displayed in its entirety when first
Xpresented and is marked as read.")
X
X(defvar vm-preview-read-messages t
X  "*Non-nil value means to preview messages, even if they've already been read.
XA nil value causes VM to preview messages only if new or unread.")
X
X(defvar vm-folder-type nil
X  "*Value specifies the type of mail folder VM should expect to read and
Xwrite.  Nil means expect the UNIX style folders characterized by the
X\"\\n\\nFrom \" message separators.  The only other supported value for
Xthis variable is the symbol `mmdf' which causes VM to use
X\"^A^A^A^A\\n\" MMDF style leaders and trailers.")
X
X(defvar vm-folder-directory nil
X  "*Directory where folders of mail are kept.")
X
X(defvar vm-confirm-new-folders nil
X  "*Non-nil value causes interactive calls to vm-save-message
Xto ask for confirmation before creating a new folder.")
X
X(defvar vm-delete-empty-folders t
X  "*Non-nil value causes VM to remove empty (zero length) folder files
Xafter saving them.")
X
X(defvar vm-included-text-prefix " > "
X  "*String used to prefix included text in replies.")
X
X(defvar vm-auto-folder-alist nil
X  "*Non-nil value should be an alist that VM will use to choose a default
Xfolder name when messages are saved.  The alist should be of the form
X\((HEADER-NAME
X   (REGEXP . FOLDER-NAME) ...
X  ...))
Xwhere HEADER-NAME and REGEXP are strings, and FOLDER-NAME is a string or an s-expression that evaluates to a string.
X
XIf any part of the contents of the message header named by HEADER-NAME
Xis matched by the regular expression REGEXP, VM will evaluate the
Xcorresponding FOLDER-NAME and use the result as the default when
Xprompting for a folder to save the message in.  If trhe resulting folder
Xname is a relative pathname, then it will resolve to the directory named by
Xvm-folder-directory, or the default-directory of the currently visited
Xfolder if vm-folder-directory is nil.
X
XWhen FOLDER-NAME is evaluated, the current buffer will contain only the
Xcontents of the header named by HEADER-NAME.  It is safe to modify this
Xbuffer.  You can use the match data from any \\( ... \\) grouping
Xconstructs in REGEXP along with the function buffer-substring to build a
Xfolder name based on the header information.
X
XMatching is case sensitive.")
X
X(defvar vm-visit-when-saving nil
X  "*Non-nil causes VM to visit folders when saving messages.  This means
XVM will read the folder into Emacs and append the message to the buffer
Xinstead of appending to the folder file directly.  This behavior is
Xideal when folders are encrypted or compressed since appending plaintext
Xto such files is a ghastly mistake.
X
XNote the setting of this variable does not affect how the primary inbox
Xis accessed, i.e. the primary inbox must be a plaintext file.")
X
X(defvar vm-in-reply-to-format "%i"
X  "*String which specifies the format of the contents of the In-Reply-To
Xheader that is generated for replies.  See the documentation for the
Xvariable vm-summary-format for information on what this string may
Xcontain.  The format should *not* end with a newline.
XNil means don't put an In-Reply-To: header in replies.")
X
X(defvar vm-included-text-attribution-format "%F writes:\n"
X  "*String which specifies the format of the attribution that precedes the
Xincluded text from a message in a reply.  See the documentation for the
Xvariable vm-summary-format for information on what this string may contain.
XNil means don't attribute included text in replies.")
X
X(defvar vm-forwarding-subject-format "forwarded message from %F"
X  "*String which specifies the format of the contents of the Subject
Xheader that is generated for a forwarded message.  See the documentation
Xfor the variable vm-summary-format for information on what this string
Xmay contain.  The format should *not* end with a newline.  Nil means
Xleave the Subject header empty when forwarding.")
X
X(defvar vm-summary-format "%2n %a %-17.17F %3m %2d %3l/%-5c \"%s\"\n"
X  "*String which specifies the message summary line format.
XThe string may contain the printf-like `%' conversion specifiers which
Xsubstitute information about the message into the final summary line.
X
XRecognized specifiers are:
X   a - attribute indicators (always three characters wide)
X       The first char is  `D', `N', `U' or ` ' for deleted, new, unread
X       and read message respectively.
X       The second char is `F' for filed (saved) messages.
X       The third char is `R' if the message has been replied to.
X   c - number of characters in message (ignoring headers)
X   d - date of month message sent
X   f - author's address
X   F - author's full name (same as f if full name not found)
X   h - hour message sent
X   i - message ID
X   l - number of lines in message (ignoring headers)
X   m - month message sent
X   n - message number
X   s - message subject
X   w - day of the week message sent
X   y - year message sent
X   z - timezone of date when the message was sent
X
XUse %% to get a single %.
X
XA numeric field width may be specified between the `%' and the specifier;
Xthis causes right justification of the substituted string.  A negative field
Xwidth causes left justification.
X
XThe field width may be followed by a `.' and a number specifying the maximum
Xallowed length of the substituted string.  If the string is longer than this
Xvalue it is truncated.
X
XThe summary format need not be one line per message but it must end with
Xa newline, otherwise the message pointer will not be displayed correctly
Xin the summary window.")
X
X(defvar vm-mail-window-percentage 75
X  "*Percentage of the screen that should be used to show mail messages.
XThe rest of the screen will be used by the summary buffer, if displayed.")
X
X(defvar vm-mutable-windows t
X  "*This variable's value controls VM's window usage.
X
XA value of t gives VM free run of the Emacs display; it will commandeer
Xthe entire screen for its purposes.
X
XA value of nil restricts VM's window usage to the window from which
Xit was invoked.  VM will not create, delete, or use any other windows,
Xnor will it resize it's own window.
X
XA value that is neither t nor nil allows VM to use other windows, but it
Xwill not create new ones, or resize or delete the current ones.")
X
X(defvar vm-startup-with-summary nil
X  "*Value tells VM what to display when a folder is visited.
XNil means display folder only, t means display the summary only.  A
Xvalue that is neither t not nil means to display both folder and summary.
XThe latter only works if the variable pop-up-windows's value is non-nil.
XSee the documentation for vm-mail-window-percentage to see how to change how
Xthe screen is apportioned between the folder and summary windows.")
X
X(defvar vm-follow-summary-cursor nil
X  "*Non-nil value causes VM to select the message under the cursor in the
Xsummary window before executing commands that operate on the current message.
XThis occurs only when the summary buffer window is the selected window.")
X
X(defvar vm-group-by nil
X  "*Non-nil value tells VM how to group message presentation.
XCurrently, the valid non-nil values for this variable are
X  \"subject\", which causes messages with the same subject (ignoring
X    Re:'s) to be presented together,
X  \"author\", which causes messages with the same author to be presented
X    together, and
X  \"date-sent\", which causes message sent on the same day to be
X    presented together.
X  \"arrival-time\" which appears only for completeness, this is the
X    default behavior and is the same as nil.
X
XThe ordering of the messages in the folder itself is not altered, messages
Xare simply numbered and ordered differently internally.")
X
X(defvar vm-skip-deleted-messages t
X  "*Non-nil value causes VM's `n' and 'p' commands to skip over
Xdeleted messages.  If all messages are marked deleted then this variable
Xis, of course, ignored.")
X
X(defvar vm-skip-read-messages nil
X  "*Non-nil value causes VM's `n' and `p' commands to skip over
Xmessage that have already been read in favor of new or unread messages.
XIf there are no unread message then this variable is, of course, ignored.")
X
X(defvar vm-move-after-deleting nil
X  "*Non-nil value causes VM's `d' command to automatically invoke
Xvm-next-message or vm-previous-message after deleting, to move
Xpast the deleted messages.")
X
X(defvar vm-delete-after-saving nil
X  "*Non-nil value causes VM automatically to mark messages for deletion
Xafter successfully saving them to a folder.")
X
X(defvar vm-circular-folders 0
X  "*Value determines whether VM folders will be considered circular by
Xvarious commands.  `Circular' means VM will wrap from the end of the folder
Xto the start and vice versa when moving the message pointer or deleting,
Xundeleting or saving messages before or after the current message.
X
XA value of t causes all VM commands to consider folders circular.
X
XA value of nil causes all of VM commands to signal an error if the start
Xor end of the folder would have to be passed to complete the command.
XFor movement commands, this occurs after the message pointer has been
Xmoved as far it can go.  For other commands the error occurs before any
Xpart of the command has been executed, i.e. no moves, saves, etc. will
Xbe done unless they can be done in their entirety.
X
XA value that is not nil and not t causes only VM's movement commands to
Xconsider folders circular.  Saves, deletes and undeleted command will
Xbehave the same as if the value is nil.")
X
X(defvar vm-search-using-regexps nil
X  "*Non-nil value causes VM's search command will interpret user input as a
Xregular expression instead of as a literal string.")
X
X(defvar vm-mode-hooks nil
X  "*List of hook functions to run when a buffer enters vm-mode.
XThese hook functions should generally be used to set key bindings
Xand local variables.  Mucking about in the folder buffer is certainly
Xpossible but it is not encouraged.")
X
X(defvar vm-berkeley-mail-compatibility
X  (memq system-type '(berkeley-unix))
X  "*Non-nil means to read and write BSD Mail(1) style Status: headers.
XThis makes sense if you plan to use VM to read mail archives created by
XMail.")
X
X(defvar vm-gargle-uucp nil
X  "*Non-nil value means to use a crufty regular expression that does
Xsurprisingly well at beautifying UUCP addresses that are substitued for
X%f as part of summary and attribution formats.")
X
X(defvar vm-strip-reply-headers nil
X  "*Non-nil value causes VM to strip away all comments and extraneous text
Xfrom the headers generated in reply messages.  If you use the \"fakemail\"
Xprogram as distributed with Emacs, you probably want to set this variable to
Xto t, because as of Emacs v18.52 \"fakemail\" could not handle unstripped
Xheaders.")
X
X(defvar vm-rfc934-forwarding t
X  "*Non-nil value causes VM to use char stuffing as described in RFC 934
Xwhen packaging a message to be forwarded.  This will allow the recipient
Xto use a standard bursting agent on the message and act upon it as if it
Xwere sent directly.")
X
X(defvar vm-inhibit-startup-message nil
X  "*Non-nil causes VM not to display its copyright notice, disclaimers
Xetc. when started in the usual way.")
X
X(defvar mail-yank-hooks nil
X  "*List of hooks functions called after yanking a message into a *mail*
Xbuffer.")
X
X(defvar vm-mode-map nil
X  "Keymap for VM mode and VM Summary mode.")
X
X(defconst vm-version "4.37"
X  "Version number of VM.")
X
X;; internal vars
X(defvar vm-message-list nil)
X(make-variable-buffer-local 'vm-message-list)
X(defvar vm-message-pointer nil)
X(make-variable-buffer-local 'vm-message-pointer)
X(defvar vm-last-message-pointer nil)
X(make-variable-buffer-local 'vm-last-message-pointer)
X(defvar vm-primary-inbox-p nil)
X(make-variable-buffer-local 'vm-primary-inbox-p)
X(defvar vm-visible-header-alist nil)
X(make-variable-buffer-local 'vm-visible-header-alist)
X(defvar vm-mail-buffer nil)
X(make-variable-buffer-local 'vm-mail-buffer)
X(defvar vm-summary-buffer nil)
X(make-variable-buffer-local 'vm-summary-buffer)
X(defvar vm-system-state nil)
X(make-variable-buffer-local 'vm-system-state)
X(defvar vm-undo-record-list nil)
X(make-variable-buffer-local 'vm-undo-record-list)
X(defvar vm-undo-record-pointer nil)
X(make-variable-buffer-local 'vm-undo-record-pointer)
X(defvar vm-messages-needing-display-update nil)
X(make-variable-buffer-local 'vm-messages-needing-display-update)
X(defvar vm-current-grouping nil)
X(make-variable-buffer-local 'vm-current-grouping)
X(defvar vm-last-save-folder nil)
X(make-variable-buffer-local 'vm-last-save-folder)
X(defvar vm-last-pipe-command nil)
X(make-variable-buffer-local 'vm-last-pipe-command)
X(defvar vm-messages-not-on-disk 0)
X(make-variable-buffer-local 'vm-messages-not-on-disk)
X(defvar vm-inhibit-write-file-hook nil)
X(defvar vm-session-beginning t)
X(defconst vm-spool-directory
X  (or (and (boundp 'rmail-spool-directory) rmail-spool-directory)
X      "/usr/spool/mail"))
X(defconst vm-attributes-header-regexp
X  "^X-VM-Attributes:\\(.*\n\\([ \t]+.*\n\\)*\\)")
X(defconst vm-attributes-header "X-VM-Attributes:")
X(defconst vm-berkeley-mail-status-header "Status: ")
X(defconst vm-berkeley-mail-status-header-regexp "^Status: ..?\n")
X(defconst vm-generic-header-regexp "^[^:\n]+:\\(.*\n\\([ \t]+.*\n\\)*\\)")
X(defconst vm-header-regexp-format "^%s:[ \t]*\\(.*\\(\n[ \t]+.*\\)*\\)")
X(defconst vm-supported-groupings-alist
X  '(("arrival-time") ("subject") ("author") ("date-sent")))
X(defconst vm-total-count 0)
X(defconst vm-new-count 0)
X(defconst vm-unread-count 0)
X;; for the mode line
X(defvar vm-ml-message-number nil)
X(make-variable-buffer-local 'vm-ml-message-number)
X(defvar vm-ml-highest-message-number nil)
X(make-variable-buffer-local 'vm-ml-highest-message-number)
X(defvar vm-ml-attributes-string nil)
X(make-variable-buffer-local 'vm-ml-attributes-string)
X
X;; general purpose macros and functions
X(defmacro vm-marker (pos &optional buffer)
X  (list 'set-marker '(make-marker) pos buffer))
X
X(defmacro vm-increment (variable)
X  (list 'setq variable (list '1+ variable)))
X
X(defmacro vm-decrement (variable)
X  (list 'setq variable (list '1- variable)))
X
X(defun vm-abs (n) (if (< n 0) (- n) n))
X
X;; save-restriction flubs restoring the clipping region if you
X;; (widen) and modify text outside the old region.
X;; This should do it right.
X(defmacro vm-save-restriction (&rest forms)
X  (let ((vm-sr-clip (make-symbol "vm-sr-clip"))
X	(vm-sr-min (make-symbol "vm-sr-min"))
X	(vm-sr-max (make-symbol "vm-sr-max")))
X    (list 'let (list (list vm-sr-clip '(> (buffer-size)
X					  (- (point-max) (point-min)))))
X	  (list 'and vm-sr-clip
X		(list 'setq vm-sr-min '(set-marker (make-marker) (point-min)))
X		(list 'setq vm-sr-max '(set-marker (make-marker) (point-max))))
X	  (list 'unwind-protect (cons 'progn forms)
X		'(widen)
X		(list 'and vm-sr-clip
X		      (list 'progn
X			    (list 'narrow-to-region vm-sr-min vm-sr-max)
X			    (list 'set-marker vm-sr-min nil)
X			    (list 'set-marker vm-sr-max nil)))))))
X
X;; macros and functions dealing with accessing messages struct fields
X(defun vm-make-message () (make-vector 20 nil))
X
X;; where message begins (From_ line)
X(defmacro vm-start-of (message) (list 'aref message 0))
X;; where visible headers start
X(defun vm-vheaders-of (message)
X  (or (aref message 1)
X      (progn (vm-reorder-message-headers message)
X	     (aref message 1))))
X;; where text section starts
X(defmacro vm-text-of (message) (list 'aref message 2))
X;; where message ends
X(defmacro vm-end-of (message) (list 'aref message 3))
X;; message number
X(defmacro vm-number-of (message) (list 'aref message 4))
X;; message attribute vector
X(defmacro vm-attributes-of (message) (list 'aref message 5))
X(defmacro vm-new-flag (message) (list 'aref (list 'aref message 5) 0))
X(defmacro vm-unread-flag (message) (list 'aref (list 'aref message 5) 1))
X(defmacro vm-deleted-flag (message) (list 'aref (list 'aref message 5) 2))
X(defmacro vm-filed-flag (message) (list 'aref (list 'aref message 5) 3))
X(defmacro vm-replied-flag (message) (list 'aref (list 'aref message 5) 4))
X;; message size in bytes (as a string)
X(defmacro vm-byte-count-of (message) (list 'aref message 6))
X;; weekday sent
X(defmacro vm-weekday-of (message) (list 'aref message 7))
X;; month day
X(defmacro vm-monthday-of (message) (list 'aref message 8))
X;; month sent
X(defmacro vm-month-of (message) (list 'aref message 9))
X;; year sent
X(defmacro vm-year-of (message) (list 'aref message 10))
X;; hour sent
X(defmacro vm-hour-of (message) (list 'aref message 11))
X;; timezone
X(defmacro vm-zone-of (message) (list 'aref message 12))
X;; message author's full name (Full-Name: or gouged from From:)
X(defmacro vm-full-name-of (message) (list 'aref message 13))
X;; message author address (gouged from From:)
X(defmacro vm-from-of (message) (list 'aref message 14))
X;; message ID (Message-Id:)
X(defmacro vm-message-id-of (message) (list 'aref message 15))
X;; number of lines in message (as a string)
X(defmacro vm-line-count-of (message) (list 'aref message 16))
X;; message subject (Subject:)
X(defmacro vm-subject-of (message) (list 'aref message 17))
X(defmacro vm-su-start-of (message) (list 'aref message 18))
X(defmacro vm-su-end-of (message) (list 'aref message 19))
X
X(defmacro vm-set-start-of (message start) (list 'aset message 0 start))
X(defmacro vm-set-vheaders-of (message vh) (list 'aset message 1 vh))
X(defmacro vm-set-text-of (message text) (list 'aset message 2 text))
X(defmacro vm-set-end-of (message end) (list 'aset message 3 end))
X(defmacro vm-set-number-of (message n) (list 'aset message 4 n))
X(defmacro vm-set-attributes-of (message attrs) (list 'aset message 5 attrs))
X(defmacro vm-set-byte-count-of (message count) (list 'aset message 6 count))
X(defmacro vm-set-weekday-of (message val) (list 'aset message 7 val))
X(defmacro vm-set-monthday-of (message val) (list 'aset message 8 val))
X(defmacro vm-set-month-of (message val) (list 'aset message 9 val))
X(defmacro vm-set-year-of (message val) (list 'aset message 10 val))
X(defmacro vm-set-hour-of (message val) (list 'aset message 11 val))
X(defmacro vm-set-zone-of (message val) (list 'aset message 12 val))
X(defmacro vm-set-full-name-of (message author) (list 'aset message 13 author))
X(defmacro vm-set-from-of (message author) (list 'aset message 14 author))
X(defmacro vm-set-message-id-of (message id) (list 'aset message 15 id))
X(defmacro vm-set-line-count-of (message count) (list 'aset message 16 count))
SHAR_EOF
echo "End of part 2"
echo "File vm.el is continued in part 3"
echo "3" > s2_seq_.tmp
exit 0

kjones@UUNET.UU.NET (Kyle Jones) (07/13/89)

#!/bin/sh
# this is part 3 of a multipart archive
# do not concatenate these parts, unpack them in order with /bin/sh
# file vm.el continued
#
CurArch=3
if test ! -r s2_seq_.tmp
then echo "Please unpack part 1 first!"
     exit 1; fi
( read Scheck
  if test "$Scheck" != $CurArch
  then echo "Please unpack part $Scheck next!"
       exit 1;
  else exit 0; fi
) < s2_seq_.tmp || exit 1
echo "x - Continuing file vm.el"
sed 's/^X//' << 'SHAR_EOF' >> vm.el
X(defmacro vm-set-subject-of (message subject) (list 'aset message 17 subject))
X(defmacro vm-set-su-start-of (message start) (list 'aset message 18 start))
X(defmacro vm-set-su-end-of (message end) (list 'aset message 19 end))
X
X(defun vm-text-end-of (message)
X  (- (vm-end-of message)
X     (cond ((eq vm-folder-type 'mmdf) 5)
X	   (t 1))))
X
X;; The remaining routines in this group are part of the undo system.
X
X;; init
X(if vm-mode-map
X    ()
X  (setq vm-mode-map (make-keymap))
X  (suppress-keymap vm-mode-map)
X  (define-key vm-mode-map "h" 'vm-summarize)
X  (define-key vm-mode-map "\M-n" 'vm-next-unread-message)
X  (define-key vm-mode-map "\M-p" 'vm-previous-unread-message)
X  (define-key vm-mode-map "n" 'vm-next-message)
X  (define-key vm-mode-map "p" 'vm-previous-message)
X  (define-key vm-mode-map "N" 'vm-Next-message)
X  (define-key vm-mode-map "P" 'vm-Previous-message)
X  (define-key vm-mode-map "\t" 'vm-goto-message-last-seen)
X  (define-key vm-mode-map "\r" 'vm-goto-message)
X  (define-key vm-mode-map "t" 'vm-expose-hidden-headers)
X  (define-key vm-mode-map " " 'vm-scroll-forward)
X  (define-key vm-mode-map "b" 'vm-scroll-backward)
X  (define-key vm-mode-map "\C-?" 'vm-scroll-backward)
X  (define-key vm-mode-map "d" 'vm-delete-message)
X  (define-key vm-mode-map "u" 'vm-undelete-message)
X  (define-key vm-mode-map "k" 'vm-kill-subject)
X  (define-key vm-mode-map "f" 'vm-followup)
X  (define-key vm-mode-map "F" 'vm-followup-include-text)
X  (define-key vm-mode-map "r" 'vm-reply)
X  (define-key vm-mode-map "R" 'vm-reply-include-text)
X  (define-key vm-mode-map "z" 'vm-forward-message)
X  (define-key vm-mode-map "@" 'vm-send-digest)
X  (define-key vm-mode-map "*" 'vm-burst-digest)
X  (define-key vm-mode-map "m" 'vm-mail)
X  (define-key vm-mode-map "g" 'vm-get-new-mail)
X  (define-key vm-mode-map "G" 'vm-group-messages)
X  (define-key vm-mode-map "v" 'vm-visit-folder)
X  (define-key vm-mode-map "s" 'vm-save-message)
X  (define-key vm-mode-map "w" 'vm-save-message-sans-headers)
X  (define-key vm-mode-map "A" 'vm-auto-archive-messages)
X  (define-key vm-mode-map "S" 'vm-save-folder)
X  (define-key vm-mode-map "|" 'vm-pipe-message-to-command)
X  (define-key vm-mode-map "#" 'vm-expunge-folder)
X  (define-key vm-mode-map "q" 'vm-quit)
X  (define-key vm-mode-map "x" 'vm-quit-no-change)
X  (define-key vm-mode-map "?" 'vm-help)
X  (define-key vm-mode-map "\C-_" 'vm-undo)
X  (define-key vm-mode-map "\C-xu" 'vm-undo)
X  (define-key vm-mode-map "!" 'shell-command)
X  (define-key vm-mode-map "<" 'beginning-of-buffer)
X  (define-key vm-mode-map ">" 'vm-end-of-message)
X  (define-key vm-mode-map "\M-s" 'vm-isearch-forward)
X  (define-key vm-mode-map "=" 'vm-summarize)
X  (define-key vm-mode-map "\M-c" 'vm-show-copying-restrictions)
X  (define-key vm-mode-map "\M-w" 'vm-show-no-warranty)
X  (define-key vm-mode-map "\M-C" 'vm-show-copying-restrictions)
X  (define-key vm-mode-map "\M-W" 'vm-show-no-warranty))
X
X(defun vm-mark-for-display-update (message)
X  (if (not (memq message vm-messages-needing-display-update))
X      (setq vm-messages-needing-display-update
X	    (cons message vm-messages-needing-display-update))))
X
X(defun vm-last (list) (while (cdr-safe list) (setq list (cdr list))) list)
X
X(put 'folder-empty 'error-conditions '(folder-empty error))
X(put 'folder-empty 'error-message "Folder is empty")
X
X(defun vm-error-if-folder-empty ()
X  (while (null vm-message-list)
X    (signal 'folder-empty nil)))
X
X(defun vm-proportion-windows ()
X  (if vm-mail-buffer
X      (set-buffer vm-mail-buffer))
X  (if (not (one-window-p t))
X      (let ((mail-w (get-buffer-window (current-buffer)))
X	    (n (- (window-height (get-buffer-window (current-buffer)))
X		  (/ (* vm-mail-window-percentage
X			(- (screen-height)
X			   (window-height (minibuffer-window))))
X		     100)))
X	    (old-w (selected-window)))
X	(if mail-w
X	    (save-excursion
X	      (select-window mail-w)
X	      (shrink-window n)
X	      (select-window old-w))))))
X
X(defun vm-number-messages ()
X  (let ((n 1) (message-list vm-message-list))
X    (while message-list
X      (vm-set-number-of (car message-list) (int-to-string n))
X      (setq n (1+ n) message-list (cdr message-list)))
X    (setq vm-ml-highest-message-number (int-to-string (1- n)))))
X
X(defun vm-match-visible-header (alist)
X  (catch 'match
X    (while alist
X      (if (looking-at (car (car alist)))
X	  (throw 'match (car alist)))
X      (setq alist (cdr alist)))
X    nil))
X
X(defun vm-delete-header ()
X  (if (looking-at vm-generic-header-regexp)
X      (delete-region (match-beginning 0) (match-end 0))))
X
X;; Build a chain of message structures.
X;; Find the start and end of each message and fill end the relevant
X;; fields in the message structures.
X
X(defun vm-build-message-list ()
X  (save-excursion
X    (vm-build-visible-header-alist)
X    (let (tail-cons message prev-message case-fold-search marker
X	  start-regexp sep-pattern trailer-length)
X      (if (eq vm-folder-type 'mmdf)
X	  (setq start-regexp "^\001\001\001\001\n"
X		separator-string "\n\001\001\001\001\n\001\001\001\001"
X		trailer-length 6)
X	(setq start-regexp "^From "
X	      separator-string "\n\nFrom "
X	      trailer-length 2))
X      (if vm-message-list
X	  (let ((mp vm-message-list)
X		(end (point-min)))
X	    (while mp
X	      (if (< end (vm-end-of (car mp)))
X		  (setq end (vm-end-of (car mp))))
X	      (setq mp (cdr mp)))
X	    ;; move back past trailer so separator-string will match below
X	    (goto-char (- end trailer-length))
X	    (setq tail-cons (vm-last vm-message-list)))
X	(goto-char (point-min))
X	(if (looking-at start-regexp)
X	    (progn
X	      (setq message (vm-make-message) prev-message message)
X	      (vm-set-start-of message (vm-marker (match-beginning 0)))
X	      (setq vm-message-list (list message)
X		    tail-cons vm-message-list))))
X      (while (search-forward separator-string nil t)
X	(setq marker (vm-marker (+ trailer-length (match-beginning 0)))
X	      message (vm-make-message))
X	(vm-set-start-of message marker)
X	(if prev-message
X	    (vm-set-end-of prev-message marker))
X	(if tail-cons
X	    (progn
X	      (setcdr tail-cons (list message))
X	      (setq tail-cons (cdr tail-cons)
X		    prev-message message))
X	  (setq vm-message-list (list message)
X		tail-cons vm-message-list
X		prev-message message)))
X      (if prev-message
X	  (vm-set-end-of prev-message (vm-marker (point-max)))))))
X
X(defun vm-build-visible-header-alist ()
X  (let ((header-alist (cons nil nil))
X	(vheaders vm-visible-headers)
X	list)
X    (setq list header-alist)
X    (while vheaders
X      (setcdr list (cons (cons (car vheaders) nil) nil))
X      (setq list (cdr list) vheaders (cdr vheaders)))
X    (setq vm-visible-header-alist (cdr header-alist))))
X
X;; Group the headers that the user wants to see at the end of the headers
X;; section so we can narrow to them.  The vheaders field of the
X;; message struct is set.  This function is called on demand whenever
X;; a vheaders field is discovered to be nil for a particular message.
X
X(defun vm-reorder-message-headers (message)
X  (save-excursion
X    (vm-save-restriction
X     (let ((header-alist vm-visible-header-alist)
X	   list buffer-read-only match-end-0
X	   (inhibit-quit t)
X	   (old-buffer-modified-p (buffer-modified-p)))
X       (goto-char (vm-start-of message))
X       (forward-line)
X       (while (and (not (= (following-char) ?\n))
X		   (looking-at vm-generic-header-regexp))
X	 (setq match-end-0 (match-end 0)
X	       list (vm-match-visible-header header-alist))
X	 (if (null list)
X	     (goto-char match-end-0)
X	   (if (cdr list)
X	       (setcdr list 
X		       (concat
X			(cdr list)
X			(buffer-substring (point) match-end-0)))
X	     (setcdr list (buffer-substring (point) match-end-0)))
X	   (delete-region (point) match-end-0)))
X       (vm-set-vheaders-of message (point-marker))
X       (setq list header-alist)
X       (while list
X	 (if (cdr (car list))
X	     (progn
X	       (insert (cdr (car list)))
X	       (setcdr (car list) nil)))
X	 (setq list (cdr list)))
X       (set-buffer-modified-p old-buffer-modified-p)))))
X
X;; Read the attribute headers from the messages and store their contents
X;; in attributes fields of the message structures.  If a message has no
X;; attributes header assume it is new.  If a message already has
X;; attributes don't bother checking the headers.
X;;
X;; Stores the position where the message text begins in the message struct.
X
X(defun vm-read-attributes ()
X  (save-excursion
X    (let ((mp vm-message-list))
X      (setq vm-new-count 0
X	    vm-unread-count 0
X	    vm-total-count 0)
X      (while mp
X	(vm-increment vm-total-count)
X	(if (vm-attributes-of (car mp))
X	    ()
X	  (goto-char (vm-start-of (car mp)))
X	  (search-forward "\n\n" (vm-text-end-of (car mp)) 0)
X	  (vm-set-text-of (car mp) (point-marker))
X	  (goto-char (vm-start-of (car mp)))
X	  (cond ((re-search-forward vm-attributes-header-regexp
X				    (vm-text-of (car mp)) t)
X		 (goto-char (match-beginning 1))
X		 (vm-set-attributes-of (car mp)
X				       (condition-case ()
X					   (read (current-buffer))
X					 (error (vector t nil nil nil nil))))
X		 ;; If attributes are unrecogniable just assume the
X		 ;; message is new.
X		 (cond ((or (not (vectorp (vm-attributes-of (car mp))))
X			    (not (= (length (vm-attributes-of (car mp)))
X				    5)))
X			(vm-set-attributes-of (car mp)
X					      (vector t nil nil nil nil)))))
X		((and vm-berkeley-mail-compatibility
X		      (re-search-forward vm-berkeley-mail-status-header-regexp
X					 (vm-text-of (car mp)) t))
X		 (vm-set-attributes-of (car mp) (vector nil (looking-at "R")
X							nil nil nil)))
X		(t
X		 (vm-set-attributes-of (car mp) (vector t nil nil nil nil)))))
X	(cond ((vm-deleted-flag (car mp))) ; don't count deleted messages
X	      ((vm-new-flag (car mp))
X	       (vm-increment vm-new-count))
X	      ((vm-unread-flag (car mp))
X	       (vm-increment vm-unread-count)))
X	(setq mp (cdr mp))))))
X
X;; Stuff the messages attributes back into the messages as headers.
X(defun vm-stuff-attributes ()
X  (save-excursion
X    (vm-save-restriction
X     (widen)
X     (let ((mp vm-message-list) attributes buffer-read-only
X	   (old-buffer-modified-p (buffer-modified-p)))
X       (while mp
X	 (setq attributes (vm-attributes-of (car mp)))
X	 (goto-char (vm-start-of (car mp)))
X	 (if (re-search-forward vm-attributes-header-regexp
X				(vm-text-of (car mp)) t)
X	     (delete-region (match-beginning 0) (match-end 0)))
X	 (cond (vm-berkeley-mail-compatibility
X		(goto-char (vm-start-of (car mp)))
X		(if (re-search-forward vm-berkeley-mail-status-header-regexp
X				       (vm-text-of (car mp)) t)
X		    (delete-region (match-beginning 0) (match-end 0)))
X		(cond ((not (vm-new-flag (car mp)))
X		       (goto-char (vm-start-of (car mp)))
X		       (forward-line)
X		       (insert-before-markers
X			vm-berkeley-mail-status-header
X			(if (vm-unread-flag (car mp)) "" "R")
X			"O\n")))))
X	 (goto-char (vm-start-of (car mp)))
X	 (forward-line)
X	 (insert-before-markers vm-attributes-header " "
X				(prin1-to-string attributes) "\n")
X	 (setq mp (cdr mp)))
X       (set-buffer-modified-p old-buffer-modified-p)))))
X	  
X;; Remove any message marked for deletion from the buffer and the
X;; message list.
X(defun vm-gobble-deleted-messages ()
X  (save-excursion
X    (vm-save-restriction
X     (widen)
X     (let ((mp vm-message-list) prev buffer-read-only did-gobble)
X       (while mp
X	 (if (not (vm-deleted-flag (car mp)))
X	     (setq prev mp)
X	   (setq did-gobble t)
X	   (delete-region (vm-start-of (car mp))
X			  (vm-end-of (car mp)))
X	   (if (null prev)
X	       (setq vm-message-list (cdr vm-message-list))
X	     (setcdr prev (cdr mp))))
X	 (setq mp (cdr mp)))
X       (if did-gobble
X	   (progn
X	     (vm-clear-expunge-invalidated-undos)
X	     (if (null vm-message-list)
X		 (setq overlay-arrow-position nil))
X	     (cond ((and vm-last-message-pointer
X			 (vm-deleted-flag (car vm-last-message-pointer)))
X		    (setq vm-last-message-pointer nil)))
X	     (cond ((and vm-message-pointer
X			 (vm-deleted-flag (car vm-message-pointer)))
X		    (setq vm-system-state nil)
X		    (setq mp (cdr vm-message-pointer))
X		    (while (and mp (vm-deleted-flag (car mp)))
X		      (setq mp (cdr mp)))
X		    (setq vm-message-pointer
X			  (or mp (vm-last vm-message-list)))))
X	     did-gobble ))))))
X
X(defun vm-change-all-new-to-unread ()
X  (let ((mp vm-message-list))
X    (while mp
X      (if (vm-new-flag (car mp))
X	  (progn
X	    (vm-set-new-flag (car mp) nil)
X	    (vm-set-unread-flag (car mp) t)))
X      (setq mp (cdr mp)))))
X
X(defun vm-update-summary-and-mode-line ()
X  (setq vm-ml-message-number (vm-number-of (car vm-message-pointer)))
X  (cond ((vm-new-flag (car vm-message-pointer))
X	 (setq vm-ml-attributes-string "new"))
X	((vm-unread-flag (car vm-message-pointer))
X	 (setq vm-ml-attributes-string "unread"))
X	(t (setq vm-ml-attributes-string "read")))
X  (cond ((vm-filed-flag (car vm-message-pointer))
X	 (setq vm-ml-attributes-string
X	       (concat vm-ml-attributes-string " filed"))))
X  (cond ((vm-replied-flag (car vm-message-pointer))
X	 (setq vm-ml-attributes-string
X	       (concat vm-ml-attributes-string " replied"))))
X  (cond ((vm-deleted-flag (car vm-message-pointer))
X	 (setq vm-ml-attributes-string
X	       (concat vm-ml-attributes-string " deleted"))))
X  (while vm-messages-needing-display-update
X    (vm-update-message-summary vm-messages-needing-display-update)
X    (setq vm-messages-needing-display-update
X	  (cdr vm-messages-needing-display-update)))
X  (save-excursion
X    (set-buffer (other-buffer))
X    (set-buffer-modified-p (buffer-modified-p))))
X
X(defun vm-goto-message (n)
X  "Go to the message numbered N.
XInteractively N is the prefix argument.  If no prefix arg is provided
XN is prompted for in the minibuffer."
X  (interactive "NGo to message: ")
X  (if vm-mail-buffer
X      (set-buffer vm-mail-buffer))
X  (vm-error-if-folder-empty)
X  (let ((cons (nthcdr (1- n) vm-message-list)))
X    (if (null cons)
X	(error "No such message."))
X    (if (eq vm-message-pointer cons)
X	(vm-preview-current-message)
X      (setq vm-last-message-pointer vm-message-pointer
X	    vm-message-pointer cons)
X      (vm-set-summary-pointer (car vm-message-pointer))
X      (vm-preview-current-message))))
X
X(defun vm-goto-message-last-seen ()
X  "Go to the message last previewed."
X  (interactive)
X  (if vm-mail-buffer
X      (set-buffer vm-mail-buffer))
X  (vm-error-if-folder-empty)
X  (if vm-last-message-pointer
X      (let (tmp)
X	(setq tmp vm-message-pointer
X	      vm-message-pointer vm-last-message-pointer
X	      vm-last-message-pointer tmp)
X	(vm-set-summary-pointer (car vm-message-pointer))
X	(vm-preview-current-message))))
X
X(put 'beginning-of-folder 'error-conditions '(beginning-of-folder error))
X(put 'beginning-of-folder 'error-message "Beginning of folder")
X(put 'end-of-folder 'error-conditions '(end-of-folder error))
X(put 'end-of-folder 'error-message "End of folder")
X
X(defun vm-check-count (count)
X  (if (>= count 0)
X      (if (< (length vm-message-pointer) count)
X	  (signal 'end-of-folder nil))
X    (if (< (1+ (- (length vm-message-list) (length vm-message-pointer)))
X	   (vm-abs count))
X	(signal 'beginning-of-folder nil))))
X
X(defun vm-move-message-pointer (direction)
X  (let ((mp vm-message-pointer))
X    (if (eq direction 'forward)
X	(progn
X	  (setq mp (cdr mp))
X	  (if (null mp)
X	      (if vm-circular-folders
X		  (setq mp vm-message-list)
X		(signal 'end-of-folder nil))))
X      (if (eq mp vm-message-list)
X	  (if vm-circular-folders
X	      (setq mp (vm-last vm-message-list))
X	    (signal 'beginning-of-folder nil))
X	(setq mp (let ((curr vm-message-list))
X		   (while (not (eq (cdr curr) mp))
X		     (setq curr (cdr curr)))
X		   curr))))
X    (setq vm-message-pointer mp)))
X
X(defun vm-should-skip-message (mp)
X  (or (and vm-skip-deleted-messages
X	   (vm-deleted-flag (car mp)))
X      (and vm-skip-read-messages
X	   (or (vm-deleted-flag (car mp))
X	       (not (or (vm-new-flag (car mp))
X			(vm-unread-flag (car mp))))))))
X
X(defun vm-next-message (&optional count retry)
X  "Go forward one message and preview it.
XWith prefix arg COUNT, go forward COUNT messages.  A negative COUNT
Xmeans go backward.  If the absolute value of COUNT > 1 the values of the
Xvariables vm-skip-deleted-messages and vm-skip-read-messages are
Xignored."
X  (interactive "p\np")
X  (if vm-mail-buffer
X      (set-buffer vm-mail-buffer))
X  (vm-error-if-folder-empty)
X  (or count (setq count 1))
X  (let ((oldmp vm-message-pointer)
X	(error)
X	(direction (if (> count 0) 'forward 'backward))
X	(count (vm-abs count)))
X    (cond
X     ((null vm-message-pointer)
X      (setq vm-message-pointer vm-message-list))
X     ((/= count 1)
X      (condition-case ()
X	  (while (not (zerop count))
X	    (vm-move-message-pointer direction)
X	    (vm-decrement count))
X	(beginning-of-folder (setq error 'beginning-of-folder))
X	(end-of-folder (setq error 'end-of-folder))))
X     (t
X      (condition-case ()
X	  (progn
X	    (vm-move-message-pointer direction)
X	    (while (and (not (eq oldmp vm-message-pointer))
X			(vm-should-skip-message vm-message-pointer))
X	      (vm-move-message-pointer direction))
X	    ;; Retry the move if we've gone a complete circle and we should
X	    ;; skip the current message and there are other messages.
X	    (and (eq vm-message-pointer oldmp) retry (cdr vm-message-list)
X		 (vm-should-skip-message vm-message-pointer)
X		 (vm-move-message-pointer direction)))
X	(beginning-of-folder
X	 (setq vm-message-pointer oldmp)
X	 (if retry
X	     (vm-move-message-pointer direction)
X	   (setq error 'beginning-of-folder)))
X	(end-of-folder
X	 (setq vm-message-pointer oldmp)
X	 (if retry
X	     (vm-move-message-pointer direction)
X	   (setq error 'end-of-folder))))))
X    (if (not (eq vm-message-pointer oldmp))
X	(progn
X	  (setq vm-last-message-pointer oldmp)
X	  (vm-set-summary-pointer (car vm-message-pointer))
X	  (vm-preview-current-message)))
X    (if error
X	(signal error nil))))
X
X(defun vm-previous-message (&optional count retry)
X  "Go back one message and preview it.
XWith prefix arg COUNT, go backward COUNT messages.  A negative COUNT
Xmeans go forward.  If the absolute value of COUNT > 1 the values of the
Xvariables vm-skip-deleted-messages and vm-skip-read-messages are
Xignored."
X  (interactive "p\np")
X  (or count (setq count 1))
X  (vm-next-message (- count) retry))
X
X(defun vm-Next-message (&optional count)
X  "Like vm-next-message but will not skip messages."
X  (interactive "p")
X  (let (vm-skip-deleted-messages vm-skip-read-messages)
X    (vm-next-message count)))
X
X(defun vm-Previous-message (&optional count)
X  "Like vm-previous-message but will not skip messages."
X  (interactive "p")
X  (let (vm-skip-deleted-messages vm-skip-read-messages)
X    (vm-previous-message count)))
X
X(defun vm-next-unread-message ()
X  "Move forward to the nearest new or unread message, if there is one."
X  (interactive)
X  (condition-case ()
X      (let ((vm-skip-read-messages t)
X	    (oldmp vm-message-pointer))
X	(vm-next-message)
X	;; in case vm-circular-folder is non-nil
X	(and (eq vm-message-pointer oldmp) (signal 'end-of-folder nil)))
X    (end-of-folder (error "No next unread message"))))
X
X(defun vm-previous-unread-message ()
X  "Move backward to the nearest new or unread message, if there is one."
X  (interactive)
X  (condition-case ()
X      (let ((vm-skip-read-messages t)
X	    (oldmp vm-message-pointer))
X	(vm-previous-message)
X	;; in case vm-circular-folder is non-nil
X	(and (eq vm-message-pointer oldmp) (signal 'beginning-of-folder nil)))
X    (beginning-of-folder (error "No previous unread message"))))
X
X(defun vm-preview-current-message ()
X  (setq vm-system-state 'previewing)
X  (widen)
X  (narrow-to-region
X   (vm-vheaders-of (car vm-message-pointer))
X   (if vm-preview-lines
X       (min
X	(vm-text-end-of (car vm-message-pointer))
X	(save-excursion
X	  (goto-char (vm-text-of (car vm-message-pointer)))
X	  (forward-line (if (natnump vm-preview-lines) vm-preview-lines 0))
X	  (point)))
X     (vm-text-of (car vm-message-pointer))))
X  (let ((w (get-buffer-window (current-buffer))))
X    (and w (progn (set-window-start w (point-min))
X		  (set-window-point w (point-max))))
X    (and w vm-highlighted-header-regexp
X	 (progn
X	   (save-restriction
X	     (narrow-to-region (point) (point))
X	     (sit-for 0))
X	   (goto-char (point-min))
X	   (while (re-search-forward vm-highlighted-header-regexp nil t)
X	     (save-restriction
X	       (goto-char (match-beginning 0))
X	       (looking-at vm-generic-header-regexp)
X	       (goto-char (match-beginning 1))
X	       (narrow-to-region (point-min) (point))
X	       (sit-for 0)
X	       (setq inverse-video t)
X	       (widen)
X	       (narrow-to-region (point-min) (match-end 1))
X	       (sit-for 0)
X	       (setq inverse-video nil)
X	       (goto-char (match-end 0)))))))
X  (goto-char (point-max))
X  ;; De Morgan's Theorems could clear away most of the following negations,
X  ;; but the resulting code would be horribly obfuscated.
X  (if (or (null vm-preview-lines)
X	  (and (not vm-preview-read-messages)
X	       (not (vm-new-flag (car vm-message-pointer)))
X	       (not (vm-unread-flag (car vm-message-pointer)))))
X      ;; Don't sit and howl unless the mail buffer is visible.
X      (vm-show-current-message (get-buffer-window (current-buffer)))
X    (vm-update-summary-and-mode-line)))
X
X(defun vm-show-current-message (&optional sit-and-howl)
X  (setq vm-system-state 'reading)
X  (save-excursion
X    (goto-char (point-min))
X    (widen)
X    (narrow-to-region (point) (vm-text-end-of (car vm-message-pointer))))
X  (cond ((vm-new-flag (car vm-message-pointer))
X	 (vm-set-new-flag (car vm-message-pointer) nil))
X	((vm-unread-flag (car vm-message-pointer))
X	 (vm-set-unread-flag (car vm-message-pointer) nil)))
X  (vm-update-summary-and-mode-line)
X  (cond (sit-and-howl
X	 (sit-for 0)
X	 (vm-howl-if-eom-visible))))
X
X(defun vm-expose-hidden-headers ()
X  "Expose headers omitted from vm-visible-headers."
X  (interactive)
X  (vm-follow-summary-cursor)
X  (if vm-mail-buffer
X      (set-buffer vm-mail-buffer))
X  (vm-error-if-folder-empty)
X  (save-excursion
X    (goto-char (point-max))
X    (widen)
X    (narrow-to-region (point) (vm-start-of (car vm-message-pointer)))
X    (let (w)
X      (and (setq w (get-buffer-window (current-buffer)))
X	   (= (window-start w) (vm-vheaders-of (car vm-message-pointer)))
X	   (set-window-start w (vm-start-of (car vm-message-pointer)))))))
X
X(defun vm-howl-if-eom-visible ()
X  (let ((w (get-buffer-window (current-buffer))))
X    (and w (pos-visible-in-window-p (point-max) w)
X	 (message "End of message %s from %s"
X		  (vm-number-of (car vm-message-pointer))
X		  (vm-su-full-name (car vm-message-pointer))))))
X
X;; message-changed is an old-fashoined local variable.
X(defun vm-scroll-forward (&optional arg message-changed)
X  "Scroll forward a screenful of text.
XIf the current message is being previewed, the message body is revealed.
XIf at the end of the current message, move to the next message."
X  (interactive "P")
X  (setq message-changed (vm-follow-summary-cursor))
X  (if vm-mail-buffer
X      (set-buffer vm-mail-buffer))
X  (vm-error-if-folder-empty)
X  (if (null (get-buffer-window (current-buffer)))
X      (progn
X	(if vm-mutable-windows
X	    (let ((pop-up-windows
X		   (and pop-up-windows (eq vm-mutable-windows t))))
X	      (display-buffer (current-buffer)))
X	  (switch-to-buffer (current-buffer)))
X	(if (and vm-summary-buffer (get-buffer-window vm-summary-buffer)
X		 (eq vm-mutable-windows t))
X	    (vm-proportion-windows))
X	(if (eq vm-system-state 'previewing)
X	    (vm-show-current-message t)
X	  (vm-howl-if-eom-visible)))
X    (if (eq vm-system-state 'previewing)
X	(vm-show-current-message t)
X      (if message-changed
X	  (vm-howl-if-eom-visible)
X	(let ((w (get-buffer-window (current-buffer)))
X	      (old-w (selected-window)))
X	  (unwind-protect
X	      (progn
X		(select-window w)
X		(if (not (eq (condition-case () (scroll-up arg)
X			       (end-of-buffer (if (null arg)
X						  (progn
X						    (vm-next-message)
X						    'next-message))))
X			     'next-message))
X		    (vm-howl-if-eom-visible)))
X	    (select-window old-w)))))))
X
X(defun vm-scroll-backward (&optional arg)
X  "Scroll backward a screenful of text."
X  (interactive "P")
X  (vm-follow-summary-cursor)
X  (if vm-mail-buffer
X      (set-buffer vm-mail-buffer))
X  (vm-error-if-folder-empty)
X  (if (null (get-buffer-window (current-buffer)))
X      (progn
X	(if vm-mutable-windows
X	    (let ((pop-up-windows
X		   (and pop-up-windows (eq vm-mutable-windows t))))
X	      (display-buffer (current-buffer)))
X	  (switch-to-buffer (current-buffer)))
X	(if (and vm-summary-buffer (get-buffer-window vm-summary-buffer)
X		 (eq vm-mutable-windows t))
X	    (vm-proportion-windows)))
X    (let ((w (get-buffer-window (current-buffer)))
X	  (old-w (selected-window)))
X      (unwind-protect
X	  (progn
X	    (select-window w)
X	    (scroll-down arg))
X	(select-window old-w)))))
X
X(defun vm-end-of-message ()
X  "Displays the end of the current message, exposing and marking it read
Xas necessary."
X  (interactive)
X  (vm-follow-summary-cursor)
X  (if vm-mail-buffer
X      (set-buffer vm-mail-buffer))
X  (vm-error-if-folder-empty)
X  (if (eq vm-system-state 'previewing)
X      (vm-show-current-message))
X  (goto-char (point-max))
X  (vm-howl-if-eom-visible))
X
X(defun vm-quit-no-change ()
X  "Exit VM without saving changes made to the folder."
X  (interactive)
X  (vm-quit t))
X
X(defun vm-quit (&optional no-change)
X  "Quit VM, saving changes and expunging messages marked for deletion.
XNew messages are changed to unread."
X  (interactive)
X  (if vm-mail-buffer
X      (set-buffer vm-mail-buffer))
X  (and no-change (buffer-modified-p)
X       (not (zerop vm-messages-not-on-disk))
X       ;; Folder may have been saved with C-x C-s and atriutes may have
X       ;; been changed after that; in that case vm-messages-not-on-disk
X       ;; would not have been zeroed.  However, all modification flag
X       ;; undos are cleared if VM actually modifies the folder buffer
X       ;; (as opposed to the folder's attributes), so this can be used
X       ;; to verify that there are indeed unsaved messages.
X       (null (assq 'set-buffer-modified-p vm-undo-record-list))
X       (not (y-or-n-p
X	     (format "%d message%s have not been saved to disk, exit anyway? "
X		     vm-messages-not-on-disk
X		     (if (= 1 vm-messages-not-on-disk) "" "s"))))
X       (error "Aborted"))
X  (let ((inhibit-quit t))
X    (if (not no-change)
X	(vm-change-all-new-to-unread))
X    (if (and (buffer-modified-p) (not no-change))
X	(vm-save-folder t))
X    (let ((summary-buffer vm-summary-buffer)
X	  (mail-buffer (current-buffer)))
X      (if summary-buffer
X	  (progn
X	    (setq overlay-arrow-position nil)
X	    (if (eq vm-mutable-windows t)
X		(delete-windows-on vm-summary-buffer))
X	    (kill-buffer summary-buffer)))
X      (set-buffer mail-buffer)
X      (set-buffer-modified-p nil)
X      (kill-buffer (current-buffer)))
X    ;; Make sure we are now dealing with the buffer and window that
X    ;; would be selected were we to give up control now.
X    (set-buffer (window-buffer (selected-window)))
X    ;; If we land on a buffer that VM knows about
X    ;; do some nice things for the user, if we're allowed.
X    (cond ((and (eq major-mode 'vm-mode) (eq vm-mutable-windows t))
X	   (if (null vm-startup-with-summary)
X	       (delete-other-windows)
X	     (condition-case () (vm-summarize t) (error nil))
X	     (and (not (eq major-mode 'vm-summary-mode))
X		  (eq vm-startup-with-summary t)
X		  (not (one-window-p t))
X		  vm-summary-buffer
X		  (get-buffer-window vm-summary-buffer)
X		  (progn
X		    (select-window (get-buffer-window vm-summary-buffer))
X		    (delete-other-windows)))))
X	  ((eq major-mode 'vm-summary-mode)
X	   (cond ((eq vm-startup-with-summary nil)
X		  (switch-to-buffer vm-mail-buffer)
X		  (and (not (one-window-p t)) (eq vm-mutable-windows t)
X		       (delete-other-windows)))
X		 ((not (eq vm-startup-with-summary t))
X		  (let ((pop-up-windows
X			 (and pop-up-windows (eq vm-mutable-windows t))))
X		    (display-buffer vm-mail-buffer))
X		  (if (eq vm-mutable-windows t)
X		      (if (eq major-mode 'vm-summary-mode)
X			  (vm-proportion-windows)
X			(switch-to-buffer vm-summary-buffer))))
X		 ((eq vm-mutable-windows t)
X		  (delete-other-windows)))))))
X
X;; This allows C-x C-s to do the right thing for VM mail buffers.
X;; Note that deleted messages are not expunged.
X(defun vm-write-file-hook ()
X  (if (not (eq major-mode 'vm-mode))
X      ()
X    (if vm-inhibit-write-file-hook
X	()
X      ;; The vm-save-restriction isn't really necessary here (since
X      ;; vm-stuff-atributes cleans up after itself) but should remain
X      ;; as a safeguard against the time when other stuff is added here.
X      (vm-save-restriction
X       (let ((inhibit-quit t)
X	     (buffer-read-only))
X	 (vm-stuff-attributes)
X	 nil )))))
X
X(defun vm-save-folder (&optional quitting)
X  "Save current folder to disk."
X  (interactive)
X  (if vm-mail-buffer
X      (set-buffer vm-mail-buffer))
X  (if (buffer-modified-p)
X      (let ((inhibit-quit t))
X	;; may get error if folder is emptied by the expunge.
X	(condition-case ()
X	    (vm-expunge-folder quitting t)
X	  (error nil))
X	(vm-stuff-attributes)
X	(let ((vm-inhibit-write-file-hook t))
X	  (save-buffer))
X	(setq vm-messages-not-on-disk 0)
X	(and (zerop (buffer-size)) vm-delete-empty-folders
X	     (condition-case ()
X		 (progn
X		   (delete-file buffer-file-name)
X		   (message "%s removed" buffer-file-name))
X	       (error nil)))
X	(if (not quitting)
X	    (if vm-message-pointer
X		(vm-update-summary-and-mode-line)
X	      (vm-next-message))))))
X
X(defun vm-visit-folder (folder)
X  "Visit a mail file.
XVM will parse and present its messages to you in the usual way."
X  (interactive
X   (list (read-file-name
X	  "Visit folder: " (if vm-folder-directory
X			       (expand-file-name vm-folder-directory)
X			     default-directory) nil t)))
X  (if vm-mail-buffer
X      (set-buffer vm-mail-buffer))
X  (vm folder))
X
X(defun vm-help ()
X  "Display VM command and variable information."
X  (interactive)
X  (if (and vm-mail-buffer (get-buffer-window vm-mail-buffer))
X      (set-buffer vm-mail-buffer))
X  (let ((pop-up-windows (and pop-up-windows (eq vm-mutable-windows t))))
X    (cond
X     ((eq last-command 'vm-help)
X      (describe-mode))
X     ((eq vm-system-state 'previewing)
X      (message "Type SPC to read message, n previews next message   (? gives more help)"))
X     ((eq vm-system-state 'reading)
X      (message "SPC and b scroll, (d)elete, (s)ave, (n)ext, (r)eply   (? gives more help)"))
X     (t (describe-mode)))))
X
X(defun vm-move-mail (source destination)
X  (call-process "movemail" nil nil nil (expand-file-name source)
X		(expand-file-name destination)))
X
X(defun vm-gobble-crash-box ()
X  (save-excursion
X    (vm-save-restriction
X     (widen)
X     (let ((opoint-max (point-max)) crash-buf buffer-read-only
X	   (old-buffer-modified-p (buffer-modified-p)))
X       (setq crash-buf (find-file-noselect vm-crash-box))
X       (goto-char (point-max))
X       (insert-buffer-substring crash-buf
X				1 (1+ (save-excursion
X					(set-buffer crash-buf)
X					(widen)
X					(buffer-size))))
X       (write-region opoint-max (point-max) buffer-file-name t t)
X       (backup-buffer)
X       ;; make sure primary inbox is private.  384 = octal 600
X       (condition-case () (set-file-modes buffer-file-name 384) (error nil))
X       (set-buffer-modified-p old-buffer-modified-p)
X       (kill-buffer crash-buf)
X       (condition-case () (delete-file vm-crash-box)
X	 (error nil))))))
X
X(defun vm-get-spooled-mail ()
X  (let ((spool-files (or vm-spool-files
X			 (list (concat vm-spool-directory (user-login-name)))))
X	(inhibit-quit t)
X	(got-mail))
X    (if (file-exists-p vm-crash-box)
X	(progn
X	  (message "Recovering messages from crash box...")
X	  (vm-gobble-crash-box)
X	  (message "Recovering messages from crash box... done")
X	  (setq got-mail t)))
X    (while spool-files
X      (if (file-readable-p (car spool-files))
X	  (progn
X	    (message "Getting new mail from %s..." (car spool-files))
X	    (vm-move-mail (car spool-files) vm-crash-box)
X	    (vm-gobble-crash-box)
X	    (message "Getting new mail from %s... done" (car spool-files))
X	    (setq got-mail t)))
X      (setq spool-files (cdr spool-files)))
X    got-mail ))
X
X(defun vm-get-new-mail ()
X  "Move any new mail that has arrived in the system mailbox into the
Xprimary inbox.  New mail is appended to the disk and buffer copies of
Xthe primary inbox.
X
XThis command is valid only from the primary inbox buffer."
X  (interactive)
X  (if vm-mail-buffer
X      (set-buffer vm-mail-buffer))
X  (if (not vm-primary-inbox-p)
X      (error "This is not your primary inbox."))
X  (if (not (and (vm-get-spooled-mail) (vm-assimilate-new-messages)))
X      (message "No new mail.")
X    (vm-emit-totals-blurb)
X    ;; If there's a current grouping, then the summary has already
X    ;; been redone in vm-group-messages.
X    (if (and vm-summary-buffer (not vm-current-grouping))
X	(progn
X	  (vm-do-summary)
X	  (vm-emit-totals-blurb)))
X    (vm-thoughtfully-select-message)
X    (if vm-summary-buffer
X	(vm-set-summary-pointer (car vm-message-pointer)))))
X
X(defun vm-emit-totals-blurb ()
X  (message "%d message%s, %d new, %d unread."
X	   vm-total-count (if (= vm-total-count 1) "" "s")
X	   vm-new-count vm-unread-count))
X
X(defun vm-find-first-unread-message ()
X  (let (mp unread-mp)
X    (setq mp vm-message-list)
X    (while mp
X      (if (and (vm-new-flag (car mp)) (not (vm-deleted-flag (car mp))))
X	  (setq unread-mp mp mp nil)
X	(setq mp (cdr mp))))
X    (if (null unread-mp)
X	(progn
X	  (setq mp vm-message-list)
X	  (while mp
X	    (if (and (vm-unread-flag (car mp))
X		     (not (vm-deleted-flag (car mp))))
X		(setq unread-mp mp mp nil)
X	      (setq mp (cdr mp))))))
X    unread-mp))
X
X;; returns non-nil if there were any new messages
X(defun vm-assimilate-new-messages ()
X  (let ((tail-cons (vm-last vm-message-list))
X	(new-messages-p (null vm-message-pointer)))
X    (vm-save-restriction
X     (widen)
X     (vm-build-message-list)
X     (vm-read-attributes)
X     (setq new-messages-p (or new-messages-p (cdr tail-cons)))
X     (if (and vm-current-grouping new-messages-p)
X	 (condition-case data
X	     (vm-group-messages vm-current-grouping)
X	   ;; presumably an unsupported grouping
X	   (error (message (car (cdr data)))
X		  (sleep-for 2)
X		  (vm-number-messages)))
X       (vm-number-messages)))
X    new-messages-p ))
X
X(defun vm-thoughtfully-select-message ()
X  ;; This is called after new messages have been assimilated in a folder.
X  ;; We move to a new message only if the user is not "reading" the current
X  ;; message, or if there is no current message.
X  ;;
X  ;; Most of the complications in the `if' test below are due to the presence
X  ;; of the variables vm-preview-lines and vm-preview-read-messages.
X  ;; These can cause previewing never to be done, or not be done for
X  ;; specific messages.  In these cases VM assumes a user is "reading"
X  ;; an exposed message if the top of the message is not visible in the
X  ;; folder buffer window.
X  (if (or (null vm-message-pointer)
X	  (not (eq vm-system-state 'reading))
X	  (and (or (null vm-preview-lines)
X		   (and (not vm-preview-read-messages)
X			(not (vm-new-flag (car vm-message-pointer)))
X			(not (vm-unread-flag (car vm-message-pointer)))))
X	       (let ((w (get-buffer-window (current-buffer))))
X		 (and w (pos-visible-in-window-p (point-min) w)))))
X      (let ((mp (vm-find-first-unread-message)))
X	(if mp
X	    (progn
X	      (if vm-message-pointer
X		  (setq vm-last-message-pointer vm-message-pointer
X			vm-message-pointer mp)
X		(setq vm-message-pointer mp))
X	      (vm-preview-current-message))
X	  (if (null vm-message-pointer)
X	      (vm-Next-message))))))
X
X(defun vm-display-startup-message ()
X  (if (sit-for 5)
X      (let ((lines
X	     '(
X"You may give out copies of VM.  Type \\[vm-show-copying-restrictions] to see the conditions"
X"VM comes with ABSOLUTELY NO WARRANTY; type \\[vm-show-no-warranty] for full details"
X	       )))
X	(message "VM %s, Copyright (C) 1989 Kyle E. Jones; type ? for help"
X		 vm-version)
X	(while (and (sit-for 4) lines)
X	  (message (substitute-command-keys (car lines)))
X	  (setq lines (cdr lines)))))
X  (message ""))
X
X(defun vm (&optional folder)
X  "Read mail under Emacs.
XOptional first arg FOLDER specifies the folder to visit.  It defaults
Xto the value of vm-primary-inbox.  The folder buffer is put into VM
Xmode, a major mode for reading mail.
X
XVisiting the primary inbox causes any contents of the system mailbox to
Xbe moved and appended to the resulting buffer.
X
XAll the messages can be read by repeatedly pressing SPC.  Messages are
Xmarked for deletion with `d', and saved to a folder with `s'.  Quitting
XVM with `q' expunges messages marked for deletion and saves the buffered
Xfolder to disk.
X
XSee the documentation for vm-mode for more information."
X  (interactive)
X  (if vm-session-beginning
X      (progn
X	(random t)
X	(load "vm-undo")
X	(load "vm-summary")))
X  (if vm-mail-buffer
X      (set-buffer vm-mail-buffer))
X  (let ((mail-buffer (find-file-noselect
X		      (or folder (expand-file-name vm-primary-inbox)))))
X    (set-buffer mail-buffer)
X    (or (buffer-modified-p) (setq vm-messages-not-on-disk 0))
X    (let ((first-time (not (eq major-mode 'vm-mode)))
X	  (inhibit-quit t))
X      (if first-time
X	  (progn
X	    (buffer-flush-undo (current-buffer))
X	    (abbrev-mode 0)
X	    (auto-fill-mode 0)
X	    (vm-mode)))
X      (if (or (and vm-primary-inbox-p (vm-get-spooled-mail)) first-time)
X	  (progn
X	    (vm-assimilate-new-messages)
X	    ;; Can't allow a folder-empty error here because execution
X	    ;; would abort before the session startup code below.
X	    (if (null vm-message-list)
X		(message "Folder is empty.")
X	      (vm-emit-totals-blurb)
X	      ;; If there's a current grouping, then the summary has already
X	      ;; been redone in vm-group-messages.
X	      (if (and vm-summary-buffer (not vm-current-grouping))
X		  (progn
X		    (vm-do-summary)
X		    ;; The summary update messages erased this info
X		    ;; from the echo area.
X		    (vm-emit-totals-blurb)))
X	      (save-window-excursion
X		;; Make sure the mail buffer is not visible.  This is
X		;; needed to insure that if vm-preview-lines is nil, the
X		;; mail window won't be momentarily displayed and then
X		;; disappear behind the summary window, if
X		;; vm-startup-with-summary is t.
X		(if (get-buffer-window mail-buffer)
X		    (if (one-window-p)
X			(switch-to-buffer (other-buffer))
X		      (delete-windows-on mail-buffer)))
X		(set-buffer mail-buffer)
X		(vm-thoughtfully-select-message))
X	      (if vm-summary-buffer
X		  (vm-set-summary-pointer (car vm-message-pointer))))))
X      (switch-to-buffer mail-buffer)
X      (if (and vm-message-list vm-startup-with-summary)
X	  (progn
X	    (vm-summarize t)
X	    (vm-emit-totals-blurb)
X	    (and (eq vm-startup-with-summary t)
X		 (eq vm-mutable-windows t)
X		 (if (eq major-mode 'vm-summary-mode)
X		     (delete-other-windows)
X		   (select-window (get-buffer-window vm-summary-buffer))
X		   (delete-other-windows))))
X	(if (eq vm-mutable-windows t)
X	    (delete-other-windows)))
X      (if vm-session-beginning
X	  (progn
X	    (setq vm-session-beginning nil)
X	    (or vm-inhibit-startup-message folder
X		(vm-display-startup-message))
X	    (if (and vm-message-list (not (input-pending-p)))
X		(vm-emit-totals-blurb)))))))
X
X(defun vm-mode ()
X  "Major mode for reading mail.
X
XCommands:
X   h - summarize folder contents
X
X   n - go to next message
X   p - go to previous message
X   N - like `n' but ignores skip-variable settings
X   P - like `p' but ignores skip-variable settings
X M-n - go to next unread message
X M-p - go to previous unread message
X RET - go to numbered message (uses prefix arg or prompts in minibuffer)
X TAB - go to last message seen
X M-s - incremental search through the folder
X
X   t - display hidden headers
X SPC - scroll forward a page (if at end of message, then display next message)
X   b - scroll backward a page
X   > - go to end of current message
X
X   d - delete current message (mark as deleted)
X   u - undelete
X   k - mark for deletion all messages with same subject as the current message
X
X   r - reply (only to the sender of the message)
X   R - reply with included text for current message
X   f - followup (reply to all recipients of message)
X   F - followup with included text from the current message
X   z - forward the current message
X   m - send a message
X
X   @ - digestify and mail entire folder contents (the folder is not modified)
X   * - burst a digest into indivdual messages, and append and assimilate these
X       message into the current folder.
X
X   G - group messages according to some criteria
X
X   g - get any new mail that has arrived in the system mailbox
X       (new mail is appended to the disk and buffer copies of the
X       primary inbox.)
X   v - visit another mail folder
X
X   s - save current message in a folder (appends if folder already exists)
X   w - write current message to a file without its headers (appends if exists)
X   S - save entire folder to disk, expunging deleted messages
X   A - save unfiled messages to their vm-auto-folder-alist specified folders
X   # - expunge deleted messages (without saving folder)
X   q - quit VM, deleted messages are expunged, folder saved to disk
X   x - exit VM with no change to the folder
X
X C-_ - undo, special undo that retracts the most recent
X             changes in message attributes.  Expunges and saves
X             cannot be undone.
X
X   ? - help
X
X   ! - run a shell command
X   | - run a shell command with the current message as input
X
X M-c - view conditions under which youmay redistribute of VM
X M-w - view the details of VM's lack of a warranty
X
XVariables:
X   vm-auto-folder-alist
X   vm-berkeley-mail-compatibility
X   vm-circular-folders
X   vm-confirm-new-folders
X   vm-crash-box
X   vm-delete-after-saving
X   vm-delete-empty-folders
X   vm-folder-directory
X   vm-folder-type
X   vm-follow-summary-cursor
X   vm-forwarding-subject-format
X   vm-gargle-uucp
X   vm-group-by
X   vm-highlighted-header-regexp
X   vm-in-reply-to-format
X   vm-included-text-attribution-format
X   vm-included-text-prefix
X   vm-inhibit-startup-message
X   vm-mail-window-percentage
X   vm-mode-hooks
X   vm-move-after-deleting
X   vm-mutable-windows
X   vm-preview-lines
X   vm-preview-read-messages
X   vm-primary-inbox
X   vm-rfc934-forwarding
X   vm-search-using-regexps
X   vm-skip-deleted-messages
X   vm-skip-read-messages
X   vm-spool-files
X   vm-startup-with-summary
X   vm-strip-reply-headers
X   vm-summary-format
X   vm-visible-headers
X   vm-visit-when-saving"
X  (widen)
X  (make-local-variable 'require-final-newline)
X  (make-local-variable 'file-precious-flag)
X  (setq
X   buffer-read-only nil
X   case-fold-search t
X   file-precious-flag t
X   major-mode 'vm-mode
X   mode-line-format
X   '("" mode-line-modified mode-line-buffer-identification "   "
X     global-mode-string
X     (vm-message-list
X      ("   %[(" vm-ml-attributes-string ")%]----")
X      ("   %[()%]----"))
X     (-3 . "%p") "-%-")
X   mode-line-buffer-identification
X   '("VM " vm-version ": %b"
X     (vm-message-list
X      ("   " vm-ml-message-number
X       " (of " vm-ml-highest-message-number ")")
X      "  (no messages)"))
X   mode-name "VM"
X   require-final-newline nil
X   vm-current-grouping vm-group-by
X   vm-primary-inbox-p (equal buffer-file-name
X			     (expand-file-name vm-primary-inbox)))
X  (use-local-map vm-mode-map)
X  (run-hooks 'vm-mode-hooks))
X
X(put 'vm-mode 'mode-class 'special)
X
X(autoload 'vm-group-messages "vm-group" nil t)
X
X(autoload 'vm-reply "vm-reply" nil t)
X(autoload 'vm-reply-include-text "vm-reply" nil t)
X(autoload 'vm-followup "vm-reply" nil t)
X(autoload 'vm-followup-include-text "vm-reply" nil t)
X(autoload 'vm-mail "vm-reply" nil t)
X(autoload 'vm-forward-message "vm-reply" nil t)
X(autoload 'vm-send-digest "vm-reply" nil t)
X
X(autoload 'vm-isearch-forward "vm-search" nil t)
X
X(autoload 'vm-burst-digest "vm-digest" nil t)
X(autoload 'vm-rfc934-char-stuff-region "vm-digest")
X(autoload 'vm-digestify-region "vm-digest")
X
X(autoload 'vm-show-no-warranty "vm-license" nil t)
X(autoload 'vm-show-copying-restrictions "vm-license" nil t)
X
X(autoload 'vm-auto-archive-messages "vm-save" nil t)
X(autoload 'vm-save-message "vm-save" nil t)
X(autoload 'vm-save-message-sans-headers "vm-save" nil t)
X(autoload 'vm-pipe-message-to-command "vm-save" nil t)
X
X(autoload 'vm-delete-message "vm-delete" nil t)
X(autoload 'vm-undelete-message "vm-delete" nil t)
X(autoload 'vm-kill-subject "vm-delete" nil t)
X(autoload 'vm-expunge-folder "vm-delete" nil t)
X
X(if (not (memq 'vm-write-file-hook write-file-hooks))
X    (setq write-file-hooks
X	  (cons 'vm-write-file-hook write-file-hooks)))
SHAR_EOF
echo "File vm.el is complete"
chmod 0664 vm.el || echo "restore of vm.el fails"
echo "x - extracting vm.texinfo (Text)"
sed 's/^X//' << 'SHAR_EOF' > vm.texinfo &&
X\input texinfo  @comment -*-Texinfo-*-
X@setfilename vm.info
X@settitle VM User's Manual
X@iftex
X@finalout
X@end iftex
X@c @setchapternewpage odd		% For book style double sided manual.
X@c      @smallbook
X@tex
X\overfullrule=0pt
X%\global\baselineskip 30pt      % For printing in double spaces
X@end tex
X@ifinfo
XThis file documents the VM mail reader.
X
XCopyright (C) 1989 Kyle E. Jones
X
XPermission is granted to make and distribute verbatim copies of
Xthis manual provided the copyright notice and this permission notice
Xare preserved on all copies.
X
X@ignore
XPermission is granted to process this file through Tex and print the
Xresults, provided the printed document carries copying permission
Xnotice identical to this one except for the removal of this paragraph
X(this paragraph not being relevant to the printed manual).
X
X@end ignore
X@end ifinfo
X@c
X@titlepage
X@sp 6
X@center @titlefont{VM User's Manual}
X@sp 4
X@center First Edition, VM Version 4
X@sp 1
X@center June 1989
X@sp 5
X@center Kyle E. Jones
X@center @t{kyle@@cs.odu.edu}
X@page
X@vskip 0pt plus 1filll
XCopyright @copyright{} 1989 Kyle E. Jones
X
XPermission is granted to make and distribute verbatim copies of
Xthis manual provided the copyright notice and this permission notice
Xare preserved on all copies.
X
X@end titlepage
X@page
X@ifinfo
X@node Top, Introduction,, (DIR)
X
XThis manual documents the VM mail reader which runs as a subsystem under
XEmacs.  The manual is divided into the following chapters.
X
X@menu
X* Introduction::	Overview of the VM interface.
X* Starting Up::		What happens when your start VM.
X* Selecting Messages::	How to select messages for reading.
X* Sending Messages::	How to send messages from within VM.
X* Saving Messages::	How to save messages.
X* Deleting Messages::	How to delete, undelete and expunge messages
X* Undoing::		How to undo changes to message attributes.
X* Grouping Messages::	How to make VM present messages
X* Reading Digests::	How to read digests under VM.
X* Summaries::		How to view and customize the summary of a folder.
X* Miscellaneous::	Various customization variables undescribed elsewhere.
X
XIndices:
X
X* Key Index::		Menus of command keys and their references.
X* Command Index::	Menus of commands and their references.
X* Variable Index::	Menus of variables and their references.
X@end menu
X@end ifinfo
X
X@node Introduction, Starting Up, Variable Index, Top
X@unnumbered Introduction
X
XVM (View Mail) is an Emacs subsystem that allows UNIX mail to be read
Xand disposed of within Emacs.  Commands exist to do the normal things
Xexpected of a mail user agent, such as generating replies, saving
Xmessages to folders, deleting messages and so on.  There are other more
Xadvanced commands that do tasks like bursting and creating digests,
Xmessage forwarding, and organizing message presentation according to
Xvarious criteria.
X
XTo invoke VM simply type @kbd{M-x vm}.  VM gathers any mail that has
Xarrived in your system mailbox and appends it to a file known as your
X@dfn{primary inbox}, and visits that file for reading.  @xref{Starting Up}.
XA file visited for reading by VM is called the @dfn{current folder}.@refill
X
X@findex vm-scroll-forward
X@findex vm-scroll-backward
X@kindex SPC
X@kindex b
XIf there are any messages in the primary inbox, VM selects the first new
Xor unread message, and previews it.  @dfn{Previewing} is VM's way of
Xshowing you part of message and allowing you to decide whether you want
Xto read it.  @xref{Previewing}.  By default VM shows you the message's
Xsender, recipient, subject and date headers.  Typing @key{SPC}
X(@code{vm-scroll-forward}) exposes the body of the message and marks the
Xmessage as read.  Subsequent @key{SPC}'s scroll forward through the
Xmessage, @kbd{b} scrolls backward.  When you reach the end of a message,
Xtyping a @key{SPC} or @kbd{n} moves you forward to preview the next
Xmessage.@refill
X
XIf you do not want to read a message that's being previewed, just type
X@kbd{n} and VM will move on to the next message (if there is one).
X@xref{Selecting Messages}.@refill
X
XTo save a message to a mail folder use @kbd{s} (@code{vm-save-message}).
XVM will prompt you for the folder name in the minibuffer.
X@xref{Saving Messages}.@refill
X
XMessages are deleted by typing @kbd{d} (@code{vm-delete-message}) while
Xpreviewing or reading them.  The message is not deleted right away; it
Xis simply marked for deletion.  If you change your mind about deleting a
Xmessage just select it and type @kbd{u} (@code{vm-undelete-message}),
Xand the message will be undeleted.  @xref{Deleting Messages}.  The
Xactual removal of deleted messages from the current folder is called
X@dfn{expunging} and it is accomplished by typing @kbd{#}
X(@code{vm-expunge-folder}).  The message is still present in the on-disk
Xversion of the folder until the folder is saved.@refill
X
XTyping @kbd{h} (@code{vm-summarize}) causes VM to pop up a window
Xcontaining a summary of contents of the current folder.  The summary is
Xpresented one line per message, by message number, listing each message's
Xauthor, date sent, line and byte count, and subject.  Also various
Xletters appear beside the message number to indicate that a message is
Xnew, unread, marked for deletion, etc.  An arrow @samp{->} appears to
Xthe left of the line summarizing the current message.  The summary
Xformat is user configurable, @pxref{Summaries}.@refill
X
X@findex vm-save-folder
X@kindex S
XWhen you are finished reading mail the current folder must be saved, so
Xthat the next time the folder is visited VM will know which messages have
Xbeen already read, replied to and so on.  Typing @kbd{S}
X(@code{vm-save-folder}) expunges all deleted messages and saves the
Xfolder.  @kbd{C-x C-s} saves the folder without expunging deleted
Xmessages but the messages are still marked deleted.  The next time the
Xfolder is visited these messages will still be marked for deletion.@refill
X
X@findex vm-quit
X@findex vm-quit-no-change
X@kindex q
X@kindex x
XTo quit VM you can type @kbd{q} (@code{vm-quit}) or @kbd{x}
X(@code{vm-quit-no-change}).  Typing @kbd{q} expunges and saves the
Xcurrent folder before quitting.  Also, any messages marked new are
Xchanged to be marked unread, before saving.  The @kbd{x} command quits
XVM without expunging, saving or otherwise modifying the current folder.
XQuitting is not required; you can simply switch to another Emacs buffer
Xwhen you've finished reading mail.@refill
X
X@findex vm-get-new-mail
X@kindex g
XAt any time while reading mail in the primary inbox you can type @kbd{g}
X(@code{vm-get-new-mail}) to check to see if new mail has arrived.  If
Xnew mail has arrived it will be merged into the primary inbox.  If you
Xare not in the middle of another message, VM will also jump to the first
Xnew message.
X
X@node Starting Up, Selecting Messages, Introduction, Top
X@chapter Starting Up
X
XThere are two ways to start VM: @kbd{M-x vm} and @kbd{M-x vm-visit-folder}.
X
X@findex vm
X@vindex vm-primary-inbox
X@kbd{M-x vm} causes VM to gather any mail present in your system mailbox
Xand append it to a file known as your @dfn{primary inbox}, creating
Xthis file if necessary.  The default name of this file is
X@file{~/INBOX}, but VM will use whatever file is named by the variable
X@code{vm-primary-inbox}.@refill
X
X@vindex vm-crash-box
XVM transfers the mail from the system mailbox to the primary inbox via a
Xtemporary file known as the @dfn{crash box}.  The variable
X@code{vm-crash-box} names the crash box file.  VM first copies the mail
Xto the crash box, deletes the system mailbox, merges the crash box
Xcontents into the primary inbox, and then deletes the crash box.  If the
Xsystem or Emacs should crash in the midst of this transfer, any message
Xnot present in the primary inbox will be either in the system mailbox or
Xthe crash box.  Some messages may be duplicated but no mail will be
Xlost.@refill
X
XIf the file named by @code{vm-crash-box} already exists when VM is
Xstarted up, VM will merge that with the primary inbox before getting any
Xnew messages from the system mailbox.@refill
X
X@vindex vm-spool-files
XBy default, the location of the system mailbox is determined
Xheuristically by VM based on what type of system you're using.  VM can
Xbe told explicitly where the system mailbox is through the variable
X@code{vm-spool-files}.  The value of this variable should be a list of
Xstrings naming files VM should try when searching for newly arrived
Xmail.  Multiple mailboxes can be specified if you receive mail in
Xmore than one place.@refill
X
X@findex vm-visit-folder
X@kindex v
X@kbd{M-x vm-visit-folder} (@kbd{v} from within VM) allows you to visit
Xsome other mail folder than the primary inbox.  The folder name will be
Xprompted for in the minibuffer.@refill
X
XOnce VM has read the folder, the first new or unread message will be
Xselected.  If there is no such message, the first message in the folder
Xis selected.
X
X@vindex vm-startup-with-summary
XThe variable @code{vm-startup-with-summary} controls whether VM
Xautomatically displays a summary of the folder's contents at startup.  A
SHAR_EOF
echo "End of part 3"
echo "File vm.texinfo is continued in part 4"
echo "4" > s2_seq_.tmp
exit 0

kjones@UUNET.UU.NET (Kyle Jones) (07/13/89)

#!/bin/sh
# this is part 4 of a multipart archive
# do not concatenate these parts, unpack them in order with /bin/sh
# file vm.texinfo continued
#
CurArch=4
if test ! -r s2_seq_.tmp
then echo "Please unpack part 1 first!"
     exit 1; fi
( read Scheck
  if test "$Scheck" != $CurArch
  then echo "Please unpack part $Scheck next!"
       exit 1;
  else exit 0; fi
) < s2_seq_.tmp || exit 1
echo "x - Continuing file vm.texinfo"
sed 's/^X//' << 'SHAR_EOF' >> vm.texinfo
Xvalue of @code{nil} gives no summary; a value of @code{t} gives a full
Xscreen summary.  A value that is neither @code{t} nor @code{nil} splits
Xthe screen between the summary and the folder display.  The latter only
Xworks if the variable @code{pop-up-windows}'s value is non-@code{nil},
Xand the value of @code{vm-mutable-windows} is non-@code{nil}.  The
Xdefault value of @code{vm-startup-with-summary} is @code{nil}.@refill
X
X@vindex vm-mail-window-percentage
XThe variable @code{vm-mail-window-percentage} tells VM what percentage of
Xthe screen should be given to the folder display when both it and the
Xfolder summary are being displayed.  Note that Emacs enforces a minimum
Xwindow size limit, so a very high or very low value for this variable
Xmay squeeze out one of the displays entirely.  This variable's default
Xvalue is 75, which works with Emacs' default minimum window size limit,
Xon a 24 line terminal.  Note that the value of @code{vm-mutable-windows}
Xmust be @code{t} or VM will not do window resizing regardless of the
Xvalue of @code{vm-mail-window-percentage}.@refill
X
X@vindex vm-inhibit-startup-message
XA non-@code{nil} value for the variable @code{vm-inhibit-startup-message}
Xdisables the display of the VM's copyright, copying and warranty
Xdisclaimer.  If you must, set this variable in your own @file{.emacs} file;
Xdon't set it globally for everyone.  Users should be told their rights.
XThe startup messages abort at the first keystroke after startup, so they do not
Ximpede mail reading.@refill
X
X@node Selecting Messages, Sending Messages, Starting Up, Top
X@chapter Selecting Messages
X
X@findex vm-next-message
X@findex vm-previous-message
X@kindex n
X@kindex p
X@vindex vm-skip-deleted-messages
X@vindex vm-skip-read-messages
XThe primary commands for selecting messages in VM are @kbd{n}
X(@code{vm-next-message}) and @kbd{p} (@code{vm-previous-message}).
XThese commands move forward and backward through the current folder.
XWhen they go beyond the end or beginning of the folder they wrap to the
Xbeginning and end respectively.  By default these commands skip messages
Xmarked for deletion.  This behavior can be disabled by setting the value
Xof the variable @code{vm-skip-deleted-messages} to @code{nil}.  These
Xcommands can also be made to skip messages that have been read; set
X@code{vm-skip-read-messages} to @code{t} to do this.  If all the messages
Xin the current folder would be skipped (i.e all are read and/or
Xdeleted), @kbd{n} and @kbd{p} simply move to the next message.@refill
X
XThe commands @kbd{n} and @kbd{p} also take prefix arguments that specify
Xthe number of messages to move forward or backward.  If the magnitude of
Xthe prefix argument is greater than 1, no message skipping will be done
Xregardless of the settings of the previously mentioned skip control
Xvariables.@refill
X
X@vindex vm-circular-folders
XThe variable @code{vm-circular-folders} determines whether VM folders
Xwill be considered circular by various commands.  @dfn{Circular} means VM
Xwill wrap from the end of the folder to the start and vice versa when
Xmoving the message pointer, deleting, undeleting or saving messages
Xbefore or after the current message.@refill
X
XA value of @code{t} causes all VM commands to consider folders circular.
XA value of @code{nil} causes all of VM commands to signal an error if
Xthe start or end of the folder would have to be passed to complete the
Xcommand.  For movement commands, this occurs after the message pointer
Xhas been moved as far it can go.  For other commands the error occurs
Xbefore any part of the command has been executed, i.e. no moves, saves,
Xetc. will be done unless they can be done in their entirety.  A value
Xother than @code{nil} or @code{t} causes only VM's movement
Xcommands to consider folders circular.  Saves, deletes and undeletes
Xwill behave as if the value is @code{nil}.  The default value of
X@code{vm-circular-folders} is @code{0}.@refill
X
XOther commands to select messages:
X
X@iftex
X@table @asis
X@end iftex
X@ifinfo
X@table @key
X@end ifinfo
X@findex vm-goto-message
X@kindex RET
X@item RET (@code{vm-goto-message})
XGo to message number @var{n}.  @var{n} is the prefix argument, if
Xprovided, otherwise it is prompted for in the minibuffer.
X@findex vm-goto-message
X@kindex TAB
X@item TAB (@code{vm-goto-message-last-seen})
XGo to message last previewed or read.
X@findex vm-Next-message
X@findex vm-Previous-message
X@kindex N
X@kindex P
X@item N (@code{vm-Next-message})
X@itemx P (@code{vm-Previous-message})
XGo to the next (previous) message, ignoring the settings of the skip
Xcontrol variables.
X@findex vm-next-unread-message
X@findex vm-previous-unread-message
X@kindex M-n
X@kindex M-p
X@item M-n (@code{vm-next-unread-message})
X@itemx M-p (@code{vm-previous-unread-message})
XMove forward (backward) to the nearest new or unread message.  If no
Xsuch message exists then these commands work like @kbd{n} and @kbd{p}.
X@findex vm-isearch-forward
X@kindex M-s
X@vindex vm-search-using-regexps
X@item M-s (@code{vm-isearch-forward})
XThis works just like Emacs' normal incremental search except that when
Xthe search ends, VM selects the message containing point.  If the value
Xof the variable @code{vm-search-using-regexps} is non-@code{nil},
Xa regular expression may be used instead of a fixed string for the
Xsearch pattern.  VM defaults to the fixed string search.
X@xref{Incremental Search,,,emacs, the GNU Emacs Manual}.@refill
X@end table
X
XSelecting a message within VM normally causes VM to preview it.
X@iftex
X@xref{Previewing}.
X@end iftex
X
X@menu
X* Previewing:: Customizing message previews.
X@end menu
X
X@node Previewing,, Selecting Messages, Selecting Messages
X@section Previewing
X
X@dfn{Previewing} is VM's way of showing you a small portion of a message
Xand allowing you to decide whether you want to read it.  Typing
X@key{SPC} exposes the body of the message.
X
XBy default the sender, recipient, subject and date headers are shown
Xwhen previewing; the rest of the message is hidden.  This behavior may
Xbe augmented by the settings of two variables: @code{vm-visible-headers}
Xand @code{vm-preview-lines}.@refill
X
X@vindex vm-preview-lines
XThe value of @code{vm-preview-lines} should be a number that tells VM
Xhow many lines of the text of the message should be visible.  The default
Xvalue of this variable is 0.  If @code{vm-preview-lines} is @code{nil},
Xthen previewing is not done at all; when a message is first presented it
Xis immediately exposed in its entirety and is marked as read.@refill
X
X@vindex vm-visible-headers
XThe value of @code{vm-visible-headers} should be a list of regular
Xexpressions matching the beginnings of headers that should be made
Xvisible when a message is presented.  The regexps should be listed in
Xthe preferred order of presentation for the headers they match.@refill
X
X@vindex vm-highlighted-header-regexp
XAnother variable of interest is @code{vm-highlighted-header-regexp}.  The
Xvalue of this variable should be a single regular expression that
Xmatches the beginnings of any header that should be presented in inverse
Xvideo when previewing.  For example, a value of @samp{"^From\\|^Subject"}
Xcauses the From and Subject headers to be highlighted.@refill
X
X@vindex vm-preview-read-messages
XBy default VM previews all messages, even if they have already been read.
XTo have VM preview only those messages that have not been read, set the
Xvalue of @code{vm-preview-read-messages} to @code{nil}.
X
X@node Sending Messages, Saving Messages, Selecting Messages, Top
X@chapter Sending Messages
X
XWhen sending messages from within VM, you will be using the standard
XMail major mode provided with GNU Emacs.  @xref{Mail Mode,,,emacs, the
XGNU Emacs Manual}.
XHowever, @samp{*mail*} buffers created by VM have extra command keys:
X
X@iftex
X@table @asis
X@end iftex
X@ifinfo
X@table @key
X@end ifinfo
X@findex vm-yank-message
X@kindex C-c C-y
X@item C-c C-y
XCopies a message from the current folder into the @samp{*mail*} buffer.
XThe message number is read from the minibuffer.  By default each line of
Xthe copy is prepended with the value of the variable
X@code{vm-included-text-prefix}.  If a prefix argument is given, this
Xprepending is not done.@refill
X@kindex C-c C-v
X@item C-c C-v <Any VM command key>
XAll VM commands may be accessed in the @samp{*mail*} buffer by prefixing them
Xwith C-c C-v.
X@end table
X
X@findex vm-mail
X@kindex m
XThe simplest command is @kbd{m} (@code{vm-mail}) which sends a mail
Xmessage much as @kbd{M-x mail} does but allows the added commands
Xdescribed above.@refill
X
X@menu
X* Replying::		Describes the various way to reply to a message.
X* Forwarding Messages::	How to forward a message to a third party.
X@end menu
X
X@node Replying, Forwarding Messages, Sending Messages, Sending Messages
X@section Replying
X
XVM has special commands that make it easy to reply to a message.  When a
Xreply command is invoked VM fills in the subject and recipient headers
Xfor you, since it is apparent to whom the message should be sent.  You
Xcan change these headers manually if you wish.
X
X@vindex vm-included-text-prefix
XVM also helps you quote material from a message to which you are
Xreplying by providing @dfn{included text} as a feature of some of the
Xcommands.  @dfn{Included text} is a copy of the message being replied to with
Xsome fixed string prepended to each line so that included text can be
Xdistinguished from the text of the reply.  The variable
X@code{vm-included-text-prefix} specifies what the prepended string will
Xbe.@refill
X
X@vindex vm-included-text-attribution-format
XThe variable @code{vm-included-text-attribution-format} specifies the
Xformat for the attribution of included text.  This attribution is a line
Xof text that tells who wrote the text that is to be included; it will be
Xinserted before the included text.  If non-@code{nil}, the value of
X@code{vm-included-text-attribution-format} should be a string format
Xspecifiation similar to @code{vm-summary-format}.  @xref{Summaries}.  A
X@code{nil} value causes the attribution to be omitted.@refill
X
X@vindex vm-in-reply-to-format
XThe variable @code{vm-in-reply-to-format} specifies the format of the
XIn-Reply-To header that is inserted into header section of the reply
Xbuffer.  Like @code{vm-included-text-attribution-format},
X@code{vm-in-reply-to-format} should be a string similar to that of
X@code{vm-summary-format}.  A @code{nil} value causes the In-Reply-To
Xheader to be omitted.@refill
X
X@vindex vm-strip-reply-headers
XThe recipient headers generated for reply messages are normally made by
Xsimply copying the appropriate headers for the message to which you are
Xreplying.  This includes any full name information, comments, etc. in
Xthese headers.  If the variable @code{vm-strip-reply-headers} is
Xnon-@code{nil}, the reply headers will stripped of all information but
Xthe actual addresses.
X
XThe reply commands are:
X
X@iftex
X@table @asis
X@end iftex
X@ifinfo
X@table @key
X@end ifinfo
X@findex vm-reply
X@kindex r
X@item r (@code{vm-reply})
XReplies to the author of the current message.
X@findex vm-reply-include-text
X@kindex R
X@item R (@code{vm-reply-include-text})
XReplies to the author of the current message and provides included text.
X@findex vm-followup
X@kindex f
X@item f (@code{vm-followup})
XReplies to the all recipients of the current message.
X@findex vm-followup-include-text
X@kindex F
X@item F (@code{vm-followup-include-text})
XReplies to the all recipients of the current message and provides
Xincluded text.
X@end table
X
XAll the reply commands mark the message to which you are responding as
X``replied'' when the reply is actually sent.@refill
X
X@node Forwarding Messages,, Replying, Sending Messages
X@section Forwarding Messages
X
XVM has two commands to forward messages: @kbd{z}
X(@code{vm-forward-message}) and @key{@@} (@code{vm-send-digest}).@refill
X
X@findex vm-forward-message
X@kindex z
X@vindex vm-rfc934-forwarding
X@vindex vm-forwarding-subject-format
XTyping @kbd{z} puts you into a @samp{*mail*} buffer just like @kbd{m},
Xexcept the current message appears as the body of the message in the
X@samp{*mail*} buffer. The forwarded message is surrounded by RFC 934
Xcomplaint message delimiters.  If the variable
X@code{vm-rfc934-forwarding} is non-@code{nil} "^-" to "- -" character
Xstuffing is done to the forwarded message (this is the default).  This
Xbehavior is required if the recipient of the forwarded message wants to
Xuse a RFC 934 standard bursting agent to access the message.  If the
Xvariable @code{vm-forwarding-subject-format} is non-@code{nil} it should
Xspecify the format of the Subject header of the forwarded message.  This
Xsubject will be used as the contents of the Subject header automatically
Xinserted into the @samp{*mail*} buffer.  A @code{nil} value causes the
XSubject header to be left blank.@refill
X@findex vm-send-digest
X@kindex @@
XThe command @key{@@} (@code{vm-send-digest}) works like @kbd{z} except
Xthat a digest of all the messages in the current folder is made and
Xinserted into the @samp{*mail*} buffer.@refill
X
X@node Saving Messages, Deleting Messages, Sending Messages, Top
X@chapter Saving Messages
X
XMail messages are normally saved to files that contain only mail
Xmessages.  Such files are called @dfn{folders}.
X
X@findex vm-save-message
X@kindex s
XThe VM command to save a message to a folder is @kbd{s}
X(@code{vm-save-message}); invoking this command causes the current
Xmessage to be saved to a folder whose name you specify in the
Xminibuffer.  If @code{vm-save-message} is given a prefix argument
X@var{n}, the current message plus the next @var{n-1} message are saved.
XIf @var{n} is negative, the current message and the previous @var{n-1}
Xmessages are saved.  Messages saved with @code{vm-save-message} are
Xmarked ``filed''.@refill
X
X@vindex vm-confirm-new-folders
XIf the value of the variable @code{vm-confirm-new-folders} is
Xnon-@code{nil}, VM will ask for confirmation before creating a new
Xfolder on interactive saves.@refill
X
X@vindex vm-folder-directory
XIf you have a directory where you keep all your mail folders, you should
Xset the variable @code{vm-folder-directory} to point to it.  If this
Xvariable is set, @code{vm-save-message} will insert this directory name
Xinto the minibuffer before prompting you for a folder name; this will save
Xyou some typing.@refill
X
X@vindex vm-auto-folder-alist
XAnother aid to selecting folders in which to save mail is the variable
X@code{vm-auto-folder-alist}.  The value of this variable should be a
Xlist of the form,@refill
X
X@display
X((@var{header-name}
X   (@var{regexp} . @var{folder-name}) ...)
X  ...)
X@end display
X
Xwhere @var{header-name} and @var{regexp} are strings, and
X@var{folder-name} is a string or an s-expression that evaluates to a
Xstring.@refill
X
XIf any part of the contents of the message header named by
X@var{header-name} is matched by the regular expression @var{regexp}, VM
Xwill evaluate the corresponding @var{folder-name} and use the result as
Xthe default when prompting for a folder to save the message in.  If
Xthe resulting folder name is a relative pathname it resolves to the directory
Xnamed by @code{vm-folder-directory}, or the @code{default-directory} of
Xthe currently visited folder if @code{vm-folder-directory} is nil.@refill
X
XWhen @var{folder-name} is evaluated, the current buffer will contain
Xonly the contents of the header named by @var{header-name}.  It is safe
Xto modify this buffer.  You can use the match data from any
X@samp{\( @dots{} \)} grouping constructs in @var{regexp} along with the
Xfunction buffer-substring to build a folder name based on the header
Xinformation.@refill
X
XAll @code{vm-auto-folder-alist} matching is case sensitive.
X
X@vindex vm-visit-when-saving
XVM can save messages to a folder in two distinct ways.  The message can
Xbe appended directly to the folder on disk, or the folder can be visited
Xas Emacs would visit any other file and the message be appended to that
Xbuffer.  In the latter method you must save the buffer yourself to
Xchange the on-disk copy of the folder.  The variable
X@code{vm-visit-when-saving} controls which method is used.  A @code{nil}
Xvalue (the default) causes VM to append directly to the folder file, a
Xnon-@code{nil} value makes VM load the file into a buffer and append to
Xthat.@refill
X
X@vindex vm-delete-after-saving
XAfter a message is saved to a folder, the usual thing to do next is to
Xdelete it.  If the variable @code{vm-delete-after-saving} is
Xnon-@code{nil} VM will mark messages for deletion automatically after
Xsaving them.  This applies only to saves to folders, not for the @key{w}
Xcommand (see below).@refill
X
XOther commands:
X
X@iftex
X@table @asis
X@end iftex
X@ifinfo
X@table @key
X@end ifinfo
X@findex vm-save-message-sans-headers
X@kindex w
X@item w (@code{vm-save-message-sans-headers})
XSaves a message or messages to a file without their headers.  This
Xcommand responds to a prefix argument exactly as @code{vm-save-message}
Xdoes.  Messages saved this way are @emph{not} marked as filed, as ``filed''
Xis meant to mean saved to a folder.  You should @emph{not} use this
Xcommand to save to mail folders.
X@findex vm-auto-archive-messages
X@kindex A
X@item A (@code{vm-auto-archive-messages})
XSave all unfiled messages that auto-match a folder via
X@code{vm-auto-folder-alist} to their appropriate folders.
X@findex vm-pipe-message-to-command
X@kindex |
X@item | (@code{vm-pipe-message-to-command})
XRuns a shell command with the some or all of the current message as input.
XBy default the entire message is used.@*
X@*
XIf invoked with one @t{C-u} the text portion of the message is used.@*
XIf invoked with two @t{C-u}'s the header portion of the message is used.@*
X@*
XIf the shell command generates any output, it is displayed in a
X@samp{*Shell Command Output*} buffer.  The message is not altered or
Xmarked as filed.
X@end table
X
X@node Deleting Messages, Undoing, Saving Messages, Top
X@chapter Deleting Messages
X
XIn VM messages are marked for deletion, and then are subsequently
X@dfn{expunged} or removed from the folder.  The messages are not removed
Xfrom the on-disk copy of the folder until the folder is saved.
X
X@iftex
X@table @asis
X@end iftex
X@ifinfo
X@table @key
X@end ifinfo
X@findex vm-delete-message
X@kindex d
X@item d (@code{vm-delete-message})
XMarks the current message for deletion.  A prefix argument @var{n}
Xcauses the current message and the next @var{n-1} messages to be marked.
XA negative @var{n} causes the current message and the previous @var{n-1}
Xmessages to be marked.
X@findex vm-undelete-message
X@kindex u
X@item u (@code{vm-undelete-message})
XRemoves the deletion mark from the current message.  A prefix argument @var{n}
Xcauses the current message and the next @var{n-1} messages to be unmarked.
XA negative @var{n} causes the current message and the previous @var{n-1}
Xmessages to be unmarked.
X@findex vm-kill-subject
X@kindex k
X@item k (@code{vm-kill-subject})
XMarks all message with the same subject as the current message (ignoring
X``Re:'') for deletion.
X@findex vm-expunge-folder
X@kindex #
X@item # (@code{vm-expunge-folder})
XDoes the actual removal of messages marked for deletion in the current
Xfolder.
X@end table
X
X@vindex vm-move-after-deleting
XSetting the variable @code{vm-move-after-deleting} non-@code{nil} causes
XVM to move past the messages after marking them for deletion.
X
X@node Undoing, Grouping Messages, Deleting Messages, Top
X@chapter Undoing
X
XVM provides a special form of undo which allows message attribute
Xchanges to be undone.
X
X@findex vm-undo
X@kindex C-x u
X@kindex C-_
XTyping @kbd{C-x u} or @key{C-_} (@code{vm-undo}) undoes the last
Xattribute change.  Consecutive @code{vm-undo}'s undo further and further
Xback.  Any intervening command breaks the undo chain, after which the
Xundos themselves become undoable by subsequent invocations of
X@code{vm-undo}.@refill
X
XNote that expunges and saves are @emph{not} undoable.
X
X@node Grouping Messages, Reading Digests, Undoing, Top
X@chapter Grouping Messages
X
X@findex vm-group-messages
X@kindex G
XIn order to make numerous related messages easier to cope with, VM
Xprovides the command @kbd{G} (@code{vm-group-messages}), which groups
Xall message in a folder according to some criterion.  @dfn{Grouping}
Xcauses messages that are related in some way to be presented
Xconsecutively.  The actual order of the folder is not altered;
Xthe messages are simply numbered and presented differently.  Grouping
Xshould not be confused with sorting; grouping only moves messages that
Xoccur later in the folder backward to ``clump'' with other related
Xmessages.@refill
X
XThe grouping criteria currently supported are:
X@table @samp
X@item subject
XMessages with the same subject (ignoring ``Re:'' prefixes) are grouped
Xtogether.
X@item author
XMessages with the same author are grouped together.
X@item date-sent
XMessages sent on the same day are grouped together.
X@item arrival-time
XMessage presentation reverts to arrival time ordering (the default).
X@end table
X
X@vindex vm-group-by
XIf the variable @code{vm-group-by} has a non-@code{nil} value it
Xspecifies the default grouping that will be used for all folders.  So if
Xyou like having your mail presented to you grouped by subject, then put
X@code{(setq vm-group-by "subject")} in your @file{.emacs} file to get this
Xbehavior.@refill
X
X@node Reading Digests, Summaries, Grouping Messages, Top
X@chapter Reading Digests
X
XA @dfn{digest} is one or more mail messages encapsulated in a single message.
X
XVM supports digests by providing a command to ``burst'' them into their
Xindividual messages.  These messages can then be handled like any other
Xmessages under VM.
X
X@findex vm-burst-digest
X@kindex *
XThe command @kbd{*} (@code{vm-burst-digest}) bursts a digest into its
Xindividual messages and appends them to current folder.  These
Xmessages are then assimilated into the current folder using the default
Xgrouping.  @xref{Grouping Messages}.  The original digest message is not
Xaltered, and the messages extracted from it are not part of the on-disk copy
Xof the folder until a save is done.@refill
X
X@node Summaries, Miscellaneous, Reading Digests, Top
X@chapter Summaries
X
X@findex vm-summarize
X@kindex h
XTyping @kbd{h} (@code{vm-summarize}) causes VM to display a summary of
Xcontents of the current folder.  An arrow @samp{->} appears to the left
Xof the line summarizing the current message.  The information in the
Xsummary is automatically updated as changes are made to the current
Xfolder.@refill
X
X@vindex vm-summary-format
XThe variable @code{vm-summary-format} controls the format of each
Xmessage's summary.  Its value should be a string.  This string should
Xcontain the printf-like ``%'' conversion specifiers which substitute
Xinformation about the message into the final summary.
X
XRecognized specifiers are:
X@display
X   a - attribute indicators (always three characters wide)
X       The first char is  `D', `N', `U' or ` ' for deleted, new, unread
X       and read message respectively.
X       The second char is `F' for filed (saved) messages.
X       The third char is `R' if the message has been replied to.
X   c - number of characters in message (ignoring headers)
X   d - date of month message sent
X   f - author's address
X   F - author's full name (same as f if full name not found)
X   h - hour message sent
X   i - message ID
X   l - number of lines in message (ignoring headers)
X   m - month message sent
X   n - message number
X   s - message subject
X   w - day of the week message sent
X   y - year message sent
X   z - timezone of date when the message was sent
X@end display
X
XUse ``%%'' to get a single ``%''.
X
XA numeric field width may be specified between the ``%'' and the
Xspecifier; this causes right justification of the substituted string.  A
Xnegative field width causes left justification.  The field width may be
Xfollowed by a ``.'' and a number specifying the maximum allowed length
Xof the substituted string.  If the string is longer than this value it
Xis truncated.
X
XThe summary format need not be one line per message but it must end with
Xa newline, otherwise the message pointer will not be displayed correctly
Xin the summary window.
X
XYou can have a summary generated automatically at startup,
X@pxref{Starting Up}.@refill
X
X@vindex vm-follow-summary-cursor
XAll VM commands are available in the summary buffer just as they
Xare in the folder buffer itself.  If you set
X@code{vm-follow-summary-cursor} non-@code{nil}, VM will select the
Xmessage under the cursor in the summary window before executing commands
Xthat operate on the current message.  Note that this occurs @emph{only}
Xwhen executing a command from the summary buffer window.@refill
X
X@node Miscellaneous,, Summaries, Top
X@chapter Miscellaneous
X
XHere are some VM customization variables that don't really fit into the
Xother chapters.
X
X@iftex
X@table @asis
X@end iftex
X@ifinfo
X@table @code
X@end ifinfo
X@vindex vm-berkeley-mail-compatibility
X@item vm-berkeley-mail-compatibility
XA non-@code{nil} value means to read and write BSD @i{Mail(1)} style Status:
Xheaders.  This makes sense if you plan to use VM to read mail archives
Xcreated by @i{Mail}.
X@vindex vm-gargle-uucp
X@item vm-gargle-uucp
XA non-@code{nil} value means to use a crufty regular expression that
Xdoes surprisingly well at beautifying UUCP addresses that are substituted
Xfor %f as part of summary and attribution formats.
X@vindex vm-mode-hooks
X@item vm-mode-hooks
XA non-@code{nil} value should be a list of hook functions to run when a
Xbuffer enters vm-mode.  These hook functions should generally be used to
Xset key bindings and local variables.  Mucking about in the folder
Xbuffer is certainly possible, but it is not encouraged.
X@vindex vm-folder-type
X@item vm-folder-type
XThis variable specifies the type of mail folder VM should expect to read
Xand write.  @code{Nil} means expect the UNIX style folders characterized
Xby the ``\n\nFrom '' message separators.  The only other supported value
Xfor this variable is the symbol @code{mmdf} which causes VM to use
X``^A^A^A^A\n'' MMDF style leaders and trailers.@refill
X@vindex vm-delete-empty-folders
X@item vm-delete-empty-folders
XA non-@code{nil} value for this variable causes VM to remove empty (zero
Xlength) folder files after saving them.
X@vindex vm-mutable-windows
X@item vm-mutable-windows
XThis variable's value controls VM's window usage.  A value of @code{t} gives VM
Xfree run of the Emacs display; it will commandeer the entire screen for
Xits purposes.  A value of @code{nil} restricts VM's window usage to the window
Xfrom which it was invoked.  VM will not create, delete, or use any other
Xwindows, nor will it resize it's own window.  A value that is neither @code{t}
Xnor @code{nil} allows VM to use other windows, but it will not create new ones,
Xor resize or delete the current ones.@refill
X@end table
X
X@node Key Index, Command Index, Top, Top
X@unnumbered Key Index
X@printindex ky
X
X@node Command Index, Variable Index, Key Index, Top
X@unnumbered Command Index
X@printindex fn
X
X@node Variable Index, Introduction, Command Index, Top
X@unnumbered Variable Index
X@printindex vr
X
X@summarycontents
X@contents
X@bye
SHAR_EOF
echo "File vm.texinfo is complete"
chmod 0664 vm.texinfo || echo "restore of vm.texinfo fails"
echo "x - extracting COPYING (Text)"
sed 's/^X//' << 'SHAR_EOF' > COPYING &&
X		    GNU GENERAL PUBLIC LICENSE
X		     Version 1, February 1989
X
X Copyright (C) 1989 Free Software Foundation, Inc.
X                    675 Mass Ave, Cambridge, MA 02139, USA
X Everyone is permitted to copy and distribute verbatim copies
X of this license document, but changing it is not allowed.
X
X			    Preamble
X
X  The license agreements of most software companies try to keep users
Xat the mercy of those companies.  By contrast, our General Public
XLicense is intended to guarantee your freedom to share and change free
Xsoftware--to make sure the software is free for all its users.  The
XGeneral Public License applies to the Free Software Foundation's
Xsoftware and to any other program whose authors commit to using it.
XYou can use it for your programs, too.
X
X  When we speak of free software, we are referring to freedom, not
Xprice.  Specifically, the General Public License is designed to make
Xsure that you have the freedom to give away or sell copies of free
Xsoftware, that you receive source code or can get it if you want it,
Xthat you can change the software or use pieces of it in new free
Xprograms; and that you know you can do these things.
X
X  To protect your rights, we need to make restrictions that forbid
Xanyone to deny you these rights or to ask you to surrender the rights.
XThese restrictions translate to certain responsibilities for you if you
Xdistribute copies of the software, or if you modify it.
X
X  For example, if you distribute copies of a such a program, whether
Xgratis or for a fee, you must give the recipients all the rights that
Xyou have.  You must make sure that they, too, receive or can get the
Xsource code.  And you must tell them their rights.
X
X  We protect your rights with two steps: (1) copyright the software, and
X(2) offer you this license which gives you legal permission to copy,
Xdistribute and/or modify the software.
X
X  Also, for each author's protection and ours, we want to make certain
Xthat everyone understands that there is no warranty for this free
Xsoftware.  If the software is modified by someone else and passed on, we
Xwant its recipients to know that what they have is not the original, so
Xthat any problems introduced by others will not reflect on the original
Xauthors' reputations.
X
X  The precise terms and conditions for copying, distribution and
Xmodification follow.
X
X		    GNU GENERAL PUBLIC LICENSE
X   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
X
X  0. This License Agreement applies to any program or other work which
Xcontains a notice placed by the copyright holder saying it may be
Xdistributed under the terms of this General Public License.  The
X"Program", below, refers to any such program or work, and a "work based
Xon the Program" means either the Program or any work containing the
XProgram or a portion of it, either verbatim or with modifications.  Each
Xlicensee is addressed as "you".
X
X  1. You may copy and distribute verbatim copies of the Program's source
Xcode as you receive it, in any medium, provided that you conspicuously and
Xappropriately publish on each copy an appropriate copyright notice and
Xdisclaimer of warranty; keep intact all the notices that refer to this
XGeneral Public License and to the absence of any warranty; and give any
Xother recipients of the Program a copy of this General Public License
Xalong with the Program.  You may charge a fee for the physical act of
Xtransferring a copy.
X
X  2. You may modify your copy or copies of the Program or any portion of
Xit, and copy and distribute such modifications under the terms of Paragraph
X1 above, provided that you also do the following:
X
X    a) cause the modified files to carry prominent notices stating that
X    you changed the files and the date of any change; and
X
X    b) cause the whole of any work that you distribute or publish, that
X    in whole or in part contains the Program or any part thereof, either
X    with or without modifications, to be licensed at no charge to all
X    third parties under the terms of this General Public License (except
X    that you may choose to grant warranty protection to some or all
X    third parties, at your option).
X
X    c) If the modified program normally reads commands interactively when
X    run, you must cause it, when started running for such interactive use
X    in the simplest and most usual way, to print or display an
X    announcement including an appropriate copyright notice and a notice
X    that there is no warranty (or else, saying that you provide a
X    warranty) and that users may redistribute the program under these
X    conditions, and telling the user how to view a copy of this General
X    Public License.
X
X    d) You may charge a fee for the physical act of transferring a
X    copy, and you may at your option offer warranty protection in
X    exchange for a fee.
X
XMere aggregation of another independent work with the Program (or its
Xderivative) on a volume of a storage or distribution medium does not bring
Xthe other work under the scope of these terms.
X
X  3. You may copy and distribute the Program (or a portion or derivative of
Xit, under Paragraph 2) in object code or executable form under the terms of
XParagraphs 1 and 2 above provided that you also do one of the following:
X
X    a) accompany it with the complete corresponding machine-readable
X    source code, which must be distributed under the terms of
X    Paragraphs 1 and 2 above; or,
X
X    b) accompany it with a written offer, valid for at least three
X    years, to give any third party free (except for a nominal charge
X    for the cost of distribution) a complete machine-readable copy of the
X    corresponding source code, to be distributed under the terms of
X    Paragraphs 1 and 2 above; or,
X
X    c) accompany it with the information you received as to where the
X    corresponding source code may be obtained.  (This alternative is
X    allowed only for noncommercial distribution and only if you
X    received the program in object code or executable form alone.)
X
XSource code for a work means the preferred form of the work for making
Xmodifications to it.  For an executable file, complete source code means
Xall the source code for all modules it contains; but, as a special
Xexception, it need not include source code for modules which are standard
Xlibraries that accompany the operating system on which the executable
Xfile runs, or for standard header files or definitions files that
Xaccompany that operating system.
X
X  4. You may not copy, modify, sublicense, distribute or transfer the
XProgram except as expressly provided under this General Public License.
XAny attempt otherwise to copy, modify, sublicense, distribute or transfer
Xthe Program is void, and will automatically terminate your rights to use
Xthe Program under this License.  However, parties who have received
Xcopies, or rights to use copies, from you under this General Public
XLicense will not have their licenses terminated so long as such parties
Xremain in full compliance.
X
X  5. By copying, distributing or modifying the Program (or any work based
Xon the Program) you indicate your acceptance of this license to do so,
Xand all its terms and conditions.
X
X  6. Each time you redistribute the Program (or any work based on the
XProgram), the recipient automatically receives a license from the original
Xlicensor to copy, distribute or modify the Program subject to these
Xterms and conditions.  You may not impose any further restrictions on the
Xrecipients' exercise of the rights granted herein.
X
X  7. The Free Software Foundation may publish revised and/or new versions
Xof the General Public License from time to time.  Such new versions will
Xbe similar in spirit to the present version, but may differ in detail to
Xaddress new problems or concerns.
X
XEach version is given a distinguishing version number.  If the Program
Xspecifies a version number of the license which applies to it and "any
Xlater version", you have the option of following the terms and conditions
Xeither of that version or of any later version published by the Free
XSoftware Foundation.  If the Program does not specify a version number of
Xthe license, you may choose any version ever published by the Free Software
XFoundation.
X
X  8. If you wish to incorporate parts of the Program into other free
Xprograms whose distribution conditions are different, write to the author
Xto ask for permission.  For software which is copyrighted by the Free
XSoftware Foundation, write to the Free Software Foundation; we sometimes
Xmake exceptions for this.  Our decision will be guided by the two goals
Xof preserving the free status of all derivatives of our free software and
Xof promoting the sharing and reuse of software generally.
X
X			    NO WARRANTY
X
X  9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
XFOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
XOTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
XPROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
XOR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
XMERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
XTO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
XPROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
XREPAIR OR CORRECTION.
X
X  10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
XWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
XREDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
XINCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
XOUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
XTO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
XYOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
XPROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
XPOSSIBILITY OF SUCH DAMAGES.
X
X		     END OF TERMS AND CONDITIONS
SHAR_EOF
chmod 0444 COPYING || echo "restore of COPYING fails"
echo "x - extracting README (Text)"
sed 's/^X//' << 'SHAR_EOF' > README &&
XHow to setup VM:
X
X1) Install all the source modules in a Lisp directory that Emacs knows
X   about.
X
X2) Startup an Emacs session.
X
X3) Byte-compile vm.el.
X
X4) Byte-compile the other modules.
X
X5) Visit the vm.texinfo file and execute `M-x texinfo-format-buffer'.
X   Save the resulting "vm.info" buffer and copy the "vm.info" file into
X   Emacs' info directory under the name "vm".  Edit the "dir" file in
X   that directory and add a menu entry for VM.
X
X6) MMDF users must (setq vm-folder-type 'mmdf).
X
XSend bug reports to kyle@cs.odu.edu.
SHAR_EOF
chmod 0664 README || echo "restore of README fails"
rm -f s2_seq_.tmp
echo "You have unpacked the last part"
exit 0