home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 2: Applications / Linux Cubed Series 2 - Applications.iso / editors / emacs / xemacs / xemacs-1.006 / xemacs-1 / lib / xemacs-19.13 / lisp / vm / vm-folder.el < prev    next >
Encoding:
Text File  |  1995-08-18  |  104.0 KB  |  2,890 lines

  1. ;;; VM folder related functions
  2. ;;; Copyright (C) 1989, 1990, 1991, 1993, 1994, 1995 Kyle E. Jones
  3. ;;;
  4. ;;; This program is free software; you can redistribute it and/or modify
  5. ;;; it under the terms of the GNU General Public License as published by
  6. ;;; the Free Software Foundation; either version 1, or (at your option)
  7. ;;; any later version.
  8. ;;;
  9. ;;; This program is distributed in the hope that it will be useful,
  10. ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12. ;;; GNU General Public License for more details.
  13. ;;;
  14. ;;; You should have received a copy of the GNU General Public License
  15. ;;; along with this program; if not, write to the Free Software
  16. ;;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  17.  
  18. (provide 'vm-folder)
  19.  
  20. (defun vm-number-messages (&optional start-point end-point)
  21.   "Set the number-of and padded-number-of slots of messages
  22. in vm-message-list.
  23.  
  24. If non-nil, START-POINT should point to a cons cell in
  25. vm-message-list and the numbering will begin there, else the
  26. numbering will begin at the head of vm-message-list.  If
  27. START-POINT is non-nil the reverse-link-of slot of the message in
  28. the cons must be valid and the message pointed to (if any) must
  29. have a non-nil number-of slot, because it is used to determine
  30. what the starting message number should be.
  31.  
  32. If non-nil, END-POINT should point to a cons cell in
  33. vm-message-list and the numbering will end with the message just
  34. before this cell.  A nil value means numbering will be done until
  35. the end of vm-message-list is reached."
  36.   (let ((n 1) (message-list (or start-point vm-message-list)))
  37.     (if (and start-point (vm-reverse-link-of (car start-point)))
  38.     (setq n (1+ (string-to-int
  39.              (vm-number-of
  40.               (car
  41.                (vm-reverse-link-of
  42.             (car start-point))))))))
  43.     (while (not (eq message-list end-point))
  44.       (vm-set-number-of (car message-list) (int-to-string n))
  45.       (vm-set-padded-number-of (car message-list) (format "%3d" n))
  46.       (setq n (1+ n) message-list (cdr message-list)))
  47.     (or end-point (setq vm-ml-highest-message-number (int-to-string (1- n))))
  48.     (if vm-summary-buffer
  49.     (vm-copy-local-variables vm-summary-buffer
  50.                  'vm-ml-highest-message-number))))
  51.  
  52. (defun vm-set-numbering-redo-start-point (start-point)
  53.   "Set vm-numbering-redo-start-point to START-POINT if appropriate.
  54. Also mark the current buffer as needing a display update.
  55.  
  56. START-POINT should be a cons in vm-message-list or just t.
  57.  (t means start from the beginning of vm-message-list.)
  58. If START-POINT is closer to the head of vm-message-list than
  59. vm-numbering-redo-start-point or is equal to t, then
  60. vm-numbering-redo-start-point is set to match it."
  61.   (intern (buffer-name) vm-buffers-needing-display-update)
  62.   (if (and (consp start-point) (consp vm-numbering-redo-start-point)
  63.        (not (eq vm-numbering-redo-start-point t)))
  64.       (let ((mp vm-message-list))
  65.     (while (and mp (not (or (eq mp start-point)
  66.                 (eq mp vm-numbering-redo-start-point))))
  67.       (setq mp (cdr mp)))
  68.     (if (null mp)
  69.         (error "Something is wrong in vm-set-numbering-redo-start-point"))
  70.     (if (eq mp start-point)
  71.         (setq vm-numbering-redo-start-point start-point)))
  72.     (setq vm-numbering-redo-start-point start-point)))
  73.  
  74. (defun vm-set-numbering-redo-end-point (end-point)
  75.   "Set vm-numbering-redo-end-point to END-POINT if appropriate.
  76. Also mark the current buffer as needing a display update.
  77.  
  78. END-POINT should be a cons in vm-message-list or just t.
  79.  (t means number all the way to the end of vm-message-list.)
  80. If END-POINT is closer to the end of vm-message-list or is equal
  81. to t, then vm-numbering-redo-start-point is set to match it.
  82. The number-of slot is used to determine proximity to the end of
  83. vm-message-list, so this slot must be valid in END-POINT's message
  84. and the message in the cons pointed to by vm-numbering-redo-end-point."
  85.   (intern (buffer-name) vm-buffers-needing-display-update)
  86.   (cond ((eq end-point t)
  87.      (setq vm-numbering-redo-end-point t))
  88.     ((and (consp end-point)
  89.           (> (string-to-int
  90.           (vm-number-of
  91.            (car end-point)))
  92.          (string-to-int
  93.           (vm-number-of
  94.            (car vm-numbering-redo-end-point)))))
  95.      (setq vm-numbering-redo-end-point end-point))
  96.     ((null end-point)
  97.      (setq vm-numbering-redo-end-point end-point))))
  98.  
  99. (defun vm-do-needed-renumbering ()
  100.   "Number messages in vm-message-list as specified by
  101. vm-numbering-redo-start-point and vm-numbering-redo-end-point.
  102.  
  103. vm-numbering-redo-start-point = t means start at the head
  104. of vm-message-list.
  105. vm-numbering-redo-end-point = t means number all the way to the
  106. end of vm-message-list.
  107.  
  108. Otherwise the variables' values should be conses in vm-message-list
  109. or nil."
  110.   (if vm-numbering-redo-start-point
  111.       (progn
  112.     (vm-number-messages (and (consp vm-numbering-redo-start-point)
  113.                  vm-numbering-redo-start-point)
  114.                 vm-numbering-redo-end-point)
  115.     (setq vm-numbering-redo-start-point nil
  116.           vm-numbering-redo-end-point nil))))
  117.  
  118. (defun vm-set-summary-redo-start-point (start-point)
  119.   "Set vm-summary-redo-start-point to START-POINT if appropriate.
  120. Also mark the current buffer as needing a display update.
  121.  
  122. START-POINT should be a cons in vm-message-list or just t.
  123.  (t means start from the beginning of vm-message-list.)
  124. If START-POINT is closer to the head of vm-message-list than
  125. vm-numbering-redo-start-point or is equal to t, then
  126. vm-numbering-redo-start-point is set to match it."
  127.   (intern (buffer-name) vm-buffers-needing-display-update)
  128.   (if (and (consp start-point) (consp vm-summary-redo-start-point)
  129.        (not (eq vm-summary-redo-start-point t)))
  130.       (let ((mp vm-message-list))
  131.     (while (and mp (not (or (eq mp start-point)
  132.                 (eq mp vm-summary-redo-start-point))))
  133.       (setq mp (cdr mp)))
  134.     (if (null mp)
  135.         (error "Something is wrong in vm-set-summary-redo-start-point"))
  136.     (if (eq mp start-point)
  137.         (setq vm-summary-redo-start-point start-point)))
  138.     (setq vm-summary-redo-start-point start-point)))
  139.  
  140. (defun vm-mark-for-summary-update (m &optional dont-kill-cache)
  141.   "Mark message M for a summary update.
  142. Also mark M's buffer as needing a display update. Any virtual
  143. messages of M and their buffers are similarly marked for update.
  144. If M is a virtual message and virtual mirroring is in effect for
  145. M (i.e. attribute-of eq attributes-of M's real message), M's real
  146. message and its buffer are scheduled for an update.
  147.  
  148. Optional arg DONT-KILL-CACHE non-nil means don't invalidate the
  149. summary-of slot for any messages marked for update.  This is
  150. meant to be used by functions that update message information
  151. that is not cached in the summary-of slot, e.g. message numbers
  152. and thread indentation."
  153.   (cond ((eq m (vm-real-message-of m))
  154.      ;; this is a real message.
  155.      ;; its summary and modeline need to be updated.
  156.      (if (not dont-kill-cache)
  157.          ;; toss the cache.  this also tosses the cache of any
  158.          ;; virtual messages mirroring this message.  the summary
  159.          ;; entry cache must be cleared when an attribute of a
  160.          ;; message that could appear in the summary has changed.
  161.          (vm-set-summary-of m nil))
  162.      (if (vm-su-start-of m)
  163.          (setq vm-messages-needing-summary-update 
  164.            (cons m vm-messages-needing-summary-update)))
  165.      (intern (buffer-name (vm-buffer-of m))
  166.          vm-buffers-needing-display-update)
  167.      ;; find the virtual messages of this real message that
  168.      ;; need a summary update.
  169.      (let ((m-list (vm-virtual-messages-of m)))
  170.        (while m-list
  171.          (if (eq (vm-attributes-of m) (vm-attributes-of (car m-list)))
  172.          (progn
  173.            (and (vm-su-start-of (car m-list))
  174.             (setq vm-messages-needing-summary-update
  175.                   (cons (car m-list)
  176.                     vm-messages-needing-summary-update)))
  177.            (intern (buffer-name (vm-buffer-of (car m-list)))
  178.                vm-buffers-needing-display-update)))
  179.          (setq m-list (cdr m-list)))))
  180.     (t
  181.      ;; this is a virtual message.
  182.      ;;
  183.      ;; if this message has virtual messages then we need to
  184.      ;; schedule updates for all the virtual messages that
  185.      ;; share a cache with this message and we need to
  186.      ;; schedule an update for the underlying real message
  187.      ;; since we are mirroring it.
  188.      ;;
  189.      ;; if there are no virtual messages, then this virtual
  190.      ;; message is not mirroring its real message so we need
  191.      ;; only take care of this one message.
  192.      (if (vm-virtual-messages-of m)
  193.          (let ((m-list (vm-virtual-messages-of m)))
  194.            ;; schedule updates for all the virtual message who share
  195.            ;; the same cache as this message.
  196.            (while m-list
  197.          (if (eq (vm-attributes-of m) (vm-attributes-of (car m-list)))
  198.              (progn
  199.                (and (vm-su-start-of (car m-list))
  200.                 (setq vm-messages-needing-summary-update
  201.                   (cons (car m-list)
  202.                     vm-messages-needing-summary-update)))
  203.                (intern (buffer-name (vm-buffer-of (car m-list)))
  204.                    vm-buffers-needing-display-update)))
  205.          (setq m-list (cdr m-list)))
  206.            ;; now take care of the real message
  207.            (if (not dont-kill-cache)
  208.            ;; toss the cache.  this also tosses the cache of
  209.            ;; any virtual messages sharing the same cache as
  210.            ;; this message.
  211.            (vm-set-summary-of m nil))
  212.            (and (vm-su-start-of (vm-real-message-of m))
  213.             (setq vm-messages-needing-summary-update
  214.               (cons (vm-real-message-of m)
  215.                 vm-messages-needing-summary-update)))
  216.            (intern (buffer-name (vm-buffer-of (vm-real-message-of m)))
  217.                vm-buffers-needing-display-update))
  218.        (if (not dont-kill-cache)
  219.            (vm-set-virtual-summary-of m nil))
  220.        (and (vm-su-start-of m)
  221.         (setq vm-messages-needing-summary-update
  222.               (cons m vm-messages-needing-summary-update)))
  223.        (intern (buffer-name (vm-buffer-of m))
  224.            vm-buffers-needing-display-update)))))
  225.  
  226. (defun vm-force-mode-line-update ()
  227.   "Force a mode line update in all frames."
  228.   (if (fboundp 'force-mode-line-update)
  229.       (force-mode-line-update t)
  230.     (save-excursion
  231.       (set-buffer (other-buffer))
  232.       (set-buffer-modified-p (buffer-modified-p)))))
  233.  
  234. (defun vm-do-needed-mode-line-update ()
  235.   "Do a modeline updates for the current folder buffer.
  236. This means setting up all the various vm-ml attribute variables
  237. in the folder buffer and copying necessary variables to the
  238. folder buffer's summary buffer, and then forcing Emacs to update
  239. all modelines.
  240.  
  241. Also if a virtual folder being updated has no messages,
  242. erase-buffer is called on its buffer."
  243.   ;; XXX This last bit should probably should be moved to
  244.   ;; XXX vm-expunge-folder.
  245.  
  246.   (if (null vm-message-pointer)
  247.       ;; erase the leftover message if the folder is really empty.
  248.       (if (eq major-mode 'vm-virtual-mode)
  249.       (let ((buffer-read-only nil)
  250.         (omodified (buffer-modified-p)))
  251.         (unwind-protect
  252.         (erase-buffer)
  253.           (set-buffer-modified-p omodified))))
  254.     ;; try to avoid calling vm-su-labels if possible so as to
  255.     ;; avoid loading vm-summary.el.
  256.     (if (vm-labels-of (car vm-message-pointer))
  257.     (setq vm-ml-labels (vm-su-labels (car vm-message-pointer)))
  258.       (setq vm-ml-labels nil))
  259.     (setq vm-ml-message-number (vm-number-of (car vm-message-pointer)))
  260.     (setq vm-ml-message-new (vm-new-flag (car vm-message-pointer)))
  261.     (setq vm-ml-message-unread (vm-unread-flag (car vm-message-pointer)))
  262.     (setq vm-ml-message-read
  263.       (and (not (vm-new-flag (car vm-message-pointer)))
  264.            (not (vm-unread-flag (car vm-message-pointer)))))
  265.     (setq vm-ml-message-edited (vm-edited-flag (car vm-message-pointer)))
  266.     (setq vm-ml-message-filed (vm-filed-flag (car vm-message-pointer)))
  267.     (setq vm-ml-message-written (vm-written-flag (car vm-message-pointer)))
  268.     (setq vm-ml-message-replied (vm-replied-flag (car vm-message-pointer)))
  269.     (setq vm-ml-message-forwarded (vm-forwarded-flag (car vm-message-pointer)))
  270.     (setq vm-ml-message-redistributed (vm-redistributed-flag (car vm-message-pointer)))
  271.     (setq vm-ml-message-deleted (vm-deleted-flag (car vm-message-pointer)))
  272.     (setq vm-ml-message-marked (vm-mark-of (car vm-message-pointer))))
  273.   (if vm-summary-buffer
  274.       (let ((modified (buffer-modified-p)))
  275.     (save-excursion
  276.       (vm-copy-local-variables vm-summary-buffer
  277.                    'vm-ml-message-new
  278.                    'vm-ml-message-unread
  279.                    'vm-ml-message-read
  280.                    'vm-ml-message-edited
  281.                    'vm-ml-message-replied
  282.                    'vm-ml-message-forwarded
  283.                    'vm-ml-message-filed
  284.                    'vm-ml-message-written
  285.                    'vm-ml-message-deleted
  286.                    'vm-ml-message-marked
  287.                    'vm-ml-message-number
  288.                    'vm-ml-highest-message-number
  289.                    'vm-folder-read-only
  290.                    'vm-folder-type
  291.                    'vm-virtual-folder-definition
  292.                    'vm-virtual-mirror
  293.                    'vm-ml-sort-keys
  294.                    'vm-ml-labels
  295.                    'vm-message-list)
  296.       (set-buffer vm-summary-buffer)
  297.       (set-buffer-modified-p modified))))
  298.   (vm-force-mode-line-update))
  299.  
  300. (defun vm-update-summary-and-mode-line ()
  301.   "Update summary and mode line for all VM folder and summary buffers.
  302. Really this updates all the visible status indicators.
  303.  
  304. Message lists are renumbered.
  305. Summary entries are wiped and regenerated.
  306. Mode lines are updated.
  307. Toolbars are updated."
  308.   (save-excursion
  309.     (mapatoms (function
  310.            (lambda (b)
  311.          (setq b (get-buffer (symbol-name b)))
  312.          (if b
  313.              (progn
  314.                (set-buffer b)
  315.                (vm-check-for-killed-summary)
  316.                (and vm-use-toolbar
  317.                 (vm-toolbar-support-possible-p)
  318.                 (vm-toolbar-update-toolbar))
  319.                (vm-do-needed-renumbering)
  320.                (if vm-summary-buffer
  321.                (vm-do-needed-summary-rebuild))
  322.                (vm-do-needed-mode-line-update)))))
  323.           vm-buffers-needing-display-update)
  324.     (fillarray vm-buffers-needing-display-update 0))
  325.   (if vm-messages-needing-summary-update
  326.       (progn
  327.     (mapcar (function vm-update-message-summary)
  328.         vm-messages-needing-summary-update)
  329.     (setq vm-messages-needing-summary-update nil)))
  330.   (vm-force-mode-line-update))
  331.  
  332. (defun vm-reverse-link-messages ()
  333.   "Set reverse links for all messages in vm-message-list."
  334.   (let ((mp vm-message-list)
  335.     (prev nil))
  336.     (while mp
  337.       (vm-set-reverse-link-of (car mp) prev)
  338.       (setq prev mp mp (cdr mp)))))
  339.  
  340. (defun vm-match-ordered-header (alist)
  341.   "Try to match a header in ALIST and return the matching cell.
  342. This is used by header ordering code.
  343.  
  344. ALIST looks like this ((\"From\") (\"To\")).  This function returns
  345. the alist element whose car matches the header starting at point.
  346. The header ordering code uses the cdr of the element
  347. returned to hold headers to be output later."
  348.   (let ((case-fold-search t))
  349.     (catch 'match
  350.       (while alist
  351.     (if (looking-at (car (car alist)))
  352.         (throw 'match (car alist)))
  353.     (setq alist (cdr alist)))
  354.       nil)))
  355.  
  356. (defun vm-match-header (&optional header-name)
  357.   "Match a header and save some state information about the matched header.
  358. Optional first arg HEADER-NAME means match the header only
  359. if it matches HEADER-NAME.  HEADER-NAME should be a string
  360. containing a header name.  The string should end with a colon if just
  361. that name should be matched.  A string that does not end in a colon
  362. will match all headers that begin with that string.
  363.  
  364. State information is stored in vm-matched-header-vector bound to a vector
  365. of this form.
  366.  
  367.  [ header-start header-end
  368.    header-name-start header-name-end
  369.    header-contents-start header-contents-end ]
  370.  
  371. Elements are integers.
  372. There are functions to access and use this info."
  373.   (let ((case-fold-search t)
  374.     (header-name-regexp "\\([^ \t\n:]+\\):"))
  375.     (if (if header-name
  376.         (and (looking-at header-name) (looking-at header-name-regexp))
  377.       (looking-at header-name-regexp))
  378.     (save-excursion
  379.       (aset vm-matched-header-vector 0 (point))
  380.       (aset vm-matched-header-vector 2 (point))
  381.       (aset vm-matched-header-vector 3 (match-end 1))
  382.       (goto-char (match-end 0))
  383.       ;; skip leading whitespace
  384.       (skip-chars-forward " \t")
  385.       (aset vm-matched-header-vector 4 (point))
  386.       (forward-line 1)
  387.       (while (looking-at "[ \t]")
  388.         (forward-line 1))
  389.       (aset vm-matched-header-vector 1 (point))
  390.       ;; drop the trailing newline
  391.       (aset vm-matched-header-vector 5 (1- (point)))))))
  392.  
  393. (defun vm-matched-header ()
  394.   "Returns the header last matched by vm-match-header.
  395. Trailing newline is included."
  396.   (vm-buffer-substring-no-properties (aref vm-matched-header-vector 0)
  397.                      (aref vm-matched-header-vector 1)))
  398.  
  399. (defun vm-matched-header-name ()
  400.   "Returns the name of the header last matched by vm-match-header."
  401.   (vm-buffer-substring-no-properties (aref vm-matched-header-vector 2)
  402.                      (aref vm-matched-header-vector 3)))
  403.  
  404. (defun vm-matched-header-contents ()
  405.   "Returns the contents of the header last matched by vm-match-header.
  406. Trailing newline is not included."
  407.   (vm-buffer-substring-no-properties (aref vm-matched-header-vector 4)
  408.                      (aref vm-matched-header-vector 5)))
  409.  
  410. (defun vm-matched-header-start ()
  411.   "Returns the start position of the header last matched by vm-match-header."
  412.   (aref vm-matched-header-vector 0))
  413.  
  414. (defun vm-matched-header-end ()
  415.   "Returns the end position of the header last matched by vm-match-header."
  416.   (aref vm-matched-header-vector 1))
  417.  
  418. (defun vm-matched-header-name-start ()
  419.   "Returns the start position of the name of the header last matched
  420. by vm-match-header."
  421.   (aref vm-matched-header-vector 2))
  422.  
  423. (defun vm-matched-header-name-end ()
  424.   "Returns the end position of the name of the header last matched
  425. by vm-match-header."
  426.   (aref vm-matched-header-vector 3))
  427.  
  428. (defun vm-matched-header-contents-start ()
  429.   "Returns the start position of the contents of the header last matched
  430. by vm-match-header."
  431.   (aref vm-matched-header-vector 4))
  432.  
  433. (defun vm-matched-header-contents-end ()
  434.   "Returns the end position of the contents of the header last matched
  435. by vm-match-header."
  436.   (aref vm-matched-header-vector 5))
  437.  
  438. (defun vm-get-folder-type (&optional file start end)
  439.   "Return a symbol indicating the folder type of the current buffer.
  440. This function works by examining the beginning of a folder.
  441. If optional arg FILE is present the type of FILE is returned instead.
  442. If optional second and third arg START and END are provided,
  443. vm-get-folder-type will examine the the text between those buffer
  444. positions.  START and END default to 1 and (buffer-size) + 1.
  445.  
  446. Returns
  447.   nil      if folder has no type (empty)
  448.   unknown  if the type is not known to VM
  449.   mmdf     for MMDF folders
  450.   babyl    for BABYL folders
  451.   From_    for UNIX From_ folders
  452.  
  453. If vm-trust-From_-with-Content-Length is non-nil,
  454. From_-with-Content-Length is returned if the first message in the
  455. folder has a Content-Length header and the folder otherwise looks
  456. like a From_ folder."
  457.   (let ((temp-buffer nil)
  458.     b
  459.     (case-fold-search nil))
  460.     (unwind-protect
  461.     (save-excursion
  462.       (if file
  463.           (progn
  464.         (setq b (vm-get-file-buffer file))
  465.         (if b
  466.             (set-buffer b)
  467.           (setq temp-buffer (generate-new-buffer "*vm-work*"))
  468.           (set-buffer temp-buffer)
  469.           (if (file-readable-p file)
  470.               (condition-case nil
  471.               (insert-file-contents file nil 0 4096)
  472.             (wrong-number-of-arguments
  473.              (call-process "sed" file temp-buffer nil
  474.                        "-n" "1,/^$/p")))))))
  475.       (save-excursion
  476.         (save-restriction
  477.           (or start (setq start 1))
  478.           (or end (setq end (1+ (buffer-size))))
  479.           (widen)
  480.           (narrow-to-region start end)
  481.           (goto-char (point-min))
  482.           (cond ((zerop (buffer-size)) nil)
  483.             ((looking-at "\n*From ")
  484.              (if (not vm-trust-From_-with-Content-Length)
  485.              'From_
  486.                (let ((case-fold-search t))
  487.              (re-search-forward vm-content-length-search-regexp
  488.                         nil t))
  489.                (cond ((match-beginning 1)
  490.                   'From_)
  491.                  ((match-beginning 0)
  492.                   'From_-with-Content-Length)
  493.                  (t 'From_))))
  494.             ((looking-at "\001\001\001\001\n") 'mmdf)
  495.             ((looking-at "BABYL OPTIONS:") 'babyl)
  496.             (t 'unknown)))))
  497.       (and temp-buffer (kill-buffer temp-buffer)))))
  498.  
  499. (defun vm-convert-folder-type (old-type new-type)
  500.   "Convert buffer from OLD-TYPE to NEW-TYPE.
  501. OLD-TYPE and NEW-TYPE should be symbols returned from vm-get-folder-type.
  502. This should be called on non-live buffer like crash boxes.
  503. This will confuse VM if called on a folder buffer in vm-mode."
  504.   (let ((vm-folder-type old-type)
  505.     (pos-list nil)
  506.     beg end)
  507.     (goto-char (point-min))
  508.     (vm-skip-past-folder-header)
  509.     (while (vm-find-leading-message-separator)
  510.       (setq pos-list (cons (point-marker) pos-list))
  511.       (vm-skip-past-leading-message-separator)
  512.       (setq pos-list (cons (point-marker) pos-list))
  513.       (vm-find-trailing-message-separator)
  514.       (setq pos-list (cons (point-marker) pos-list))
  515.       (vm-skip-past-trailing-message-separator)
  516.       (setq pos-list (cons (point-marker) pos-list)))
  517.     (setq pos-list (nreverse pos-list))
  518.     (goto-char (point-min))
  519.     (vm-convert-folder-header old-type new-type)
  520.     (while pos-list
  521.       (setq beg (car pos-list))
  522.       (goto-char (car pos-list))
  523.       (insert-before-markers (vm-leading-message-separator new-type))
  524.       (delete-region (car pos-list) (car (cdr pos-list)))
  525.       (vm-convert-folder-type-headers old-type new-type)
  526.       (setq pos-list (cdr (cdr pos-list)))
  527.       (setq end (marker-position (car pos-list)))
  528.       (goto-char (car pos-list))
  529.       (insert-before-markers (vm-trailing-message-separator new-type))
  530.       (delete-region (car pos-list) (car (cdr pos-list)))
  531.       (goto-char beg)
  532.       (vm-munge-message-separators new-type beg end)
  533.       (setq pos-list (cdr (cdr pos-list))))))
  534.  
  535. (defun vm-convert-folder-header (old-type new-type)
  536.   "Convert the folder header form OLD-TYPE to NEW-TYPE.
  537. The folder header is the text at the beginning of a folder that
  538. is a legal part of the folder but is not part of the first
  539. message.  This is for dealing with BABYL files."
  540.   (if (eq old-type 'babyl)
  541.       (save-excursion
  542.     (let ((beg (point))
  543.           (case-fold-search t))
  544.       (cond ((and (looking-at "BABYL OPTIONS:")
  545.               (search-forward "\037" nil t))
  546.          (delete-region beg (point)))))))
  547.   (if (eq new-type 'babyl)
  548.       ;; insert before markers so that message location markers
  549.       ;; for the first message get moved forward.
  550.       (insert-before-markers "BABYL OPTIONS:\nVersion: 5\n\037")))
  551.  
  552. (defun vm-skip-past-folder-header ()
  553.   "Move point past the folder header.
  554. The folder header is the text at the beginning of a folder that
  555. is a legal part of the folder but is not part of the first
  556. message.  This is for dealing with BABYL files."
  557.   (cond ((eq vm-folder-type 'babyl)
  558.      (search-forward "\037" nil 0))))
  559.  
  560. (defun vm-convert-folder-type-headers (old-type new-type)
  561.   "Convert headers in the message around point from OLD-TYPE to NEW-TYPE.
  562. This means to add/delete Content-Length and any other
  563. headers related to folder-type as needed for folder type
  564. conversions.  This function expects point to be at the beginning
  565. of the header section of a message, and it only deals with that
  566. message."
  567.   (let (length)
  568.     ;; get the length now before the content-length headers are
  569.     ;; removed.
  570.     (if (eq new-type 'From_-with-Content-Length)
  571.     (let (start)
  572.       (save-excursion
  573.         (save-excursion
  574.           (search-forward "\n\n" nil 0)
  575.           (setq start (point)))
  576.         (let ((vm-folder-type old-type))
  577.           (vm-find-trailing-message-separator))
  578.         (setq length (- (point) start)))))
  579.     ;; chop out content-length header if new format doesn't need
  580.     ;; it or if the new format computed his own copy.
  581.     (if (or (eq old-type 'From_-with-Content-Length)
  582.         (eq new-type 'From_-with-Content-Length))
  583.     (save-excursion
  584.       (while (and (let ((case-fold-search t))
  585.             (re-search-forward vm-content-length-search-regexp
  586.                        nil t))
  587.               (null (match-beginning 1))
  588.               (progn (goto-char (match-beginning 0))
  589.                  (vm-match-header vm-content-length-header)))
  590.         (delete-region (vm-matched-header-start)
  591.                (vm-matched-header-end)))))
  592.     ;; insert the content-length header if needed
  593.     (if (eq new-type 'From_-with-Content-Length)
  594.     (save-excursion
  595.       (insert vm-content-length-header " " (int-to-string length) "\n")))))
  596.  
  597. (defun vm-munge-message-separators (folder-type start end)
  598.   "Munge message separators of FOLDER-TYPE found between START and END.
  599. This function is used to eliminate message separators for a particular
  600. folder type that happen to occur in a message.  \">\" is prepended to such
  601. separators."
  602.   (save-excursion
  603.     (let ((vm-folder-type folder-type))
  604.       (cond ((memq folder-type '(From_ From_-with-Content-Length mmdf babyl))
  605.          (setq end (vm-marker end))
  606.          (goto-char start)
  607.          (while (and (vm-find-leading-message-separator)
  608.              (< (point) end))
  609.            (insert ">"))
  610.          (set-marker end nil))))))
  611.  
  612. (defun vm-compatible-folder-p (file)
  613.   "Return non-nil if FILE is a compatible folder with the current buffer.
  614. The current folder must have vm-folder-type initialized.
  615. FILE is compatible if
  616.   - it is empty
  617.   - the current folder is empty
  618.   - the two folder types are equal"
  619.   (let ((type (vm-get-folder-type file)))
  620.     (or (not (and vm-folder-type type))
  621.     (eq vm-folder-type type))))
  622.  
  623. (defun vm-leading-message-separator (&optional folder-type message
  624.                      for-other-folder)
  625.   "Returns a leading message separator for the current folder.
  626. Defaults to returning a separator for the current folder type.
  627.  
  628. Optional first arg FOLDER-TYPE means return a separator for that
  629. folder type instead.
  630.  
  631. Optional second arg MESSAGE should be a message struct.  This is used
  632. generating BABYL separators, because they contain message attributes
  633. and labels that must must be copied from the message.
  634.  
  635. Optional third arg FOR-OTHER-FOLDER non-nil means that this separator will
  636. be used a `foreign' folder.  This means that the `deleted'
  637. attributes should not be copied for BABYL folders."
  638.   (let ((type (or folder-type vm-folder-type)))
  639.     (cond ((memq type '(From_ From_-with-Content-Length))
  640.        (concat "From VM " (current-time-string) "\n"))
  641.       ((eq type 'mmdf)
  642.        "\001\001\001\001\n")
  643.       ((eq type 'babyl)
  644.        (cond (message
  645.           (concat "\014\n0,"
  646.               (vm-babyl-attributes-string message for-other-folder)
  647.               ",\n*** EOOH ***\n"))
  648.          (t "\014\n0, recent, unseen,,\n*** EOOH ***\n"))))))
  649.  
  650. (defun vm-trailing-message-separator (&optional folder-type)
  651.   "Returns a leading message separator for the current folder.
  652. Defaults to returning a separator for the current folder type.
  653.  
  654. Optional first arg FOLDER-TYPE means return a separator for that
  655. folder type instead."
  656.   (let ((type (or folder-type vm-folder-type)))
  657.     (cond ((eq type 'From_) "\n")
  658.       ((eq type 'From_-with-Content-Length) "")
  659.       ((eq type 'mmdf) "\001\001\001\001\n")
  660.       ((eq type 'babyl) "\037"))))
  661.  
  662. (defun vm-folder-header (&optional folder-type label-obarray)
  663.   "Returns a folder header for the current folder.
  664. Defaults to returning a folder header for the current folder type.
  665.  
  666. Optional first arg FOLDER-TYPE means return a folder header for that
  667. folder type instead.
  668.  
  669. Optional second arg LABEL-OBARRAY should be an obarray of labels
  670. that have been used in this folder.  This is used for BABYL folders."
  671.   (let ((type (or folder-type vm-folder-type)))
  672.     (cond ((eq type 'babyl)
  673.        (let ((list nil))
  674.          (if label-obarray
  675.          (mapatoms (function
  676.                 (lambda (sym)
  677.                   (setq list (cons sym list))))
  678.                label-obarray))
  679.          (if list
  680.          (format "BABYL OPTIONS:\nVersion: 5\nLabels: %s\n\037"
  681.              (mapconcat (function symbol-name) list ", "))
  682.            "BABYL OPTIONS:\nVersion: 5\n\037")))
  683.       (t ""))))
  684.  
  685. (defun vm-find-leading-message-separator ()
  686.   "Find the next leading message separator in a folder.
  687. Returns non-nil if the separator is found, nil otherwise."
  688.   (cond
  689.    ((eq vm-folder-type 'From_)
  690.     (let ((reg1 "^From ")
  691.       (reg2 "^>From ")
  692.       (case-fold-search nil))
  693.       (catch 'done
  694.     (while (re-search-forward reg1 nil 'no-error)
  695.       (goto-char (match-beginning 0))
  696.           ;; remove the requirement that there be two
  697.           ;; consecutive newlines (or the beginning of the
  698.           ;; buffer) before "From ".  Hopefully this will not
  699.           ;; break more than it fixes.  (18 August 1995)
  700.       (if ;; (and (or (< (point) 3)
  701.               ;;          (equal (char-after (- (point) 2)) ?\n))
  702.            (save-excursion
  703.              (and (= 0 (forward-line 1))
  704.               (or (vm-match-header)
  705.                   (looking-at reg2))))
  706.           ;; )
  707.           (throw 'done t)
  708.         (forward-char 1)))
  709.     nil )))
  710.    ((eq vm-folder-type 'From_-with-Content-Length)
  711.     (let ((reg1 "\\(^\\|\n+\\)From ")
  712.       (case-fold-search nil))
  713.       (if (re-search-forward reg1 nil 'no-error)
  714.       (progn (goto-char (match-end 1)) t)
  715.     nil )))
  716.    ((eq vm-folder-type 'mmdf)
  717.     (let ((reg1 "^\001\001\001\001")
  718.       (case-fold-search nil))
  719.       (if (re-search-forward reg1 nil 'no-error)
  720.       (progn
  721.         (goto-char (match-beginning 0))
  722.         t )
  723.     nil )))
  724.    ((eq vm-folder-type 'babyl)
  725.     (let ((reg1 "\014\n[01],")
  726.       (case-fold-search nil))
  727.       (catch 'done
  728.     (while (re-search-forward reg1 nil 'no-error)
  729.       (goto-char (match-beginning 0))
  730.       (if (and (not (bobp)) (= (preceding-char) ?\037))
  731.           (throw 'done t)
  732.         (forward-char 1)))
  733.     nil )))))
  734.  
  735. (defun vm-find-trailing-message-separator ()
  736.   "Find the next trailing message separator in a folder."
  737.   (cond
  738.    ((eq vm-folder-type 'From_)
  739.     (vm-find-leading-message-separator)
  740.     (forward-char -1))
  741.    ((eq vm-folder-type 'From_-with-Content-Length)
  742.     (let ((reg1 "^From ")
  743.       content-length
  744.       (start-point (point))
  745.       (case-fold-search nil))
  746.       (if (and (let ((case-fold-search t))
  747.          (re-search-forward vm-content-length-search-regexp nil t))
  748.            (null (match-beginning 1))
  749.            (progn (goto-char (match-beginning 0))
  750.               (vm-match-header vm-content-length-header)))
  751.       (progn
  752.         (setq content-length
  753.           (string-to-int (vm-matched-header-contents)))
  754.         ;; if search fails, we'll be at point-max
  755.         ;; if specified content-length is too long, go to point-max
  756.         (if (search-forward "\n\n" nil 0)
  757.         (if (>= (- (point-max) (point)) content-length)
  758.             (forward-char content-length)
  759.           (goto-char (point-max))))
  760.         ;; Some systems seem to add a trailing newline that's
  761.         ;; not counted in the Content-Length header.  Allow
  762.         ;; any number of them to avoid trouble.
  763.         (skip-chars-forward "\n")))
  764.       (if (or (eobp) (looking-at reg1))
  765.       nil
  766.     (goto-char start-point)
  767.     (if (re-search-forward reg1 nil 0)
  768.         (forward-char -5)))))
  769.    ((eq vm-folder-type 'mmdf)
  770.     (vm-find-leading-message-separator))
  771.    ((eq vm-folder-type 'babyl)
  772.     (vm-find-leading-message-separator)
  773.     (forward-char -1))))
  774.  
  775. (defun vm-skip-past-leading-message-separator ()
  776.   "Move point past a leading message separator at point."
  777.   (cond
  778.    ((memq vm-folder-type '(From_ From_-with-Content-Length))
  779.     (let ((reg1 "^>From ")
  780.       (case-fold-search nil))
  781.       (forward-line 1)
  782.       (while (looking-at reg1)
  783.     (forward-line 1))))
  784.    ((eq vm-folder-type 'mmdf)
  785.     (forward-char 5)
  786.     ;; skip >From.  Either SCO's MMDF implementation leaves this
  787.     ;; stuff in the message, or many sysadmins have screwed up
  788.     ;; their mail configuration.  Either way I'm tired of getting
  789.     ;; bug reports about it.
  790.     (let ((reg1 "^>From ")
  791.       (case-fold-search nil))
  792.       (while (looking-at reg1)
  793.     (forward-line 1))))
  794.    ((eq vm-folder-type 'babyl)
  795.     (search-forward "\n*** EOOH ***\n" nil 0))))
  796.  
  797. (defun vm-skip-past-trailing-message-separator ()
  798.   "Move point past a trailing message separator at point."
  799.   (cond
  800.    ((eq vm-folder-type 'From_)
  801.     (forward-char 1))
  802.    ((eq vm-folder-type 'From_-with-Content-Length))
  803.    ((eq vm-folder-type 'mmdf)
  804.     (forward-char 5))
  805.    ((eq vm-folder-type 'babyl)
  806.     (forward-char 1))))
  807.  
  808. (defun vm-build-message-list ()
  809.   "Build a chain of message structures, stored them in vm-message-list.
  810. Finds the start and end of each message and fills in the relevant
  811. fields in the message structures.
  812.  
  813. Also finds the beginning of the header section and the end of the
  814. text section and fills in these fields in the message structures.
  815.  
  816. vm-text-of and vm-vheaders-of field don't get filled until they
  817. are needed.
  818.  
  819. If vm-message-list already contained messages, the end of the last
  820. known message is found and then the parsing of new messages begins
  821. there and the message are appended to vm-message-list.
  822.  
  823. vm-folder-type is initialized here."
  824.   (setq vm-folder-type (vm-get-folder-type))
  825.   (save-excursion
  826.     (let ((tail-cons nil)
  827.       (n 0)
  828.       ;; Just for yucks, make the update interval vary.
  829.       (modulus (+ (% (vm-abs (random)) 11) 25))
  830.       message last-end)
  831.       (if vm-message-list
  832.       ;; there are already messages, therefore we're supposed
  833.       ;; to add to this list.
  834.       (let ((mp vm-message-list)
  835.         (end (point-min)))
  836.         ;; first we have to find physical end of the folder
  837.         ;; prior to the new messages that just came in.
  838.         (while mp
  839.           (if (< end (vm-end-of (car mp)))
  840.           (setq end (vm-end-of (car mp))))
  841.           (if (not (consp (cdr mp)))
  842.           (setq tail-cons mp))
  843.           (setq mp (cdr mp)))
  844.         (goto-char end))
  845.     ;; there are no messages so we're building the whole list.
  846.     ;; start from the beginning of the folder.
  847.     (goto-char (point-min))
  848.     ;; whine about newlines at the beginning of the folder.
  849.     ;; technically I think this is corruption, but there are
  850.     ;; too many busted mail-do-fcc's installed out there to
  851.     ;; do more than whine.
  852.     (if (and (memq vm-folder-type '(From_ From_-with-Content-Length))
  853.          (= (following-char) ?\n))
  854.         (progn
  855.           (message "Warning: newline found at beginning of folder, %s"
  856.                (or buffer-file-name (buffer-name)))
  857.           (sleep-for 2)))
  858.     (vm-skip-past-folder-header))
  859.       (setq last-end (point))
  860.       ;; parse the messages, set the markers that specify where
  861.       ;; things are.
  862.       (while (vm-find-leading-message-separator)
  863.     (setq message (vm-make-message))
  864.     (vm-set-message-type-of message vm-folder-type)
  865.     (vm-set-start-of message (vm-marker (point)))
  866.     (vm-skip-past-leading-message-separator)
  867.     (vm-set-headers-of message (vm-marker (point)))
  868.     (vm-find-trailing-message-separator)
  869.     (vm-set-text-end-of message (vm-marker (point)))
  870.     (vm-skip-past-trailing-message-separator)
  871.     (setq last-end (point))
  872.     (vm-set-end-of message (vm-marker (point)))
  873.     (vm-set-reverse-link-of message tail-cons)
  874.     (if (null tail-cons)
  875.         (setq vm-message-list (list message)
  876.           tail-cons vm-message-list)
  877.       (setcdr tail-cons (list message))
  878.       (setq tail-cons (cdr tail-cons)))
  879.     (vm-increment n)
  880.     (if (zerop (% n modulus))
  881.         (vm-unsaved-message "Parsing messages... %d" n)))
  882.       (if (>= n modulus)
  883.       (vm-unsaved-message "Parsing messages... done"))
  884.       (if (and (not (= last-end (point-max)))
  885.            (not (eq vm-folder-type 'unknown)))
  886.       (progn
  887.         (message "Warning: garbage found at end of folder, %s"
  888.              (or buffer-file-name (buffer-name)))
  889.         (sleep-for 2))))))
  890.  
  891. (defun vm-build-header-order-alist (vheaders)
  892.   (let ((order-alist (cons nil nil))
  893.     list)
  894.     (setq list order-alist)
  895.     (while vheaders
  896.       (setcdr list (cons (cons (car vheaders) nil) nil))
  897.       (setq list (cdr list) vheaders (cdr vheaders)))
  898.     (cdr order-alist)))
  899.  
  900. ;; Reorder the headers in a message.
  901. ;;
  902. ;; If a message struct is passed into this function, then we're
  903. ;; operating on a message in a folder buffer.  Headers are
  904. ;; grouped so that the headers that the user wants to see are at
  905. ;; the end of the headers section so we can narrow to them.  This
  906. ;; is done according to the preferences specified in
  907. ;; vm-visible-header and vm-invisible-header-regexp.  The
  908. ;; vheaders field of the message struct is also set.  This
  909. ;; function is called on demand whenever a vheaders field is
  910. ;; discovered to be nil for a particular message.
  911. ;;
  912. ;; If the message argument is nil, then we are operating on a
  913. ;; freestanding message that is not part of a folder buffer.  The
  914. ;; keep-list and discard-regexp parameters are used in this case.
  915. ;; Headers not matched by the keep list or matched by the discard
  916. ;; list are stripped from the message.  The remaining headers
  917. ;; are ordered according to the order of the keep list.
  918.  
  919. (defun vm-reorder-message-headers (message keep-list discard-regexp)
  920.   (save-excursion
  921.     (if message
  922.     (progn
  923.       (set-buffer (vm-buffer-of message))
  924.       (setq keep-list vm-visible-headers
  925.         discard-regexp vm-invisible-header-regexp)))
  926.     (save-excursion
  927.       (save-restriction
  928.     (widen)
  929.     ;; if there is a cached regexp that points to the already
  930.     ;; ordered headers then use it and avoid a lot of work.
  931.     (if (and message (vm-vheaders-regexp-of message))
  932.         (save-excursion
  933.           (goto-char (vm-headers-of message))
  934.           (let ((case-fold-search t))
  935.         (re-search-forward (vm-vheaders-regexp-of message)
  936.                    (vm-text-of message) t))
  937.           (vm-set-vheaders-of message (vm-marker (match-beginning 0))))
  938.       ;; oh well, we gotta do it the hard way.
  939.       ;;
  940.       ;; header-alist will contain an assoc list version of
  941.       ;; keep-list.  For messages associated with a folder
  942.       ;; buffer:  when a matching header is found, the header
  943.       ;; is stuffed into its corresponding assoc cell and the
  944.       ;; header text is deleted from the buffer.  After all
  945.       ;; the visible headers have been collected, they are
  946.       ;; inserted into the buffer in a clump at the end of
  947.       ;; the header section.  Unmatched headers are skipped over.
  948.       ;;
  949.       ;; For free standing messages, unmatched headers are
  950.       ;; stripped from the message.
  951.       (vm-save-restriction
  952.        (let ((header-alist (vm-build-header-order-alist keep-list))
  953.          (buffer-read-only nil)
  954.          (work-buffer nil)
  955.          (extras nil)
  956.          list end-of-header vheader-offset
  957.          (folder-buffer (current-buffer))
  958.          ;; This prevents file locking from occuring.  Disabling
  959.          ;; locking can speed things noticeably if the lock directory
  960.          ;; is on a slow device.  We don't need locking here because
  961.          ;; in a mail context reordering headers is harmless.
  962.          (buffer-file-name nil)
  963.          (case-fold-search t)
  964.          (old-buffer-modified-p (buffer-modified-p)))
  965.          (unwind-protect
  966.          (progn
  967.            (if message
  968.                (progn
  969.              ;; for babyl folders, keep an untouched
  970.              ;; copy of the headers between the
  971.              ;; attributes line and the *** EOOH ***
  972.              ;; line.
  973.              (if (and (eq vm-folder-type 'babyl)
  974.                   (null (vm-babyl-frob-flag-of message)))
  975.                  (progn
  976.                    (goto-char (vm-start-of message))
  977.                    (forward-line 2)
  978.                    (vm-set-babyl-frob-flag-of message t)
  979.                    (insert-buffer-substring
  980.                 (current-buffer)
  981.                 (vm-headers-of message)
  982.                 (1- (vm-text-of message)))))
  983.              (setq work-buffer (generate-new-buffer "*vm-work*"))
  984.              (set-buffer work-buffer)
  985.              (insert-buffer-substring
  986.               folder-buffer 
  987.               (vm-headers-of message)
  988.               (vm-text-of message))
  989.              (goto-char (point-min))))
  990.            (while (and (not (= (following-char) ?\n))
  991.                    (vm-match-header))
  992.              (setq end-of-header (vm-matched-header-end)
  993.                list (vm-match-ordered-header header-alist))
  994.              ;; don't display/keep this header if
  995.              ;;  keep-list not matched
  996.              ;;  and discard-regexp is nil
  997.              ;;       or
  998.              ;;  discard-regexp is matched
  999.              (if (or (and (null list) (null discard-regexp))
  1000.                  (and discard-regexp (looking-at discard-regexp)))
  1001.              ;; skip the unwanted header if doing
  1002.              ;; work for a folder buffer, otherwise
  1003.              ;; discard the header.
  1004.              (if message
  1005.                  (goto-char end-of-header)
  1006.                (delete-region (point) end-of-header))
  1007.                ;; got a match
  1008.                ;; stuff the header into the cdr of the
  1009.                ;; returned alist element
  1010.                (if list
  1011.                (if (cdr list)
  1012.                    (setcdr list 
  1013.                        (concat
  1014.                     (cdr list)
  1015.                     (buffer-substring (point)
  1016.                               end-of-header)))
  1017.                  (setcdr list (buffer-substring (point)
  1018.                                 end-of-header)))
  1019.              (setq extras
  1020.                    (cons (buffer-substring (point) end-of-header)
  1021.                      extras)))
  1022.                (delete-region (point) end-of-header)))
  1023.            ;; remember the offset of where the visible
  1024.            ;; header start so we can initialize the
  1025.            ;; vm-vheaders-of field later.
  1026.            (if message
  1027.                (setq vheader-offset (1- (point))))
  1028.            ;; now dump out the headers we saved.
  1029.            ;; the keep-list headers go first.
  1030.            (setq list header-alist)
  1031.            (while list
  1032.              (if (cdr (car list))
  1033.              (progn
  1034.                (insert (cdr (car list)))
  1035.                (setcdr (car list) nil)))
  1036.              (setq list (cdr list)))
  1037.            ;; now the headers that were not explicitly
  1038.            ;; undesirable, if any.
  1039.            (if extras
  1040.                (progn
  1041.              (setq extras (nreverse extras))
  1042.              (while extras
  1043.                (insert (car extras))
  1044.                (setq extras (cdr extras)))))
  1045.            ;; update the folder buffer if we're supposed to.
  1046.            ;; lock out interrupts.
  1047.            (if message
  1048.                (let ((inhibit-quit t))
  1049.              (set-buffer (vm-buffer-of message))
  1050.              (goto-char (vm-headers-of message))
  1051.              (insert-buffer-substring work-buffer)
  1052.              (delete-region (point) (vm-text-of message))
  1053.              (set-buffer-modified-p old-buffer-modified-p))))
  1054.            (and work-buffer (kill-buffer work-buffer)))
  1055.          (if message
  1056.          (progn
  1057.            (vm-set-vheaders-of message
  1058.                        (vm-marker (+ (vm-headers-of message)
  1059.                              vheader-offset)))
  1060.            ;; cache a regular expression that can be used to
  1061.            ;; find the start of the reordered header the next
  1062.            ;; time this folder is visited.
  1063.            (goto-char (vm-vheaders-of message))
  1064.            (if (vm-match-header)
  1065.                (vm-set-vheaders-regexp-of
  1066.             message
  1067.             (concat "^" (vm-matched-header-name) ":"))))))))))))
  1068.  
  1069. ;; Reads the message attributes and cached header information from the
  1070. ;; header portion of the each message, if our X-VM- attributes header is
  1071. ;; present.  If the header is not present, assume the message is new,
  1072. ;; unless we are being compatible with Berkeley Mail in which case we
  1073. ;; also check for a Status header.
  1074. ;;
  1075. ;; If a message already has attributes don't bother checking the
  1076. ;; headers.
  1077. ;;
  1078. ;; This function also discovers and stores the position where the
  1079. ;; message text begins.
  1080. ;;
  1081. ;; Totals are gathered for use by vm-emit-totals-blurb.
  1082. ;;
  1083. ;; Supports version 4 format of attribute storage, for backward compatibility.
  1084.  
  1085. (defun vm-read-attributes (message-list)
  1086.   (save-excursion
  1087.     (let ((mp (or message-list vm-message-list))
  1088.       (vm-new-count 0)
  1089.       (vm-unread-count 0)
  1090.       (vm-deleted-count 0)
  1091.       (vm-total-count 0)
  1092.       (modulus (+ (% (vm-abs (random)) 11) 25))
  1093.       (case-fold-search t)
  1094.       data)
  1095.       (while mp
  1096.     (vm-increment vm-total-count)
  1097.     (if (vm-attributes-of (car mp))
  1098.         ()
  1099.       (goto-char (vm-headers-of (car mp)))
  1100.       ;; find start of text section and save it
  1101.       (search-forward "\n\n" (vm-text-end-of (car mp)) 0)
  1102.       (vm-set-text-of (car mp) (point-marker))
  1103.       ;; now look for our header
  1104.       (goto-char (vm-headers-of (car mp)))
  1105.       (cond
  1106.        ((re-search-forward vm-attributes-header-regexp
  1107.                    (vm-text-of (car mp)) t)
  1108.         (goto-char (match-beginning 2))
  1109.         (condition-case ()
  1110.         (setq data (read (current-buffer)))
  1111.           (error (setq data
  1112.                (list
  1113.                 (make-vector vm-attributes-vector-length nil)
  1114.                 (make-vector vm-cache-vector-length nil)
  1115.                 nil))
  1116.              ;; In lieu of a valid attributes header
  1117.              ;; assume the message is new.  avoid
  1118.              ;; vm-set-new-flag because it asks for a
  1119.              ;; summary update.
  1120.              (vm-set-new-flag-in-vector (car data) t)))
  1121.         ;; support version 4 format
  1122.         (cond ((vectorp data)
  1123.            (setq data (vm-convert-v4-attributes data))
  1124.            ;; tink the message modflag so that if the
  1125.            ;; user saves we get rid of the old v4
  1126.            ;; attributes header.  otherwise we could be
  1127.            ;; dealing with these things for all eternity.
  1128.            (vm-set-modflag-of (car mp) t))
  1129.           (t
  1130.            ;; extend vectors if necessary to accomodate
  1131.            ;; more caching and attributes without alienating
  1132.            ;; other version 5 folders.
  1133.            (cond ((< (length (car data))
  1134.                  vm-attributes-vector-length)
  1135.               ;; tink the message modflag so that if
  1136.               ;; the user saves we get rid of the old
  1137.               ;; short vector.  otherwise we could be
  1138.               ;; dealing with these things for all
  1139.               ;; eternity.
  1140.               (vm-set-modflag-of (car mp) t)
  1141.               (setcar data (vm-extend-vector
  1142.                     (car data)
  1143.                     vm-attributes-vector-length))))
  1144.            (cond ((< (length (car (cdr data)))
  1145.                  vm-cache-vector-length)
  1146.               ;; tink the message modflag so that if
  1147.               ;; the user saves we get rid of the old
  1148.               ;; short vector.  otherwise we could be
  1149.               ;; dealing with these things for all
  1150.               ;; eternity.
  1151.               (vm-set-modflag-of (car mp) t)
  1152.               (setcar (cdr data)
  1153.                   (vm-extend-vector
  1154.                    (car (cdr data))
  1155.                    vm-cache-vector-length))))))
  1156.         (vm-set-labels-of (car mp) (nth 2 data))
  1157.         (vm-set-cache-of (car mp) (car (cdr data)))
  1158.         (vm-set-attributes-of (car mp) (car data)))
  1159.        ((and vm-berkeley-mail-compatibility
  1160.          (re-search-forward vm-berkeley-mail-status-header-regexp
  1161.                     (vm-text-of (car mp)) t))
  1162.         (vm-set-cache-of (car mp) (make-vector vm-cache-vector-length
  1163.                            nil))
  1164.         (goto-char (match-beginning 1))
  1165.         (vm-set-attributes-of
  1166.          (car mp)
  1167.          (make-vector vm-attributes-vector-length nil))
  1168.         (vm-set-unread-flag (car mp) (not (looking-at ".*R.*")) t))
  1169.        (t
  1170.         (vm-set-cache-of (car mp) (make-vector vm-cache-vector-length
  1171.                            nil))
  1172.         (vm-set-attributes-of
  1173.          (car mp)
  1174.          (make-vector vm-attributes-vector-length nil))
  1175.         ;; In lieu of a valid attributes header
  1176.         ;; assume the message is new.  avoid
  1177.         ;; vm-set-new-flag because it asks for a
  1178.         ;; summary update.
  1179.         (vm-set-new-flag-of (car mp) t)))
  1180.       ;; let babyl attributes override the normal VM
  1181.       ;; attributes header.
  1182.       (cond ((eq vm-folder-type 'babyl)
  1183.          (vm-read-babyl-attributes (car mp)))))
  1184.     (cond ((vm-deleted-flag (car mp))
  1185.            (vm-increment vm-deleted-count))
  1186.           ((vm-new-flag (car mp))
  1187.            (vm-increment vm-new-count))
  1188.           ((vm-unread-flag (car mp))
  1189.            (vm-increment vm-unread-count)))
  1190.     (if (zerop (% vm-total-count modulus))
  1191.         (vm-unsaved-message "Reading attributes... %d" vm-total-count))
  1192.     (setq mp (cdr mp)))
  1193.       (if (>= vm-total-count modulus)
  1194.       (vm-unsaved-message "Reading attributes... done"))
  1195.       (if (null message-list)
  1196.       (setq vm-totals (list vm-modification-counter
  1197.                 vm-total-count
  1198.                 vm-new-count
  1199.                 vm-unread-count
  1200.                 vm-deleted-count))))))
  1201.  
  1202. (defun vm-read-babyl-attributes (message)
  1203.   (let ((case-fold-search t)
  1204.     (labels nil)
  1205.     (vect (make-vector vm-attributes-vector-length nil)))
  1206.     (vm-set-attributes-of message vect)
  1207.     (save-excursion
  1208.       (goto-char (vm-start-of message))
  1209.       ;; skip past ^L\n
  1210.       (forward-char 2)
  1211.       (vm-set-babyl-frob-flag-of message (if (= (following-char) ?1) t nil))
  1212.       ;; skip past 0,
  1213.       (forward-char 2)
  1214.       ;; loop, noting attributes as we go.
  1215.       (while (and (not (eobp)) (not (looking-at ",")))
  1216.     (cond ((looking-at " unseen,")
  1217.            (vm-set-unread-flag-of message t))
  1218.           ((looking-at " recent,")
  1219.            (vm-set-new-flag-of message t))
  1220.           ((looking-at " deleted,")
  1221.            (vm-set-deleted-flag-of message t))
  1222.           ((looking-at " answered,")
  1223.            (vm-set-replied-flag-of message t))
  1224.           ((looking-at " forwarded,")
  1225.            (vm-set-forwarded-flag-of message t))
  1226.           ((looking-at " filed,")
  1227.            (vm-set-filed-flag-of message t))
  1228.           ((looking-at " redistributed,")
  1229.            (vm-set-redistributed-flag-of message t))
  1230.           ;; only VM knows about these, as far as I know.
  1231.           ((looking-at " edited,")
  1232.            (vm-set-forwarded-flag-of message t))
  1233.           ((looking-at " written,")
  1234.            (vm-set-forwarded-flag-of message t)))
  1235.     (skip-chars-forward "^,")
  1236.     (and (not (eobp)) (forward-char 1)))
  1237.       (and (not (eobp)) (forward-char 1))
  1238.       (while (looking-at " \\([^\000-\040,\177-\377]+\\),")
  1239.     (setq labels (cons (vm-buffer-substring-no-properties
  1240.                 (match-beginning 1)
  1241.                 (match-end 1))
  1242.                labels))
  1243.     (goto-char (match-end 0)))
  1244.       (vm-set-labels-of message labels))))
  1245.  
  1246. (defun vm-set-default-attributes (message-list)
  1247.   (let ((mp (or message-list vm-message-list)) attr cache)
  1248.     (while mp
  1249.       (setq attr (make-vector vm-attributes-vector-length nil)
  1250.         cache (make-vector vm-cache-vector-length nil))
  1251.       (vm-set-cache-of (car mp) cache)
  1252.       (vm-set-attributes-of (car mp) attr)
  1253.       ;; make message be new by default, but avoid vm-set-new-flag
  1254.       ;; because it asks for a summary update for the message.
  1255.       (vm-set-new-flag-of (car mp) t)
  1256.       ;; since this function is usually called in lieu of reading
  1257.       ;; attributes from the buffer, the attributes may be
  1258.       ;; untrustworthy.  tink the message modflag to force the
  1259.       ;; new attributes out if the user saves.
  1260.       (vm-set-modflag-of (car mp) t)
  1261.       (setq mp (cdr mp)))))
  1262.  
  1263. (defun vm-emit-totals-blurb ()
  1264.   (save-excursion
  1265.     (vm-select-folder-buffer)
  1266.     (if (not (equal (nth 0 vm-totals) vm-modification-counter))
  1267.     (let ((mp vm-message-list)
  1268.           (vm-new-count 0)
  1269.           (vm-unread-count 0)
  1270.           (vm-deleted-count 0)
  1271.           (vm-total-count 0))
  1272.       (while mp
  1273.         (vm-increment vm-total-count)
  1274.         (cond ((vm-deleted-flag (car mp))
  1275.            (vm-increment vm-deleted-count))
  1276.           ((vm-new-flag (car mp))
  1277.            (vm-increment vm-new-count))
  1278.           ((vm-unread-flag (car mp))
  1279.            (vm-increment vm-unread-count)))
  1280.         (setq mp (cdr mp)))
  1281.       (setq vm-totals (list vm-modification-counter
  1282.                 vm-total-count
  1283.                 vm-new-count
  1284.                 vm-unread-count
  1285.                 vm-deleted-count))))
  1286.     (if (equal (nth 1 vm-totals) 0)
  1287.     (message "No messages.")
  1288.       (message "%d message%s, %d new, %d unread, %d deleted"
  1289.            (nth 1 vm-totals) (if (= (nth 1 vm-totals) 1) "" "s")
  1290.            (nth 2 vm-totals)
  1291.            (nth 3 vm-totals)
  1292.            (nth 4 vm-totals)))))
  1293.  
  1294. (defun vm-convert-v4-attributes (data)
  1295.   (list (apply 'vector
  1296.            (nconc (vm-vector-to-list data)
  1297.               (make-list (- vm-attributes-vector-length
  1298.                     (length data))
  1299.                  nil)))
  1300.     (make-vector vm-cache-vector-length nil)))
  1301.  
  1302. (defun vm-gobble-labels ()
  1303.   (let ((case-fold-search t)
  1304.     lim)
  1305.     (save-excursion
  1306.       (vm-save-restriction
  1307.        (widen)
  1308.        (if (eq vm-folder-type 'babyl)
  1309.        (progn
  1310.          (goto-char (point-min))
  1311.          (vm-skip-past-folder-header)
  1312.          (setq lim (point))
  1313.          (goto-char (point-min))
  1314.          (if (re-search-forward "^Labels:" lim t)
  1315.          (let (string list)
  1316.            (setq string (buffer-substring
  1317.                  (point)
  1318.                  (progn (end-of-line) (point)))
  1319.              list (vm-parse string
  1320. "[\000-\040,\177-\377]*\\([^\000-\040,\177-\377]+\\)[\000-\040,\177-\377]*"))
  1321.            (mapcar (function
  1322.                 (lambda (s)
  1323.                   (intern (downcase s) vm-label-obarray)))
  1324.                list))))
  1325.      (goto-char (point-min))
  1326.      (vm-skip-past-folder-header)
  1327.      (vm-skip-past-leading-message-separator)
  1328.      (search-forward "\n\n" nil t)
  1329.      (setq lim (point))
  1330.      (goto-char (point-min))
  1331.      (vm-skip-past-folder-header)
  1332.      (vm-skip-past-leading-message-separator)
  1333.      (if (re-search-forward vm-labels-header-regexp lim t)
  1334.          (let (list)
  1335.            (setq list (read (current-buffer)))
  1336.            (mapcar (function
  1337.             (lambda (s)
  1338.               (intern s vm-label-obarray)))
  1339.                list))))))
  1340.     t ))
  1341.  
  1342. ;; Go to the message specified in a bookmark and eat the bookmark.
  1343. ;; Returns non-nil if successful, nil otherwise.
  1344. (defun vm-gobble-bookmark ()
  1345.   (let ((case-fold-search t)
  1346.     n lim)
  1347.     (save-excursion
  1348.       (vm-save-restriction
  1349.        (widen)
  1350.        (goto-char (point-min))
  1351.        (vm-skip-past-folder-header)
  1352.        (vm-skip-past-leading-message-separator)
  1353.        (search-forward "\n\n" nil t)
  1354.        (setq lim (point))
  1355.        (goto-char (point-min))
  1356.        (vm-skip-past-folder-header)
  1357.        (vm-skip-past-leading-message-separator)
  1358.        (if (re-search-forward vm-bookmark-header-regexp lim t)
  1359.        (setq n (read (current-buffer))))))
  1360.     (if n
  1361.     (vm-record-and-change-message-pointer
  1362.      vm-message-pointer
  1363.      (nthcdr (1- n) vm-message-list)))
  1364.     t ))
  1365.  
  1366. (defun vm-gobble-visible-header-variables ()
  1367.   (save-excursion
  1368.     (vm-save-restriction
  1369.      (let ((case-fold-search t)
  1370.        lim)
  1371.        (widen)
  1372.        (goto-char (point-min))
  1373.        (vm-skip-past-folder-header)
  1374.        (vm-skip-past-leading-message-separator)
  1375.        (search-forward "\n\n" nil t)
  1376.        (setq lim (point))
  1377.        (goto-char (point-min))
  1378.        (vm-skip-past-folder-header)
  1379.        (vm-skip-past-leading-message-separator)
  1380.        (if (re-search-forward vm-vheader-header-regexp lim t)
  1381.        (let (vis invis (got nil))
  1382.          (condition-case ()
  1383.          (setq vis (read (current-buffer))
  1384.                invis (read (current-buffer))
  1385.                got t)
  1386.            (error nil))
  1387.          ;; if the variables don't match the values stored when this
  1388.          ;; folder was saved, then we have to discard any cached
  1389.          ;; vheader info so the user will see the right headers.
  1390.          (and got (or (not (equal vis vm-visible-headers))
  1391.               (not (equal invis vm-invisible-header-regexp)))
  1392.           (let ((mp vm-message-list))
  1393.             (vm-unsaved-message "Discarding visible header info...")
  1394.             (while mp
  1395.               (vm-set-vheaders-regexp-of (car mp) nil)
  1396.               (vm-set-vheaders-of (car mp) nil)
  1397.               (setq mp (cdr mp)))))))))))
  1398.  
  1399. ;; Read and delete the header that gives the folder's desired
  1400. ;; message order.
  1401. (defun vm-gobble-message-order ()
  1402.   (let ((case-fold-search t)
  1403.     lim v order
  1404.     (mp vm-message-list)
  1405.     list-length)
  1406.     (save-excursion
  1407.       (save-restriction
  1408.     (widen)
  1409.     (goto-char (point-min))
  1410.     (vm-skip-past-folder-header)
  1411.     (vm-skip-past-leading-message-separator)
  1412.     (search-forward "\n\n" nil t)
  1413.     (setq lim (point))
  1414.     (goto-char (point-min))
  1415.     (vm-skip-past-folder-header)
  1416.     (vm-skip-past-leading-message-separator)
  1417.     (if (re-search-forward vm-message-order-header-regexp lim t)
  1418.         (progn
  1419.           (vm-unsaved-message "Reordering messages...")
  1420.           (setq order (read (current-buffer))
  1421.             list-length (length vm-message-list)
  1422.             v (make-vector (max list-length (length order)) nil))
  1423.           (while (and order mp)
  1424.         (aset v (1- (car order)) (car mp))
  1425.         (setq order (cdr order) mp (cdr mp)))
  1426.           ;; lock out interrupts while the message list is in
  1427.           ;; an inconsistent state.
  1428.           (let ((inhibit-quit t))
  1429.         (setq vm-message-list (delq nil (append v mp))
  1430.               vm-message-order-changed nil
  1431.               vm-message-order-header-present t
  1432.               vm-message-pointer (memq (car vm-message-pointer)
  1433.                            vm-message-list))
  1434.         (vm-set-numbering-redo-start-point t)
  1435.         (vm-reverse-link-messages))
  1436.           (vm-unsaved-message "Reordering messages... done")))))))
  1437.  
  1438. ;; Read the header that gives the folder's cached summary format
  1439. ;; If the current summary format is different, then the cached
  1440. ;; summary lines are discarded.
  1441. (defun vm-gobble-summary ()
  1442.   (let ((case-fold-search t)
  1443.     summary lim
  1444.     (mp vm-message-list))
  1445.     (save-excursion
  1446.       (vm-save-restriction
  1447.        (widen)
  1448.        (goto-char (point-min))
  1449.        (vm-skip-past-folder-header)
  1450.        (vm-skip-past-leading-message-separator)
  1451.        (search-forward "\n\n" nil t)
  1452.        (setq lim (point))
  1453.        (goto-char (point-min))
  1454.        (vm-skip-past-folder-header)
  1455.        (vm-skip-past-leading-message-separator)
  1456.        (if (re-search-forward vm-summary-header-regexp lim t)
  1457.        (progn
  1458.          (setq summary (read (current-buffer)))
  1459.          (if (not (equal summary vm-summary-format))
  1460.          (while mp
  1461.            (vm-set-summary-of (car mp) nil)
  1462.            ;; force restuffing of cache to clear old
  1463.            ;; summary entry cache.
  1464.            (vm-set-modflag-of (car mp) t)
  1465.            (setq mp (cdr mp))))))))))
  1466.  
  1467. ;; Stuff the message attributes back into the message as headers.
  1468. (defun vm-stuff-attributes (m &optional for-other-folder)
  1469.   (save-excursion
  1470.     (vm-save-restriction
  1471.      (widen)
  1472.      (let ((old-buffer-modified-p (buffer-modified-p))
  1473.        attributes cache
  1474.        (case-fold-search t)
  1475.        (buffer-read-only nil)
  1476.        opoint
  1477.        ;; This prevents file locking from occuring.  Disabling
  1478.        ;; locking can speed things noticeably if the lock
  1479.        ;; directory is on a slow device.  We don't need locking
  1480.        ;; here because the user shouldn't care about VM stuffing
  1481.        ;; its own status headers.
  1482.        (buffer-file-name nil)
  1483.        (delflag (vm-deleted-flag m)))
  1484.        (unwind-protect
  1485.        (progn
  1486.          ;; don't put this folder's summary entry into another folder.
  1487.          (if for-other-folder
  1488.          (vm-set-summary-of m nil)
  1489.            (if (vm-su-start-of m)
  1490.            ;; fill the summary cache if it's not done already.
  1491.            (vm-su-summary m)))
  1492.          (setq attributes (vm-attributes-of m)
  1493.            cache (vm-cache-of m))
  1494.          (and delflag for-other-folder
  1495.           (vm-set-deleted-flag-in-vector
  1496.            (setq attributes (copy-sequence attributes)) nil))
  1497.          (if (eq vm-folder-type 'babyl)
  1498.          (vm-stuff-babyl-attributes m for-other-folder))
  1499.          (goto-char (vm-headers-of m))
  1500.          (while (re-search-forward vm-attributes-header-regexp
  1501.                        (vm-text-of m) t)
  1502.            (delete-region (match-beginning 0) (match-end 0)))
  1503.          (goto-char (vm-headers-of m))
  1504.          (setq opoint (point))
  1505.          (insert-before-markers
  1506.           vm-attributes-header " ("
  1507.           (let ((print-escape-newlines t))
  1508.         (prin1-to-string attributes))
  1509.           "\n\t"
  1510.           (let ((print-escape-newlines t))
  1511.         (prin1-to-string cache))
  1512.           "\n\t"
  1513.           (let ((print-escape-newlines t))
  1514.         (prin1-to-string (vm-labels-of m)))
  1515.           ")\n")
  1516.          (set-marker (vm-headers-of m) opoint)
  1517.          (cond ((and (eq vm-folder-type 'From_)
  1518.              vm-berkeley-mail-compatibility)
  1519.             (goto-char (vm-headers-of m))
  1520.             (while (re-search-forward
  1521.                 vm-berkeley-mail-status-header-regexp
  1522.                 (vm-text-of m) t)
  1523.               (delete-region (match-beginning 0) (match-end 0)))
  1524.             (goto-char (vm-headers-of m))
  1525.             (cond ((not (vm-new-flag m))
  1526.                (insert-before-markers
  1527.                 vm-berkeley-mail-status-header
  1528.                 (if (vm-unread-flag m) "" "R")
  1529.                 "O\n")
  1530.                (set-marker (vm-headers-of m) opoint)))))
  1531.          (vm-set-modflag-of m nil))
  1532.      (set-buffer-modified-p old-buffer-modified-p))))))
  1533.  
  1534. ;; we can be a bit lazy in this function since it's only called
  1535. ;; from within vm-stuff-attributes.  we don't worry about
  1536. ;; restoring the modified flag, setting buffer-read-only, or
  1537. ;; about not moving point.
  1538. (defun vm-stuff-babyl-attributes (m for-other-folder)
  1539.   (goto-char (vm-start-of m))
  1540.   (forward-char 2)
  1541.   (if (vm-babyl-frob-flag-of m)
  1542.       (insert "1")
  1543.     (insert "0"))
  1544.   (delete-char 1)
  1545.   (forward-char 1)
  1546.   (if (looking-at "\\( [^\000-\040,\177-\377]+,\\)+")
  1547.       (delete-region (match-beginning 0) (match-end 0)))
  1548.   (if (vm-new-flag m)
  1549.       (insert " recent, unseen,")
  1550.     (if (vm-unread-flag m)
  1551.     (insert " unseen,")))
  1552.   (if (and (not for-other-folder) (vm-deleted-flag m))
  1553.       (insert " deleted,"))
  1554.   (if (vm-replied-flag m)
  1555.       (insert " answered,"))
  1556.   (if (vm-forwarded-flag m)
  1557.       (insert " forwarded,"))
  1558.   (if (vm-redistributed-flag m)
  1559.       (insert " redistributed,"))
  1560.   (if (vm-filed-flag m)
  1561.       (insert " filed,"))
  1562.   (if (vm-edited-flag m)
  1563.       (insert " edited,"))
  1564.   (if (vm-written-flag m)
  1565.       (insert " written,"))
  1566.   (forward-char 1)
  1567.   (if (looking-at "\\( [^\000-\040,\177-\377]+,\\)+")
  1568.       (delete-region (match-beginning 0) (match-end 0)))
  1569.   (mapcar (function (lambda (label) (insert " " label ",")))
  1570.       (vm-labels-of m)))
  1571.  
  1572. (defun vm-babyl-attributes-string (m for-other-folder)
  1573.   (concat
  1574.    (if (vm-new-flag m)
  1575.        " recent, unseen,"
  1576.      (if (vm-unread-flag m)
  1577.      " unseen,"))
  1578.    (if (and (not for-other-folder) (vm-deleted-flag m))
  1579.        " deleted,")
  1580.    (if (vm-replied-flag m)
  1581.        " answered,")
  1582.    (if (vm-forwarded-flag m)
  1583.        " forwarded,")
  1584.    (if (vm-redistributed-flag m)
  1585.        " redistributed,")
  1586.    (if (vm-filed-flag m)
  1587.        " filed,")
  1588.    (if (vm-edited-flag m)
  1589.        " edited,")
  1590.    (if (vm-written-flag m)
  1591.        " written,")))
  1592.  
  1593. (defun vm-babyl-labels-string (m)
  1594.   (let ((list nil)
  1595.     (labels (vm-labels-of m)))
  1596.     (while labels
  1597.       (setq list (cons "," (cons (car labels) (cons " " list)))
  1598.         labels (cdr labels)))
  1599.     (apply 'concat (nreverse list))))
  1600.  
  1601. (defun vm-stuff-virtual-attributes (message)
  1602.   (let ((virtual (vm-virtual-message-p message)))
  1603.     (if (or (not virtual) (and virtual (vm-virtual-messages-of message)))
  1604.     (save-excursion
  1605.       (set-buffer
  1606.        (vm-buffer-of
  1607.         (vm-real-message-of message)))
  1608.       (vm-stuff-attributes (vm-real-message-of message))))))
  1609.  
  1610. (defun vm-stuff-labels ()
  1611.   (if vm-message-pointer
  1612.       (save-excursion
  1613.     (vm-save-restriction
  1614.      (widen)
  1615.      (let ((old-buffer-modified-p (buffer-modified-p))
  1616.            (case-fold-search t)
  1617.            ;; This prevents file locking from occuring.  Disabling
  1618.            ;; locking can speed things noticeably if the lock
  1619.            ;; directory is on a slow device.  We don't need locking
  1620.            ;; here because the user shouldn't care about VM stuffing
  1621.            ;; its own status headers.
  1622.            (buffer-file-name nil)
  1623.            (buffer-read-only nil)
  1624.            lim)
  1625.        (if (eq vm-folder-type 'babyl)
  1626.            (progn
  1627.          (goto-char (point-min))
  1628.          (vm-skip-past-folder-header)
  1629.          (delete-region (point) (point-min))
  1630.          (insert-before-markers (vm-folder-header vm-folder-type
  1631.                               vm-label-obarray))))
  1632.        (goto-char (point-min))
  1633.        (vm-skip-past-folder-header)
  1634.        (vm-find-leading-message-separator)
  1635.        (vm-skip-past-leading-message-separator)
  1636.        (search-forward "\n\n" nil t)
  1637.        (setq lim (point))
  1638.        (goto-char (point-min))
  1639.        (vm-skip-past-folder-header)
  1640.        (vm-find-leading-message-separator)
  1641.        (vm-skip-past-leading-message-separator)
  1642.        (while (re-search-forward vm-labels-header-regexp lim t)
  1643.          (progn (goto-char (match-beginning 0))
  1644.             (if (vm-match-header vm-labels-header)
  1645.             (delete-region (vm-matched-header-start)
  1646.                        (vm-matched-header-end)))))
  1647.        ;; To insert or to insert-before-markers, that is the question.
  1648.        ;;
  1649.        ;; If we insert-before-markers we push a header behind
  1650.        ;; vm-headers-of, which is clearly undesirable.  So we
  1651.        ;; just insert.  This will cause the summary header
  1652.        ;; to be visible if there are no non-visible headers,
  1653.        ;; oh well, no way around this.
  1654.        (insert vm-labels-header " "
  1655.            (let ((print-escape-newlines t)
  1656.              (list nil))
  1657.              (mapatoms (function
  1658.                 (lambda (sym)
  1659.                   (setq list (cons (symbol-name sym) list))))
  1660.                    vm-label-obarray)
  1661.              (prin1-to-string list))
  1662.            "\n")
  1663.        (set-buffer-modified-p old-buffer-modified-p))))))
  1664.  
  1665. ;; Insert a bookmark into the first message in the folder.
  1666. (defun vm-stuff-bookmark ()
  1667.   (if vm-message-pointer
  1668.       (save-excursion
  1669.     (vm-save-restriction
  1670.      (widen)
  1671.      (let ((old-buffer-modified-p (buffer-modified-p))
  1672.            (case-fold-search t)
  1673.            ;; This prevents file locking from occuring.  Disabling
  1674.            ;; locking can speed things noticeably if the lock
  1675.            ;; directory is on a slow device.  We don't need locking
  1676.            ;; here because the user shouldn't care about VM stuffing
  1677.            ;; its own status headers.
  1678.            (buffer-file-name nil)
  1679.            (buffer-read-only nil)
  1680.            lim)
  1681.        (goto-char (point-min))
  1682.        (vm-skip-past-folder-header)
  1683.        (vm-find-leading-message-separator)
  1684.        (vm-skip-past-leading-message-separator)
  1685.        (search-forward "\n\n" nil t)
  1686.        (setq lim (point))
  1687.        (goto-char (point-min))
  1688.        (vm-skip-past-folder-header)
  1689.        (vm-find-leading-message-separator)
  1690.        (vm-skip-past-leading-message-separator)
  1691.        (if (re-search-forward vm-bookmark-header-regexp lim t)
  1692.            (progn (goto-char (match-beginning 0))
  1693.               (if (vm-match-header vm-bookmark-header)
  1694.               (delete-region (vm-matched-header-start)
  1695.                      (vm-matched-header-end)))))
  1696.        ;; To insert or to insert-before-markers, that is the question.
  1697.        ;;
  1698.        ;; If we insert-before-markers we push a header behind
  1699.        ;; vm-headers-of, which is clearly undesirable.  So we
  1700.        ;; just insert.  This will cause the bookmark header
  1701.        ;; to be visible if there are no non-visible headers,
  1702.        ;; oh well, no way around this.
  1703.        (insert vm-bookmark-header " "
  1704.            (vm-number-of (car vm-message-pointer))
  1705.            "\n")
  1706.        (set-buffer-modified-p old-buffer-modified-p))))))
  1707.  
  1708. ;; Insert the summary format variable header into the first message.
  1709. (defun vm-stuff-summary ()
  1710.   (if vm-message-pointer
  1711.       (save-excursion
  1712.     (vm-save-restriction
  1713.      (widen)
  1714.      (let ((old-buffer-modified-p (buffer-modified-p))
  1715.            (case-fold-search t)
  1716.            ;; This prevents file locking from occuring.  Disabling
  1717.            ;; locking can speed things noticeably if the lock
  1718.            ;; directory is on a slow device.  We don't need locking
  1719.            ;; here because the user shouldn't care about VM stuffing
  1720.            ;; its own status headers.
  1721.            (buffer-file-name nil)
  1722.            (buffer-read-only nil)
  1723.            lim)
  1724.        (goto-char (point-min))
  1725.        (vm-skip-past-folder-header)
  1726.        (vm-find-leading-message-separator)
  1727.        (vm-skip-past-leading-message-separator)
  1728.        (search-forward "\n\n" nil t)
  1729.        (setq lim (point))
  1730.        (goto-char (point-min))
  1731.        (vm-skip-past-folder-header)
  1732.        (vm-find-leading-message-separator)
  1733.        (vm-skip-past-leading-message-separator)
  1734.        (while (re-search-forward vm-summary-header-regexp lim t)
  1735.          (progn (goto-char (match-beginning 0))
  1736.             (if (vm-match-header vm-summary-header)
  1737.             (delete-region (vm-matched-header-start)
  1738.                        (vm-matched-header-end)))))
  1739.        ;; To insert or to insert-before-markers, that is the question.
  1740.        ;;
  1741.        ;; If we insert-before-markers we push a header behind
  1742.        ;; vm-headers-of, which is clearly undesirable.  So we
  1743.        ;; just insert.  This will cause the summary header
  1744.        ;; to be visible if there are no non-visible headers,
  1745.        ;; oh well, no way around this.
  1746.        (insert vm-summary-header " "
  1747.            (let ((print-escape-newlines t))
  1748.              (prin1-to-string vm-summary-format))
  1749.            "\n")
  1750.        (set-buffer-modified-p old-buffer-modified-p))))))
  1751.  
  1752. ;; stuff the current values of the header variables for future messages.
  1753. (defun vm-stuff-header-variables ()
  1754.   (if vm-message-pointer
  1755.       (save-excursion
  1756.     (vm-save-restriction
  1757.      (widen)
  1758.      (let ((old-buffer-modified-p (buffer-modified-p))
  1759.            (case-fold-search t)
  1760.            (print-escape-newlines t)
  1761.            lim
  1762.            (buffer-read-only nil)
  1763.            ;; This prevents file locking from occuring.  Disabling
  1764.            ;; locking can speed things noticeably if the lock
  1765.            ;; directory is on a slow device.  We don't need locking
  1766.            ;; here because the user shouldn't care about VM stuffing
  1767.            ;; its own status headers.
  1768.            (buffer-file-name nil))
  1769.        (goto-char (point-min))
  1770.        (vm-skip-past-folder-header)
  1771.        (vm-find-leading-message-separator)
  1772.        (vm-skip-past-leading-message-separator)
  1773.        (search-forward "\n\n" nil t)
  1774.        (setq lim (point))
  1775.        (goto-char (point-min))
  1776.        (vm-skip-past-folder-header)
  1777.        (vm-find-leading-message-separator)
  1778.        (vm-skip-past-leading-message-separator)
  1779.        (while (re-search-forward vm-vheader-header-regexp lim t)
  1780.          (progn (goto-char (match-beginning 0))
  1781.             (if (vm-match-header vm-vheader-header)
  1782.             (delete-region (vm-matched-header-start)
  1783.                        (vm-matched-header-end)))))
  1784.        ;; To insert or to insert-before-markers, that is the question.
  1785.        ;;
  1786.        ;; If we insert-before-markers we push a header behind
  1787.        ;; vm-headers-of, which is clearly undesirable.  So we
  1788.        ;; just insert.  This header will be visible if there
  1789.        ;; are no non-visible headers, oh well, no way around this.
  1790.        (insert vm-vheader-header " "
  1791.            (prin1-to-string vm-visible-headers) " "
  1792.            (prin1-to-string vm-invisible-header-regexp)
  1793.            "\n")
  1794.        (set-buffer-modified-p old-buffer-modified-p))))))
  1795.  
  1796. ;; Insert a header into the first message of the folder that lists
  1797. ;; the folder's message order.
  1798. (defun vm-stuff-message-order ()
  1799.   (if (cdr vm-message-list)
  1800.       (save-excursion
  1801.     (vm-save-restriction
  1802.      (widen)
  1803.      (let ((old-buffer-modified-p (buffer-modified-p))
  1804.            (case-fold-search t)
  1805.            ;; This prevents file locking from occuring.  Disabling
  1806.            ;; locking can speed things noticeably if the lock
  1807.            ;; directory is on a slow device.  We don't need locking
  1808.            ;; here because the user shouldn't care about VM stuffing
  1809.            ;; its own status headers.
  1810.            (buffer-file-name nil)
  1811.            lim n
  1812.            (buffer-read-only nil)
  1813.            (mp (copy-sequence vm-message-list)))
  1814.        (setq mp
  1815.          (sort mp
  1816.                (function
  1817.             (lambda (p q)
  1818.               (< (vm-start-of p) (vm-start-of q))))))
  1819.        (goto-char (point-min))
  1820.        (vm-skip-past-folder-header)
  1821.        (vm-find-leading-message-separator)
  1822.        (vm-skip-past-leading-message-separator)
  1823.        (search-forward "\n\n" nil t)
  1824.        (setq lim (point))
  1825.        (goto-char (point-min))
  1826.        (vm-skip-past-folder-header)
  1827.        (vm-find-leading-message-separator)
  1828.        (vm-skip-past-leading-message-separator)
  1829.        (while (re-search-forward vm-message-order-header-regexp lim t)
  1830.          (progn (goto-char (match-beginning 0))
  1831.             (if (vm-match-header vm-message-order-header)
  1832.             (delete-region (vm-matched-header-start)
  1833.                        (vm-matched-header-end)))))
  1834.        ;; To insert or to insert-before-markers, that is the question.
  1835.        ;;
  1836.        ;; If we insert-before-markers we push a header behind
  1837.        ;; vm-headers-of, which is clearly undesirable.  So we
  1838.        ;; just insert.  This header will be visible if there
  1839.        ;; are no non-visible headers, oh well, no way around this.
  1840.        (insert vm-message-order-header "\n\t(")
  1841.        (setq n 0)
  1842.        (while mp
  1843.          (insert (vm-number-of (car mp)))
  1844.          (setq n (1+ n) mp (cdr mp))
  1845.          (and mp (insert
  1846.               (if (zerop (% n 15))
  1847.               "\n\t "
  1848.             " "))))
  1849.        (insert ")\n")
  1850.        (setq vm-message-order-changed nil
  1851.          vm-message-order-header-present t)
  1852.        (set-buffer-modified-p old-buffer-modified-p))))))
  1853.  
  1854. ;; Remove the message order header.
  1855. (defun vm-remove-message-order ()
  1856.   (if (cdr vm-message-list)
  1857.       (save-excursion
  1858.     (vm-save-restriction
  1859.      (widen)
  1860.      (let ((old-buffer-modified-p (buffer-modified-p))
  1861.            (case-fold-search t)
  1862.            lim
  1863.            ;; This prevents file locking from occuring.  Disabling
  1864.            ;; locking can speed things noticeably if the lock
  1865.            ;; directory is on a slow device.  We don't need locking
  1866.            ;; here because the user shouldn't care about VM stuffing
  1867.            ;; its own status headers.
  1868.            (buffer-file-name nil)
  1869.            (buffer-read-only nil))
  1870.        (goto-char (point-min))
  1871.        (vm-skip-past-folder-header)
  1872.        (vm-skip-past-leading-message-separator)
  1873.        (search-forward "\n\n" nil t)
  1874.        (setq lim (point))
  1875.        (goto-char (point-min))
  1876.        (vm-skip-past-folder-header)
  1877.        (vm-skip-past-leading-message-separator)
  1878.        (while (re-search-forward vm-message-order-header-regexp lim t)
  1879.          (progn (goto-char (match-beginning 0))
  1880.             (if (vm-match-header vm-message-order-header)
  1881.             (delete-region (vm-matched-header-start)
  1882.                        (vm-matched-header-end)))))
  1883.        (setq vm-message-order-header-present nil)
  1884.        (set-buffer-modified-p old-buffer-modified-p))))))
  1885.  
  1886. (defun vm-change-all-new-to-unread ()
  1887.   (let ((mp vm-message-list))
  1888.     (while mp
  1889.       (if (vm-new-flag (car mp))
  1890.       (progn
  1891.         (vm-set-new-flag (car mp) nil)
  1892.         (vm-set-unread-flag (car mp) t)))
  1893.       (setq mp (cdr mp)))))
  1894.  
  1895. (defun vm-unread-message (&optional count)
  1896.   "Set the `unread' attribute for the current message.  If the message is
  1897. already new or unread, then it is left unchanged.
  1898.  
  1899. Numeric prefix argument N means to unread the current message plus the
  1900. next N-1 messages.  A negative N means unread the current message and
  1901. the previous N-1 messages.
  1902.  
  1903. When invoked on marked messages (via vm-next-command-uses-marks),
  1904. all marked messages are affected, other messages are ignored."
  1905.   (interactive "p")
  1906.   (or count (setq count 1))
  1907.   (vm-follow-summary-cursor)
  1908.   (vm-select-folder-buffer)
  1909.   (vm-check-for-killed-summary)
  1910.   (vm-error-if-folder-empty)
  1911.   (let ((mlist (vm-select-marked-or-prefixed-messages count)))
  1912.     (while mlist
  1913.       (if (and (not (vm-unread-flag (car mlist)))
  1914.            (not (vm-new-flag (car mlist))))
  1915.       (vm-set-unread-flag (car mlist) t))
  1916.       (setq mlist (cdr mlist))))
  1917.   (vm-display nil nil '(vm-unread-message) '(vm-unread-message))
  1918.   (vm-update-summary-and-mode-line))
  1919.  
  1920. (defun vm-quit-just-bury ()
  1921.   "Bury the current VM folder and summary buffers.
  1922. The folder is not altered and Emacs is still visiting it.  You
  1923. can switch back to it with switch-to-buffer or by using the
  1924. Buffer Menu."
  1925.   (interactive)
  1926.   (vm-select-folder-buffer)
  1927.   (if (not (memq major-mode '(vm-mode vm-virtual-mode)))
  1928.       (error "%s must be invoked from a VM buffer." this-command))
  1929.   (vm-check-for-killed-summary)
  1930.  
  1931.   (run-hooks 'vm-quit-hook)
  1932.  
  1933.   (vm-display nil nil '(vm-quit-just-bury)
  1934.           '(vm-quit-just-bury quitting))
  1935.   (if vm-summary-buffer
  1936.       (vm-display vm-summary-buffer nil nil nil))
  1937.   (vm-display (current-buffer) nil nil nil)
  1938.   (vm-bury-buffer (current-buffer))
  1939.   (if vm-summary-buffer
  1940.       (vm-bury-buffer vm-summary-buffer)))
  1941.  
  1942. (defun vm-quit-just-iconify ()
  1943.   "Iconify the frame and bury the current VM folder and summary buffers.
  1944. The folder is not altered and Emacs is still visiting it."
  1945.   (interactive)
  1946.   (vm-select-folder-buffer)
  1947.   (if (not (memq major-mode '(vm-mode vm-virtual-mode)))
  1948.       (error "%s must be invoked from a VM buffer." this-command))
  1949.   (vm-check-for-killed-summary)
  1950.  
  1951.   (run-hooks 'vm-quit-hook)
  1952.  
  1953.   (vm-display nil nil '(vm-quit-just-iconify)
  1954.           '(vm-quit-just-iconify quitting))
  1955.   (vm-bury-buffer (current-buffer))
  1956.   (if vm-summary-buffer
  1957.       (vm-bury-buffer vm-summary-buffer))
  1958.   (vm-iconify-frame))
  1959.  
  1960. (defun vm-quit-no-change ()
  1961.   "Exit VM without saving changes made to the folder."
  1962.   (interactive)
  1963.   (vm-quit t))
  1964.  
  1965. (defun vm-quit (&optional no-change)
  1966.   "Quit VM, saving changes.  Deleted messages are not expunged."
  1967.   (interactive)
  1968.   (vm-select-folder-buffer)
  1969.   (if (not (memq major-mode '(vm-mode vm-virtual-mode)))
  1970.       (error "%s must be invoked from a VM buffer." this-command))
  1971.   (vm-check-for-killed-summary)
  1972.   (vm-display nil nil '(vm-quit vm-quit-no-change)
  1973.           (list this-command 'quitting))
  1974.   (let ((virtual (eq major-mode 'vm-virtual-mode)))
  1975.     (cond
  1976.      ((and (not virtual) no-change (buffer-modified-p)
  1977.        (not (zerop vm-messages-not-on-disk))
  1978.        ;; Folder may have been saved with C-x C-s and attributes may have
  1979.        ;; been changed after that; in that case vm-messages-not-on-disk
  1980.        ;; would not have been zeroed.  However, all modification flag
  1981.        ;; undos are cleared if VM actually modifies the folder buffer
  1982.        ;; (as opposed to the folder's attributes), so this can be used
  1983.        ;; to verify that there are indeed unsaved messages.
  1984.        (null (assq 'vm-set-buffer-modified-p vm-undo-record-list))
  1985.        (not
  1986.         (y-or-n-p
  1987.          (format
  1988.           "%d message%s have not been saved to disk, quit anyway? "
  1989.           vm-messages-not-on-disk
  1990.           (if (= 1 vm-messages-not-on-disk) "" "s")))))
  1991.       (error "Aborted"))
  1992.      ((and (not virtual)
  1993.        no-change (buffer-modified-p) vm-confirm-quit
  1994.        (not (y-or-n-p "There are unsaved changes, quit anyway? ")))
  1995.       (error "Aborted"))
  1996.      ((and (eq vm-confirm-quit t)
  1997.        (not (y-or-n-p "Do you really want to quit? ")))
  1998.       (error "Aborted")))
  1999.  
  2000.     (run-hooks 'vm-quit-hook)
  2001.  
  2002.     (vm-virtual-quit)
  2003.     (if (and (not no-change) (not virtual))
  2004.     (progn
  2005.       ;; this could take a while, so give the user some feedback
  2006.       (vm-unsaved-message "Quitting...")
  2007.       (or vm-folder-read-only (eq major-mode 'vm-virtual-mode)
  2008.           (vm-change-all-new-to-unread))))
  2009.     (if (and (buffer-modified-p) (not no-change) (not virtual))
  2010.     (vm-save-folder))
  2011.     (vm-unsaved-message "")
  2012.     (let ((summary-buffer vm-summary-buffer)
  2013.       (mail-buffer (current-buffer)))
  2014.       (if summary-buffer
  2015.       (progn
  2016.         (vm-display vm-summary-buffer nil nil nil)
  2017.         (kill-buffer summary-buffer)))
  2018.       (set-buffer mail-buffer)
  2019.       (vm-display mail-buffer nil nil nil)
  2020.       ;; vm-display is not supposed to change the current buffer.
  2021.       ;; still better to be safe here.
  2022.       (set-buffer mail-buffer)
  2023.       (set-buffer-modified-p nil)
  2024.       (kill-buffer (current-buffer)))
  2025.     (vm-update-summary-and-mode-line)))
  2026.  
  2027. (defun vm-start-itimers-if-needed ()
  2028.   (if (or (natnump vm-flush-interval)
  2029.       (natnump vm-auto-get-new-mail))
  2030.       (progn
  2031.     (if (null
  2032.          (condition-case data
  2033.          (progn (require 'itimer) t)
  2034.            (error nil)))
  2035.         (setq vm-flush-interval t
  2036.           vm-auto-get-new-mail t)
  2037.       (and (natnump vm-flush-interval) (not (get-itimer "vm-flush"))
  2038.            (start-itimer "vm-flush" 'vm-flush-itimer-function
  2039.                  vm-flush-interval nil))
  2040.       (and (natnump vm-auto-get-new-mail) (not (get-itimer "vm-get-mail"))
  2041.            (start-itimer "vm-get-mail" 'vm-get-mail-itimer-function
  2042.                  vm-auto-get-new-mail nil))))))
  2043.  
  2044. ;; support for numeric vm-auto-get-new-mail
  2045. (defun vm-get-mail-itimer-function ()
  2046.   (if (integerp vm-auto-get-new-mail)
  2047.       (set-itimer-restart current-itimer vm-auto-get-new-mail))
  2048.   (let ((b-list (buffer-list)))
  2049.     (while (and (not (input-pending-p)) b-list)
  2050.       (save-excursion
  2051.     (set-buffer (car b-list))
  2052.     (if (and (eq major-mode 'vm-mode)
  2053.          (not (and (not (buffer-modified-p))
  2054.                buffer-file-name
  2055.                (file-newer-than-file-p
  2056.                 (make-auto-save-file-name)
  2057.                 buffer-file-name)))
  2058.          (not vm-block-new-mail)
  2059.          (not vm-folder-read-only)
  2060.          (vm-get-spooled-mail)
  2061.          (vm-assimilate-new-messages t))
  2062.         (progn
  2063.           ;; don't move the message pointer unless the folder
  2064.           ;; was empty.
  2065.           (if (and (null vm-message-pointer)
  2066.                (vm-thoughtfully-select-message))
  2067.           (vm-preview-current-message)
  2068.         (vm-update-summary-and-mode-line)))))
  2069.       (setq b-list (cdr b-list)))))
  2070.  
  2071. ;; support for numeric vm-flush-interval
  2072. (defun vm-flush-itimer-function ()
  2073.   (if (integerp vm-flush-interval)
  2074.       (set-itimer-restart current-itimer vm-flush-interval))
  2075.   ;; if no vm-mode buffers are found, we might as well shut down the
  2076.   ;; flush itimer.
  2077.   (if (not (vm-flush-cached-data))
  2078.       (set-itimer-restart current-itimer nil)))
  2079.  
  2080. ;; flush cached data in all vm-mode buffers.
  2081. ;; returns non-nil if any vm-mode buffers were found.
  2082. (defun vm-flush-cached-data ()
  2083.   (save-excursion
  2084.     (let ((buf-list (buffer-list))
  2085.       (found-one nil))
  2086.       (while (and buf-list (not (input-pending-p)))
  2087.     (set-buffer (car buf-list))
  2088.     (cond ((and (eq major-mode 'vm-mode) vm-message-list)
  2089.            (setq found-one t)
  2090.            (if (not (eq vm-modification-counter
  2091.                 vm-flushed-modification-counter))
  2092.            (let ((mp vm-message-list))
  2093.              (vm-stuff-summary)
  2094.              (vm-stuff-labels)
  2095.              (and vm-message-order-changed
  2096.               (vm-stuff-message-order))
  2097.              (while (and mp (not (input-pending-p)))
  2098.                (if (vm-modflag-of (car mp))
  2099.                (vm-stuff-attributes (car mp)))
  2100.                (setq mp (cdr mp)))
  2101.              (and (null mp)
  2102.               (setq vm-flushed-modification-counter
  2103.                 vm-modification-counter))))))
  2104.     (setq buf-list (cdr buf-list)))
  2105.       ;; if we haven't checked them all return non-nil so
  2106.       ;; the flusher won't give up trying.
  2107.       (or buf-list found-one) )))
  2108.  
  2109. ;; This allows C-x C-s to do the right thing for VM mail buffers.
  2110. ;; Note that deleted messages are not expunged.
  2111. (defun vm-write-file-hook ()
  2112.   (if (and (eq major-mode 'vm-mode) (not vm-inhibit-write-file-hook))
  2113.     ;; The vm-save-restriction isn't really necessary here, since
  2114.     ;; the stuff routines clean up after themselves, but should remain
  2115.     ;; as a safeguard against the time when other stuff is added here.
  2116.     (vm-save-restriction
  2117.      (let ((mp vm-message-list)
  2118.        (buffer-read-only))
  2119.     (while mp
  2120.       (if (vm-modflag-of (car mp))
  2121.           (vm-stuff-attributes (car mp)))
  2122.       (setq mp (cdr mp)))
  2123.     (if vm-message-list
  2124.         (progn
  2125.           ;; get summary cache up-to-date
  2126.           (vm-update-summary-and-mode-line)
  2127.           (vm-stuff-bookmark)
  2128.           (vm-stuff-header-variables)
  2129.           (vm-stuff-labels)
  2130.           (vm-stuff-summary)
  2131.           (and vm-message-order-changed
  2132.            (vm-stuff-message-order))))
  2133.     nil ))))
  2134.  
  2135. (defun vm-save-buffer (prefix)
  2136.   (interactive "P")
  2137.   (vm-select-folder-buffer)
  2138.   (vm-error-if-virtual-folder)
  2139.   (save-buffer prefix)
  2140.   (intern (buffer-name) vm-buffers-needing-display-update)
  2141.   (setq vm-block-new-mail nil)
  2142.   (vm-display nil nil '(vm-save-buffer) '(vm-save-buffer))
  2143.   (vm-update-summary-and-mode-line))
  2144.  
  2145. (defun vm-write-file ()
  2146.   (interactive)
  2147.   (vm-select-folder-buffer)
  2148.   (vm-error-if-virtual-folder)
  2149.   (call-interactively 'write-file)
  2150.   (intern (buffer-name) vm-buffers-needing-display-update)
  2151.   (setq vm-block-new-mail nil)
  2152.   (vm-display nil nil '(vm-write-file) '(vm-write-file))
  2153.   (vm-update-summary-and-mode-line))
  2154.  
  2155. (defun vm-save-folder (&optional prefix)
  2156.   "Save current folder to disk.
  2157. Deleted messages are not expunged.
  2158. Prefix arg is handled the same as for the command save-buffer.
  2159.  
  2160. When applied to a virtual folder, this command runs itself on
  2161. each of the underlying real folders associated with the virtual
  2162. folder."
  2163.   (interactive (list current-prefix-arg))
  2164.   (vm-select-folder-buffer)
  2165.   (vm-check-for-killed-summary)
  2166.   (vm-display nil nil '(vm-save-folder) '(vm-save-folder))
  2167.   (if (eq major-mode 'vm-virtual-mode)
  2168.       (vm-virtual-save-folder prefix)
  2169.     (if (buffer-modified-p)
  2170.     (let (mp)
  2171.       ;; stuff the attributes of messages that need it.
  2172.       (vm-unsaved-message "Stuffing attributes...")
  2173.       (setq mp vm-message-list)
  2174.       (while mp
  2175.         (if (vm-modflag-of (car mp))
  2176.         (vm-stuff-attributes (car mp)))
  2177.         (setq mp (cdr mp)))
  2178.       ;; stuff bookmark and header variable values
  2179.       (if vm-message-list
  2180.           (progn
  2181.         ;; get summary cache up-to-date
  2182.         (vm-update-summary-and-mode-line)
  2183.         (vm-stuff-bookmark)
  2184.         (vm-stuff-header-variables)
  2185.         (vm-stuff-labels)
  2186.         (vm-stuff-summary)
  2187.         (and vm-message-order-changed
  2188.              (vm-stuff-message-order))))
  2189.       (vm-unsaved-message "Saving...")
  2190.       (let ((vm-inhibit-write-file-hook t))
  2191.         (save-buffer prefix))
  2192.       (vm-set-buffer-modified-p nil)
  2193.       (vm-clear-modification-flag-undos)
  2194.       (setq vm-messages-not-on-disk 0)
  2195.       (setq vm-block-new-mail nil)
  2196.       (and (zerop (buffer-size))
  2197.            vm-delete-empty-folders
  2198.            buffer-file-name
  2199.            (or (eq vm-delete-empty-folders t)
  2200.            (y-or-n-p (format "%s is empty, remove it? "
  2201.                      (or buffer-file-name (buffer-name)))))
  2202.            (condition-case ()
  2203.            (progn
  2204.              (delete-file buffer-file-name)
  2205.              (message "%s removed" buffer-file-name))
  2206.          ;; no can do, oh well.
  2207.          (error nil)))
  2208.       (vm-update-summary-and-mode-line))
  2209.       (message "No changes need to be saved"))))
  2210.  
  2211. (defun vm-save-and-expunge-folder (&optional prefix)
  2212.   "Expunge folder, then save it to disk.
  2213. Prefix arg is handled the same as for the command save-buffer.
  2214. Expunge won't be done if folder is read-only.
  2215.  
  2216. When applied to a virtual folder, this command works as if you had
  2217. run vm-expunge-folder followed by vm-save-folder."
  2218.   (interactive (list current-prefix-arg))
  2219.   (vm-select-folder-buffer)
  2220.   (vm-check-for-killed-summary)
  2221.   (vm-display nil nil '(vm-save-and-expunge-folder)
  2222.           '(vm-save-and-expunge-folder))
  2223.   (if (not vm-folder-read-only)
  2224.       (progn
  2225.     (vm-unsaved-message "Expunging...")
  2226.     (vm-expunge-folder t)))
  2227.   (vm-save-folder prefix))
  2228.  
  2229. (defun vm-handle-file-recovery-or-reversion (recovery)
  2230.   (if (and vm-summary-buffer (buffer-name vm-summary-buffer))
  2231.       (kill-buffer vm-summary-buffer))
  2232.   (vm-virtual-quit)
  2233.   ;; reset major mode, this will cause vm to start from scratch.
  2234.   (setq major-mode 'fundamental-mode)
  2235.   ;; If this is a recovery, we can't allow the user to get new
  2236.   ;; mail until a real save is performed.  Until then the buffer
  2237.   ;; and the disk don't match.
  2238.   (if recovery
  2239.       (setq vm-block-new-mail t))
  2240.   (vm buffer-file-name))
  2241.  
  2242. ;; detect if a recover-file is being performed
  2243. ;; and handle things properly.
  2244. (defun vm-handle-file-recovery ()
  2245.   (if (and (buffer-modified-p)
  2246.        (eq major-mode 'vm-mode)
  2247.        vm-message-list
  2248.        (= (vm-end-of (car vm-message-list)) 1))
  2249.       (vm-handle-file-recovery-or-reversion t)))
  2250.  
  2251. ;; detect if a revert-buffer is being performed
  2252. ;; and handle things properly.
  2253. (defun vm-handle-file-reversion ()
  2254.   (if (and (not (buffer-modified-p))
  2255.        (eq major-mode 'vm-mode)
  2256.        vm-message-list
  2257.        (= (vm-end-of (car vm-message-list)) 1))
  2258.       (vm-handle-file-recovery-or-reversion nil)))
  2259.  
  2260. ;; FSF v19.23 revert-buffer doesn't mash all the markers together
  2261. ;; like v18 and prior v19 versions, so the check in
  2262. ;; vm-handle-file-reversion doesn't work.  However v19.23 has a
  2263. ;; hook we can use, after-revert-hook.
  2264. (defun vm-after-revert-buffer-hook ()
  2265.   (if (eq major-mode 'vm-mode)
  2266.       (vm-handle-file-recovery-or-reversion nil)))
  2267.  
  2268. (defun vm-help ()
  2269.   "Display help for various VM activities."
  2270.   (interactive)
  2271.   (if (eq major-mode 'vm-summary-mode)
  2272.       (vm-select-folder-buffer))
  2273.   (let ((pop-up-windows (and pop-up-windows (eq vm-mutable-windows t)))
  2274.     (pop-up-frames vm-mutable-frames))
  2275.     (cond
  2276.      ((eq last-command 'vm-help)
  2277.       (describe-mode))
  2278.      ((eq vm-system-state 'previewing)
  2279.       (message "Type SPC to read message, n previews next message   (? gives more help)"))
  2280.      ((memq vm-system-state '(showing reading))
  2281.       (message "SPC and b scroll, (d)elete, (s)ave, (n)ext, (r)eply   (? gives more help)"))
  2282.      ((eq vm-system-state 'editing)
  2283.       (message 
  2284.        (substitute-command-keys
  2285.     "Type \\[vm-edit-message-end] to end edit, \\[vm-edit-message-abort] to abort with no change.")))
  2286.      ((eq major-mode 'mail-mode)
  2287.       (message
  2288.        (substitute-command-keys
  2289.     "Type \\[vm-mail-send-and-exit] to send message, \\[kill-buffer] to discard this message")))
  2290.      (t (describe-mode)))))
  2291.  
  2292. (defun vm-spool-move-mail (source destination)
  2293.   (let ((handler (and (fboundp 'find-file-name-handler)
  2294.               (condition-case ()
  2295.               (find-file-name-handler source 'vm-spool-move-mail)
  2296.             (wrong-number-of-arguments
  2297.               (find-file-name-handler source)))))
  2298.     status error-buffer)
  2299.     (if handler
  2300.     (funcall handler 'vm-spool-move-mail source destination)
  2301.       (setq error-buffer
  2302.         (get-buffer-create
  2303.          (format "*output of %s %s %s*"
  2304.              vm-movemail-program source destination)))
  2305.       (save-excursion
  2306.     (set-buffer error-buffer)
  2307.     (erase-buffer))
  2308.       (setq status
  2309.         (call-process vm-movemail-program nil error-buffer t
  2310.               source destination))
  2311.       (save-excursion
  2312.     (set-buffer error-buffer)
  2313.     (if (and (numberp status) (not (= 0 status)))
  2314.         (insert (format "\n%s exited with code %s\n"
  2315.                 vm-movemail-program status)))
  2316.     (if (> (buffer-size) 0)
  2317.         (progn
  2318.           (vm-display-buffer error-buffer)
  2319.           (if (and (numberp status) (not (= 0 status)))
  2320.           (error "Failed getting new mail from %s" source)
  2321.         (message "Warning: unexpected output from %s"
  2322.              vm-movemail-program)
  2323.         (sleep-for 2)))
  2324.       ;; nag, nag, nag.
  2325.       (kill-buffer error-buffer))
  2326.     t ))))
  2327.  
  2328. (defun vm-gobble-crash-box (crash-box)
  2329.   (save-excursion
  2330.     (vm-save-restriction
  2331.      (widen)
  2332.      (let ((opoint-max (point-max)) crash-buf
  2333.        (buffer-read-only nil)
  2334.        (inbox-buffer-file buffer-file-name)
  2335.        (inbox-folder-type vm-folder-type)
  2336.        (inbox-empty (zerop (buffer-size)))
  2337.        got-mail crash-folder-type
  2338.        (old-buffer-modified-p (buffer-modified-p)))
  2339.        (setq crash-buf
  2340.          ;; crash box could contain a letter bomb...
  2341.          ;; force user notification of file variables for v18 Emacses
  2342.          ;; enable-local-variables == nil disables them for newer Emacses
  2343.          (let ((inhibit-local-variables t)
  2344.            (enable-local-variables nil))
  2345.            (find-file-noselect crash-box)))
  2346.        (save-excursion
  2347.      (set-buffer crash-buf)
  2348.      (setq crash-folder-type (vm-get-folder-type))
  2349.      (if (and crash-folder-type vm-check-folder-types)
  2350.          (cond ((eq crash-folder-type 'unknown)
  2351.             (error "crash box %s's type is unrecognized" crash-box))
  2352.            ((eq inbox-folder-type 'unknown)
  2353.             (error "inbox %s's type is unrecognized"
  2354.                inbox-buffer-file))
  2355.            ((null inbox-folder-type)
  2356.             (if vm-default-folder-type
  2357.             (if (not (eq vm-default-folder-type
  2358.                      crash-folder-type))
  2359.                 (if vm-convert-folder-types
  2360.                 (progn
  2361.                   (vm-convert-folder-type
  2362.                    crash-folder-type
  2363.                    vm-default-folder-type)
  2364.                   ;; so that kill-buffer won't ask a
  2365.                   ;; question later...
  2366.                   (set-buffer-modified-p nil))
  2367.                   (error "crash box %s mismatches vm-default-folder-type: %s, %s"
  2368.                      crash-box crash-folder-type
  2369.                      vm-default-folder-type)))))
  2370.            ((not (eq inbox-folder-type crash-folder-type))
  2371.             (if vm-convert-folder-types
  2372.             (progn
  2373.               (vm-convert-folder-type crash-folder-type
  2374.                           inbox-folder-type)
  2375.               ;; so that kill-buffer won't ask a
  2376.               ;; question later...
  2377.               (set-buffer-modified-p nil))
  2378.               (error "crash box %s mismatches %s's folder type: %s, %s"
  2379.                  crash-box inbox-buffer-file
  2380.                  crash-folder-type inbox-folder-type)))))
  2381.      ;; toss the folder header if the inbox is not empty
  2382.      (goto-char (point-min))
  2383.      (if (not inbox-empty)
  2384.          (progn
  2385.            (vm-convert-folder-header (or inbox-folder-type
  2386.                          vm-default-folder-type)
  2387.                      nil)
  2388.            (set-buffer-modified-p nil))))
  2389.        (goto-char (point-max))
  2390.        (insert-buffer-substring crash-buf
  2391.                 1 (1+ (save-excursion
  2392.                     (set-buffer crash-buf)
  2393.                     (widen)
  2394.                     (buffer-size))))
  2395.        (write-region opoint-max (point-max) buffer-file-name t t)
  2396.        (vm-increment vm-modification-counter)
  2397.        (setq got-mail (/= opoint-max (point-max)))
  2398.        (set-buffer-modified-p old-buffer-modified-p)
  2399.        (kill-buffer crash-buf)
  2400.        (if (not (stringp vm-keep-crash-boxes))
  2401.        (vm-error-free-call 'delete-file crash-box)
  2402.      (rename-file crash-box
  2403.               (concat (expand-file-name vm-keep-crash-boxes)
  2404.                   (if (not
  2405.                    (= (aref vm-keep-crash-boxes
  2406.                         (1- (length vm-keep-crash-boxes)))
  2407.                       ?/))
  2408.                   "/"
  2409.                 "")
  2410.                   "Z"
  2411.                   (substring
  2412.                    (timezone-make-date-sortable
  2413.                 (current-time-string))
  2414.                    4)))
  2415.      ;; guarantee that each new saved crashbox will have a
  2416.      ;; different name, assuming time doesn't reverse.
  2417.      (sleep-for 1))
  2418.        got-mail ))))
  2419.  
  2420. (defun vm-get-spooled-mail ()
  2421.   (if vm-block-new-mail
  2422.       (error "Can't get new mail until you save this folder."))
  2423.   (let ((triples nil)
  2424.     ;; since we could accept-process-output here (POP code),
  2425.     ;; a timer process might try to start retrieving mail
  2426.     ;; before we finish.  block these attempts.
  2427.     (vm-block-new-mail t)
  2428.     crash in maildrop popdrop
  2429.     (got-mail nil))
  2430.     (cond ((null (vm-spool-files))
  2431.        (setq triples (list
  2432.               (list vm-primary-inbox
  2433.                 (concat vm-spool-directory (user-login-name))
  2434.                 vm-crash-box))))
  2435.       ((stringp (car (vm-spool-files)))
  2436.        (setq triples
  2437.          (mapcar (function
  2438.               (lambda (s) (list vm-primary-inbox s vm-crash-box)))
  2439.              (vm-spool-files))))
  2440.       ((consp (car (vm-spool-files)))
  2441.        (setq triples (vm-spool-files))))
  2442.     (while triples
  2443.       (setq in (expand-file-name (nth 0 (car triples)) vm-folder-directory)
  2444.         maildrop (nth 1 (car triples))
  2445.         crash (nth 2 (car triples)))
  2446.       (if (eq (current-buffer) (vm-get-file-buffer in))
  2447.       (progn
  2448.         (if (file-exists-p crash)
  2449.         (progn
  2450.           (message "Recovering messages from %s..." crash)
  2451.           (setq got-mail (or (vm-gobble-crash-box crash) got-mail))
  2452.           (message "Recovering messages from %s... done" crash)))
  2453.         (setq popdrop (and vm-recognize-pop-maildrops
  2454.                    (string-match vm-recognize-pop-maildrops
  2455.                          maildrop)
  2456.                    ;; maildrop with password clipped
  2457.                    (vm-safe-popdrop-string maildrop)))
  2458.         (if (or popdrop
  2459.             (and (not (equal 0 (nth 7 (file-attributes maildrop))))
  2460.              (file-readable-p maildrop)))
  2461.         (progn
  2462.           (setq crash (expand-file-name crash vm-folder-directory))
  2463.           (if (not popdrop)
  2464.               (setq maildrop (expand-file-name maildrop)))
  2465.           (if (if popdrop
  2466.               (vm-pop-move-mail maildrop crash)
  2467.             (vm-spool-move-mail maildrop crash))
  2468.               (if (vm-gobble-crash-box crash)              
  2469.               (progn
  2470.                 (setq got-mail t)
  2471.                 (message "Got mail from %s."
  2472.                      (or popdrop maildrop)))))))))
  2473.       (setq triples (cdr triples)))
  2474.     (if got-mail
  2475.     (run-hooks 'vm-retrieved-spooled-mail-hook))
  2476.     got-mail ))
  2477.  
  2478. (defun vm-safe-popdrop-string (drop)
  2479.   (or (and (string-match "^\\([^:]+\\):[^:]+:[^:]+:\\([^:]+\\):[^:]+" drop)
  2480.        (concat (substring drop (match-beginning 2) (match-end 2))
  2481.            "@"
  2482.            (substring drop (match-beginning 1) (match-end 1))))
  2483.       "???"))
  2484.  
  2485. (defun vm-get-new-mail (&optional arg)
  2486.   "Move any new mail that has arrived in any of the spool files for the
  2487. current folder into the folder.  New mail is appended to the disk
  2488. and buffer copies of the folder.
  2489.  
  2490. Prefix arg means to gather mail from a user specified folder, instead of
  2491. the usual spool files.  The file name will be read from the minibuffer.
  2492. Unlike when getting mail from a spool file, the source file is left
  2493. undisturbed after its messages have been copied.
  2494.  
  2495. When applied to a virtual folder, this command runs itself on
  2496. each of the underlying real folders associated with this virtual folder.
  2497. A prefix argument has no effect; mail is always gathered from the
  2498. spool files."
  2499.   (interactive "P")
  2500.   (vm-select-folder-buffer)
  2501.   (vm-check-for-killed-summary)
  2502.   (vm-error-if-folder-read-only)
  2503.   (cond ((eq major-mode 'vm-virtual-mode)
  2504.      (vm-virtual-get-new-mail))
  2505.     ((null arg)
  2506.      (if (not (eq major-mode 'vm-mode))
  2507.          (vm-mode))
  2508.      (if (consp (car (vm-spool-files)))
  2509.          (vm-unsaved-message "Checking for new mail for %s..."
  2510.               (or buffer-file-name (buffer-name)))
  2511.        (vm-unsaved-message "Checking for new mail..."))
  2512.      (let (totals-blurb)
  2513.        (if (and (vm-get-spooled-mail) (vm-assimilate-new-messages t))
  2514.            (progn
  2515.          ;; say this NOW, before the non-previewers read
  2516.          ;; a message, alter the new message count and
  2517.          ;; confuse themselves.
  2518.          (setq totals-blurb (vm-emit-totals-blurb))
  2519.          (vm-display nil nil '(vm-get-new-mail) '(vm-get-new-mail))
  2520.          (if (vm-thoughtfully-select-message)
  2521.              (vm-preview-current-message)
  2522.            (vm-update-summary-and-mode-line))
  2523.          (message totals-blurb))
  2524.          (if (consp (car (vm-spool-files)))
  2525.          (message "No new mail for %s"
  2526.               (or buffer-file-name (buffer-name)))
  2527.            (message "No new mail."))
  2528.          (and (interactive-p) (sit-for 4) (vm-unsaved-message "")))))
  2529.     (t
  2530.      (let ((buffer-read-only nil)
  2531.            folder mcount totals-blurb)
  2532.        (setq folder (read-file-name "Gather mail from folder: "
  2533.                     vm-folder-directory t))
  2534.        (if (and vm-check-folder-types
  2535.             (not (vm-compatible-folder-p folder)))
  2536.            (error "Folder %s is not the same format as this folder."
  2537.               folder))
  2538.        (save-excursion
  2539.          (vm-save-restriction
  2540.           (widen)
  2541.           (goto-char (point-max))
  2542.           (insert-file-contents folder)))
  2543.        (setq mcount (length vm-message-list))
  2544.        (if (vm-assimilate-new-messages)
  2545.            (progn
  2546.          ;; say this NOW, before the non-previewers read
  2547.          ;; a message, alter the new message count and
  2548.          ;; confuse themselves.
  2549.          (setq totals-blurb (vm-emit-totals-blurb))
  2550.          (vm-display nil nil '(vm-get-new-mail) '(vm-get-new-mail))
  2551.          (if (vm-thoughtfully-select-message)
  2552.              (vm-preview-current-message)
  2553.            (vm-update-summary-and-mode-line))
  2554.          (message totals-blurb)
  2555.          ;; The gathered messages are actually still on disk
  2556.          ;; unless the user deletes the folder himself.
  2557.          ;; However, users may not understand what happened if
  2558.          ;; the messages go away after a "quit, no save".
  2559.          (setq vm-messages-not-on-disk
  2560.                (+ vm-messages-not-on-disk
  2561.               (- (length vm-message-list)
  2562.                  mcount))))
  2563.          (message "No messages gathered."))))))
  2564.  
  2565. ;; returns non-nil if there were any new messages
  2566. (defun vm-assimilate-new-messages (&optional dont-read-attributes gobble-order)
  2567.   (let ((tail-cons (vm-last vm-message-list))
  2568.     b-list new-messages)
  2569.     (save-excursion
  2570.       (vm-save-restriction
  2571.        (widen)
  2572.        (vm-build-message-list)
  2573.        (if (or (null tail-cons) (cdr tail-cons))
  2574.        (progn
  2575.          (setq vm-ml-sort-keys nil)
  2576.          (if dont-read-attributes
  2577.          (vm-set-default-attributes (cdr tail-cons))
  2578.            (vm-read-attributes (cdr tail-cons)))
  2579.          ;; Yuck.  This has to be done here instead of in the
  2580.          ;; vm function because this needs to be done before
  2581.          ;; any initial thread sort (so that if the thread
  2582.          ;; sort matches the saved order the folder won't be
  2583.          ;; modified) but after the message list is created.
  2584.          ;; Since thread sorting is done here this has to be
  2585.          ;; done here too.
  2586.          (if gobble-order
  2587.          (vm-gobble-message-order))
  2588.          (if vm-thread-obarray
  2589.          (vm-build-threads (cdr tail-cons))))))
  2590.       (setq new-messages (if tail-cons (cdr tail-cons) vm-message-list))
  2591.       (vm-set-numbering-redo-start-point new-messages)
  2592.       (vm-set-summary-redo-start-point new-messages))
  2593.     ;; copy the new-messages list because sorting might scramble
  2594.     ;; it.  Also something the user does when
  2595.     ;; vm-arrived-message-hook is run might affect it.
  2596.     ;; vm-assimilate-new-messages returns this value so it must
  2597.     ;; not be mangled.
  2598.     (setq new-messages (copy-sequence new-messages))
  2599.     (if vm-summary-show-threads
  2600.     (progn
  2601.       ;; get numbering and summary of new messages done now
  2602.       ;; so that the sort code only has to worry about the
  2603.       ;; changes it needs to make.
  2604.       (vm-update-summary-and-mode-line)
  2605.       (vm-sort-messages "thread")))
  2606.     (if (and vm-arrived-message-hook
  2607.          new-messages
  2608.          ;; tail-cons == nil means vm-message-list was empty.
  2609.          ;; Thus new-messages == vm-message-list.  In this
  2610.          ;; case, run the hooks only if this is not the first
  2611.          ;; time vm-assimilate-new-messages has been called
  2612.          ;; in this folder.  gobble-order non-nil is a good
  2613.          ;; indicator that this is the first time because the
  2614.          ;; order is gobbled only once per visit and always
  2615.          ;; the first time vm-assimilate-new-messages is
  2616.          ;; called.
  2617.          (or tail-cons (null gobble-order)))
  2618.     (let ((new-messages new-messages))
  2619.       ;; seems wise to do this so that if the user runs VM
  2620.       ;; command here they start with as much of a clean
  2621.       ;; slate as we can provide, given we're currently deep
  2622.       ;; in the guts of VM.
  2623.       (vm-update-summary-and-mode-line)
  2624.       (while new-messages
  2625.         (vm-run-message-hook (car new-messages) 'vm-arrived-message-hook)
  2626.         (setq new-messages (cdr new-messages)))))
  2627.     (vm-update-summary-and-mode-line)
  2628.     (run-hooks 'vm-arrived-messages-hook)
  2629.     (if (and new-messages vm-virtual-buffers)
  2630.     (save-excursion
  2631.       (setq b-list vm-virtual-buffers)
  2632.       (while b-list
  2633.         ;; buffer might be dead
  2634.         (if (buffer-name (car b-list))
  2635.         (let (tail-cons)
  2636.           (set-buffer (car b-list))
  2637.           (setq tail-cons (vm-last vm-message-list))
  2638.           (vm-build-virtual-message-list new-messages)
  2639.           (if (or (null tail-cons) (cdr tail-cons))
  2640.               (progn
  2641.             (setq vm-ml-sort-keys nil)
  2642.             (if vm-thread-obarray
  2643.                 (vm-build-threads (cdr tail-cons)))
  2644.             (vm-set-summary-redo-start-point
  2645.              (or (cdr tail-cons) vm-message-list))
  2646.             (vm-set-numbering-redo-start-point
  2647.              (or (cdr tail-cons) vm-message-list))
  2648.             (if (null vm-message-pointer)
  2649.                 (progn (setq vm-message-pointer vm-message-list
  2650.                      vm-need-summary-pointer-update t)
  2651.                    (if vm-message-pointer
  2652.                        (vm-preview-current-message))))
  2653.             (if vm-summary-show-threads
  2654.                 (progn
  2655.                   (vm-update-summary-and-mode-line)
  2656.                   (vm-sort-messages "thread")))))))
  2657.         (setq b-list (cdr b-list)))))
  2658.     new-messages ))
  2659.  
  2660. ;; return a list of all marked messages or the messages indicated by a
  2661. ;; prefix argument.
  2662. (defun vm-select-marked-or-prefixed-messages (prefix)
  2663.   (let (mlist)
  2664.     (if (eq last-command 'vm-next-command-uses-marks)
  2665.     (setq mlist (vm-marked-messages))
  2666.       (let ((direction (if (< prefix 0) 'backward 'forward))
  2667.          (count (vm-abs prefix))
  2668.          (vm-message-pointer vm-message-pointer))
  2669.     (if (not (eq vm-circular-folders t))
  2670.         (vm-check-count prefix))
  2671.     (while (not (zerop count))
  2672.       (setq mlist (cons (car vm-message-pointer) mlist))
  2673.       (vm-decrement count)
  2674.       (if (not (zerop count))
  2675.           (vm-move-message-pointer direction))))
  2676.       (nreverse mlist))))
  2677.  
  2678. (defun vm-display-startup-message ()
  2679.   (if (sit-for 5)
  2680.       (let ((lines vm-startup-message-lines))
  2681.     (message "VM %s, Copyright (C) 1995 Kyle E. Jones; type ? for help"
  2682.          vm-version)
  2683.     (setq vm-startup-message-displayed t)
  2684.     (while (and (sit-for 4) lines)
  2685.       (message (substitute-command-keys (car lines)))
  2686.       (setq lines (cdr lines)))))
  2687.   (vm-unsaved-message ""))
  2688.  
  2689. (defun vm-load-init-file (&optional interactive)
  2690.   (interactive "p")
  2691.   (if (or (not vm-init-file-loaded) interactive)
  2692.       (progn
  2693.     (and vm-init-file
  2694.          (load vm-init-file (not interactive) (not interactive) t))
  2695.     (and vm-options-file (load vm-options-file t t t))))
  2696.   (setq vm-init-file-loaded t)
  2697.   (vm-display nil nil '(vm-load-init-file) '(vm-load-init-file)))
  2698.  
  2699. (defun vm-session-initialization ()
  2700.   ;; If this is the first time VM has been run in this Emacs session,
  2701.   ;; do some necessary preparations.
  2702.   (if (or (not (boundp 'vm-session-beginning))
  2703.       vm-session-beginning)
  2704.       (progn
  2705.     (random t)
  2706.     (vm-load-init-file)
  2707.     (if (not vm-window-configuration-file)
  2708.         (setq vm-window-configurations vm-default-window-configuration)
  2709.       (or (vm-load-window-configurations vm-window-configuration-file)
  2710.           (setq vm-window-configurations vm-default-window-configuration)))
  2711.     (setq vm-buffers-needing-display-update (make-vector 29 0))
  2712.     (setq vm-session-beginning nil))))
  2713.  
  2714. (defun vm-toggle-read-only ()
  2715.   (interactive)
  2716.   (vm-select-folder-buffer)
  2717.   (setq vm-folder-read-only (not vm-folder-read-only))
  2718.   (intern (buffer-name) vm-buffers-needing-display-update)
  2719.   (message "Folder is now %s"
  2720.        (if vm-folder-read-only "read-only" "modifiable"))
  2721.   (vm-display nil nil '(vm-toggle-read-only) '(vm-toggle-read-only))
  2722.   (vm-update-summary-and-mode-line))
  2723.  
  2724. ;; this does the real major mode scutwork.
  2725. (defun vm-mode-internal ()
  2726.   (widen)
  2727.   (make-local-variable 'require-final-newline)
  2728.   ;; don't kill local variables, as there is some state we'd like to
  2729.   ;; keep.  rather than non-portably marking the variables we
  2730.   ;; want to keep, just avoid calling kill-local-variables and
  2731.   ;; reset everything that needs to be reset.
  2732.   (setq
  2733.    major-mode 'vm-mode
  2734.    mode-line-format vm-mode-line-format
  2735.    mode-name "VM"
  2736.    ;; must come after the setting of major-mode
  2737.    mode-popup-menu (and vm-use-menus
  2738.             (vm-menu-support-possible-p)
  2739.             (vm-menu-mode-menu))
  2740.    buffer-read-only t
  2741.    require-final-newline nil
  2742.    vm-thread-obarray nil
  2743.    vm-thread-subject-obarray nil
  2744.    vm-label-obarray (make-vector 29 0)
  2745.    vm-last-message-pointer nil
  2746.    vm-modification-counter 0
  2747.    vm-message-list nil
  2748.    vm-message-pointer nil
  2749.    vm-message-order-changed nil
  2750.    vm-message-order-header-present nil
  2751.    vm-summary-buffer nil
  2752.    vm-system-state nil
  2753.    vm-undo-record-list nil
  2754.    vm-undo-record-pointer nil
  2755.    vm-virtual-buffers (vm-link-to-virtual-buffers)
  2756.    vm-folder-type (vm-get-folder-type))
  2757.   (use-local-map vm-mode-map)
  2758.   (and (vm-menu-support-possible-p)
  2759.        (vm-menu-install-menus))
  2760.   (run-hooks 'vm-mode-hook)
  2761.   ;; compatibility
  2762.   (run-hooks 'vm-mode-hooks))
  2763.  
  2764. (defun vm-link-to-virtual-buffers ()
  2765.   (let ((b-list (buffer-list))
  2766.     (vbuffers nil)
  2767.     (folder-buffer (current-buffer))
  2768.     folders clauses)
  2769.     (save-excursion
  2770.       (while b-list
  2771.     (set-buffer (car b-list))
  2772.     (cond ((eq major-mode 'vm-virtual-mode)
  2773.            (setq clauses (cdr vm-virtual-folder-definition))
  2774.            (while clauses
  2775.          (setq folders (car (car clauses)))
  2776.          (while folders
  2777.            (if (eq folder-buffer (vm-get-file-buffer
  2778.                       (expand-file-name
  2779.                        (car folders)
  2780.                        vm-folder-directory)))
  2781.                (setq vbuffers (cons (car b-list) vbuffers)
  2782.                  vm-real-buffers (cons folder-buffer
  2783.                            vm-real-buffers)
  2784.                  folders nil
  2785.                  clauses nil))
  2786.            (setq folders (cdr folders)))
  2787.          (setq clauses (cdr clauses)))))
  2788.     (setq b-list (cdr b-list)))
  2789.       vbuffers )))
  2790.  
  2791. (defun vm-change-folder-type (type)
  2792.   "Change folder type to TYPE.
  2793. TYPE may be one of the following symbol values:
  2794.  
  2795.     From_
  2796.     From_-with-Content-Length
  2797.     mmdf
  2798.     babyl
  2799.  
  2800. Interactively TYPE will be read from the minibuffer."
  2801.   (interactive
  2802.    (let ((this-command this-command)
  2803.      (last-command last-command)
  2804.      (types vm-supported-folder-types))
  2805.      (vm-select-folder-buffer)
  2806.      (vm-error-if-virtual-folder)
  2807.      (setq types (vm-delqual (symbol-name vm-folder-type)
  2808.                  (copy-sequence types)))
  2809.      (list (intern (vm-read-string "Change folder to type: " types)))))
  2810.   (vm-select-folder-buffer)
  2811.   (vm-check-for-killed-summary)
  2812.   (vm-error-if-virtual-folder)
  2813.   (vm-error-if-folder-empty)
  2814.   (if (not (memq type '(From_ From_-with-Content-Length mmdf babyl)))
  2815.       (error "Unknown folder type: %s" type))
  2816.   (if (or (null vm-folder-type)
  2817.       (eq vm-folder-type 'unknown))
  2818.       (error "Current folder's type is unknown, can't change it."))
  2819.   (let ((mp vm-message-list)
  2820.     (buffer-read-only nil)
  2821.     (old-type vm-folder-type)
  2822.     ;; no interruptions
  2823.     (inhibit-quit t)
  2824.     (n 0)
  2825.     ;; Just for laughs, make the update interval vary.
  2826.     (modulus (+ (% (vm-abs (random)) 11) 5))
  2827.     text-end opoint)
  2828.     (save-excursion
  2829.       (vm-save-restriction
  2830.        (widen)
  2831.        (setq vm-folder-type type)
  2832.        (goto-char (point-min))
  2833.        (vm-convert-folder-header old-type type)
  2834.        (while mp
  2835.      (goto-char (vm-start-of (car mp)))
  2836.      (setq opoint (point))
  2837.      (insert (vm-leading-message-separator type (car mp)))
  2838.      (if (> (vm-headers-of (car mp)) (vm-start-of (car mp)))
  2839.          (delete-region (point) (vm-headers-of (car mp)))
  2840.        (set-marker (vm-headers-of (car mp)) (point))
  2841.        ;; if headers-of == start-of then so could vheaders-of
  2842.        ;; and text-of.  clear them to force a recompute.
  2843.        (vm-set-vheaders-of (car mp) nil)
  2844.        (vm-set-text-of (car mp) nil))
  2845.      (vm-convert-folder-type-headers old-type type)
  2846.      (goto-char (vm-text-end-of (car mp)))
  2847.      (setq text-end (point))
  2848.      (insert-before-markers (vm-trailing-message-separator type))
  2849.      (delete-region (vm-text-end-of (car mp)) (vm-end-of (car mp)))
  2850.      (set-marker (vm-text-end-of (car mp)) text-end)
  2851.      (goto-char (vm-headers-of (car mp)))
  2852.      (vm-munge-message-separators type (vm-headers-of (car mp))
  2853.                       (vm-text-end-of (car mp)))
  2854.      (vm-set-byte-count-of (car mp) nil)
  2855.      (vm-set-babyl-frob-flag-of (car mp) nil)
  2856.      (vm-set-message-type-of (car mp) type)
  2857.      ;; Technically we should mark each message for a
  2858.      ;; summary update since the message byte counts might
  2859.      ;; have changed.  But I don't think anyone cares that
  2860.      ;; much and the summary regeneration would make this
  2861.      ;; process slower.
  2862.      (setq mp (cdr mp) n (1+ n))
  2863.      (if (zerop (% n modulus))
  2864.          (vm-unsaved-message "Converting... %d" n))))))
  2865.   (vm-clear-modification-flag-undos)
  2866.   (intern (buffer-name) vm-buffers-needing-display-update)
  2867.   (vm-update-summary-and-mode-line)
  2868.   (message "Conversion complete.")
  2869.   ;; message separator strings may have leaked into view
  2870.   (if (> (point-max) (vm-text-end-of (car vm-message-pointer)))
  2871.       (narrow-to-region (point-min) (vm-text-end-of (car vm-message-pointer))))
  2872.   (vm-display nil nil '(vm-change-folder-type) '(vm-change-folder-type)))
  2873.  
  2874. (if (not (memq 'vm-write-file-hook write-file-hooks))
  2875.     (setq write-file-hooks
  2876.       (cons 'vm-write-file-hook write-file-hooks)))
  2877.  
  2878. (if (not (memq 'vm-handle-file-recovery find-file-hooks))
  2879.     (setq find-file-hooks
  2880.       (nconc find-file-hooks
  2881.          '(vm-handle-file-recovery
  2882.            vm-handle-file-reversion))))
  2883.  
  2884. ;; after-revert-hook is new to FSF v19.23
  2885. (defvar after-revert-hook)
  2886. (if (boundp 'after-revert-hook)
  2887.     (setq after-revert-hook
  2888.       (cons 'vm-after-revert-buffer-hook after-revert-hook))
  2889.   (setq after-revert-hook (list 'vm-after-revert-buffer-hook)))
  2890.