Test.
[gnus] / lisp / nnfolder.el
index db9666b..25613f6 100644 (file)
@@ -1,7 +1,10 @@
 ;;; nnfolder.el --- mail folder access for Gnus
-;; Copyright (C) 1995,96,97,98 Free Software Foundation, Inc.
+;; Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2001
+;;        Free Software Foundation, Inc.
 
-;; Author: Scott Byer <byer@mv.us.adobe.com>
+;; Author: Simon Josefsson <simon@josefsson.org> (adding MARKS)
+;;      ShengHuo Zhu <zsh@cs.rochester.edu> (adding NOV)
+;;      Scott Byer <byer@mv.us.adobe.com>
 ;;     Lars Magne Ingebrigtsen <larsi@gnus.org>
 ;;     Masanobu UMEDA <umerin@flab.flab.fujitsu.junet>
 ;; Keywords: mail
 (require 'nnmail)
 (require 'nnoo)
 (eval-when-compile (require 'cl))
+(require 'gnus)
 (require 'gnus-util)
+(require 'gnus-range)
+
+(eval-and-compile
+  (autoload 'gnus-article-unpropagatable-p "gnus-sum")
+  (autoload 'gnus-intersection "gnus-range"))
 
 (nnoo-declare nnfolder)
 
 (defvoo nnfolder-directory (expand-file-name message-directory)
   "The name of the nnfolder directory.")
 
+(defvoo nnfolder-nov-directory nil
+  "The name of the nnfolder NOV directory.
+If nil, `nnfolder-directory' is used.")
+
+(defvoo nnfolder-marks-directory nil
+  "The name of the nnfolder MARKS directory.
+If nil, `nnfolder-directory' is used.")
+
 (defvoo nnfolder-active-file
-  (nnheader-concat nnfolder-directory "active")
+    (nnheader-concat nnfolder-directory "active")
   "The name of the active file.")
 
 ;; I renamed this variable to something more in keeping with the general GNU
 ;; style. -SLB
 
 (defvoo nnfolder-ignore-active-file nil
-  "If non-nil, causes nnfolder to do some extra work in order to determine
-the true active ranges of an mbox file.  Note that the active file is still
-saved, but it's values are not used.  This costs some extra time when
-scanning an mbox when opening it.")
+  "If non-nil, the active file is ignored.
+This causes nnfolder to do some extra work in order to determine the
+true active ranges of an mbox file.  Note that the active file is
+still saved, but its values are not used.  This costs some extra time
+when scanning an mbox when opening it.")
 
 (defvoo nnfolder-distrust-mbox nil
-  "If non-nil, causes nnfolder to not trust the user with respect to
-inserting unaccounted for mail in the middle of an mbox file.  This can greatly
-slow down scans, which now must scan the entire file for unmarked messages.
-When nil, scans occur forward from the last marked message, a huge
-time saver for large mailboxes.")
+  "If non-nil, the folder will be distrusted.
+This means that nnfolder will not trust the user with respect to
+inserting unaccounted for mail in the middle of an mbox file.  This
+can greatly slow down scans, which now must scan the entire file for
+unmarked messages.  When nil, scans occur forward from the last marked
+message, a huge time saver for large mailboxes.")
 
 (defvoo nnfolder-newsgroups-file
-  (concat (file-name-as-directory nnfolder-directory) "newsgroups")
+    (concat (file-name-as-directory nnfolder-directory) "newsgroups")
   "Mail newsgroups description file.")
 
 (defvoo nnfolder-get-new-mail t
@@ -72,12 +91,13 @@ time saver for large mailboxes.")
 (defvoo nnfolder-save-buffer-hook nil
   "Hook run before saving the nnfolder mbox buffer.")
 
+
 (defvoo nnfolder-inhibit-expiry nil
   "If non-nil, inhibit expiry.")
 
 \f
 
-(defconst nnfolder-version "nnfolder 1.0"
+(defconst nnfolder-version "nnfolder 2.0"
   "nnfolder version.")
 
 (defconst nnfolder-article-marker "X-Gnus-Article-Number: "
@@ -90,6 +110,43 @@ time saver for large mailboxes.")
 (defvoo nnfolder-buffer-alist nil)
 (defvoo nnfolder-scantime-alist nil)
 (defvoo nnfolder-active-timestamp nil)
+(defvoo nnfolder-active-file-coding-system mm-text-coding-system)
+(defvoo nnfolder-active-file-coding-system-for-write
+    nnmail-active-file-coding-system)
+(defvoo nnfolder-file-coding-system mm-text-coding-system)
+(defvoo nnfolder-file-coding-system-for-write nnheader-file-coding-system
+  "Coding system for save nnfolder file.
+if nil, `nnfolder-file-coding-system' is used.") ; FIXME: fill-in the doc-string of this variable
+
+(defvoo nnfolder-nov-is-evil nil
+  "If non-nil, Gnus will never generate and use nov databases for mail groups.
+Using nov databases will speed up header fetching considerably.
+This variable shouldn't be flipped much.  If you have, for some reason,
+set this to t, and want to set it to nil again, you should always run
+the `nnfolder-generate-active-file' command.  The function will go
+through all nnfolder directories and generate nov databases for them
+all.  This may very well take some time.")
+
+(defvoo nnfolder-nov-file-suffix ".nov")
+
+(defvoo nnfolder-nov-buffer-alist nil)
+
+(defvar nnfolder-nov-buffer-file-name nil)
+
+(defvoo nnfolder-marks-is-evil nil
+  "If non-nil, Gnus will never generate and use marks file for mail groups.
+Using marks files makes it possible to backup and restore mail groups
+separately from `.newsrc.eld'.  If you have, for some reason, set
+this to t, and want to set it to nil again, you should always remove
+the corresponding marks file (usually base nnfolder file name
+concatenated with `.mrk', but see `nnfolder-marks-file-suffix') for
+the group.  Then the marks file will be regenerated properly by Gnus.")
+
+(defvoo nnfolder-marks nil)
+
+(defvoo nnfolder-marks-file-suffix ".mrk")
+
+(defvar nnfolder-marks-modtime (gnus-make-hashtable))
 
 \f
 
@@ -108,26 +165,38 @@ time saver for large mailboxes.")
        (goto-char (point-min))
        (if (stringp (car articles))
            'headers
-         (while (setq article (pop articles))
-           (set-buffer nnfolder-current-buffer)
-           (when (nnfolder-goto-article article)
-             (setq start (point))
-             (search-forward "\n\n" nil t)
-             (setq stop (1- (point)))
-             (set-buffer nntp-server-buffer)
-             (insert (format "221 %d Article retrieved.\n" article))
-             (insert-buffer-substring nnfolder-current-buffer start stop)
-             (goto-char (point-max))
-             (insert ".\n")))
-
-         (set-buffer nntp-server-buffer)
-         (nnheader-fold-continuation-lines)
-         'headers)))))
+         (if (nnfolder-retrieve-headers-with-nov articles fetch-old)
+             'nov
+           (setq articles (gnus-sorted-intersection
+                           ;; Is ARTICLES sorted?
+                           (sort articles '<)
+                           (nnfolder-existing-articles)))
+           (while (setq article (pop articles))
+             (set-buffer nnfolder-current-buffer)
+             (when (nnfolder-goto-article article)
+               (setq start (point))
+               (setq stop (if (search-forward "\n\n" nil t)
+                              (1- (point))
+                            (point-max)))
+               (set-buffer nntp-server-buffer)
+               (insert (format "221 %d Article retrieved.\n" article))
+               (insert-buffer-substring nnfolder-current-buffer start stop)
+               (goto-char (point-max))
+               (insert ".\n")))
+           (set-buffer nntp-server-buffer)
+           (nnheader-fold-continuation-lines)
+           'headers))))))
 
 (deffoo nnfolder-open-server (server &optional defs)
   (nnoo-change-server 'nnfolder server defs)
   (nnmail-activate 'nnfolder t)
   (gnus-make-directory nnfolder-directory)
+  (unless (or gnus-nov-is-evil nnfolder-nov-is-evil)
+    (and nnfolder-nov-directory
+        (gnus-make-directory nnfolder-nov-directory)))
+  (unless nnfolder-marks-is-evil
+    (and nnfolder-marks-directory
+        (gnus-make-directory nnfolder-marks-directory)))
   (cond
    ((not (file-exists-p nnfolder-directory))
     (nnfolder-close-server)
@@ -176,11 +245,13 @@ time saver for large mailboxes.")
          (if (numberp article)
              (cons nnfolder-current-group article)
            (goto-char (point-min))
-           (search-forward (concat "\n" nnfolder-article-marker))
            (cons nnfolder-current-group
-                 (string-to-int
-                  (buffer-substring
-                   (point) (progn (end-of-line) (point)))))))))))
+                 (if (search-forward (concat "\n" nnfolder-article-marker)
+                                     nil t)
+                     (string-to-int
+                      (buffer-substring
+                       (point) (progn (end-of-line) (point))))
+                   -1))))))))
 
 (deffoo nnfolder-request-group (group &optional server dont-check)
   (nnfolder-possibly-change-group group server t)
@@ -266,15 +337,14 @@ time saver for large mailboxes.")
   (when group
     (unless (assoc group nnfolder-group-alist)
       (push (list group (cons 1 0)) nnfolder-group-alist)
-      (nnmail-save-active nnfolder-group-alist nnfolder-active-file)
+      (nnfolder-save-active nnfolder-group-alist nnfolder-active-file)
       (nnfolder-read-folder group)))
   t)
 
 (deffoo nnfolder-request-list (&optional server)
   (nnfolder-possibly-change-group nil server)
   (save-excursion
-    (let ((nnmail-file-coding-system nnmail-active-file-coding-system)
-         (pathname-coding-system 'binary))
+    (let ((nnmail-file-coding-system nnfolder-active-file-coding-system))
       (nnmail-find-file nnfolder-active-file)
       (setq nnfolder-group-alist (nnmail-get-active)))
     t))
@@ -286,38 +356,79 @@ time saver for large mailboxes.")
 (deffoo nnfolder-request-list-newsgroups (&optional server)
   (nnfolder-possibly-change-group nil server)
   (save-excursion
-    (nnmail-find-file nnfolder-newsgroups-file)))
+    (let ((nnmail-file-coding-system nnfolder-file-coding-system))
+      (nnmail-find-file nnfolder-newsgroups-file))))
+
+;; Return a list consisting of all article numbers existing in the
+;; current folder.
+
+(defun nnfolder-existing-articles ()
+  (save-excursion
+    (when nnfolder-current-buffer
+      (set-buffer nnfolder-current-buffer)
+      (goto-char (point-min))
+      (let ((marker (concat "\n" nnfolder-article-marker))
+           (number "[0-9]+")
+           numbers)
+       (while (and (search-forward marker nil t)
+                   (re-search-forward number nil t))
+         (let ((newnum (string-to-number (match-string 0))))
+           (if (nnmail-within-headers-p)
+               (push newnum numbers))))
+      ;; The article numbers are increasing, so this result is sorted.
+       (nreverse numbers)))))
 
 (deffoo nnfolder-request-expire-articles
-  (articles newsgroup &optional server force)
+    (articles newsgroup &optional server force)
   (nnfolder-possibly-change-group newsgroup server)
   (let* ((is-old t)
-        rest)
+        ;; The articles we have deleted so far.
+        (deleted-articles nil)
+        ;; The articles that really exist and will
+        ;; be expired if they are old enough.
+        (maybe-expirable
+         (gnus-sorted-intersection articles (nnfolder-existing-articles))))
     (nnmail-activate 'nnfolder)
 
     (save-excursion
       (set-buffer nnfolder-current-buffer)
-      (while (and articles is-old)
+      ;; Since messages are sorted in arrival order and expired in the
+      ;; same order, we can stop as soon as we find a message that is
+      ;; too old.
+      (while (and maybe-expirable is-old)
        (goto-char (point-min))
-       (when (nnfolder-goto-article (car articles))
-         (if (setq is-old
-                   (nnmail-expired-article-p
-                    newsgroup
-                    (buffer-substring
-                     (point) (progn (end-of-line) (point)))
-                    force nnfolder-inhibit-expiry))
-             (progn
-               (nnheader-message 5 "Deleting article %d..."
-                                 (car articles) newsgroup)
-               (nnfolder-delete-mail))
-           (push (car articles) rest)))
-       (setq articles (cdr articles)))
+       (when (and (nnfolder-goto-article (car maybe-expirable))
+                  (search-forward (concat "\n" nnfolder-article-marker)
+                                  nil t))
+         (forward-sexp)
+         (when (setq is-old
+                     (nnmail-expired-article-p
+                      newsgroup
+                      (buffer-substring
+                       (point) (progn (end-of-line) (point)))
+                      force nnfolder-inhibit-expiry))
+           (unless (eq nnmail-expiry-target 'delete)
+             (with-temp-buffer
+               (nnfolder-request-article (car maybe-expirable)
+