comparison lisp/vm/vm-folder.el @ 0:376386a54a3c r19-14

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