1 ;;; vm-folder.el --- VM folder related functions
3 ;; Copyright (C) 1989-2001 Kyle E. Jones
4 ;; Copyright (C) 2003-2006 Robert Widhopf-Fenk
6 ;; This program is free software; you can redistribute it and/or modify
7 ;; it under the terms of the GNU General Public License as published by
8 ;; the Free Software Foundation; either version 2 of the License, or
9 ;; (at your option) any later version.
11 ;; This program is distributed in the hope that it will be useful,
12 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 ;; GNU General Public License for more details.
16 ;; You should have received a copy of the GNU General Public License along
17 ;; with this program; if not, write to the Free Software Foundation, Inc.,
18 ;; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 (defun vm-number-messages (&optional start-point end-point)
22 "Set the number-of and padded-number-of slots of messages
25 If non-nil, START-POINT should point to a cons cell in
26 vm-message-list and the numbering will begin there, else the
27 numbering will begin at the head of vm-message-list. If
28 START-POINT is non-nil the reverse-link-of slot of the message in
29 the cons must be valid and the message pointed to (if any) must
30 have a non-nil number-of slot, because it is used to determine
31 what the starting message number should be.
33 If non-nil, END-POINT should point to a cons cell in
34 vm-message-list and the numbering will end with the message just
35 before this cell. A nil value means numbering will be done until
36 the end of vm-message-list is reached."
37 (let ((n 1) (message-list (or start-point vm-message-list)))
38 (if (and start-point (vm-reverse-link-of (car start-point)))
39 (setq n (1+ (string-to-number
43 (car start-point))))))))
44 (while (not (eq message-list end-point))
45 (vm-set-number-of (car message-list) (int-to-string n))
46 (vm-set-padded-number-of (car message-list) (format "%3d" n))
47 (setq n (1+ n) message-list (cdr message-list)))
48 (or end-point (setq vm-ml-highest-message-number (int-to-string (1- n))))
50 (vm-copy-local-variables vm-summary-buffer
51 'vm-ml-highest-message-number))))
53 (defun vm-set-numbering-redo-start-point (start-point)
54 "Set vm-numbering-redo-start-point to START-POINT if appropriate.
55 Also mark the current buffer as needing a display update.
57 START-POINT should be a cons in vm-message-list or just t.
58 (t means start from the beginning of vm-message-list.)
59 If START-POINT is closer to the head of vm-message-list than
60 vm-numbering-redo-start-point or is equal to t, then
61 vm-numbering-redo-start-point is set to match it."
62 (intern (buffer-name) vm-buffers-needing-display-update)
63 (if (eq vm-numbering-redo-start-point t)
65 (if (and (consp start-point) (consp vm-numbering-redo-start-point))
66 (let ((mp vm-message-list))
69 (or (eq (car mp) (car start-point))
70 (eq (car mp) (car vm-numbering-redo-start-point)))))
73 (error "Something is wrong in vm-set-numbering-redo-start-point"))
74 (if (eq (car mp) (car start-point))
75 (setq vm-numbering-redo-start-point start-point)))
76 (setq vm-numbering-redo-start-point start-point))))
78 (defun vm-set-numbering-redo-end-point (end-point)
79 "Set vm-numbering-redo-end-point to END-POINT if appropriate.
80 Also mark the current buffer as needing a display update.
82 END-POINT should be a cons in vm-message-list or just t.
83 (t means number all the way to the end of vm-message-list.)
84 If END-POINT is closer to the end of vm-message-list or is equal
85 to t, then vm-numbering-redo-start-point is set to match it.
86 The number-of slot is used to determine proximity to the end of
87 vm-message-list, so this slot must be valid in END-POINT's message
88 and the message in the cons pointed to by vm-numbering-redo-end-point."
89 (intern (buffer-name) vm-buffers-needing-display-update)
90 (cond ((eq end-point t)
91 (setq vm-numbering-redo-end-point t))
92 ((and (consp end-point)
98 (car vm-numbering-redo-end-point)))))
99 (setq vm-numbering-redo-end-point end-point))
101 (setq vm-numbering-redo-end-point end-point))))
103 (defun vm-do-needed-renumbering ()
104 "Number messages in vm-message-list as specified by
105 vm-numbering-redo-start-point and vm-numbering-redo-end-point.
107 vm-numbering-redo-start-point = t means start at the head
109 vm-numbering-redo-end-point = t means number all the way to the
110 end of vm-message-list.
112 Otherwise the variables' values should be conses in vm-message-list
114 (if vm-numbering-redo-start-point
116 (vm-number-messages (and (consp vm-numbering-redo-start-point)
117 vm-numbering-redo-start-point)
118 vm-numbering-redo-end-point)
119 (setq vm-numbering-redo-start-point nil
120 vm-numbering-redo-end-point nil))))
122 (defun vm-set-summary-redo-start-point (start-point)
123 "Set vm-summary-redo-start-point to START-POINT if appropriate.
124 Also mark the current buffer as needing a display update.
126 START-POINT should be a cons in vm-message-list or just t.
127 (t means start from the beginning of vm-message-list.)
128 If START-POINT is closer to the head of vm-message-list than
129 vm-summary-redo-start-point or is equal to t, then
130 vm-summary-redo-start-point is set to match it."
131 (intern (buffer-name) vm-buffers-needing-display-update)
132 (if (eq vm-summary-redo-start-point t)
134 (if (and (consp start-point) (consp vm-summary-redo-start-point))
135 (let ((mp vm-message-list))
136 (while (and mp (not (or (eq mp start-point)
137 (eq mp vm-summary-redo-start-point))))
140 (error "Something is wrong in vm-set-summary-redo-start-point"))
141 (if (eq mp start-point)
142 (setq vm-summary-redo-start-point start-point)))
143 (setq vm-summary-redo-start-point start-point))))
145 (defun vm-mark-for-summary-update (m &optional dont-kill-cache)
146 "Mark message M for a summary update.
147 Also mark M's buffer as needing a display update. Any virtual
148 messages of M and their buffers are similarly marked for update.
149 If M is a virtual message and virtual mirroring is in effect for
150 M (i.e. attribute-of eq attributes-of M's real message), M's real
151 message and its buffer are scheduled for an update.
153 Optional arg DONT-KILL-CACHE non-nil means don't invalidate the
154 summary-of slot for any messages marked for update. This is
155 meant to be used by functions that update message information
156 that is not cached in the summary-of slot, e.g. message numbers
157 and thread indentation."
158 (cond ((eq m (vm-real-message-of m))
159 ;; this is a real message.
160 ;; its summary and modeline need to be updated.
161 (if (not dont-kill-cache)
162 ;; toss the cache. this also tosses the cache of any
163 ;; virtual messages mirroring this message. the summary
164 ;; entry cache must be cleared when an attribute of a
165 ;; message that could appear in the summary has changed.
166 (vm-set-summary-of m nil))
167 (if (vm-su-start-of m)
168 (setq vm-messages-needing-summary-update
169 (cons m vm-messages-needing-summary-update)))
170 (intern (buffer-name (vm-buffer-of m))
171 vm-buffers-needing-display-update)
172 ;; find the virtual messages of this real message that
173 ;; need a summary update.
174 (let ((m-list (vm-virtual-messages-of m)))
176 (if (eq (vm-attributes-of m) (vm-attributes-of (car m-list)))
178 (and (vm-su-start-of (car m-list))
179 (setq vm-messages-needing-summary-update
181 vm-messages-needing-summary-update)))
182 (intern (buffer-name (vm-buffer-of (car m-list)))
183 vm-buffers-needing-display-update)))
184 (setq m-list (cdr m-list)))))
186 ;; this is a virtual message.
188 ;; if this message has virtual messages then we need to
189 ;; schedule updates for all the virtual messages that
190 ;; share a cache with this message and we need to
191 ;; schedule an update for the underlying real message
192 ;; since we are mirroring it.
194 ;; if there are no virtual messages, then this virtual
195 ;; message is not mirroring its real message so we need
196 ;; only take care of this one message.
197 (if (vm-virtual-messages-of m)
198 (let ((m-list (vm-virtual-messages-of m)))
199 ;; schedule updates for all the virtual message who share
200 ;; the same cache as this message.
202 (if (eq (vm-attributes-of m) (vm-attributes-of (car m-list)))
204 (and (vm-su-start-of (car m-list))
205 (setq vm-messages-needing-summary-update
207 vm-messages-needing-summary-update)))
208 (intern (buffer-name (vm-buffer-of (car m-list)))
209 vm-buffers-needing-display-update)))
210 (setq m-list (cdr m-list)))
211 ;; now take care of the real message
212 (if (not dont-kill-cache)
213 ;; toss the cache. this also tosses the cache of
214 ;; any virtual messages sharing the same cache as
216 (vm-set-summary-of m nil))
217 (and (vm-su-start-of (vm-real-message-of m))
218 (setq vm-messages-needing-summary-update
219 (cons (vm-real-message-of m)
220 vm-messages-needing-summary-update)))
221 (intern (buffer-name (vm-buffer-of (vm-real-message-of m)))
222 vm-buffers-needing-display-update))
223 (if (not dont-kill-cache)
224 (vm-set-virtual-summary-of m nil))
225 (and (vm-su-start-of m)
226 (setq vm-messages-needing-summary-update
227 (cons m vm-messages-needing-summary-update)))
228 (intern (buffer-name (vm-buffer-of m))
229 vm-buffers-needing-display-update)))))
231 (defun vm-force-mode-line-update ()
232 "Force a mode line update in all frames."
233 (if (fboundp 'force-mode-line-update)
234 (force-mode-line-update t)
236 (set-buffer (other-buffer))
237 (set-buffer-modified-p (buffer-modified-p)))))
239 (defun vm-do-needed-mode-line-update ()
240 "Do a modeline update for the current folder buffer.
241 This means setting up all the various vm-ml attribute variables
242 in the folder buffer and copying necessary variables to the
243 folder buffer's summary and presentation buffers, and then
244 forcing Emacs to update all modelines.
246 If a virtual folder being updated has no messages, then
247 erase-buffer is called on its buffer.
249 If any type of folder is empty, erase-buffer is called
250 on its presentation buffer, if any."
251 ;; XXX This last bit should probably should be moved to
252 ;; XXX vm-expunge-folder.
254 (if (null vm-message-pointer)
256 ;; erase the leftover message if the folder is really empty.
257 (if (eq major-mode 'vm-virtual-mode)
258 (let ((buffer-read-only nil)
259 (omodified (buffer-modified-p)))
262 (set-buffer-modified-p omodified))))
263 (if vm-presentation-buffer
264 (let ((omodified (buffer-modified-p)))
267 (set-buffer vm-presentation-buffer)
268 (let ((buffer-read-only nil))
270 (set-buffer-modified-p omodified)))))
271 ;; try to avoid calling vm-su-labels if possible so as to
272 ;; avoid loading vm-summary.el.
273 (if (vm-labels-of (car vm-message-pointer))
274 (setq vm-ml-labels (vm-su-labels (car vm-message-pointer)))
275 (setq vm-ml-labels nil))
276 (setq vm-ml-message-number (vm-number-of (car vm-message-pointer)))
277 (setq vm-ml-message-new (vm-new-flag (car vm-message-pointer)))
278 (setq vm-ml-message-unread (vm-unread-flag (car vm-message-pointer)))
279 (setq vm-ml-message-read
280 (and (not (vm-new-flag (car vm-message-pointer)))
281 (not (vm-unread-flag (car vm-message-pointer)))))
282 (setq vm-ml-message-edited (vm-edited-flag (car vm-message-pointer)))
283 (setq vm-ml-message-filed (vm-filed-flag (car vm-message-pointer)))
284 (setq vm-ml-message-written (vm-written-flag (car vm-message-pointer)))
285 (setq vm-ml-message-replied (vm-replied-flag (car vm-message-pointer)))
286 (setq vm-ml-message-forwarded (vm-forwarded-flag (car vm-message-pointer)))
287 (setq vm-ml-message-redistributed (vm-redistributed-flag (car vm-message-pointer)))
288 (setq vm-ml-message-deleted (vm-deleted-flag (car vm-message-pointer)))
289 (setq vm-ml-message-marked (vm-mark-of (car vm-message-pointer))))
290 (if vm-summary-buffer
291 (let ((modified (buffer-modified-p)))
293 (vm-copy-local-variables vm-summary-buffer
296 'vm-ml-message-unread
298 'vm-ml-message-edited
299 'vm-ml-message-replied
300 'vm-ml-message-forwarded
302 'vm-ml-message-written
303 'vm-ml-message-deleted
304 'vm-ml-message-marked
305 'vm-ml-message-redistributed
306 'vm-ml-message-number
307 'vm-ml-highest-message-number
310 'vm-virtual-folder-definition
314 'vm-spooled-mail-waiting
316 (set-buffer vm-summary-buffer)
317 (set-buffer-modified-p modified))))
318 (if vm-presentation-buffer
319 (let ((modified (buffer-modified-p)))
321 (vm-copy-local-variables vm-presentation-buffer
324 'vm-ml-message-unread
326 'vm-ml-message-edited
327 'vm-ml-message-replied
328 'vm-ml-message-forwarded
330 'vm-ml-message-written
331 'vm-ml-message-deleted
332 'vm-ml-message-marked
333 'vm-ml-message-number
334 'vm-ml-message-redistributed
335 'vm-ml-highest-message-number
338 'vm-virtual-folder-definition
341 'vm-spooled-mail-waiting
343 (set-buffer vm-presentation-buffer)
344 (set-buffer-modified-p modified))))
345 (vm-force-mode-line-update))
347 (defun vm-update-summary-and-mode-line ()
348 "Update summary and mode line for all VM folder and summary buffers.
349 Really this updates all the visible status indicators.
351 Message lists are renumbered.
352 Summary entries are wiped and regenerated.
353 Mode lines are updated.
354 Toolbars are updated."
358 (setq b (get-buffer (symbol-name b)))
362 (intern (buffer-name)
363 vm-buffers-needing-undo-boundaries)
364 (vm-check-for-killed-summary)
366 (vm-toolbar-support-possible-p)
367 (vm-toolbar-update-toolbar))
368 (vm-do-needed-renumbering)
369 (if vm-summary-buffer
370 (vm-do-needed-summary-rebuild))
371 (vm-do-needed-mode-line-update)))))
372 vm-buffers-needing-display-update)
373 (fillarray vm-buffers-needing-display-update 0))
374 (if vm-messages-needing-summary-update
376 (mapcar (function vm-update-message-summary)
377 vm-messages-needing-summary-update)
378 (setq vm-messages-needing-summary-update nil)))
379 (vm-do-needed-folders-summary-update)
380 (vm-force-mode-line-update))
382 (defun vm-reverse-link-messages ()
383 "Set reverse links for all messages in vm-message-list."
384 (let ((mp vm-message-list)
387 (vm-set-reverse-link-of (car mp) prev)
388 (setq prev mp mp (cdr mp)))))
390 (defun vm-match-ordered-header (alist)
391 "Try to match a header in ALIST and return the matching cell.
392 This is used by header ordering code.
394 ALIST looks like this ((\"From\") (\"To\")). This function returns
395 the alist element whose car matches the header starting at point.
396 The header ordering code uses the cdr of the element
397 returned to hold headers to be output later."
398 (let ((case-fold-search t))
401 (if (looking-at (car (car alist)))
402 (throw 'match (car alist)))
403 (setq alist (cdr alist)))
406 (defun vm-match-header (&optional header-name)
407 "Match a header and save some state information about the matched header.
408 Optional first arg HEADER-NAME means match the header only
409 if it matches HEADER-NAME. HEADER-NAME should be a string
410 containing a header name. The string should end with a colon if just
411 that name should be matched. A string that does not end in a colon
412 will match all headers that begin with that string.
414 State information is stored in vm-matched-header-vector bound to a vector
417 [ header-start header-end
418 header-name-start header-name-end
419 header-contents-start header-contents-end ]
421 Elements are integers.
422 There are functions to access and use this info."
423 (let ((case-fold-search t)
424 (header-name-regexp "\\([^ \t\n:]+\\):"))
426 (and (looking-at header-name) (looking-at header-name-regexp))
427 (looking-at header-name-regexp))
429 (aset vm-matched-header-vector 0 (point))
430 (aset vm-matched-header-vector 2 (point))
431 (aset vm-matched-header-vector 3 (match-end 1))
432 (goto-char (match-end 0))
433 ;; skip leading whitespace
434 (skip-chars-forward " \t")
435 (aset vm-matched-header-vector 4 (point))
437 (while (looking-at "[ \t]")
439 (aset vm-matched-header-vector 1 (point))
440 ;; drop the trailing newline
441 (aset vm-matched-header-vector 5 (1- (point)))))))
443 (defun vm-matched-header ()
444 "Returns the header last matched by vm-match-header.
445 Trailing newline is included."
446 (vm-buffer-substring-no-properties (aref vm-matched-header-vector 0)
447 (aref vm-matched-header-vector 1)))
449 (defun vm-matched-header-name ()
450 "Returns the name of the header last matched by vm-match-header."
451 (vm-buffer-substring-no-properties (aref vm-matched-header-vector 2)
452 (aref vm-matched-header-vector 3)))
454 (defun vm-matched-header-contents ()
455 "Returns the contents of the header last matched by vm-match-header.
456 Trailing newline is not included."
457 (vm-buffer-substring-no-properties (aref vm-matched-header-vector 4)
458 (aref vm-matched-header-vector 5)))
460 (defun vm-matched-header-start ()
461 "Returns the start position of the header last matched by vm-match-header."
462 (aref vm-matched-header-vector 0))
464 (defun vm-matched-header-end ()
465 "Returns the end position of the header last matched by vm-match-header."
466 (aref vm-matched-header-vector 1))
468 (defun vm-matched-header-name-start ()
469 "Returns the start position of the name of the header last matched
471 (aref vm-matched-header-vector 2))
473 (defun vm-matched-header-name-end ()
474 "Returns the end position of the name of the header last matched
476 (aref vm-matched-header-vector 3))
478 (defun vm-matched-header-contents-start ()
479 "Returns the start position of the contents of the header last matched
481 (aref vm-matched-header-vector 4))
483 (defun vm-matched-header-contents-end ()
484 "Returns the end position of the contents of the header last matched
486 (aref vm-matched-header-vector 5))
488 (defun vm-get-folder-type (&optional file start end ignore-visited)
489 "Return a symbol indicating the folder type of the current buffer.
490 This function works by examining the beginning of a folder.
491 If optional arg FILE is present the type of FILE is returned instead.
492 If FILE is being visited, the type of the buffer is returned.
493 If optional second and third arg START and END are provided,
494 vm-get-folder-type will examine the text between those buffer
495 positions. START and END default to 1 and (buffer-size) + 1.
496 If IGNORED-VISITED is non-nil, even if FILE is being visited, its
497 buffer is ignored and the disk copy of FILE is examined.
500 nil if folder has no type (empty)
501 unknown if the type is not known to VM
502 mmdf for MMDF folders
503 babyl for BABYL folders
504 From_ for BSD UNIX From_ folders
505 BellFrom_ for old SysV From_ folders
506 From_-with-Content-Length
507 for new SysV folders that use the Content-Length header
509 If vm-trust-From_-with-Content-Length is non-nil,
510 From_-with-Content-Length is returned if the first message in the
511 folder has a Content-Length header and the folder otherwise looks
514 Since BellFrom_ and From_ folders cannot be reliably distinguished
515 from each other, you must tell VM which one your system uses by
516 setting the variable vm-default-From_-folder-type to either From_ or
517 BellFrom_. For folders that could be From_ or BellFrom_ folders,
518 the value of vm-default-From_folder-type will be returned."
519 (let ((temp-buffer nil)
521 (case-fold-search nil))
526 (if (not ignore-visited)
527 (setq b (vm-get-file-buffer file)))
530 (setq temp-buffer (vm-make-work-buffer))
531 (set-buffer temp-buffer)
532 (if (file-readable-p file)
534 (let ((coding-system-for-read
535 (vm-binary-coding-system)))
536 (insert-file-contents file nil 0 4096))
537 (wrong-number-of-arguments
538 (call-process "sed" file temp-buffer nil
539 "-n" "1,/^$/p")))))))
542 (or start (setq start 1))
543 (or end (setq end (1+ (buffer-size))))
545 (narrow-to-region start end)
546 (goto-char (point-min))
547 (cond ((zerop (buffer-size)) nil)
548 ((looking-at "\n*From ")
549 (if (not vm-trust-From_-with-Content-Length)
550 vm-default-From_-folder-type
551 (let ((case-fold-search t))
552 (re-search-forward vm-content-length-search-regexp
554 (cond ((match-beginning 1)
555 vm-default-From_-folder-type)
557 'From_-with-Content-Length)
558 (t vm-default-From_-folder-type))))
559 ((looking-at "\001\001\001\001\n") 'mmdf)
560 ((looking-at "BABYL OPTIONS:") 'babyl)
562 (and temp-buffer (kill-buffer temp-buffer)))))
564 (defun vm-convert-folder-type (old-type new-type)
565 "Convert buffer from OLD-TYPE to NEW-TYPE.
566 OLD-TYPE and NEW-TYPE should be symbols returned from vm-get-folder-type.
567 This should be called on non-live buffers like crash boxes.
568 This will confuse VM if called on a folder buffer in vm-mode."
569 (let ((vm-folder-type old-type)
572 (goto-char (point-min))
573 (vm-skip-past-folder-header)
574 (while (vm-find-leading-message-separator)
575 (setq pos-list (cons (point-marker) pos-list))
576 (vm-skip-past-leading-message-separator)
577 (setq pos-list (cons (point-marker) pos-list))
578 (vm-find-trailing-message-separator)
579 (setq pos-list (cons (point-marker) pos-list))
580 (vm-skip-past-trailing-message-separator)
581 (setq pos-list (cons (point-marker) pos-list)))
582 (setq pos-list (nreverse pos-list))
583 (goto-char (point-min))
584 (vm-convert-folder-header old-type new-type)
586 (setq beg (car pos-list))
587 (goto-char (car pos-list))
588 (insert-before-markers (vm-leading-message-separator new-type))
589 (delete-region (car pos-list) (car (cdr pos-list)))
590 (vm-convert-folder-type-headers old-type new-type)
591 (setq pos-list (cdr (cdr pos-list)))
592 (setq end (marker-position (car pos-list)))
593 (goto-char (car pos-list))
594 (insert-before-markers (vm-trailing-message-separator new-type))
595 (delete-region (car pos-list) (car (cdr pos-list)))
597 (vm-munge-message-separators new-type beg end)
598 (setq pos-list (cdr (cdr pos-list))))))
600 (defun vm-convert-folder-header (old-type new-type)
601 "Convert the folder header form OLD-TYPE to NEW-TYPE.
602 The folder header is the text at the beginning of a folder that
603 is a legal part of the folder but is not part of the first
604 message. This is for dealing with BABYL files."
605 (if (eq old-type 'babyl)
608 (case-fold-search t))
609 (cond ((and (looking-at "BABYL OPTIONS:")
610 (search-forward "\037" nil t))
611 (delete-region beg (point)))))))
612 (if (eq new-type 'babyl)
613 ;; insert before markers so that message location markers
614 ;; for the first message get moved forward.
615 (insert-before-markers "BABYL OPTIONS:\nVersion: 5\n\037")))
617 (defun vm-skip-past-folder-header ()
618 "Move point past the folder header.
619 The folder header is the text at the beginning of a folder that
620 is a legal part of the folder but is not part of the first
621 message. This is for dealing with BABYL files."
622 (cond ((eq vm-folder-type 'babyl)
623 (search-forward "\037" nil 0))))
625 (defun vm-convert-folder-type-headers (old-type new-type)
626 "Convert headers in the message around point from OLD-TYPE to NEW-TYPE.
627 This means to add/delete Content-Length and any other
628 headers related to folder-type as needed for folder type
629 conversions. This function expects point to be at the beginning
630 of the header section of a message, and it only deals with that
633 ;; get the length now before the content-length headers are
635 (if (eq new-type 'From_-with-Content-Length)
639 (search-forward "\n\n" nil 0)
640 (setq start (point)))
641 (let ((vm-folder-type old-type))
642 (vm-find-trailing-message-separator))
643 (setq length (- (point) start)))))
644 ;; chop out content-length header if new format doesn't need
645 ;; it or if the new format computed his own copy.
646 (if (or (eq old-type 'From_-with-Content-Length)
647 (eq new-type 'From_-with-Content-Length))
649 (while (and (let ((case-fold-search t))
650 (re-search-forward vm-content-length-search-regexp
652 (null (match-beginning 1))
653 (progn (goto-char (match-beginning 0))
654 (vm-match-header vm-content-length-header)))
655 (delete-region (vm-matched-header-start)
656 (vm-matched-header-end)))))
657 ;; insert the content-length header if needed
658 (if (eq new-type 'From_-with-Content-Length)
660 (insert vm-content-length-header " " (int-to-string length) "\n")))))
662 (defun vm-munge-message-separators (folder-type start end)
663 "Munge message separators of FOLDER-TYPE found between START and END.
664 This function is used to eliminate message separators for a particular
665 folder type that happen to occur in a message. \">\" is prepended to such
668 ;; when munging From-type separators it is best to use the
669 ;; least forgiving of the folder types, so that we don't
670 ;; create folders that other mailers or older versions of VM
672 (if (eq folder-type 'From_)
673 (setq folder-type 'BellFrom_))
674 (let ((vm-folder-type folder-type))
675 (cond ((memq folder-type '(From_ From_-with-Content-Length mmdf
677 (setq end (vm-marker end))
679 (while (and (vm-find-leading-message-separator)
682 (set-marker end nil))))))
684 (defun vm-compatible-folder-p (file)
685 "Return non-nil if FILE is a compatible folder with the current buffer.
686 The current folder must have vm-folder-type initialized.
687 FILE is compatible if
689 - the current folder is empty
690 - the two folder types are equal"
691 (let ((type (vm-get-folder-type file)))
692 (or (not (and vm-folder-type type))
693 (eq vm-folder-type type))))
695 (defun vm-leading-message-separator (&optional folder-type message
697 "Returns a leading message separator for the current folder.
698 Defaults to returning a separator for the current folder type.
700 Optional first arg FOLDER-TYPE means return a separator for that
703 Optional second arg MESSAGE should be a message struct. This is used
704 generating BABYL separators, because they contain message attributes
705 and labels that must must be copied from the message.
707 Optional third arg FOR-OTHER-FOLDER non-nil means that this separator will
708 be used a `foreign' folder. This means that the `deleted'
709 attributes should not be copied for BABYL folders."
710 (let ((type (or folder-type vm-folder-type)))
711 (cond ((memq type '(From_ From_-with-Content-Length BellFrom_))
712 (concat "From VM " (current-time-string) "\n"))
714 "\001\001\001\001\n")
718 (vm-babyl-attributes-string message for-other-folder)
719 ",\n*** EOOH ***\n"))
720 (t "\014\n0, recent, unseen,,\n*** EOOH ***\n"))))))
722 (defun vm-trailing-message-separator (&optional folder-type)
723 "Returns a trailing message separator for the current folder.
724 Defaults to returning a separator for the current folder type.
726 Optional first arg FOLDER-TYPE means return a separator for that
727 folder type instead."
728 (let ((type (or folder-type vm-folder-type)))
729 (cond ((eq type 'From_) "\n")
730 ((eq type 'From_-with-Content-Length) "")
731 ((eq type 'BellFrom_) "")
732 ((eq type 'mmdf) "\001\001\001\001\n")
733 ((eq type 'babyl) "\037"))))
735 (defun vm-folder-header (&optional folder-type label-obarray)
736 "Returns a folder header for the current folder.
737 Defaults to returning a folder header for the current folder type.
739 Optional first arg FOLDER-TYPE means return a folder header for that
742 Optional second arg LABEL-OBARRAY should be an obarray of labels
743 that have been used in this folder. This is used for BABYL folders."
744 (let ((type (or folder-type vm-folder-type)))
745 (cond ((eq type 'babyl)
750 (setq list (cons sym list))))
753 (format "BABYL OPTIONS:\nVersion: 5\nLabels: %s\n\037"
754 (mapconcat (function symbol-name) list ", "))
755 "BABYL OPTIONS:\nVersion: 5\n\037")))
758 (defun vm-find-leading-message-separator ()
759 "Find the next leading message separator in a folder.
760 Returns non-nil if the separator is found, nil otherwise."
762 ((eq vm-folder-type 'From_)
763 (let ((reg1 "^From .*[0-9]$")
764 (case-fold-search nil))
766 (while (re-search-forward reg1 nil 'no-error)
767 (goto-char (match-beginning 0))
768 (if (or (< (point) 3)
769 (equal (char-after (- (point) 2)) ?\n))
773 ((eq vm-folder-type 'BellFrom_)
774 (let ((reg1 "^From .*[0-9]$")
775 (case-fold-search nil))
776 (if (re-search-forward reg1 nil 'no-error)
778 (goto-char (match-beginning 0))
781 ((eq vm-folder-type 'From_-with-Content-Length)
782 (let ((reg1 "\\(^\\|\n+\\)From ")
783 (case-fold-search nil))
784 (if (re-search-forward reg1 nil 'no-error)
785 (progn (goto-char (match-end 1)) t)
787 ((eq vm-folder-type 'mmdf)
788 (let ((reg1 "^\001\001\001\001")
789 (case-fold-search nil))
790 (if (re-search-forward reg1 nil 'no-error)
792 (goto-char (match-beginning 0))
795 ((eq vm-folder-type 'baremessage)
796 (goto-char (point-max)))
797 ((eq vm-folder-type 'babyl)
798 (let ((reg1 "\014\n[01],")
799 (case-fold-search nil))
801 (while (re-search-forward reg1 nil 'no-error)
802 (goto-char (match-beginning 0))
803 (if (and (not (bobp)) (= (preceding-char) ?\037))
808 (defun vm-find-trailing-message-separator ()
809 "Find the next trailing message separator in a folder."
811 ((eq vm-folder-type 'From_)
812 (vm-find-leading-message-separator)
814 ((eq vm-folder-type 'BellFrom_)
815 (vm-find-leading-message-separator))
816 ((eq vm-folder-type 'From_-with-Content-Length)
817 (let ((reg1 "^From ")
819 (start-point (point))
820 (case-fold-search nil))
821 (if (and (let ((case-fold-search t))
822 (re-search-forward vm-content-length-search-regexp nil t))
823 (null (match-beginning 1))
824 (progn (goto-char (match-beginning 0))
825 (vm-match-header vm-content-length-header)))
828 (string-to-number (vm-matched-header-contents)))
829 ;; if search fails, we'll be at point-max
830 ;; if specified content-length is too long, go to point-max
831 (if (search-forward "\n\n" nil 0)
832 (if (>= (- (point-max) (point)) content-length)
833 (forward-char content-length)
834 (goto-char (point-max))))
835 ;; Some systems seem to add a trailing newline that's
836 ;; not counted in the Content-Length header. Allow
837 ;; any number of them to avoid trouble.
838 (skip-chars-forward "\n")))
839 (if (or (eobp) (looking-at reg1))
841 (goto-char start-point)
842 (if (re-search-forward reg1 nil 0)
843 (forward-char -5)))))
844 ((eq vm-folder-type 'mmdf)
845 (vm-find-leading-message-separator))
846 ((eq vm-folder-type 'baremessage)
847 (goto-char (point-max)))
848 ((eq vm-folder-type 'babyl)
849 (vm-find-leading-message-separator)
852 (defun vm-skip-past-leading-message-separator ()
853 "Move point past a leading message separator at point."
855 ((memq vm-folder-type '(From_ BellFrom_ From_-with-Content-Length))
856 (let ((reg1 "^>From ")
857 (case-fold-search nil))
859 (while (looking-at reg1)
861 ((eq vm-folder-type 'mmdf)
863 ;; skip >From. Either SCO's MMDF implementation leaves this
864 ;; stuff in the message, or many sysadmins have screwed up
865 ;; their mail configuration. Either way I'm tired of getting
866 ;; bug reports about it.
867 (let ((reg1 "^>From ")
868 (case-fold-search nil))
869 (while (looking-at reg1)
871 ((eq vm-folder-type 'babyl)
872 (search-forward "\n*** EOOH ***\n" nil 0))))
874 (defun vm-skip-past-trailing-message-separator ()
875 "Move point past a trailing message separator at point."
877 ((eq vm-folder-type 'From_)
880 ((eq vm-folder-type 'From_-with-Content-Length))
881 ((eq vm-folder-type 'BellFrom_))
882 ((eq vm-folder-type 'mmdf)
884 ((eq vm-folder-type 'babyl)
887 (defun vm-build-message-list ()
888 "Build a chain of message structures, stored them in vm-message-list.
889 Finds the start and end of each message and fills in the relevant
890 fields in the message structures.
892 Also finds the beginning of the header section and the end of the
893 text section and fills in these fields in the message structures.
895 vm-text-of and vm-vheaders-of fields don't get filled until they
898 If vm-message-list already contained messages, the end of the last
899 known message is found and then the parsing of new messages begins
900 there and the message are appended to vm-message-list.
902 vm-folder-type is initialized here."
903 (setq vm-folder-type (vm-get-folder-type))
905 (let ((tail-cons nil)
907 ;; Just for yucks, make the update interval vary.
908 (modulus (+ (% (vm-abs (random)) 11) 25))
911 ;; there are already messages, therefore we're supposed
912 ;; to add to this list.
913 (let ((mp vm-message-list)
915 ;; first we have to find physical end of the folder
916 ;; prior to the new messages that just came in.
918 (if (< end (vm-end-of (car mp)))
919 (setq end (vm-end-of (car mp))))
920 (if (not (consp (cdr mp)))
924 ;; there are no messages so we're building the whole list.
925 ;; start from the beginning of the folder.
926 (goto-char (point-min))
927 ;; whine about newlines at the beginning of the folder.
928 ;; technically I think this is corruption, but there are
929 ;; too many busted mail-do-fcc's installed out there to
930 ;; do more than whine.
931 (if (and (memq vm-folder-type '(From_ BellFrom_
932 From_-with-Content-Length))
933 (= (following-char) ?\n))
935 (message "Warning: newline found at beginning of folder, %s"
936 (or buffer-file-name (buffer-name)))
938 (vm-skip-past-folder-header))
939 (setq last-end (point))
940 ;; parse the messages, set the markers that specify where
942 (while (vm-find-leading-message-separator)
943 (setq message (vm-make-message))
944 (vm-set-message-type-of message vm-folder-type)
945 (vm-set-message-access-method-of message vm-folder-access-method)
946 (vm-set-start-of message (vm-marker (point)))
947 (vm-skip-past-leading-message-separator)
948 (vm-set-headers-of message (vm-marker (point)))
949 (vm-find-trailing-message-separator)
950 (vm-set-text-end-of message (vm-marker (point)))
951 (vm-skip-past-trailing-message-separator)
952 (setq last-end (point))
953 (vm-set-end-of message (vm-marker (point)))
954 (vm-set-reverse-link-of message tail-cons)
956 (setq vm-message-list (list message)
957 tail-cons vm-message-list)
958 (setcdr tail-cons (list message))
959 (setq tail-cons (cdr tail-cons)))
961 (if (zerop (% n modulus))
962 (message "Parsing messages... %d" n)))
964 (message "Parsing messages... done"))
965 (if (and (not (= last-end (point-max)))
966 (not (eq vm-folder-type 'unknown)))
968 (message "Warning: garbage found at end of folder, %s, starting at %d"
969 (or buffer-file-name (buffer-name))
973 (defun vm-build-header-order-alist (vheaders)
974 (let ((order-alist (cons nil nil))
976 (setq list order-alist)
978 (setcdr list (cons (cons (car vheaders) nil) nil))
979 (setq list (cdr list) vheaders (cdr vheaders)))
982 ;; Reorder the headers in a message.
984 ;; If a message struct is passed into this function, then we're
985 ;; operating on a message in a folder buffer. Headers are
986 ;; grouped so that the headers that the user wants to see are at
987 ;; the end of the headers section so we can narrow to them. This
988 ;; is done according to the preferences specified in
989 ;; vm-visible-header and vm-invisible-header-regexp. The
990 ;; vheaders field of the message struct is also set. This
991 ;; function is called on demand whenever a vheaders field is
992 ;; discovered to be nil for a particular message.
994 ;; If the message argument is nil, then we are operating on a
995 ;; freestanding message that is not part of a folder buffer. The
996 ;; keep-list and discard-regexp parameters are used in this case.
997 ;; Headers not matched by the keep list or matched by the discard
998 ;; list are stripped from the message. The remaining headers
999 ;; are ordered according to the order of the keep list.
1001 (defun vm-reorder-message-headers (message keep-list discard-regexp)
1005 (set-buffer (vm-buffer-of message))
1006 (setq keep-list vm-visible-headers
1007 discard-regexp vm-invisible-header-regexp)))
1011 ;; if there is a cached regexp that points to the already
1012 ;; ordered headers then use it and avoid a lot of work.
1013 (if (and message (vm-vheaders-regexp-of message))
1015 (goto-char (vm-headers-of message))
1016 (let ((case-fold-search t))
1017 (re-search-forward (vm-vheaders-regexp-of message)
1018 (vm-text-of message) t))
1019 (vm-set-vheaders-of message (vm-marker (match-beginning 0))))
1020 ;; oh well, we gotta do it the hard way.
1022 ;; header-alist will contain an assoc list version of
1023 ;; keep-list. For messages associated with a folder
1024 ;; buffer: when a matching header is found, the
1025 ;; header's start and end positions are added to its
1026 ;; corresponding assoc cell. The positions of unwanted
1027 ;; headers are remember also so that they can be copied
1028 ;; to the top of the message, to be out of sight after
1029 ;; narrowing. Once the positions have all been
1030 ;; recorded a new copy of the headers is inserted in
1031 ;; the proper order and the old headers are deleted.
1033 ;; For free standing messages, unwanted headers are
1034 ;; stripped from the message, unremembered.
1035 (vm-save-restriction
1036 (let ((header-alist (vm-build-header-order-alist keep-list))
1037 (buffer-read-only nil)
1040 list end-of-header vheader-offset
1041 (folder-buffer (current-buffer))
1042 ;; This prevents file locking from occuring. Disabling
1043 ;; locking can speed things noticeably if the lock directory
1044 ;; is on a slow device. We don't need locking here because
1045 ;; in a mail context reordering headers is harmless.
1046 (buffer-file-name nil)
1047 (case-fold-search t)
1052 (old-buffer-modified-p (buffer-modified-p)))
1057 ;; for babyl folders, keep an untouched
1058 ;; copy of the headers between the
1059 ;; attributes line and the *** EOOH ***
1061 (if (and (eq vm-folder-type 'babyl)
1062 (null (vm-babyl-frob-flag-of message)))
1064 (goto-char (vm-start-of message))
1066 (vm-set-babyl-frob-flag-of message t)
1067 (insert-buffer-substring
1069 (vm-headers-of message)
1070 (1- (vm-text-of message)))
1071 ;; Yep, messages can come in
1072 ;; without the two newlines after
1073 ;; the header section.
1074 (if (not (eq (char-after (1- (point))) ?\n))
1076 (setq work-buffer (vm-make-work-buffer))
1077 (set-buffer work-buffer)
1078 (insert-buffer-substring
1080 (vm-headers-of message)
1081 (vm-text-of message))
1082 (goto-char (point-min))))
1083 (setq old-header-start (point))
1084 ;; as we loop through the headers, skip >From
1085 ;; lines. these can occur anywhere in the
1086 ;; header section if the message has been
1087 ;; manhandled by some dumb delivery agents
1088 ;; (SCO and Solaris are the usual suspects.)
1089 ;; it's a tough ol' world.
1090 (while (progn (while (looking-at ">From ")
1092 (and (not (= (following-char) ?\n))
1094 (setq end-of-header (vm-matched-header-end)
1095 list (vm-match-ordered-header header-alist))
1096 ;; don't display/keep this header if
1097 ;; keep-list not matched
1098 ;; and discard-regexp is nil
1100 ;; discard-regexp is matched
1101 (if (or (and (null list) (null discard-regexp))
1102 (and discard-regexp (looking-at discard-regexp)))
1103 ;; delete the unwanted header if not doing
1104 ;; work for a folder buffer, otherwise
1105 ;; remember the start and end of the
1106 ;; unwanted header so we can copy it
1109 (delete-region (point) end-of-header)
1110 (if (null unwanted-list)
1112 (cons (point) (cons end-of-header nil))
1113 unwanted-tail unwanted-list)
1114 (if (= (point) (car (cdr unwanted-tail)))
1115 (setcar (cdr unwanted-tail)
1117 (setcdr (cdr unwanted-tail)
1119 (cons end-of-header nil)))
1120 (setq unwanted-tail (cdr (cdr unwanted-tail)))))
1121 (goto-char end-of-header))
1123 ;; stuff the start and end of the header
1124 ;; into the cdr of the returned alist
1127 ;; reverse point and end-of-header.
1128 ;; list will be nreversed later.
1129 (setcdr list (cons end-of-header
1132 ;; reverse point and end-of-header.
1133 ;; list will be nreversed later.
1136 (cons (point) extras))))
1137 (goto-char end-of-header)))
1138 (setq new-header-start (point))
1139 (while unwanted-list
1140 (insert-buffer-substring (current-buffer)
1142 (car (cdr unwanted-list)))
1143 (setq unwanted-list (cdr (cdr unwanted-list))))
1144 ;; remember the offset of where the visible
1145 ;; header start so we can initialize the
1146 ;; vm-vheaders-of field later.
1148 (setq vheader-offset (- (point) new-header-start)))
1150 (setq list (nreverse (cdr (car header-alist))))
1152 (insert-buffer-substring (current-buffer)
1155 (setq list (cdr (cdr list))))
1156 (setq header-alist (cdr header-alist)))
1157 ;; now the headers that were not explicitly
1158 ;; undesirable, if any.
1159 (setq extras (nreverse extras))
1161 (insert-buffer-substring (current-buffer)
1164 (setq extras (cdr (cdr extras))))
1165 (delete-region old-header-start new-header-start)
1166 ;; update the folder buffer if we're supposed to.
1167 ;; lock out interrupts.
1169 (let ((inhibit-quit t))
1170 (set-buffer (vm-buffer-of message))
1171 (goto-char (vm-headers-of message))
1172 (insert-buffer-substring work-buffer)
1173 (delete-region (point) (vm-text-of message))
1174 (set-buffer-modified-p old-buffer-modified-p))))
1175 (and work-buffer (kill-buffer work-buffer)))
1178 (vm-set-vheaders-of message
1179 (vm-marker (+ (vm-headers-of message)
1181 ;; cache a regular expression that can be used to
1182 ;; find the start of the reordered header the next
1183 ;; time this folder is visited.
1184 (goto-char (vm-vheaders-of message))
1185 (if (vm-match-header)
1186 (vm-set-vheaders-regexp-of
1188 (concat "^" (vm-matched-header-name) ":"))))))))))))
1190 ;; Reads the message attributes and cached header information from the
1191 ;; header portion of the each message, if our X-VM- attributes header is
1192 ;; present. If the header is not present, assume the message is new,
1193 ;; unless we are being compatible with Berkeley Mail in which case we
1194 ;; also check for a Status header.
1196 ;; If a message already has attributes don't bother checking the
1199 ;; This function also discovers and stores the position where the
1200 ;; message text begins.
1202 ;; Totals are gathered for use by vm-emit-totals-blurb.
1204 ;; Supports version 4 format of attribute storage, for backward compatibility.
1206 (defun vm-read-attributes (message-list)
1208 (let ((mp (or message-list vm-message-list))
1211 (vm-deleted-count 0)
1213 (modulus (+ (% (vm-abs (random)) 11) 25))
1214 (case-fold-search t)
1217 (vm-increment vm-total-count)
1218 (if (vm-attributes-of (car mp))
1220 (goto-char (vm-headers-of (car mp)))
1221 ;; find start of text section and save it
1222 (search-forward "\n\n" (vm-text-end-of (car mp)) 0)
1223 (vm-set-text-of (car mp) (point-marker))
1224 ;; now look for our header
1225 (goto-char (vm-headers-of (car mp)))
1227 ((re-search-forward vm-attributes-header-regexp
1228 (vm-text-of (car mp)) t)
1229 (goto-char (match-beginning 2))
1232 (setq oldpoint (point)
1233 data (read (current-buffer)))
1234 (if (and (or (not (listp data)) (not (> (length data) 1)))
1235 (not (vectorp data)))
1237 (error "Bad x-vm-v5-data at %d in buffer %s"
1238 oldpoint (buffer-name))))
1241 (message "Bad x-vm-v5-data header at %d in buffer %s, ignoring"
1242 oldpoint (buffer-name))
1245 (make-vector vm-attributes-vector-length nil)
1246 (make-vector vm-cache-vector-length nil)
1248 ;; In lieu of a valid attributes header
1249 ;; assume the message is new. avoid
1250 ;; vm-set-new-flag because it asks for a
1252 (vm-set-new-flag-in-vector (car data) t)))
1253 ;; support version 4 format
1254 (cond ((vectorp data)
1255 (setq data (vm-convert-v4-attributes data))
1256 ;; tink the message stuff flag so that if the
1257 ;; user saves we get rid of the old v4
1258 ;; attributes header. otherwise we could be
1259 ;; dealing with these things for all eternity.
1260 (vm-set-stuff-flag-of (car mp) t))
1262 ;; extend vectors if necessary to accomodate
1263 ;; more caching and attributes without alienating
1264 ;; other version 5 folders.
1265 (cond ((< (length (car data))
1266 vm-attributes-vector-length)
1267 ;; tink the message stuff flag so that if
1268 ;; the user saves we get rid of the old
1269 ;; short vector. otherwise we could be
1270 ;; dealing with these things for all
1272 (vm-set-stuff-flag-of (car mp) t)
1273 (setcar data (vm-extend-vector
1275 vm-attributes-vector-length))))
1276 (cond ((< (length (car (cdr data)))
1277 vm-cache-vector-length)
1278 ;; tink the message stuff flag so that if
1279 ;; the user saves we get rid of the old
1280 ;; short vector. otherwise we could be
1281 ;; dealing with these things for all
1283 (vm-set-stuff-flag-of (car mp) t)
1287 vm-cache-vector-length))))))
1288 ;; data list might not be long enough for (nth 2 ...) but
1289 ;; that's OK because nth returns nil if you overshoot the
1291 (vm-set-labels-of (car mp) (nth 2 data))
1292 (vm-set-cache-of (car mp) (car (cdr data)))
1293 (vm-set-attributes-of (car mp) (car data)))
1294 ((and vm-berkeley-mail-compatibility
1295 (re-search-forward vm-berkeley-mail-status-header-regexp
1296 (vm-text-of (car mp)) t))
1297 (vm-set-cache-of (car mp) (make-vector vm-cache-vector-length
1299 (goto-char (match-beginning 1))
1300 (vm-set-attributes-of
1302 (make-vector vm-attributes-vector-length nil))
1303 (vm-set-unread-flag (car mp) (not (looking-at ".*R.*")) t))
1305 (vm-set-cache-of (car mp) (make-vector vm-cache-vector-length
1307 (vm-set-attributes-of
1309 (make-vector vm-attributes-vector-length nil))
1310 ;; In lieu of a valid attributes header
1311 ;; assume the message is new. avoid
1312 ;; vm-set-new-flag because it asks for a
1314 (vm-set-new-flag-of (car mp) t)))
1315 ;; let babyl attributes override the normal VM
1316 ;; attributes header.
1317 (cond ((eq vm-folder-type 'babyl)
1318 (vm-read-babyl-attributes (car mp)))))
1319 (cond ((vm-deleted-flag (car mp))
1320 (vm-increment vm-deleted-count))
1321 ((vm-new-flag (car mp))
1322 (vm-increment vm-new-count))
1323 ((vm-unread-flag (car mp))
1324 (vm-increment vm-unread-count)))
1325 (if (zerop (% vm-total-count modulus))
1326 (message "Reading attributes... %d" vm-total-count))
1328 (if (>= vm-total-count modulus)
1329 (message "Reading attributes... done"))
1330 (if (null message-list)
1331 (setq vm-totals (list vm-modification-counter
1335 vm-deleted-count))))))
1337 (defun vm-read-babyl-attributes (message)
1338 (let ((case-fold-search t)
1340 (vect (make-vector vm-attributes-vector-length nil)))
1341 (vm-set-attributes-of message vect)
1343 (goto-char (vm-start-of message))
1346 (vm-set-babyl-frob-flag-of message (if (= (following-char) ?1) t nil))
1349 ;; loop, noting attributes as we go.
1350 (while (and (not (eobp)) (not (looking-at ",")))
1351 (cond ((looking-at " unseen,")
1352 (vm-set-unread-flag-of message t))
1353 ((looking-at " recent,")
1354 (vm-set-new-flag-of message t))
1355 ((looking-at " deleted,")
1356 (vm-set-deleted-flag-of message t))
1357 ((looking-at " answered,")
1358 (vm-set-replied-flag-of message t))
1359 ((looking-at " forwarded,")
1360 (vm-set-forwarded-flag-of message t))
1361 ((looking-at " filed,")
1362 (vm-set-filed-flag-of message t))
1363 ((looking-at " redistributed,")
1364 (vm-set-redistributed-flag-of message t))
1365 ;; only VM knows about these, as far as I know.
1366 ((looking-at " edited,")
1367 (vm-set-forwarded-flag-of message t))
1368 ((looking-at " written,")
1369 (vm-set-forwarded-flag-of message t)))
1370 (skip-chars-forward "^,")
1371 (and (not (eobp)) (forward-char 1)))
1372 (and (not (eobp)) (forward-char 1))
1373 (while (looking-at " \\([^\000-\040,\177-\377]+\\),")
1374 (setq labels (cons (vm-buffer-substring-no-properties
1378 (goto-char (match-end 0)))
1379 (vm-set-labels-of message labels))))
1381 (defun vm-set-default-attributes (message-list)
1382 (let ((mp (or message-list vm-message-list)) attr cache)
1384 (setq attr (make-vector vm-attributes-vector-length nil)
1385 cache (make-vector vm-cache-vector-length nil))
1386 (vm-set-cache-of (car mp) cache)
1387 (vm-set-attributes-of (car mp) attr)
1388 ;; make message be new by default, but avoid vm-set-new-flag
1389 ;; because it asks for a summary update for the message.
1390 (vm-set-new-flag-of (car mp) t)
1391 ;; since this function is usually called in lieu of reading
1392 ;; attributes from the buffer, the buffer attributes may be
1393 ;; untrustworthy. tink the message stuff flag to force the
1394 ;; new attributes out if the user saves.
1395 (vm-set-stuff-flag-of (car mp) t)
1396 (setq mp (cdr mp)))))
1398 (defun vm-compute-totals ()
1400 (vm-select-folder-buffer)
1401 (let ((mp vm-message-list)
1404 (vm-deleted-count 0)
1407 (vm-increment vm-total-count)
1408 (cond ((vm-deleted-flag (car mp))
1409 (vm-increment vm-deleted-count))
1410 ((vm-new-flag (car mp))
1411 (vm-increment vm-new-count))
1412 ((vm-unread-flag (car mp))
1413 (vm-increment vm-unread-count)))
1415 (setq vm-totals (list vm-modification-counter
1419 vm-deleted-count)))))
1421 (defun vm-emit-totals-blurb ()
1424 (vm-select-folder-buffer)
1425 (if (not (equal (nth 0 vm-totals) vm-modification-counter))
1426 (vm-compute-totals))
1427 (if (equal (nth 1 vm-totals) 0)
1428 (message "No messages.")
1429 (message "%d message%s, %d new, %d unread, %d deleted"
1430 (nth 1 vm-totals) (if (= (nth 1 vm-totals) 1) "" "s")
1433 (nth 4 vm-totals)))))
1435 (defun vm-convert-v4-attributes (data)
1436 (list (apply 'vector
1437 (nconc (vm-vector-to-list data)
1438 (make-list (- vm-attributes-vector-length
1441 (make-vector vm-cache-vector-length nil)))
1443 (defun vm-gobble-last-modified ()
1444 (let ((case-fold-search t)
1448 (vm-save-restriction
1450 (goto-char (point-min))
1451 (vm-skip-past-folder-header)
1452 (vm-skip-past-leading-message-separator)
1453 (search-forward "\n\n" nil t)
1455 (goto-char (point-min))
1456 (vm-skip-past-folder-header)
1457 (vm-skip-past-leading-message-separator)
1458 (if (re-search-forward vm-last-modified-header-regexp lim t)
1461 (setq oldpoint (point)
1462 time (read (current-buffer)))
1463 (if (not (consp time))
1464 (error "Bad last-modified header at %d in buffer %s"
1465 oldpoint (buffer-name)))
1468 (message "Bad last-modified header at %d in buffer %s, ignoring"
1469 oldpoint (buffer-name))
1470 (setq time '(0 0 0)))))))
1473 (defun vm-gobble-labels ()
1474 (let ((case-fold-search t)
1477 (vm-save-restriction
1479 (if (eq vm-folder-type 'babyl)
1481 (goto-char (point-min))
1482 (vm-skip-past-folder-header)
1484 (goto-char (point-min))
1485 (if (re-search-forward "^Labels:" lim t)
1487 (setq string (buffer-substring
1489 (progn (end-of-line) (point)))
1490 list (vm-parse string
1491 "[\000-\040,\177-\377]*\\([^\000-\040,\177-\377]+\\)[\000-\040,\177-\377]*"))
1494 (intern (downcase s) vm-label-obarray)))
1496 (goto-char (point-min))
1497 (vm-skip-past-folder-header)
1498 (vm-skip-past-leading-message-separator)
1499 (search-forward "\n\n" nil t)
1501 (goto-char (point-min))
1502 (vm-skip-past-folder-header)
1503 (vm-skip-past-leading-message-separator)
1504 (if (re-search-forward vm-labels-header-regexp lim t)
1505 (let ((oldpoint (point))
1509 (setq list (read (current-buffer)))
1510 (if (not (listp list))
1511 (error "Bad global label list at %d in buffer %s"
1512 oldpoint (buffer-name)))
1515 (message "Bad global label list at %d in buffer %s, ignoring"
1516 oldpoint (buffer-name))
1518 (vm-startup-apply-labels list))))))
1521 (defun vm-startup-apply-labels (labels)
1522 (mapcar (function (lambda (s) (intern s vm-label-obarray))) labels))
1524 ;; Go to the message specified in a bookmark and eat the bookmark.
1525 ;; Returns non-nil if successful, nil otherwise.
1526 (defun vm-gobble-bookmark ()
1527 (let ((case-fold-search t)
1531 (vm-save-restriction
1533 (goto-char (point-min))
1534 (vm-skip-past-folder-header)
1535 (vm-skip-past-leading-message-separator)
1536 (search-forward "\n\n" nil t)
1538 (goto-char (point-min))
1539 (vm-skip-past-folder-header)
1540 (vm-skip-past-leading-message-separator)
1541 (if (re-search-forward vm-bookmark-header-regexp lim t)
1544 (setq oldpoint (point)
1545 n (read (current-buffer)))
1546 (if (not (natnump n))
1547 (error "Bad bookmark at %d in buffer %s"
1548 oldpoint (buffer-name)))
1551 (message "Bad bookmark at %d in buffer %s, ignoring"
1552 oldpoint (buffer-name))
1554 (vm-startup-apply-bookmark n)
1557 (defun vm-startup-apply-bookmark (n)
1559 (vm-record-and-change-message-pointer
1561 (nthcdr (1- n) vm-message-list))))
1563 (defun vm-gobble-pop-retrieved ()
1564 (let ((case-fold-search t)
1567 (vm-save-restriction
1569 (goto-char (point-min))
1570 (vm-skip-past-folder-header)
1571 (vm-skip-past-leading-message-separator)
1572 (search-forward "\n\n" nil t)
1574 (goto-char (point-min))
1575 (vm-skip-past-folder-header)
1576 (vm-skip-past-leading-message-separator)
1577 (if (re-search-forward vm-pop-retrieved-header-regexp lim t)
1580 (setq oldpoint (point)
1581 ob (read (current-buffer)))
1582 (if (not (listp ob))
1583 (error "Bad pop-retrieved header at %d in buffer %s"
1584 oldpoint (buffer-name)))
1585 (setq vm-pop-retrieved-messages ob))
1587 (message "Bad pop-retrieved header at %d in buffer %s, ignoring"
1588 oldpoint (buffer-name)))))))
1591 (defun vm-gobble-imap-retrieved ()
1592 (let ((case-fold-search t)
1595 (vm-save-restriction
1597 (goto-char (point-min))
1598 (vm-skip-past-folder-header)
1599 (vm-skip-past-leading-message-separator)
1600 (search-forward "\n\n" nil t)
1602 (goto-char (point-min))
1603 (vm-skip-past-folder-header)
1604 (vm-skip-past-leading-message-separator)
1605 (if (re-search-forward vm-imap-retrieved-header-regexp lim t)
1608 (setq oldpoint (point)
1609 ob (read (current-buffer)))
1610 (if (not (listp ob))
1611 (error "Bad imap-retrieved header at %d in buffer %s"
1612 oldpoint (buffer-name)))
1613 (setq vm-imap-retrieved-messages ob))
1615 (message "Bad imap-retrieved header at %d in buffer %s, ignoring"
1616 oldpoint (buffer-name)))))))
1619 (defun vm-gobble-visible-header-variables ()
1621 (vm-save-restriction
1622 (let ((case-fold-search t)
1625 (goto-char (point-min))
1626 (vm-skip-past-folder-header)
1627 (vm-skip-past-leading-message-separator)
1628 (search-forward "\n\n" nil t)
1630 (goto-char (point-min))
1631 (vm-skip-past-folder-header)
1632 (vm-skip-past-leading-message-separator)
1633 (if (re-search-forward vm-vheader-header-regexp lim t)
1634 (let (vis invis (got nil))
1636 (setq vis (read (current-buffer))
1637 invis (read (current-buffer))
1641 (vm-startup-apply-header-variables vis invis))))))))
1643 (defun vm-startup-apply-header-variables (vis invis)
1644 ;; if the variables don't match the values stored when this
1645 ;; folder was saved, then we have to discard any cached
1646 ;; vheader info so the user will see the right headers.
1647 (and (or (not (equal vis vm-visible-headers))
1648 (not (equal invis vm-invisible-header-regexp)))
1649 (let ((mp vm-message-list))
1650 (message "Discarding visible header info...")
1652 (vm-set-vheaders-regexp-of (car mp) nil)
1653 (vm-set-vheaders-of (car mp) nil)
1654 (setq mp (cdr mp))))))
1656 ;; Read and delete the header that gives the folder's desired
1658 (defun vm-gobble-message-order ()
1659 (let ((case-fold-search t)
1664 (goto-char (point-min))
1665 (vm-skip-past-folder-header)
1666 (vm-skip-past-leading-message-separator)
1667 (search-forward "\n\n" nil t)
1669 (goto-char (point-min))
1670 (vm-skip-past-folder-header)
1671 (vm-skip-past-leading-message-separator)
1672 (if (re-search-forward vm-message-order-header-regexp lim t)
1673 (let ((oldpoint (point)))
1676 (setq order (read (current-buffer)))
1677 (if (not (listp order))
1678 (error "Bad order header at %d in buffer %s"
1679 oldpoint (buffer-name)))
1682 (message "Bad order header at %d in buffer %s, ignoring"
1683 oldpoint (buffer-name))
1687 (message "Reordering messages...")
1688 (vm-startup-apply-message-order order)
1689 (message "Reordering messages... done")))))))))
1691 (defun vm-has-message-order ()
1692 (let ((case-fold-search t)
1697 (goto-char (point-min))
1698 (vm-skip-past-folder-header)
1699 (vm-skip-past-leading-message-separator)
1700 (search-forward "\n\n" nil t)
1702 (goto-char (point-min))
1703 (vm-skip-past-folder-header)
1704 (vm-skip-past-leading-message-separator)
1705 (re-search-forward vm-message-order-header-regexp lim t)))))
1707 (defun vm-startup-apply-message-order (order)
1708 (let (list-length v (mp vm-message-list))
1709 (setq list-length (length vm-message-list)
1710 v (make-vector (max list-length (length order)) nil))
1711 (while (and order mp)
1713 (aset v (1- (car order)) (car mp))
1714 (args-out-of-range nil))
1715 (setq order (cdr order) mp (cdr mp)))
1716 ;; lock out interrupts while the message list is in
1717 ;; an inconsistent state.
1718 (let ((inhibit-quit t))
1719 (setq vm-message-list (delq nil (append v mp))
1720 vm-message-order-changed nil
1721 vm-message-order-header-present t
1722 vm-message-pointer (memq (car vm-message-pointer)
1724 (vm-set-numbering-redo-start-point t)
1725 (vm-reverse-link-messages))))
1727 ;; Read the header that gives the folder's cached summary format
1728 ;; If the current summary format is different, then the cached
1729 ;; summary lines are discarded.
1730 (defun vm-gobble-summary ()
1731 (let ((case-fold-search t)
1734 (vm-save-restriction
1736 (goto-char (point-min))
1737 (vm-skip-past-folder-header)
1738 (vm-skip-past-leading-message-separator)
1739 (search-forward "\n\n" nil t)
1741 (goto-char (point-min))
1742 (vm-skip-past-folder-header)
1743 (vm-skip-past-leading-message-separator)
1744 (if (re-search-forward vm-summary-header-regexp lim t)
1745 (let ((oldpoint (point)))
1747 (setq summary (read (current-buffer)))
1749 (message "Bad summary header at %d in buffer %s, ignoring"
1750 oldpoint (buffer-name))
1752 (vm-startup-apply-summary summary)))))))
1754 (defun vm-startup-apply-summary (summary)
1755 (if (not (equal summary vm-summary-format))
1756 (let ((mp vm-message-list))
1758 (vm-set-summary-of (car mp) nil)
1759 ;; force restuffing of cache to clear old
1760 ;; summary entry cache.
1761 (vm-set-stuff-flag-of (car mp) t)
1762 (setq mp (cdr mp))))))
1764 ;; Stuff the message attributes back into the message as headers.
1765 (defun vm-stuff-attributes (m &optional for-other-folder)
1767 (vm-save-restriction
1769 (let ((old-buffer-modified-p (buffer-modified-p))
1771 (case-fold-search t)
1772 (buffer-read-only nil)
1773 ;; don't truncate the printing of large Lisp objects
1776 ;; This prevents file locking from occuring. Disabling
1777 ;; locking can speed things noticeably if the lock
1778 ;; directory is on a slow device. We don't need locking
1779 ;; here because the user shouldn't care about VM stuffing
1780 ;; its own status headers.
1781 (buffer-file-name nil)
1782 (delflag (vm-deleted-flag m)))
1785 ;; don't put this folder's summary entry into another folder.
1786 (if for-other-folder
1787 (vm-set-summary-of m nil)
1788 (if (vm-su-start-of m)
1789 ;; fill the summary cache if it's not done already.
1791 (setq attributes (vm-attributes-of m)
1792 cache (vm-cache-of m))
1793 (and delflag for-other-folder
1794 (vm-set-deleted-flag-in-vector
1795 (setq attributes (copy-sequence attributes)) nil))
1796 (if (eq vm-folder-type 'babyl)
1797 (vm-stuff-babyl-attributes m for-other-folder))
1798 (goto-char (vm-headers-of m))
1799 (while (re-search-forward vm-attributes-header-regexp
1801 (delete-region (match-beginning 0) (match-end 0)))
1802 (goto-char (vm-headers-of m))
1803 (setq opoint (point))
1804 (insert-before-markers
1805 vm-attributes-header " ("
1806 (let ((print-escape-newlines t))
1807 (prin1-to-string attributes))
1809 (vm-mime-encode-words-in-string
1810 (let ((print-escape-newlines t))
1811 (prin1-to-string cache)))
1813 (let ((print-escape-newlines t))
1814 (prin1-to-string (vm-labels-of m)))
1816 (set-marker (vm-headers-of m) opoint)
1817 (cond ((and (eq vm-folder-type 'From_)
1818 vm-berkeley-mail-compatibility)
1819 (goto-char (vm-headers-of m))
1820 (while (re-search-forward
1821 vm-berkeley-mail-status-header-regexp
1823 (delete-region (match-beginning 0) (match-end 0)))
1824 (goto-char (vm-headers-of m))
1825 (cond ((not (vm-new-flag m))
1826 (insert-before-markers
1827 vm-berkeley-mail-status-header
1828 (if (vm-unread-flag m) "" "R")
1830 (set-marker (vm-headers-of m) opoint)))))
1831 (vm-set-stuff-flag-of m (not for-other-folder)))
1832 (set-buffer-modified-p old-buffer-modified-p))))))
1834 (defun vm-stuff-folder-attributes (&optional abort-if-input-pending quiet)
1835 (let ((newlist nil) mp len (n 0))
1836 ;; stuff the attributes of messages that need it.
1837 ;; build a list of messages that need their attributes stuffed
1838 (setq mp vm-message-list)
1840 (if (vm-stuff-flag-of (car mp))
1841 (setq newlist (cons (car mp) newlist)))
1843 (if (and newlist (not quiet))
1845 (setq len (length newlist))
1846 (message "%d message%s to stuff" len (if (= 1 len) "" "s"))))
1847 ;; now sort the list by physical order so that we
1848 ;; reduce the amount of gap motion induced by modifying
1849 ;; the buffer. what we want to avoid is updating
1850 ;; message 3, then 234, then 10, then 500, thus causing
1851 ;; large chunks of memory to be copied repeatedly as
1852 ;; the gap moves to accomodate the insertions.
1854 (message "Ordering updates..."))
1855 (let ((vm-key-functions '(vm-sort-compare-physical-order-r)))
1856 (setq mp (sort newlist 'vm-sort-compare-xxxxxx)))
1857 (while (and mp (or (not abort-if-input-pending) (not (input-pending-p))))
1858 (vm-stuff-attributes (car mp))
1861 (message "Stuffing %d%% complete..." (* (/ (+ n 0.0) len) 100)))
1865 ;; we can be a bit lazy in this function since it's only called
1866 ;; from within vm-stuff-attributes. we don't worry about
1867 ;; restoring the modified flag, setting buffer-read-only, or
1868 ;; about not moving point.
1869 (defun vm-stuff-babyl-attributes (m for-other-folder)
1870 (goto-char (vm-start-of m))
1872 (if (vm-babyl-frob-flag-of m)
1877 (if (looking-at "\\( [^\000-\040,\177-\377]+,\\)+")
1878 (delete-region (match-beginning 0) (match-end 0)))
1880 (insert " recent, unseen,")
1881 (if (vm-unread-flag m)
1882 (insert " unseen,")))
1883 (if (and (not for-other-folder) (vm-deleted-flag m))
1884 (insert " deleted,"))
1885 (if (vm-replied-flag m)
1886 (insert " answered,"))
1887 (if (vm-forwarded-flag m)
1888 (insert " forwarded,"))
1889 (if (vm-redistributed-flag m)
1890 (insert " redistributed,"))
1891 (if (vm-filed-flag m)
1893 (if (vm-edited-flag m)
1894 (insert " edited,"))
1895 (if (vm-written-flag m)
1896 (insert " written,"))
1898 (if (looking-at "\\( [^\000-\040,\177-\377]+,\\)+")
1899 (delete-region (match-beginning 0) (match-end 0)))
1900 (mapcar (function (lambda (label) (insert " " label ",")))
1903 (defun vm-babyl-attributes-string (m for-other-folder)
1907 (if (vm-unread-flag m)
1909 (if (and (not for-other-folder) (vm-deleted-flag m))
1911 (if (vm-replied-flag m)
1913 (if (vm-forwarded-flag m)
1915 (if (vm-redistributed-flag m)
1917 (if (vm-filed-flag m)
1919 (if (vm-edited-flag m)
1921 (if (vm-written-flag m)
1924 (defun vm-babyl-labels-string (m)
1926 (labels (vm-labels-of m)))
1928 (setq list (cons "," (cons (car labels) (cons " " list)))
1929 labels (cdr labels)))
1930 (apply 'concat (nreverse list))))
1932 (defun vm-stuff-virtual-attributes (message)
1933 (let ((virtual (vm-virtual-message-p message)))
1934 (if (or (not virtual) (and virtual (vm-virtual-messages-of message)))
1938 (vm-real-message-of message)))
1939 (vm-stuff-attributes (vm-real-message-of message))))))
1941 (defun vm-stuff-labels ()
1944 (vm-save-restriction
1946 (let ((old-buffer-modified-p (buffer-modified-p))
1947 (case-fold-search t)
1948 ;; don't truncate the printing of large Lisp objects
1950 ;; This prevents file locking from occuring. Disabling
1951 ;; locking can speed things noticeably if the lock
1952 ;; directory is on a slow device. We don't need locking
1953 ;; here because the user shouldn't care about VM stuffing
1954 ;; its own status headers.
1955 (buffer-file-name nil)
1956 (buffer-read-only nil)
1958 (if (eq vm-folder-type 'babyl)
1960 (goto-char (point-min))
1961 (vm-skip-past-folder-header)
1962 (delete-region (point) (point-min))
1963 (insert-before-markers (vm-folder-header vm-folder-type
1964 vm-label-obarray))))
1965 (goto-char (point-min))
1966 (vm-skip-past-folder-header)
1967 (vm-find-leading-message-separator)
1968 (vm-skip-past-leading-message-separator)
1969 (search-forward "\n\n" nil t)
1971 (goto-char (point-min))
1972 (vm-skip-past-folder-header)
1973 (vm-find-leading-message-separator)
1974 (vm-skip-past-leading-message-separator)
1975 (while (re-search-forward vm-labels-header-regexp lim t)
1976 (progn (goto-char (match-beginning 0))
1977 (if (vm-match-header vm-labels-header)
1978 (delete-region (vm-matched-header-start)
1979 (vm-matched-header-end)))))
1980 ;; To insert or to insert-before-markers, that is the question.
1982 ;; If we insert-before-markers we push a header behind
1983 ;; vm-headers-of, which is clearly undesirable. So we
1984 ;; just insert. This will cause the summary header
1985 ;; to be visible if there are no non-visible headers,
1986 ;; oh well, no way around this.
1987 (insert vm-labels-header " "
1988 (let ((print-escape-newlines t)
1992 (setq list (cons (symbol-name sym) list))))
1994 (prin1-to-string list))
1996 (set-buffer-modified-p old-buffer-modified-p))))))
1998 ;; Insert a bookmark into the first message in the folder.
1999 (defun vm-stuff-bookmark ()
2002 (vm-save-restriction
2004 (let ((old-buffer-modified-p (buffer-modified-p))
2005 (case-fold-search t)
2006 ;; This prevents file locking from occuring. Disabling
2007 ;; locking can speed things noticeably if the lock
2008 ;; directory is on a slow device. We don't need locking
2009 ;; here because the user shouldn't care about VM stuffing
2010 ;; its own status headers.
2011 (buffer-file-name nil)
2012 (buffer-read-only nil)
2014 (goto-char (point-min))
2015 (vm-skip-past-folder-header)
2016 (vm-find-leading-message-separator)
2017 (vm-skip-past-leading-message-separator)
2018 (search-forward "\n\n" nil t)
2020 (goto-char (point-min))
2021 (vm-skip-past-folder-header)
2022 (vm-find-leading-message-separator)
2023 (vm-skip-past-leading-message-separator)
2024 (if (re-search-forward vm-bookmark-header-regexp lim t)
2025 (progn (goto-char (match-beginning 0))
2026 (if (vm-match-header vm-bookmark-header)
2027 (delete-region (vm-matched-header-start)
2028 (vm-matched-header-end)))))
2029 ;; To insert or to insert-before-markers, that is the question.
2031 ;; If we insert-before-markers we push a header behind
2032 ;; vm-headers-of, which is clearly undesirable. So we
2033 ;; just insert. This will cause the bookmark header
2034 ;; to be visible if there are no non-visible headers,
2035 ;; oh well, no way around this.
2036 (insert vm-bookmark-header " "
2037 (vm-number-of (car vm-message-pointer))
2039 (set-buffer-modified-p old-buffer-modified-p))))))
2041 (defun vm-stuff-last-modified ()
2044 (vm-save-restriction
2046 (let ((old-buffer-modified-p (buffer-modified-p))
2047 (case-fold-search t)
2048 ;; This prevents file locking from occuring. Disabling
2049 ;; locking can speed things noticeably if the lock
2050 ;; directory is on a slow device. We don't need locking
2051 ;; here because the user shouldn't care about VM stuffing
2052 ;; its own status headers.
2053 (buffer-file-name nil)
2054 (buffer-read-only nil)
2056 (goto-char (point-min))
2057 (vm-skip-past-folder-header)
2058 (vm-find-leading-message-separator)
2059 (vm-skip-past-leading-message-separator)
2060 (search-forward "\n\n" nil t)
2062 (goto-char (point-min))
2063 (vm-skip-past-folder-header)
2064 (vm-find-leading-message-separator)
2065 (vm-skip-past-leading-message-separator)
2066 (if (re-search-forward vm-last-modified-header-regexp lim t)
2067 (progn (goto-char (match-beginning 0))
2068 (if (vm-match-header vm-last-modified-header)
2069 (delete-region (vm-matched-header-start)
2070 (vm-matched-header-end)))))
2071 ;; To insert or to insert-before-markers, that is the question.
2073 ;; If we insert-before-markers we push a header behind
2074 ;; vm-headers-of, which is clearly undesirable. So we
2075 ;; just insert. This will cause the last-modified header
2076 ;; to be visible if there are no non-visible headers,
2077 ;; oh well, no way around this.
2078 (insert vm-last-modified-header " "
2079 (prin1-to-string (current-time))
2081 (set-buffer-modified-p old-buffer-modified-p))))))
2083 (defun vm-stuff-pop-retrieved ()
2086 (vm-save-restriction
2088 (let ((old-buffer-modified-p (buffer-modified-p))
2089 (case-fold-search t)
2090 ;; This prevents file locking from occuring. Disabling
2091 ;; locking can speed things noticeably if the lock
2092 ;; directory is on a slow device. We don't need locking
2093 ;; here because the user shouldn't care about VM stuffing
2094 ;; its own status headers.
2095 (buffer-file-name nil)
2096 (buffer-read-only nil)
2098 (p vm-pop-retrieved-messages)
2099 (curbuf (current-buffer))
2101 (goto-char (point-min))
2102 (vm-skip-past-folder-header)
2103 (vm-find-leading-message-separator)
2104 (vm-skip-past-leading-message-separator)
2105 (search-forward "\n\n" nil t)
2107 (goto-char (point-min))
2108 (vm-skip-past-folder-header)
2109 (vm-find-leading-message-separator)
2110 (vm-skip-past-leading-message-separator)
2111 (if (re-search-forward vm-pop-retrieved-header-regexp lim t)
2112 (progn (goto-char (match-beginning 0))
2113 (if (vm-match-header vm-pop-retrieved-header)
2114 (delete-region (vm-matched-header-start)
2115 (vm-matched-header-end)))))
2116 ;; To insert or to insert-before-markers, that is the question.
2118 ;; If we insert-before-markers we push a header behind
2119 ;; vm-headers-of, which is clearly undesirable. So we
2120 ;; just insert. This will cause the pop-retrieved header
2121 ;; to be visible if there are no non-visible headers,
2122 ;; oh well, no way around this.
2123 (insert vm-pop-retrieved-header)
2129 (prin1 (car p) curbuf)
2133 (set-buffer-modified-p old-buffer-modified-p))))))
2135 (defun vm-stuff-imap-retrieved ()
2138 (vm-save-restriction
2140 (let ((old-buffer-modified-p (buffer-modified-p))
2141 (case-fold-search t)
2142 ;; This prevents file locking from occuring. Disabling
2143 ;; locking can speed things noticeably if the lock
2144 ;; directory is on a slow device. We don't need locking
2145 ;; here because the user shouldn't care about VM stuffing
2146 ;; its own status headers.
2147 (buffer-file-name nil)
2148 (buffer-read-only nil)
2150 (p vm-imap-retrieved-messages)
2151 (curbuf (current-buffer))
2153 (goto-char (point-min))
2154 (vm-skip-past-folder-header)
2155 (vm-find-leading-message-separator)
2156 (vm-skip-past-leading-message-separator)
2157 (search-forward "\n\n" nil t)
2159 (goto-char (point-min))
2160 (vm-skip-past-folder-header)
2161 (vm-find-leading-message-separator)
2162 (vm-skip-past-leading-message-separator)
2163 (if (re-search-forward vm-imap-retrieved-header-regexp lim t)
2164 (progn (goto-char (match-beginning 0))
2165 (if (vm-match-header vm-imap-retrieved-header)
2166 (delete-region (vm-matched-header-start)
2167 (vm-matched-header-end)))))
2168 ;; To insert or to insert-before-markers, that is the question.
2170 ;; If we insert-before-markers we push a header behind
2171 ;; vm-headers-of, which is clearly undesirable. So we
2172 ;; just insert. This will cause the imap-retrieved header
2173 ;; to be visible if there are no non-visible headers,
2174 ;; oh well, no way around this.
2175 (insert vm-imap-retrieved-header)
2181 (prin1 (car p) curbuf)
2185 (set-buffer-modified-p old-buffer-modified-p))))))
2187 ;; Insert the summary format variable header into the first message.
2188 (defun vm-stuff-summary ()
2191 (vm-save-restriction
2193 (let ((old-buffer-modified-p (buffer-modified-p))
2194 (case-fold-search t)
2195 ;; don't truncate the printing of large Lisp objects
2197 ;; This prevents file locking from occuring. Disabling
2198 ;; locking can speed things noticeably if the lock
2199 ;; directory is on a slow device. We don't need locking
2200 ;; here because the user shouldn't care about VM stuffing
2201 ;; its own status headers.
2202 (buffer-file-name nil)
2203 (buffer-read-only nil)
2205 (goto-char (point-min))
2206 (vm-skip-past-folder-header)
2207 (vm-find-leading-message-separator)
2208 (vm-skip-past-leading-message-separator)
2209 (search-forward "\n\n" nil t)
2211 (goto-char (point-min))
2212 (vm-skip-past-folder-header)
2213 (vm-find-leading-message-separator)
2214 (vm-skip-past-leading-message-separator)
2215 (while (re-search-forward vm-summary-header-regexp lim t)
2216 (progn (goto-char (match-beginning 0))
2217 (if (vm-match-header vm-summary-header)
2218 (delete-region (vm-matched-header-start)
2219 (vm-matched-header-end)))))
2220 ;; To insert or to insert-before-markers, that is the question.
2222 ;; If we insert-before-markers we push a header behind
2223 ;; vm-headers-of, which is clearly undesirable. So we
2224 ;; just insert. This will cause the summary header
2225 ;; to be visible if there are no non-visible headers,
2226 ;; oh well, no way around this.
2227 (insert vm-summary-header " "
2228 (let ((print-escape-newlines t))
2229 (prin1-to-string vm-summary-format))
2231 (set-buffer-modified-p old-buffer-modified-p))))))
2233 ;; stuff the current values of the header variables for future messages.
2234 (defun vm-stuff-header-variables ()
2237 (vm-save-restriction
2239 (let ((old-buffer-modified-p (buffer-modified-p))
2240 (case-fold-search t)
2241 (print-escape-newlines t)
2243 ;; don't truncate the printing of large Lisp objects
2245 (buffer-read-only nil)
2246 ;; This prevents file locking from occuring. Disabling
2247 ;; locking can speed things noticeably if the lock
2248 ;; directory is on a slow device. We don't need locking
2249 ;; here because the user shouldn't care about VM stuffing
2250 ;; its own status headers.
2251 (buffer-file-name nil))
2252 (goto-char (point-min))
2253 (vm-skip-past-folder-header)
2254 (vm-find-leading-message-separator)
2255 (vm-skip-past-leading-message-separator)
2256 (search-forward "\n\n" nil t)
2258 (goto-char (point-min))
2259 (vm-skip-past-folder-header)
2260 (vm-find-leading-message-separator)
2261 (vm-skip-past-leading-message-separator)
2262 (while (re-search-forward vm-vheader-header-regexp lim t)
2263 (progn (goto-char (match-beginning 0))
2264 (if (vm-match-header vm-vheader-header)
2265 (delete-region (vm-matched-header-start)
2266 (vm-matched-header-end)))))
2267 ;; To insert or to insert-before-markers, that is the question.
2269 ;; If we insert-before-markers we push a header behind
2270 ;; vm-headers-of, which is clearly undesirable. So we
2271 ;; just insert. This header will be visible if there
2272 ;; are no non-visible headers, oh well, no way around this.
2273 (insert vm-vheader-header " "
2274 (prin1-to-string vm-visible-headers) " "
2275 (prin1-to-string vm-invisible-header-regexp)
2277 (set-buffer-modified-p old-buffer-modified-p))))))
2279 ;; Insert a header into the first message of the folder that lists
2280 ;; the folder's message order.
2281 (defun vm-stuff-message-order ()
2282 (if (cdr vm-message-list)
2284 (vm-save-restriction
2286 (let ((old-buffer-modified-p (buffer-modified-p))
2287 (case-fold-search t)
2288 ;; This prevents file locking from occuring. Disabling
2289 ;; locking can speed things noticeably if the lock
2290 ;; directory is on a slow device. We don't need locking
2291 ;; here because the user shouldn't care about VM stuffing
2292 ;; its own status headers.
2293 (buffer-file-name nil)
2295 (buffer-read-only nil)
2296 (mp (copy-sequence vm-message-list)))
2301 (< (vm-start-of p) (vm-start-of q))))))
2302 (goto-char (point-min))
2303 (vm-skip-past-folder-header)
2304 (vm-find-leading-message-separator)
2305 (vm-skip-past-leading-message-separator)
2306 (search-forward "\n\n" nil t)
2308 (goto-char (point-min))
2309 (vm-skip-past-folder-header)
2310 (vm-find-leading-message-separator)
2311 (vm-skip-past-leading-message-separator)
2312 (while (re-search-forward vm-message-order-header-regexp lim t)
2313 (progn (goto-char (match-beginning 0))
2314 (if (vm-match-header vm-message-order-header)
2315 (delete-region (vm-matched-header-start)
2316 (vm-matched-header-end)))))
2317 ;; To insert or to insert-before-markers, that is the question.
2319 ;; If we insert-before-markers we push a header behind
2320 ;; vm-headers-of, which is clearly undesirable. So we
2321 ;; just insert. This header will be visible if there
2322 ;; are no non-visible headers, oh well, no way around this.
2323 (insert vm-message-order-header "\n\t(")
2326 (insert (vm-number-of (car mp)))
2327 (setq n (1+ n) mp (cdr mp))
2329 (if (zerop (% n 15))
2333 (setq vm-message-order-changed nil
2334 vm-message-order-header-present t)
2335 (set-buffer-modified-p old-buffer-modified-p))))))
2337 ;; Remove the message order header.
2338 (defun vm-remove-message-order ()
2339 (if (cdr vm-message-list)
2341 (vm-save-restriction
2343 (let ((old-buffer-modified-p (buffer-modified-p))
2344 (case-fold-search t)
2346 ;; This prevents file locking from occuring. Disabling
2347 ;; locking can speed things noticeably if the lock
2348 ;; directory is on a slow device. We don't need locking
2349 ;; here because the user shouldn't care about VM stuffing
2350 ;; its own status headers.
2351 (buffer-file-name nil)
2352 (buffer-read-only nil))
2353 (goto-char (point-min))
2354 (vm-skip-past-folder-header)
2355 (vm-skip-past-leading-message-separator)
2356 (search-forward "\n\n" nil t)
2358 (goto-char (point-min))
2359 (vm-skip-past-folder-header)
2360 (vm-skip-past-leading-message-separator)
2361 (while (re-search-forward vm-message-order-header-regexp lim t)
2362 (progn (goto-char (match-beginning 0))
2363 (if (vm-match-header vm-message-order-header)
2364 (delete-region (vm-matched-header-start)
2365 (vm-matched-header-end)))))
2366 (setq vm-message-order-header-present nil)
2367 (set-buffer-modified-p old-buffer-modified-p))))))
2369 (defun vm-make-index-file-name ()
2370 (concat (file-name-directory buffer-file-name)
2372 (file-name-nondirectory buffer-file-name)
2373 vm-index-file-suffix))
2375 (defun vm-read-index-file-maybe ()
2377 (if (or (not (stringp buffer-file-name))
2378 (not (stringp vm-index-file-suffix)))
2380 (let ((index-file (vm-make-index-file-name)))
2381 (if (file-readable-p index-file)
2382 (vm-read-index-file index-file)
2385 (defun vm-read-index-file (index-file)
2387 (condition-case error-data
2388 (let ((work-buffer nil))
2390 (let (obj attr-list cache-list location-list label-list
2391 validity-check vis invis folder-type
2392 bookmark summary labels pop-retrieved imap-retrieved order
2393 v m (m-list nil) tail)
2394 (message "Reading index file...")
2395 (setq work-buffer (vm-make-work-buffer))
2397 (set-buffer work-buffer)
2398 (insert-file-contents-literally index-file))
2399 (goto-char (point-min))
2402 (setq obj (read work-buffer))
2403 (if (not (eq obj 1))
2404 (error "Unsupported index file version: %s") obj)
2407 (setq folder-type (read work-buffer))
2410 (setq validity-check (read work-buffer))
2411 (if (null (vm-check-index-file-validity validity-check))
2415 (setq bookmark (read work-buffer))
2418 (setq order (read work-buffer))
2420 ;; what summary format was used to produce the
2421 ;; folder's summary cache line.
2422 (setq summary (read work-buffer))
2424 ;; folder-wide list of labels
2425 (setq labels (read work-buffer))
2427 ;; what vm-visible-headers / vm-invisible-header-regexp
2428 ;; settings were used to order the headers and to
2429 ;; produce the vm-headers-regexp-of slot value.
2430 (setq vis (read work-buffer))
2431 (setq invis (read work-buffer))
2437 (setq location-list (read work-buffer))
2438 (setq attr-list (read work-buffer))
2439 (setq cache-list (read work-buffer))
2440 (setq label-list (read work-buffer))
2441 (while location-list
2442 (setq v (car location-list)
2443 m (vm-make-message))
2445 (setq m-list (list m)
2447 (setcdr tail (list m))
2448 (setq tail (cdr tail)))
2449 (vm-set-start-of m (vm-marker (aref v 0)))
2450 (vm-set-headers-of m (vm-marker (aref v 1)))
2451 (vm-set-text-end-of m (vm-marker (aref v 2)))
2452 (vm-set-end-of m (vm-marker (aref v 3)))
2453 (if (null attr-list)
2454 (error "Attribute list is shorter than location list")
2455 (setq v (car attr-list))
2456 (if (< (length v) vm-attributes-vector-length)
2457 (setq v (vm-extend-vector
2458 v vm-attributes-vector-length)))
2459 (vm-set-attributes-of m v))
2460 (if (null cache-list)
2461 (error "Cache list is shorter than location list")
2462 (setq v (car cache-list))
2463 (if (< (length v) vm-cache-vector-length)
2464 (setq v (vm-extend-vector v vm-cache-vector-length)))
2465 (vm-set-cache-of m v))
2466 (if (null label-list)
2467 (error "Label list is shorter than location list")
2468 (vm-set-labels-of m (car label-list)))
2469 (setq location-list (cdr location-list)
2470 attr-list (cdr attr-list)
2471 cache-list (cdr cache-list)
2472 label-list (cdr label-list)))
2474 ;; pop retrieved messages
2475 (setq pop-retrieved (read work-buffer))
2477 ;; imap retrieved messages
2478 (setq imap-retrieved (read work-buffer))
2480 (setq vm-message-list m-list
2481 vm-folder-type folder-type
2482 vm-pop-retrieved-messages pop-retrieved
2483 vm-imap-retrieved-messages imap-retrieved)
2485 (vm-startup-apply-bookmark bookmark)
2486 (and order (vm-startup-apply-message-order order))
2487 (if vm-summary-show-threads
2489 ;; get numbering of new messages done now
2490 ;; so that the sort code only has to worry about the
2491 ;; changes it needs to make.
2492 (vm-update-summary-and-mode-line)
2493 (vm-sort-messages "thread")))
2494 (vm-startup-apply-summary summary)
2495 (vm-startup-apply-labels labels)
2496 (vm-startup-apply-header-variables vis invis)
2498 (message "Reading index file... done")
2500 (and work-buffer (kill-buffer work-buffer))))
2501 (error (message "Index file read of %s signaled: %s"
2502 index-file error-data)
2504 (message "Ignoring index file...")
2507 (defun vm-check-index-file-validity (blob)
2511 (cond ((not (consp blob))
2512 (error "Validity check object not a cons: %s"))
2513 ((eq (car blob) 'file)
2514 (let (ch time time2)
2515 (setq blob (cdr blob))
2516 (setq time (car blob)
2517 time2 (vm-gobble-last-modified))
2518 (if (and time2 (> 0 (vm-time-difference time time2)))
2520 (setq blob (cdr blob))
2522 (setq ch (char-after (car blob)))
2523 (if (or (null ch) (not (eq (vm-char-to-int ch) (nth 1 blob))))
2525 (setq blob (cdr (cdr blob)))))
2527 (t (error "Unknown validity check type: %s" (car blob)))))))
2529 (defun vm-generate-index-file-validity-check ()
2532 (let ((step (max 1 (/ (point-max) 11)))
2533 (pos (1- (point-max)))
2537 (setq blob (cons pos (cons (vm-char-to-int (char-after pos)) blob))
2539 (cons 'file (cons (current-time) blob)))))
2541 (defun vm-write-index-file-maybe ()
2543 (if (not (stringp buffer-file-name))
2545 (if (not (stringp vm-index-file-suffix))
2547 (let ((index-file (vm-make-index-file-name)))
2548 (vm-write-index-file index-file))))
2550 (defun vm-write-index-file (index-file)
2551 (let ((work-buffer nil))
2553 (let ((print-escape-newlines t)
2556 (message "Sorting for index file...")
2557 (setq m-list (sort (copy-sequence vm-message-list)
2558 (function vm-sort-compare-physical-order)))
2559 (message "Stuffing index file...")
2560 (setq work-buffer (vm-make-work-buffer))
2562 (princ ";; index file version\n" work-buffer)
2563 (prin1 1 work-buffer)
2564 (terpri work-buffer)
2566 (princ ";; folder type\n" work-buffer)
2567 (prin1 vm-folder-type work-buffer)
2568 (terpri work-buffer)
2571 ";; timestamp + sample of folder bytes for consistency check\n"
2573 (prin1 (vm-generate-index-file-validity-check) work-buffer)
2574 (terpri work-buffer)
2576 (princ ";; bookmark\n" work-buffer)
2577 (princ (if vm-message-pointer
2578 (vm-number-of (car vm-message-pointer))
2581 (terpri work-buffer)
2583 (princ ";; message order\n" work-buffer)
2584 (let ((n 0) (mp vm-message-list))
2585 (princ "(" work-buffer)
2588 (if (zerop (% n 15))
2589 (princ "\n\t" work-buffer)
2590 (princ " " work-buffer))
2591 (princ (vm-number-of (car mp)) work-buffer)
2592 (setq n (1+ n) mp (cdr mp)))
2593 (princ "\n)\n" work-buffer))
2595 (princ ";; summary\n" work-buffer)
2596 (prin1 vm-summary-format work-buffer)
2597 (terpri work-buffer)
2599 (princ ";; labels used in this folder\n" work-buffer)
2603 (setq list (cons (symbol-name sym) list))))
2605 (prin1 list work-buffer))
2606 (terpri work-buffer)
2608 (princ ";; visible headers\n" work-buffer)
2609 (prin1 vm-visible-headers work-buffer)
2610 (terpri work-buffer)
2612 (princ ";; hidden headers\n" work-buffer)
2613 (prin1 vm-invisible-header-regexp work-buffer)
2614 (terpri work-buffer)
2616 (princ ";; location list\n" work-buffer)
2617 (princ "(\n" work-buffer)
2621 (princ " [" work-buffer)
2622 (prin1 (marker-position (vm-start-of m)) work-buffer)
2623 (princ " " work-buffer)
2624 (prin1 (marker-position (vm-headers-of m)) work-buffer)
2625 (princ " " work-buffer)
2626 (prin1 (marker-position (vm-text-end-of m)) work-buffer)
2627 (princ " " work-buffer)
2628 (prin1 (marker-position (vm-end-of m)) work-buffer)
2629 (princ "]\n" work-buffer)
2631 (princ ")\n" work-buffer)
2632 (princ ";; attribute list\n" work-buffer)
2633 (princ "(\n" work-buffer)
2637 (princ " " work-buffer)
2638 (prin1 (vm-attributes-of m) work-buffer)
2639 (princ "\n" work-buffer)
2641 (princ ")\n" work-buffer)
2642 (princ ";; cache list\n" work-buffer)
2643 (princ "(\n" work-buffer)
2647 (princ " " work-buffer)
2648 (prin1 (vm-cache-of m) work-buffer)
2649 (princ "\n" work-buffer)
2651 (princ ")\n" work-buffer)
2652 (princ ";; labels list\n" work-buffer)
2653 (princ "(\n" work-buffer)
2657 (princ " " work-buffer)
2658 (prin1 (vm-labels-of m) work-buffer)
2659 (princ "\n" work-buffer)
2661 (princ ")\n" work-buffer)
2662 (princ ";; retrieved POP messages\n" work-buffer)
2663 (let ((p vm-pop-retrieved-messages))
2665 (princ "nil\n" work-buffer)
2666 (princ "(\n" work-buffer)
2668 (princ "\t" work-buffer)
2669 (prin1 (car p) work-buffer)
2670 (princ "\n" work-buffer)
2672 (princ ")\n" work-buffer)))
2673 (princ ";; retrieved IMAP messages\n" work-buffer)
2674 (let ((p vm-imap-retrieved-messages))
2676 (princ "nil\n" work-buffer)
2677 (princ "(\n" work-buffer)
2679 (princ "\t" work-buffer)
2680 (prin1 (car p) work-buffer)
2681 (princ "\n" work-buffer)
2683 (princ ")\n" work-buffer)))
2685 (princ ";; end of index file\n" work-buffer)
2687 (message "Writing index file...")
2690 (set-buffer work-buffer)
2691 (condition-case data
2692 (let ((coding-system-for-write (vm-binary-coding-system))
2693 (selective-display nil))
2694 (write-region (point-min) (point-max) index-file))
2696 (message "Write of %s signaled: %s" index-file data)
2698 (throw 'done nil))))
2699 (vm-error-free-call 'set-file-modes index-file (vm-octal 600))
2700 (message "Writing index file... done")
2702 (and work-buffer (kill-buffer work-buffer)))))
2704 (defun vm-delete-index-file ()
2705 (if (stringp vm-index-file-suffix)
2706 (let ((index-file (vm-make-index-file-name)))
2707 (vm-error-free-call 'delete-file index-file))))
2709 (defun vm-change-all-new-to-unread ()
2710 (let ((mp vm-message-list))
2712 (if (vm-new-flag (car mp))
2714 (vm-set-new-flag (car mp) nil)
2715 (vm-set-unread-flag (car mp) t)))
2716 (setq mp (cdr mp)))))
2719 (defun vm-unread-message (&optional count)
2720 "Set the `unread' attribute for the current message. If the message is
2721 already new or unread, then it is left unchanged.
2723 Numeric prefix argument N means to unread the current message plus the
2724 next N-1 messages. A negative N means unread the current message and
2725 the previous N-1 messages.
2727 When invoked on marked messages (via vm-next-command-uses-marks),
2728 all marked messages are affected, other messages are ignored."
2730 (or count (setq count 1))
2731 (vm-follow-summary-cursor)
2732 (vm-select-folder-buffer)
2733 (vm-check-for-killed-summary)
2734 (vm-error-if-folder-empty)
2735 (let ((mlist (vm-select-marked-or-prefixed-messages count)))
2737 (if (and (not (vm-unread-flag (car mlist)))
2738 (not (vm-new-flag (car mlist))))
2739 (vm-set-unread-flag (car mlist) t))
2740 (setq mlist (cdr mlist))))
2741 (vm-display nil nil '(vm-unread-message) '(vm-unread-message))
2742 (vm-update-summary-and-mode-line))
2745 (defun vm-quit-just-bury ()
2746 "Bury the current VM folder and summary buffers.
2747 The folder is not altered and Emacs is still visiting it. You
2748 can switch back to it with switch-to-buffer or by using the
2751 (vm-select-folder-buffer)
2752 (if (not (memq major-mode '(vm-mode vm-virtual-mode)))
2753 (error "%s must be invoked from a VM buffer." this-command))
2754 (vm-check-for-killed-summary)
2755 (vm-check-for-killed-presentation)
2757 (save-excursion (run-hooks 'vm-quit-hook))
2759 (vm-garbage-collect-message)
2761 (vm-display nil nil '(vm-quit-just-bury)
2762 '(vm-quit-just-bury quitting))
2763 (if vm-summary-buffer
2764 (vm-display vm-summary-buffer nil nil nil))
2765 (if vm-summary-buffer
2766 (vm-bury-buffer vm-summary-buffer))
2767 (if vm-presentation-buffer-handle
2768 (vm-display vm-presentation-buffer-handle nil nil nil))
2769 (if vm-presentation-buffer-handle
2770 (vm-bury-buffer vm-presentation-buffer-handle))
2771 (vm-display (current-buffer) nil nil nil)
2772 (vm-bury-buffer (current-buffer)))
2775 (defun vm-quit-just-iconify ()
2776 "Iconify the frame and bury the current VM folder and summary buffers.
2777 The folder is not altered and Emacs is still visiting it."
2779 (vm-select-folder-buffer)
2780 (if (not (memq major-mode '(vm-mode vm-virtual-mode)))
2781 (error "%s must be invoked from a VM buffer." this-command))
2782 (vm-check-for-killed-summary)
2783 (vm-check-for-killed-presentation)
2785 (save-excursion (run-hooks 'vm-quit-hook))
2787 (vm-garbage-collect-message)
2789 (vm-display nil nil '(vm-quit-just-iconify)
2790 '(vm-quit-just-iconify quitting))
2791 (let ((summary-buffer vm-summary-buffer)
2792 (pres-buffer vm-presentation-buffer-handle))
2793 (vm-bury-buffer (current-buffer))
2795 (vm-bury-buffer summary-buffer))
2797 (vm-bury-buffer pres-buffer))
2798 (vm-iconify-frame)))
2801 (defun vm-quit-no-change ()
2802 "Quit visiting the current folder without saving changes made to the folder."
2807 (defun vm-quit (&optional no-change)
2808 "Quit visiting the current folder, saving changes. Deleted messages are not expunged."
2810 (vm-select-folder-buffer)
2811 (if (not (memq major-mode '(vm-mode vm-virtual-mode)))
2812 (error "%s must be invoked from a VM buffer." this-command))
2813 (vm-check-for-killed-summary)
2814 (vm-check-for-killed-presentation)
2815 (vm-display nil nil '(vm-quit vm-quit-no-change)
2816 (list this-command 'quitting))
2817 (let ((virtual (eq major-mode 'vm-virtual-mode)))
2819 ((and (not virtual) no-change (buffer-modified-p)
2820 (or buffer-file-name buffer-offer-save)
2821 (not (zerop vm-messages-not-on-disk))
2822 ;; Folder may have been saved with C-x C-s and attributes may have
2823 ;; been changed after that; in that case vm-messages-not-on-disk
2824 ;; would not have been zeroed. However, all modification flag
2825 ;; undos are cleared if VM actually modifies the folder buffer
2826 ;; (as opposed to the folder's attributes), so this can be used
2827 ;; to verify that there are indeed unsaved messages.
2828 (null (assq 'vm-set-buffer-modified-p vm-undo-record-list))
2832 "%d message%s have not been saved to disk, quit anyway? "
2833 vm-messages-not-on-disk
2834 (if (= 1 vm-messages-not-on-disk) "" "s")))))
2838 (or buffer-file-name buffer-offer-save)
2841 (not (y-or-n-p "There are unsaved changes, quit anyway? ")))
2843 ((and (eq vm-confirm-quit t)
2844 (not (y-or-n-p "Do you really want to quit? ")))
2847 (save-excursion (run-hooks 'vm-quit-hook))
2849 (vm-garbage-collect-message)
2850 (vm-garbage-collect-folder)
2853 (if (and (not no-change) (not virtual))
2855 ;; this could take a while, so give the user some feedback
2856 (message "Quitting...")
2857 (or vm-folder-read-only (eq major-mode 'vm-virtual-mode)
2858 (vm-change-all-new-to-unread))))
2859 (if (and (buffer-modified-p)
2860 (or buffer-file-name buffer-offer-save)
2865 (let ((summary-buffer vm-summary-buffer)
2866 (pres-buffer vm-presentation-buffer-handle)
2867 (mail-buffer (current-buffer)))
2870 (vm-display summary-buffer nil nil nil)
2871 (kill-buffer summary-buffer)))
2874 (vm-display pres-buffer nil nil nil)
2875 (kill-buffer pres-buffer)))
2876 (set-buffer mail-buffer)
2877 (vm-display mail-buffer nil nil nil)
2878 ;; vm-display is not supposed to change the current buffer.
2879 ;; still it's better to be safe here.
2880 (set-buffer mail-buffer)
2881 ;; if folder is selected in the folders summary, force
2882 ;; selcetion of some other folder.
2883 (if buffer-file-name
2884 (vm-mark-for-folders-summary-update buffer-file-name))
2885 (set-buffer-modified-p nil)
2886 (kill-buffer (current-buffer)))
2887 (vm-update-summary-and-mode-line)))
2889 (defun vm-start-itimers-if-needed ()
2890 (cond ((and (not (natnump vm-flush-interval))
2891 (not (natnump vm-auto-get-new-mail))
2892 (not (natnump vm-mail-check-interval))))
2893 ((condition-case data
2894 (progn (require 'itimer) t)
2896 (and (natnump vm-flush-interval) (not (get-itimer "vm-flush"))
2897 (start-itimer "vm-flush" 'vm-flush-itimer-function
2898 vm-flush-interval nil))
2899 (and (natnump vm-auto-get-new-mail) (not (get-itimer "vm-get-mail"))
2900 (start-itimer "vm-get-mail" 'vm-get-mail-itimer-function
2901 vm-auto-get-new-mail nil))
2902 (and (natnump vm-mail-check-interval)
2903 (not (get-itimer "vm-check-mail"))
2904 (start-itimer "vm-check-mail" 'vm-check-mail-itimer-function
2905 vm-mail-check-interval nil)))
2906 ((condition-case data
2907 (progn (require 'timer) t)
2910 (and (natnump vm-flush-interval)
2911 (not (vm-timer-using 'vm-flush-itimer-function))
2912 (setq timer (run-at-time vm-flush-interval vm-flush-interval
2913 'vm-flush-itimer-function nil))
2914 (timer-set-function timer 'vm-flush-itimer-function
2916 (and (natnump vm-mail-check-interval)
2917 (not (vm-timer-using 'vm-check-mail-itimer-function))
2918 (setq timer (run-at-time vm-mail-check-interval
2919 vm-mail-check-interval
2920 'vm-check-mail-itimer-function nil))
2921 (timer-set-function timer 'vm-check-mail-itimer-function
2923 (and (natnump vm-auto-get-new-mail)
2924 (not (vm-timer-using 'vm-get-mail-itimer-function))
2925 (setq timer (run-at-time vm-auto-get-new-mail
2926 vm-auto-get-new-mail
2927 'vm-get-mail-itimer-function nil))
2928 (timer-set-function timer 'vm-get-mail-itimer-function
2931 (setq vm-flush-interval t
2932 vm-auto-get-new-mail t))))
2935 (defun vm-timer-using (fun)
2936 (let ((p timer-list)
2938 (while (and p (not done))
2939 (if (eq (aref (car p) 5) fun)
2944 ;; support for vm-mail-check-interval
2945 ;; if timer argument is present, this means we're using the Emacs
2946 ;; 'timer package rather than the 'itimer package.
2947 (defun vm-check-mail-itimer-function (&optional timer)
2948 ;; FSF Emacs sets this non-nil, which means the user can't
2949 ;; interrupt the check. Bogus.
2950 (setq inhibit-quit nil)
2951 (if (integerp vm-mail-check-interval)
2953 (timer-set-time timer (timer-relative-time (current-time) vm-mail-check-interval) vm-mail-check-interval)
2954 (set-itimer-restart current-itimer vm-mail-check-interval))
2955 ;; user has changed the variable value to something that
2956 ;; isn't a number, make the timer go away.
2958 (cancel-timer timer)
2959 (set-itimer-restart current-itimer nil)))
2960 (let ((b-list (buffer-list))
2963 (while (and (not (input-pending-p)) b-list)
2965 (if (not (buffer-live-p (car b-list)))
2967 (set-buffer (car b-list))
2968 (if (and (eq major-mode 'vm-mode)
2970 ;; to avoid reentrance into the pop and imap code
2971 (not vm-global-block-new-mail))
2973 (setq oldval vm-spooled-mail-waiting)
2974 (setq vm-spooled-mail-waiting (vm-check-for-spooled-mail nil t))
2975 (if (not (eq oldval vm-spooled-mail-waiting))
2977 (intern (buffer-name) vm-buffers-needing-display-update)
2978 (run-hooks 'vm-spooled-mail-waiting-hook)))))))
2979 (setq b-list (cdr b-list)))
2980 (vm-update-summary-and-mode-line)
2981 ;; make the timer go away if we didn't encounter a vm-mode buffer.
2982 (if (and (not found-one) (null b-list))
2984 (cancel-timer timer)
2985 (set-itimer-restart current-itimer nil)))))
2987 ;; support for numeric vm-auto-get-new-mail
2988 ;; if timer argument is present, this means we're using the Emacs
2989 ;; 'timer package rather than the 'itimer package.
2990 (defun vm-get-mail-itimer-function (&optional timer)
2991 ;; FSF Emacs sets this non-nil, which means the user can't
2992 ;; interrupt mail retrieval. Bogus.
2993 (setq inhibit-quit nil)
2994 (if (integerp vm-auto-get-new-mail)
2996 (timer-set-time timer (timer-relative-time (current-time) vm-auto-get-new-mail) vm-auto-get-new-mail)
2997 (set-itimer-restart current-itimer vm-auto-get-new-mail))
2998 ;; user has changed the variable value to something that
2999 ;; isn't a number, make the timer go away.
3001 (cancel-timer timer)
3002 (set-itimer-restart current-itimer nil)))
3003 (let ((b-list (buffer-list))
3005 (while (and (not (input-pending-p)) b-list)
3007 (if (not (buffer-live-p (car b-list)))
3009 (set-buffer (car b-list))
3010 (if (and (eq major-mode 'vm-mode)
3012 (not vm-global-block-new-mail)
3013 (not vm-block-new-mail)
3014 (not vm-folder-read-only)
3015 (not (and (not (buffer-modified-p))
3017 (file-newer-than-file-p
3018 (make-auto-save-file-name)
3020 (vm-get-spooled-mail nil))
3022 ;; don't move the message pointer unless the folder
3024 (if (and (null vm-message-pointer)
3025 (vm-thoughtfully-select-message))
3026 (vm-preview-current-message)
3027 (vm-update-summary-and-mode-line))))))
3028 (setq b-list (cdr b-list)))
3029 ;; make the timer go away if we didn't encounter a vm-mode buffer.
3030 (if (and (not found-one) (null b-list))
3032 (cancel-timer timer)
3033 (set-itimer-restart current-itimer nil)))))
3035 ;; support for numeric vm-flush-interval
3036 ;; if timer argument is present, this means we're using the Emacs
3037 ;; 'timer package rather than the 'itimer package.
3038 (defun vm-flush-itimer-function (&optional timer)
3039 (if (integerp vm-flush-interval)
3041 (timer-set-time timer (timer-relative-time (current-time) vm-flush-interval) vm-flush-interval)
3042 (set-itimer-restart current-itimer vm-flush-interval)))
3043 ;; if no vm-mode buffers are found, we might as well shut down the
3045 (if (not (vm-flush-cached-data))
3047 (cancel-timer timer)
3048 (set-itimer-restart current-itimer nil))))
3050 ;; flush cached data in all vm-mode buffers.
3051 ;; returns non-nil if any vm-mode buffers were found.
3052 (defun vm-flush-cached-data ()
3054 (let ((buf-list (buffer-list))
3056 (while (and buf-list (not (input-pending-p)))
3057 (if (not (buffer-live-p (car buf-list)))
3059 (set-buffer (car buf-list))
3060 (cond ((and (eq major-mode 'vm-mode) vm-message-list)
3062 (if (not (eq vm-modification-counter
3063 vm-flushed-modification-counter))
3065 (vm-stuff-last-modified)
3066 (vm-stuff-pop-retrieved)
3067 (vm-stuff-imap-retrieved)
3070 (and vm-message-order-changed
3071 (vm-stuff-message-order))
3072 (and (vm-stuff-folder-attributes t t)
3073 (setq vm-flushed-modification-counter
3074 vm-modification-counter)))))))
3075 (setq buf-list (cdr buf-list)))
3076 ;; if we haven't checked them all return non-nil so
3077 ;; the flusher won't give up trying.
3078 (or buf-list found-one) )))
3080 ;; This allows C-x C-s to do the right thing for VM mail buffers.
3081 ;; Note that deleted messages are not expunged.
3082 (defun vm-write-file-hook ()
3083 (if (and (eq major-mode 'vm-mode) (not vm-inhibit-write-file-hook))
3084 ;; The vm-save-restriction isn't really necessary here, since
3085 ;; the stuff routines clean up after themselves, but should remain
3086 ;; as a safeguard against the time when other stuff is added here.
3087 (vm-save-restriction
3088 (let ((buffer-read-only))
3089 (message "Stuffing attributes...")
3090 (vm-stuff-folder-attributes nil)
3091 (message "Stuffing attributes... done")
3094 (if (and vm-folders-summary-database buffer-file-name)
3097 (vm-store-folder-totals buffer-file-name (cdr vm-totals))))
3098 ;; get summary cache up-to-date
3099 (vm-update-summary-and-mode-line)
3101 (vm-stuff-pop-retrieved)
3102 (vm-stuff-imap-retrieved)
3103 (vm-stuff-last-modified)
3104 (vm-stuff-header-variables)
3107 (and vm-message-order-changed
3108 (vm-stuff-message-order))))
3112 (defun vm-save-buffer (prefix)
3114 (vm-select-folder-buffer)
3115 (vm-error-if-virtual-folder)
3116 (save-buffer prefix)
3117 (intern (buffer-name) vm-buffers-needing-display-update)
3118 (setq vm-block-new-mail nil)
3119 (vm-display nil nil '(vm-save-buffer) '(vm-save-buffer))
3120 (if (and vm-folders-summary-database buffer-file-name)
3123 (vm-store-folder-totals buffer-file-name (cdr vm-totals))))
3124 (vm-update-summary-and-mode-line)
3125 (vm-write-index-file-maybe))
3128 (defun vm-write-file ()
3130 (vm-select-folder-buffer)
3131 (vm-error-if-virtual-folder)
3132 (let ((old-buffer-name (buffer-name))
3133 (oldmodebits (and (fboundp 'default-file-modes)
3134 (default-file-modes))))
3137 (and oldmodebits (set-default-file-modes
3138 vm-default-folder-permission-bits))
3139 (call-interactively 'write-file))
3140 (and oldmodebits (set-default-file-modes oldmodebits)))
3141 (if (and vm-folders-summary-database buffer-file-name)
3144 (vm-store-folder-totals buffer-file-name (cdr vm-totals))))
3145 (if (not (equal (buffer-name) old-buffer-name))
3147 (vm-check-for-killed-summary)
3148 (if vm-summary-buffer
3150 (let ((name (buffer-name)))
3151 (set-buffer vm-summary-buffer)
3152 (rename-buffer (format "%s Summary" name) t))))
3153 (vm-check-for-killed-presentation)
3154 (if vm-presentation-buffer-handle
3156 (let ((name (buffer-name)))
3157 (set-buffer vm-presentation-buffer-handle)
3158 (rename-buffer (format "%s Presentation" name) t)))))))
3159 (intern (buffer-name) vm-buffers-needing-display-update)
3160 (setq vm-block-new-mail nil)
3161 (vm-display nil nil '(vm-write-file) '(vm-write-file))
3162 (vm-update-summary-and-mode-line)
3163 (vm-write-index-file-maybe))
3165 (defun vm-unblock-new-mail ()
3166 (setq vm-block-new-mail nil))
3169 (defun vm-save-folder (&optional prefix)
3170 "Save current folder to disk.
3171 Deleted messages are not expunged.
3172 Prefix arg is handled the same as for the command `save-buffer'.
3174 When applied to a virtual folder, this command runs itself on
3175 each of the underlying real folders associated with the virtual
3177 (interactive (list current-prefix-arg))
3178 (vm-select-folder-buffer)
3179 (vm-check-for-killed-summary)
3180 (vm-display nil nil '(vm-save-folder) '(vm-save-folder))
3181 (if (eq major-mode 'vm-virtual-mode)
3182 (vm-virtual-save-folder prefix)
3183 (if (buffer-modified-p)
3184 (let (mp (newlist nil))
3185 (cond ((eq vm-folder-access-method 'pop)
3186 (vm-pop-synchronize-folder t t t nil))
3187 ((eq vm-folder-access-method 'imap)
3188 (vm-imap-synchronize-folder t t t nil t)))
3189 ;; stuff the attributes of messages that need it.
3190 (message "Stuffing attributes...")
3191 (vm-stuff-folder-attributes nil)
3192 (message "Stuffing attributes... done")
3193 ;; stuff bookmark and header variable values
3196 ;; get summary cache up-to-date
3197 (vm-update-summary-and-mode-line)
3199 (vm-stuff-pop-retrieved)
3200 (vm-stuff-imap-retrieved)
3201 (vm-stuff-last-modified)
3202 (vm-stuff-header-variables)
3205 (and vm-message-order-changed
3206 (vm-stuff-message-order))))
3207 (message "Saving...")
3208 (let ((vm-inhibit-write-file-hook t)
3209 (oldmodebits (and (fboundp 'default-file-modes)
3210 (default-file-modes))))
3213 (and oldmodebits (set-default-file-modes
3214 vm-default-folder-permission-bits))
3215 (save-buffer prefix))
3216 (and oldmodebits (set-default-file-modes oldmodebits))))
3217 (vm-set-buffer-modified-p nil)
3218 ;; clear the modified flag in virtual folders if all the
3219 ;; real buffers associated with them are unmodified.
3220 (let ((b-list vm-virtual-buffers) rb-list one-modified)
3223 (if (null (cdr (vm-buffer-variable-value (car b-list)
3225 (vm-set-buffer-modified-p nil (car b-list))
3226 (set-buffer (car b-list))
3227 (setq rb-list vm-real-buffers one-modified nil)
3229 (if (buffer-modified-p (car rb-list))
3230 (setq one-modified t rb-list nil)
3231 (setq rb-list (cdr rb-list))))
3232 (if (not one-modified)
3233 (vm-set-buffer-modified-p nil (car b-list))))
3234 (setq b-list (cdr b-list)))))
3235 (vm-clear-modification-flag-undos)
3236 (setq vm-messages-not-on-disk 0)
3237 (setq vm-block-new-mail nil)
3238 (vm-write-index-file-maybe)
3239 (if (and vm-folders-summary-database buffer-file-name)
3242 (vm-store-folder-totals buffer-file-name (cdr vm-totals))))
3243 (vm-update-summary-and-mode-line)
3244 (and (zerop (buffer-size))
3245 vm-delete-empty-folders
3247 (or (eq vm-delete-empty-folders t)
3248 (y-or-n-p (format "%s is empty, remove it? "
3249 (or buffer-file-name (buffer-name)))))
3252 (delete-file buffer-file-name)
3253 (vm-delete-index-file)
3254 (clear-visited-file-modtime)
3255 (message "%s removed" buffer-file-name))
3256 ;; no can do, oh well.
3258 (message "No changes need to be saved"))))
3261 (defun vm-save-and-expunge-folder (&optional prefix)
3262 "Expunge folder, then save it to disk.
3263 Prefix arg is handled the same as for the command save-buffer.
3264 Expunge won't be done if folder is read-only.
3266 When applied to a virtual folder, this command works as if you had
3267 run vm-expunge-folder followed by vm-save-folder."
3268 (interactive (list current-prefix-arg))
3269 (vm-select-folder-buffer)
3270 (vm-check-for-killed-summary)
3271 (vm-display nil nil '(vm-save-and-expunge-folder)
3272 '(vm-save-and-expunge-folder))
3273 (if (not vm-folder-read-only)
3275 (message "Expunging...")
3276 (vm-expunge-folder t)))
3277 (vm-save-folder prefix))
3280 (defun vm-revert-buffer (&rest args)
3282 (vm-select-folder-buffer-if-possible)
3283 (call-interactively 'revert-buffer))
3286 (defun vm-recover-file (&rest args)
3288 (vm-select-folder-buffer-if-possible)
3289 (call-interactively 'recover-file))
3291 (defun vm-handle-file-recovery-or-reversion (recovery)
3292 (if (and vm-summary-buffer (buffer-name vm-summary-buffer))
3293 (kill-buffer vm-summary-buffer))
3295 ;; reset major mode, this will cause vm to start from scratch.
3296 (setq major-mode 'fundamental-mode)
3297 ;; If this is a recovery, we can't allow the user to get new
3298 ;; mail until a real save is performed. Until then the buffer
3299 ;; and the disk don't match.
3301 (setq vm-block-new-mail t))
3302 (let ((name (cond ((eq vm-folder-access-method 'pop)
3303 (vm-pop-find-name-for-buffer (current-buffer)))
3304 ((eq vm-folder-access-method 'imap)
3305 (vm-imap-find-spec-for-buffer (current-buffer))))))
3306 (vm (or name buffer-file-name) nil vm-folder-access-method)))
3308 ;; detect if a recover-file is being performed
3309 ;; and handle things properly.
3310 (defun vm-handle-file-recovery ()
3311 (if (and (buffer-modified-p)
3312 (eq major-mode 'vm-mode)
3313 (or (null vm-message-list)
3314 (= (vm-end-of (car vm-message-list)) 1)))
3315 (vm-handle-file-recovery-or-reversion t)))
3317 ;; detect if a revert-buffer is being performed
3318 ;; and handle things properly.
3319 (defun vm-handle-file-reversion ()
3320 (if (and (not (buffer-modified-p))
3321 (eq major-mode 'vm-mode)
3322 (or (null vm-message-list)
3323 (= (vm-end-of (car vm-message-list)) 1)))
3324 (vm-handle-file-recovery-or-reversion nil)))
3326 ;; FSF v19.23 revert-buffer doesn't mash all the markers together
3327 ;; like v18 and prior v19 versions, so the check in
3328 ;; vm-handle-file-reversion doesn't work. However v19.23 has a
3329 ;; hook we can use, after-revert-hook.
3330 (defun vm-after-revert-buffer-hook ()
3331 (if (eq major-mode 'vm-mode)
3332 (vm-handle-file-recovery-or-reversion nil)))
3336 "Display help for various VM activities."
3338 (if (eq major-mode 'vm-summary-mode)
3339 (vm-select-folder-buffer))
3340 (let ((pop-up-windows (and pop-up-windows (eq vm-mutable-windows t)))
3341 (pop-up-frames (and vm-mutable-frames vm-frame-per-help)))
3343 ((eq last-command 'vm-help)
3344 (describe-function major-mode))
3345 ((eq vm-system-state 'previewing)
3346 (message "Type SPC to read message, n previews next message (? gives more help)"))
3347 ((memq vm-system-state '(showing reading))
3348 (message "SPC and b scroll, (d)elete, (s)ave, (n)ext, (r)eply (? gives more help)"))
3349 ((eq vm-system-state 'editing)
3351 (substitute-command-keys
3352 "Type \\[vm-edit-message-end] to end edit, \\[vm-edit-message-abort] to abort with no change.")))
3353 ((eq major-mode 'mail-mode)
3355 (substitute-command-keys
3356 "Type \\[vm-mail-send-and-exit] to send message, \\[kill-buffer] to discard this composition")))
3357 (t (describe-mode)))))
3360 (defun vm-spool-move-mail (source destination)
3361 (let ((handler (and (fboundp 'find-file-name-handler)
3363 (find-file-name-handler source 'vm-spool-move-mail)
3364 (wrong-number-of-arguments
3365 (find-file-name-handler source)))))
3366 status error-buffer)
3368 (funcall handler 'vm-spool-move-mail source destination)
3371 (format "*output of %s %s %s*"
3372 vm-movemail-program source destination)))
3374 (set-buffer error-buffer)
3377 (apply 'call-process
3379 (list vm-movemail-program nil error-buffer t)
3380 (copy-sequence vm-movemail-program-switches)
3381 (list source destination))))
3383 (set-buffer error-buffer)
3384 (if (and (numberp status) (not (= 0 status)))
3385 (insert (format "\n%s exited with code %s\n"
3386 vm-movemail-program status)))
3387 (if (> (buffer-size) 0)
3389 (vm-display-buffer error-buffer)
3390 (if (and (numberp status) (not (= 0 status)))
3391 (error "Failed getting new mail from %s" source)
3392 (message "Warning: unexpected output from %s"
3393 vm-movemail-program)
3396 (kill-buffer error-buffer))
3399 (defun vm-gobble-crash-box (crash-box)
3401 (vm-save-restriction
3403 (let ((opoint-max (point-max)) crash-buf
3404 (buffer-read-only nil)
3405 (inbox-buffer-file buffer-file-name)
3406 (inbox-folder-type vm-folder-type)
3407 (inbox-empty (zerop (buffer-size)))
3408 got-mail crash-folder-type
3409 (old-buffer-modified-p (buffer-modified-p)))
3411 ;; crash box could contain a letter bomb...
3412 ;; force user notification of file variables for v18 Emacses
3413 ;; enable-local-variables == nil disables them for newer Emacses
3414 (let ((inhibit-local-variables t)
3415 (enable-local-variables nil)
3416 (enable-local-eval nil)
3417 (coding-system-for-read (vm-line-ending-coding-system)))
3418 (find-file-noselect crash-box)))
3419 (if (eq (current-buffer) crash-buf)
3420 (error "folder is the same file as crash box, cannot continue"))
3422 (set-buffer crash-buf)
3423 (setq crash-folder-type (vm-get-folder-type))
3424 (if (and crash-folder-type vm-check-folder-types)
3425 (cond ((eq crash-folder-type 'unknown)
3426 (error "crash box %s's type is unrecognized" crash-box))
3427 ((eq inbox-folder-type 'unknown)
3428 (error "inbox %s's type is unrecognized"
3430 ((null inbox-folder-type)
3431 (if vm-default-folder-type
3432 (if (not (eq vm-default-folder-type
3434 (if vm-convert-folder-types
3436 (vm-convert-folder-type
3438 vm-default-folder-type)
3439 ;; so that kill-buffer won't ask a
3440 ;; question later...
3441 (set-buffer-modified-p nil))
3442 (error "crash box %s mismatches vm-default-folder-type: %s, %s"
3443 crash-box crash-folder-type
3444 vm-default-folder-type)))))
3445 ((not (eq inbox-folder-type crash-folder-type))
3446 (if vm-convert-folder-types
3448 (vm-convert-folder-type crash-folder-type
3450 ;; so that kill-buffer won't ask a
3451 ;; question later...
3452 (set-buffer-modified-p nil))
3453 (error "crash box %s mismatches %s's folder type: %s, %s"
3454 crash-box inbox-buffer-file
3455 crash-folder-type inbox-folder-type)))))
3456 ;; toss the folder header if the inbox is not empty
3457 (goto-char (point-min))
3458 (if (not inbox-empty)
3460 (vm-convert-folder-header (or inbox-folder-type
3461 vm-default-folder-type)
3463 (set-buffer-modified-p nil))))
3464 (goto-char (point-max))
3465 (insert-buffer-substring crash-buf
3466 1 (1+ (save-excursion
3467 (set-buffer crash-buf)
3470 (setq got-mail (/= opoint-max (point-max)))
3473 (let ((coding-system-for-write (vm-binary-coding-system))
3474 (selective-display nil))
3475 (write-region opoint-max (point-max) buffer-file-name t t))
3476 (vm-increment vm-modification-counter)
3477 (set-buffer-modified-p old-buffer-modified-p))
3478 (kill-buffer crash-buf)
3479 (if (not (stringp vm-keep-crash-boxes))
3480 (vm-error-free-call 'delete-file crash-box)
3481 (let ((time (decode-time (current-time)))
3484 (expand-file-name (format "Z-%02d-%02d-%02d%02d%02d-%05d"
3490 (% (vm-abs (random)) 100000))
3491 vm-keep-crash-boxes))
3492 (while (file-exists-p name)
3494 (expand-file-name (format "Z-%02d-%02d-%02d%02d%02d-%05d"
3500 (% (vm-abs (random)) 100000))
3501 vm-keep-crash-boxes)))
3502 (rename-file crash-box name)))
3505 (defun vm-compute-spool-files (&optional all)
3506 (let ((fallback-triples nil)
3507 (crash-box (or vm-crash-box
3508 (concat vm-primary-inbox vm-crash-box-suffix)))
3511 (cond ((null (vm-spool-files))
3513 (list vm-primary-inbox
3514 (concat vm-spool-directory (user-login-name))
3516 ((stringp (car (vm-spool-files)))
3519 (lambda (s) (list vm-primary-inbox s crash-box)))
3521 ((consp (car (vm-spool-files)))
3522 (setq triples (vm-spool-files))))
3523 (setq file-list (if all (mapcar 'car triples) (list buffer-file-name)))
3525 (setq file (car file-list))
3526 (setq file-list (cdr file-list))
3528 (consp vm-spool-file-suffixes)
3529 (stringp vm-crash-box-suffix))
3530 (setq fallback-triples
3534 (concat file suffix)
3536 vm-crash-box-suffix))))
3537 vm-spool-file-suffixes))))
3539 vm-make-spool-file-name vm-make-crash-box-name)
3540 (setq fallback-triples
3541 (nconc fallback-triples
3544 (funcall vm-make-spool-file-name
3547 (funcall vm-make-crash-box-name
3549 (setq triples (append triples fallback-triples))
3552 (defun vm-spool-check-mail (source)
3553 (let ((handler (and (fboundp 'find-file-name-handler)
3555 (find-file-name-handler source 'vm-spool-check-mail)
3556 (wrong-number-of-arguments
3557 (find-file-name-handler source))))))
3559 (funcall handler 'vm-spool-check-mail source)
3560 (let ((size (nth 7 (file-attributes source)))
3561 (hash vm-spool-file-message-count-hash)
3563 (setq val (symbol-value (intern-soft source hash)))
3564 (if (and val (equal size (car val)))
3566 (let ((count (vm-count-messages-in-file source)))
3569 (set (intern source hash) (list size count))
3570 (vm-store-folder-totals source (list count 0 0 0))
3573 (defun vm-count-messages-in-file (file &optional quietly)
3574 (let ((type (vm-get-folder-type file nil nil t))
3577 (if (or (memq type '(unknown nil)) (null vm-grep-program))
3582 (setq work-buffer (vm-make-work-buffer))
3583 (set-buffer work-buffer)
3584 (cond ((memq type '(From_ BellFrom_ From_-with-Content-Length))
3585 (setq regexp "^From "))
3587 (setq regexp "^\001\001\001\001"))
3589 (setq regexp "^\037")))
3590 (condition-case data
3592 (or quietly (message "Counting messages in %s..." file))
3593 (call-process vm-grep-program nil t nil "-c" regexp
3594 (expand-file-name file))
3595 (or quietly (message "Counting messages in %s... done" file)))
3596 (error (message "Attempt to run %s on %s signaled: %s"
3597 vm-grep-program file data)
3599 (setq vm-grep-program nil)))
3600 (setq count (string-to-number (buffer-string)))
3601 (cond ((memq type '(From_ BellFrom_ From_-with-Content-Length))
3604 (setq count (/ count 2)))
3606 (setq count (1- count))))
3608 (and work-buffer (kill-buffer work-buffer))))))
3610 (defun vm-movemail-specific-spool-file-p (file)
3611 (string-match "^po:[^:]+$" file))
3613 (defun vm-check-for-spooled-mail (&optional interactive this-buffer-only)
3614 (if vm-global-block-new-mail
3616 (if (and vm-folder-access-method this-buffer-only)
3617 (cond ((eq vm-folder-access-method 'pop)
3618 (vm-pop-folder-check-for-mail interactive))
3619 ((eq vm-folder-access-method 'imap)
3620 (vm-imap-folder-check-for-mail interactive)))
3621 (let ((triples (vm-compute-spool-files (not this-buffer-only)))
3622 ;; since we could accept-process-output here (POP code),
3623 ;; a timer process might try to start retrieving mail
3624 ;; before we finish. block these attempts.
3625 (vm-global-block-new-mail t)
3626 (vm-pop-ok-to-ask interactive)
3627 (vm-imap-ok-to-ask interactive)
3628 ;; for string-match calls below
3629 (case-fold-search nil)
3630 this-buffer crash in maildrop meth
3633 (setq in (expand-file-name (nth 0 (car triples)) vm-folder-directory)
3634 maildrop (nth 1 (car triples))
3635 crash (nth 2 (car triples)))
3636 (if (vm-movemail-specific-spool-file-p maildrop)
3637 ;; spool file is accessible only with movemail
3640 (setq this-buffer (eq (current-buffer) (vm-get-file-buffer in)))
3641 (if (or this-buffer (not this-buffer-only))
3643 (if (file-exists-p crash)
3645 (setq mail-waiting t))
3646 (cond ((and vm-recognize-imap-maildrops
3647 (string-match vm-recognize-imap-maildrops
3649 (setq meth 'vm-imap-check-mail))
3650 ((and vm-recognize-pop-maildrops
3651 (string-match vm-recognize-pop-maildrops
3653 (setq meth 'vm-pop-check-mail))
3654 (t (setq meth 'vm-spool-check-mail)))
3655 (if (not interactive)
3656 ;; allow no error to be signaled
3660 (funcall meth maildrop)))
3664 (funcall meth maildrop))))))))
3665 (setq triples (cdr triples)))
3668 (defun vm-get-spooled-mail (&optional interactive)
3669 (if vm-block-new-mail
3670 (error "Can't get new mail until you save this folder."))
3671 (cond ((eq vm-folder-access-method 'pop)
3672 (vm-pop-synchronize-folder interactive nil nil t))
3673 ((eq vm-folder-access-method 'imap)
3674 (vm-imap-synchronize-folder interactive nil nil t))
3675 (t (vm-get-spooled-mail-normal interactive))))
3677 (defun vm-get-spooled-mail-normal (&optional interactive)
3678 (if vm-global-block-new-mail
3680 (let ((triples (vm-compute-spool-files))
3681 ;; since we could accept-process-output here (POP code),
3682 ;; a timer process might try to start retrieving mail
3683 ;; before we finish. block these attempts.
3684 (vm-global-block-new-mail t)
3685 (vm-pop-ok-to-ask interactive)
3686 (vm-imap-ok-to-ask interactive)
3687 ;; for string-match calls below
3688 (case-fold-search nil)
3689 non-file-maildrop crash in safe-maildrop maildrop popdrop
3692 (if (and (not (verify-visited-file-modtime (current-buffer)))
3693 (or (null interactive)
3696 "Folder %s changed on disk, discard those changes? "
3697 (buffer-name (current-buffer)))))))
3699 (message "Folder %s changed on disk, consider M-x revert-buffer"
3700 (buffer-name (current-buffer)))
3704 (setq in (expand-file-name (nth 0 (car triples)) vm-folder-directory)
3705 maildrop (nth 1 (car triples))
3706 crash (nth 2 (car triples)))
3707 (setq safe-maildrop maildrop
3708 non-file-maildrop nil)
3709 (cond ((vm-movemail-specific-spool-file-p maildrop)
3710 (setq non-file-maildrop t)
3711 (setq retrieval-function 'vm-spool-move-mail))
3712 ((and vm-recognize-imap-maildrops
3713 (string-match vm-recognize-imap-maildrops
3715 (setq non-file-maildrop t)
3716 (setq safe-maildrop (vm-safe-imapdrop-string maildrop))
3717 (setq retrieval-function 'vm-imap-move-mail))
3718 ((and vm-recognize-pop-maildrops
3719 (string-match vm-recognize-pop-maildrops
3721 (setq non-file-maildrop t)
3722 (setq safe-maildrop (vm-safe-popdrop-string maildrop))
3723 (setq retrieval-function 'vm-pop-move-mail))
3724 (t (setq retrieval-function 'vm-spool-move-mail)))
3725 (if (eq (current-buffer) (vm-get-file-buffer in))
3727 (if (file-exists-p crash)
3729 (message "Recovering messages from %s..." crash)
3730 (setq got-mail (or (vm-gobble-crash-box crash) got-mail))
3731 (message "Recovering messages from %s... done" crash)))
3732 (if (or non-file-maildrop
3733 (and (not (equal 0 (nth 7 (file-attributes maildrop))))
3734 (file-readable-p maildrop)))
3736 (setq crash (expand-file-name crash vm-folder-directory))
3737 (if (not non-file-maildrop)
3738 (setq maildrop (expand-file-name maildrop
3739 vm-folder-directory)))
3741 ;; don't allow errors to be signaled unless no
3742 ;; mail has been appended to the incore
3743 ;; copy of the folder. otherwise the
3744 ;; user will wonder where the mail is,
3745 ;; since it is not in the crash box or
3746 ;; the spool file and doesn't _appear_ to
3747 ;; be in the folder either.
3748 (condition-case error-data
3749 (funcall retrieval-function maildrop crash)
3750 (error (message "%s signaled: %s"
3754 ;; we don't know if mail was
3755 ;; put into the crash box or
3756 ;; not, so return t just to be
3759 (quit (message "quitting from %s..."
3762 ;; we don't know if mail was
3763 ;; put into the crash box or
3764 ;; not, so return t just to be
3767 (funcall retrieval-function maildrop crash))
3768 (if (vm-gobble-crash-box crash)
3771 (if (not non-file-maildrop)
3772 (vm-store-folder-totals maildrop
3774 (message "Got mail from %s."
3775 safe-maildrop))))))))
3776 (setq triples (cdr triples)))
3777 ;; not really correct, but it is what the user expects to see.
3778 (setq vm-spooled-mail-waiting nil)
3779 (intern (buffer-name) vm-buffers-needing-display-update)
3780 (vm-update-summary-and-mode-line)
3782 (run-hooks 'vm-retrieved-spooled-mail-hook))
3783 (and got-mail (vm-assimilate-new-messages t))))))
3785 (defun vm-safe-popdrop-string (drop)
3786 (or (and (string-match "^\\(pop:\\|pop-ssl:\\|pop-ssh:\\)?\\([^:]+\\):[^:]+:[^:]+:\\([^:]+\\):[^:]+" drop)
3787 (concat (substring drop (match-beginning 3) (match-end 3))
3789 (substring drop (match-beginning 2) (match-end 2))))
3792 (defun vm-safe-imapdrop-string (drop)
3793 (or (and (string-match "^\\(imap\\|imap-ssl\\|imap-ssh\\):\\([^:]+\\):[^:]+:\\([^:]+\\):[^:]+:\\([^:]+\\):[^:]+" drop)
3794 (concat (substring drop (match-beginning 4) (match-end 4))
3796 (substring drop (match-beginning 2) (match-end 2))
3798 (substring drop (match-beginning 3) (match-end 3))
3803 (defun vm-get-new-mail (&optional arg)
3804 "Move any new mail that has arrived in any of the spool files for the
3805 current folder into the folder. New mail is appended to the disk
3806 and buffer copies of the folder.
3808 Prefix arg means to gather mail from a user specified folder, instead of
3809 the usual spool files. The file name will be read from the minibuffer.
3810 Unlike when getting mail from a spool file, the source file is left
3811 undisturbed after its messages have been copied.
3813 When applied to a virtual folder, this command runs itself on
3814 each of the underlying real folders associated with this virtual
3815 folder. A prefix argument has no effect when this command is
3816 applied to virtual folder; mail is always gathered from the spool
3819 (vm-select-folder-buffer)
3820 (vm-check-for-killed-summary)
3821 (vm-error-if-folder-read-only)
3822 (cond ((eq major-mode 'vm-virtual-mode)
3823 (vm-virtual-get-new-mail))
3824 ((not (eq major-mode 'vm-mode))
3825 (error "Can't get mail for a non-VM folder buffer"))
3827 (if (not (eq major-mode 'vm-mode))
3829 (if (consp (car (vm-spool-files)))
3830 (message "Checking for new mail for %s..."
3831 (or buffer-file-name (buffer-name)))
3832 (message "Checking for new mail..."))
3834 (if (vm-get-spooled-mail t)
3836 ;; say this NOW, before the non-previewers read
3837 ;; a message, alter the new message count and
3838 ;; confuse themselves.
3839 (setq totals-blurb (vm-emit-totals-blurb))
3840 (vm-display nil nil '(vm-get-new-mail) '(vm-get-new-mail))
3841 (if (vm-thoughtfully-select-message)
3842 (vm-preview-current-message)
3843 (vm-update-summary-and-mode-line))
3844 (message totals-blurb))
3845 (if (consp (car (vm-spool-files)))
3846 (message "No new mail for %s"
3847 (or buffer-file-name (buffer-name)))
3848 (message "No new mail."))
3849 (and (interactive-p) (sit-for 4) (message "")))))
3851 (let ((buffer-read-only nil)
3852 folder mcount totals-blurb)
3853 (setq folder (read-file-name "Gather mail from folder: "
3854 vm-folder-directory nil t))
3855 (if (and vm-check-folder-types
3856 (not (vm-compatible-folder-p folder)))
3857 (error "Folder %s is not the same format as this folder."
3860 (vm-save-restriction
3862 (goto-char (point-max))
3863 (let ((coding-system-for-read (vm-binary-coding-system)))
3864 (insert-file-contents folder))))
3865 (setq mcount (length vm-message-list))
3866 (if (vm-assimilate-new-messages)
3868 ;; say this NOW, before the non-previewers read
3869 ;; a message, alter the new message count and
3870 ;; confuse themselves.
3871 (setq totals-blurb (vm-emit-totals-blurb))
3872 (vm-display nil nil '(vm-get-new-mail) '(vm-get-new-mail))
3873 (if (vm-thoughtfully-select-message)
3874 (vm-preview-current-message)
3875 (vm-update-summary-and-mode-line))
3876 (message totals-blurb)
3877 ;; The gathered messages are actually still on disk
3878 ;; unless the user deletes the folder himself.
3879 ;; However, users may not understand what happened if
3880 ;; the messages go away after a "quit, no save".
3881 (setq vm-messages-not-on-disk
3882 (+ vm-messages-not-on-disk
3883 (- (length vm-message-list)
3885 (message "No messages gathered."))))))
3887 ;; returns list of new messages if there were any new messages, nil otherwise
3888 (defun vm-assimilate-new-messages (&optional
3889 dont-read-attributes
3892 (let ((tail-cons (vm-last vm-message-list))
3893 b-list new-messages)
3895 (vm-save-restriction
3897 (vm-build-message-list)
3898 (if (or (null tail-cons) (cdr tail-cons))
3900 (if (not vm-assimilate-new-messages-sorted)
3901 (setq vm-ml-sort-keys nil))
3902 (if dont-read-attributes
3903 (vm-set-default-attributes (cdr tail-cons))
3904 (vm-read-attributes (cdr tail-cons)))
3905 ;; Yuck. This has to be done here instead of in the
3906 ;; vm function because this needs to be done before
3907 ;; any initial thread sort (so that if the thread
3908 ;; sort matches the saved order the folder won't be
3909 ;; modified) but after the message list is created.
3910 ;; Since thread sorting is done here this has to be
3913 (vm-gobble-message-order))
3914 (if (or (vectorp vm-thread-obarray)
3915 vm-summary-show-threads)
3916 (vm-build-threads (cdr tail-cons))))))
3917 (setq new-messages (if tail-cons (cdr tail-cons) vm-message-list))
3918 (vm-set-numbering-redo-start-point new-messages)
3919 (vm-set-summary-redo-start-point new-messages))
3920 ;; Only update the folders summary count here if new messages
3921 ;; have arrived, not when we're reading the folder for the
3922 ;; first time, and not if we cannot assume that all the arrived
3923 ;; messages should be considered new. Use gobble-order as a
3924 ;; first time indicator along with the new messages being equal
3925 ;; to the whole message list.
3926 (if (and new-messages dont-read-attributes
3927 (or (not (eq new-messages vm-message-list))
3928 (null gobble-order)))
3929 (vm-modify-folder-totals buffer-file-name 'arrived
3930 (length new-messages)))
3931 ;; copy the new-messages list because sorting might scramble
3932 ;; it. Also something the user does when
3933 ;; vm-arrived-message-hook is run might affect it.
3934 ;; vm-assimilate-new-messages returns this value so it must
3936 (setq new-messages (copy-sequence new-messages))
3938 (if (and new-messages labels vm-burst-digest-messages-inherit-labels)
3939 (let ((mp new-messages))
3941 (vm-set-labels-of (car mp) (copy-sequence labels))
3942 (setq mp (cdr mp)))))
3943 (if (and new-messages vm-summary-show-threads)
3945 ;; get numbering of new messages done now
3946 ;; so that the sort code only has to worry about the
3947 ;; changes it needs to make.
3948 (vm-update-summary-and-mode-line)
3949 (vm-sort-messages "thread")))
3950 (if (and new-messages
3951 (or vm-arrived-message-hook vm-arrived-messages-hook)
3952 ;; Run the hooks only if this is not the first
3953 ;; time vm-assimilate-new-messages has been called
3956 (let ((new-messages new-messages))
3957 ;; seems wise to do this so that if the user runs VM
3958 ;; commands here they start with as much of a clean
3959 ;; slate as we can provide, given we're currently deep
3960 ;; in the guts of VM.
3961 (vm-update-summary-and-mode-line)
3962 (if vm-arrived-message-hook
3964 (vm-run-message-hook (car new-messages)
3965 'vm-arrived-message-hook)
3966 (setq new-messages (cdr new-messages))))
3967 (run-hooks 'vm-arrived-messages-hook)))
3968 (if (and new-messages vm-virtual-buffers)
3970 (setq b-list vm-virtual-buffers)
3972 ;; buffer might be dead
3973 (if (buffer-name (car b-list))
3975 (set-buffer (car b-list))
3976 (setq tail-cons (vm-last vm-message-list))
3977 (vm-build-virtual-message-list new-messages)
3978 (if (or (null tail-cons) (cdr tail-cons))
3980 (if (not vm-assimilate-new-messages-sorted)
3981 (setq vm-ml-sort-keys nil))
3982 (if (vectorp vm-thread-obarray)
3983 (vm-build-threads (cdr tail-cons)))
3984 (vm-set-summary-redo-start-point
3985 (or (cdr tail-cons) vm-message-list))
3986 (vm-set-numbering-redo-start-point
3987 (or (cdr tail-cons) vm-message-list))
3988 (if (null vm-message-pointer)
3989 (progn (setq vm-message-pointer vm-message-list
3990 vm-need-summary-pointer-update t)
3991 (if vm-message-pointer
3992 (vm-preview-current-message))))
3993 (if vm-summary-show-threads
3995 (vm-update-summary-and-mode-line)
3996 (vm-sort-messages "thread")))))))
3997 (setq b-list (cdr b-list)))))
3998 (if (and new-messages vm-ml-sort-keys)
3999 (vm-sort-messages vm-ml-sort-keys))
4002 (defun vm-select-marked-or-prefixed-messages (prefix)
4003 "Return a list of all marked messages or the messages indicated by a
4004 prefix argument. If the prefix argument is supplied *and we are
4005 not in a vm-next-command-uses-marks context*, then return a number
4006 of messages around vm-message-pointer equal to (abs prefix),
4007 either backward (prefix is negative) or forward (positive)."
4008 (if (eq last-command 'vm-next-command-uses-marks)
4009 (vm-marked-messages)
4011 (direction (if (< prefix 0) 'backward 'forward))
4012 (count (vm-abs prefix))
4013 (vm-message-pointer vm-message-pointer))
4014 (unless (eq vm-circular-folders t)
4015 (vm-check-count prefix))
4016 (while (not (zerop count))
4017 (setq mlist (cons (car vm-message-pointer) mlist))
4018 (vm-decrement count)
4019 (unless (zerop count)
4020 (vm-move-message-pointer direction)))
4023 (defun vm-display-startup-message ()
4025 (let ((lines vm-startup-message-lines))
4026 (message "VM %s. Type ? for help." (vm-version))
4027 (setq vm-startup-message-displayed t)
4028 (while (and (sit-for 4) lines)
4029 (message (substitute-command-keys (car lines)))
4030 (setq lines (cdr lines)))))
4034 (defun vm-toggle-read-only ()
4036 (vm-select-folder-buffer)
4037 (setq vm-folder-read-only (not vm-folder-read-only))
4038 (intern (buffer-name) vm-buffers-needing-display-update)
4039 (message "Folder is now %s"
4040 (if vm-folder-read-only "read-only" "modifiable"))
4041 (vm-display nil nil '(vm-toggle-read-only) '(vm-toggle-read-only))
4042 (vm-update-summary-and-mode-line))
4044 (defvar scroll-in-place)
4046 ;; this does the real major mode scutwork.
4047 (defun vm-mode-internal (&optional access-method)
4049 (make-local-variable 'require-final-newline)
4050 ;; don't kill local variables, as there is some state we'd like to
4051 ;; keep. rather than non-portably marking the variables we
4052 ;; want to keep, just avoid calling kill-local-variables and
4053 ;; reset everything that needs to be reset.
4056 mode-line-format vm-mode-line-format
4058 ;; must come after the setting of major-mode
4059 mode-popup-menu (and vm-use-menus
4060 (vm-menu-support-possible-p)
4061 (vm-menu-mode-menu))
4063 ;; If the user quits a vm-mode buffer, the default action is
4064 ;; to kill the buffer. Make a note that we should offer to
4065 ;; save this buffer even if it has no file associated with it.
4066 ;; We have no idea of the value of the data in the buffer
4067 ;; before it was put into vm-mode.
4069 require-final-newline nil
4070 ;; don't let CR's in folders be mashed into LF's because of a
4071 ;; stupid user setting.
4072 selective-display nil
4073 vm-thread-obarray 'bonk
4074 vm-thread-subject-obarray 'bonk
4075 vm-label-obarray (make-vector 29 0)
4076 vm-last-message-pointer nil
4077 vm-modification-counter 0
4079 vm-message-pointer nil
4080 vm-message-order-changed nil
4081 vm-message-order-header-present nil
4082 vm-imap-retrieved-messages nil
4083 vm-pop-retrieved-messages nil
4084 vm-summary-buffer nil
4086 vm-undo-record-list nil
4087 vm-undo-record-pointer nil
4088 vm-virtual-buffers (vm-link-to-virtual-buffers)
4089 vm-folder-type (vm-get-folder-type))
4090 (cond ((eq access-method 'pop)
4091 (setq vm-folder-access-method 'pop
4092 vm-folder-access-data (make-vector 2 nil)))
4093 ((eq access-method 'imap)
4094 (setq vm-folder-access-method 'imap
4095 vm-folder-access-data (make-vector 9 nil))))
4096 (use-local-map vm-mode-map)
4097 ;; if the user saves after M-x recover-file, let them get new
4099 (make-local-hook 'after-save-hook)
4100 (add-hook 'after-save-hook 'vm-unblock-new-mail)
4101 (and (vm-menu-support-possible-p)
4102 (vm-menu-install-menus))
4103 (add-hook 'kill-buffer-hook 'vm-garbage-collect-folder)
4104 (add-hook 'kill-buffer-hook 'vm-garbage-collect-message)
4105 ;; avoid the XEmacs file dialog box.
4106 (defvar use-dialog-box)
4107 (make-local-variable 'use-dialog-box)
4108 (setq use-dialog-box nil)
4109 ;; mail folders are precious. protect them by default.
4110 (make-local-variable 'file-precious-flag)
4111 (setq file-precious-flag vm-folder-file-precious-flag)
4112 ;; scroll in place messes with scroll-up and this loses
4113 (make-local-variable 'scroll-in-place)
4114 (setq scroll-in-place nil)
4115 (run-hooks 'vm-mode-hook)
4117 (run-hooks 'vm-mode-hooks))
4119 (defun vm-link-to-virtual-buffers ()
4120 (let ((b-list (buffer-list))
4122 (folder-buffer (current-buffer))
4126 (set-buffer (car b-list))
4127 (cond ((eq major-mode 'vm-virtual-mode)
4128 (setq clauses (cdr vm-virtual-folder-definition))
4130 (setq folders (car (car clauses)))
4132 (if (eq folder-buffer (vm-get-file-buffer
4135 vm-folder-directory)))
4136 (setq vbuffers (cons (car b-list) vbuffers)
4137 vm-real-buffers (cons folder-buffer
4141 (setq folders (cdr folders)))
4142 (setq clauses (cdr clauses)))))
4143 (setq b-list (cdr b-list)))
4147 (defun vm-change-folder-type (type)
4148 "Change folder type to TYPE.
4149 TYPE may be one of the following symbol values:
4152 From_-with-Content-Length
4157 Interactively TYPE will be read from the minibuffer."
4159 (let ((this-command this-command)
4160 (last-command last-command)
4161 (types vm-supported-folder-types))
4162 (vm-select-folder-buffer)
4163 (vm-error-if-virtual-folder)
4164 (setq types (vm-delqual (symbol-name vm-folder-type)
4165 (copy-sequence types)))
4166 (list (intern (vm-read-string "Change folder to type: " types)))))
4167 (vm-select-folder-buffer)
4168 (vm-check-for-killed-summary)
4169 (vm-error-if-virtual-folder)
4170 (vm-error-if-folder-empty)
4171 (if (not (memq type '(From_ BellFrom_ From_-with-Content-Length mmdf babyl)))
4172 (error "Unknown folder type: %s" type))
4173 (if (or (null vm-folder-type)
4174 (eq vm-folder-type 'unknown))
4175 (error "Current folder's type is unknown, can't change it."))
4176 (let ((mp vm-message-list)
4177 (buffer-read-only nil)
4178 (old-type vm-folder-type)
4182 ;; Just for laughs, make the update interval vary.
4183 (modulus (+ (% (vm-abs (random)) 11) 5))
4186 (vm-save-restriction
4188 (setq vm-folder-type type)
4189 (goto-char (point-min))
4190 (vm-convert-folder-header old-type type)
4192 (goto-char (vm-start-of (car mp)))
4193 (setq opoint (point))
4194 (insert (vm-leading-message-separator type (car mp)))
4195 (if (> (vm-headers-of (car mp)) (vm-start-of (car mp)))
4196 (delete-region (point) (vm-headers-of (car mp)))
4197 (set-marker (vm-headers-of (car mp)) (point))
4198 ;; if headers-of == start-of then so could vheaders-of
4199 ;; and text-of. clear them to force a recompute.
4200 (vm-set-vheaders-of (car mp) nil)
4201 (vm-set-text-of (car mp) nil))
4202 (vm-convert-folder-type-headers old-type type)
4203 (goto-char (vm-text-end-of (car mp)))
4204 (setq text-end (point))
4205 (insert-before-markers (vm-trailing-message-separator type))
4206 (delete-region (vm-text-end-of (car mp)) (vm-end-of (car mp)))
4207 (set-marker (vm-text-end-of (car mp)) text-end)
4208 (goto-char (vm-headers-of (car mp)))
4209 (vm-munge-message-separators type (vm-headers-of (car mp))
4210 (vm-text-end-of (car mp)))
4211 (vm-set-byte-count-of (car mp) nil)
4212 (vm-set-babyl-frob-flag-of (car mp) nil)
4213 (vm-set-message-type-of (car mp) type)
4214 ;; Technically we should mark each message for a
4215 ;; summary update since the message byte counts might
4216 ;; have changed. But I don't think anyone cares that
4217 ;; much and the summary regeneration would make this
4219 (setq mp (cdr mp) n (1+ n))
4220 (if (zerop (% n modulus))
4221 (message "Converting... %d" n))))))
4222 (vm-clear-modification-flag-undos)
4223 (intern (buffer-name) vm-buffers-needing-display-update)
4224 (vm-update-summary-and-mode-line)
4225 (message "Conversion complete.")
4226 ;; message separator strings may have leaked into view
4227 (if (> (point-max) (vm-text-end-of (car vm-message-pointer)))
4228 (narrow-to-region (point-min) (vm-text-end-of (car vm-message-pointer))))
4229 (vm-display nil nil '(vm-change-folder-type) '(vm-change-folder-type)))
4231 (defun vm-register-global-garbage-files (files)
4233 (setq vm-global-garbage-alist
4234 (cons (cons (car files) 'delete-file)
4235 vm-global-garbage-alist)
4236 files (cdr files))))
4238 (defun vm-register-folder-garbage-files (files)
4239 (vm-register-global-garbage-files files)
4241 (vm-select-folder-buffer)
4243 (setq vm-folder-garbage-alist
4244 (cons (cons (car files) 'delete-file)
4245 vm-folder-garbage-alist)
4246 files (cdr files)))))
4248 (defun vm-register-folder-garbage (action garbage)
4250 (vm-select-folder-buffer)
4251 (setq vm-folder-garbage-alist
4252 (cons (cons garbage action)
4253 vm-folder-garbage-alist))))
4255 (defun vm-register-message-garbage-files (files)
4256 (vm-register-folder-garbage-files files)
4258 (vm-select-folder-buffer)
4260 (setq vm-message-garbage-alist
4261 (cons (cons (car files) 'delete-file)
4262 vm-message-garbage-alist)
4263 files (cdr files)))))
4265 (defun vm-register-message-garbage (action garbage)
4266 (vm-register-folder-garbage action garbage)
4268 (vm-select-folder-buffer)
4269 (setq vm-message-garbage-alist
4270 (cons (cons garbage action)
4271 vm-message-garbage-alist))))
4273 (defun vm-garbage-collect-global ()
4275 (while vm-global-garbage-alist
4277 (funcall (cdr (car vm-global-garbage-alist))
4278 (car (car vm-global-garbage-alist)))
4280 (setq vm-global-garbage-alist (cdr vm-global-garbage-alist)))))
4282 (defun vm-garbage-collect-folder ()
4284 (while vm-folder-garbage-alist
4286 (funcall (cdr (car vm-folder-garbage-alist))
4287 (car (car vm-folder-garbage-alist)))
4289 (setq vm-folder-garbage-alist (cdr vm-folder-garbage-alist)))))
4291 (defun vm-garbage-collect-message ()
4293 (while vm-message-garbage-alist
4295 (funcall (cdr (car vm-message-garbage-alist))
4296 (car (car vm-message-garbage-alist)))
4298 (setq vm-message-garbage-alist (cdr vm-message-garbage-alist)))))
4300 (if (not (memq 'vm-write-file-hook write-file-hooks))
4301 (setq write-file-hooks
4302 (cons 'vm-write-file-hook write-file-hooks)))
4304 (if (not (memq 'vm-handle-file-recovery find-file-hooks))
4305 (setq find-file-hooks
4306 (nconc find-file-hooks
4307 '(vm-handle-file-recovery
4308 vm-handle-file-reversion))))
4310 ;; after-revert-hook is new to FSF v19.23
4311 (defvar after-revert-hook)
4312 (if (boundp 'after-revert-hook)
4313 (setq after-revert-hook
4314 (cons 'vm-after-revert-buffer-hook after-revert-hook))
4315 (setq after-revert-hook (list 'vm-after-revert-buffer-hook)))
4317 (provide 'vm-folder)
4319 ;;; vm-folder.el ends here