Mercurial > hg > xemacs-beta
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))) |