2001-08-18 Simon Josefsson <jas@extundo.com>
[gnus] / lisp / nnml.el
index 1d078e8..7617dec 100644 (file)
 (require 'nnmail)
 (require 'nnoo)
 (eval-when-compile (require 'cl))
-(eval-and-compile
-  (autoload 'gnus-sorted-intersection "gnus-range"))
 
 (nnoo-declare nnml)
 
 (defvoo nnml-directory message-directory
-  "Spool directory for the nnml mail backend.")
+  "Spool directory for the nnml mail backend.
+
+This variable is a virtual server slot.  See the Gnus manual for details.")
 
 (defvoo nnml-active-file
     (expand-file-name "active" nnml-directory)
-  "Mail active file.")
+  "Mail active file.
+
+This variable is a virtual server slot.  See the Gnus manual for details.")
 
 (defvoo nnml-newsgroups-file
     (expand-file-name "newsgroups" nnml-directory)
-  "Mail newsgroups description file.")
+  "Mail newsgroups description file.
+
+This variable is a virtual server slot.  See the Gnus manual for details.")
 
 (defvoo nnml-get-new-mail t
-  "If non-nil, nnml will check the incoming mail file and split the mail.")
+  "If non-nil, nnml will check the incoming mail file and split the mail.
+
+This variable is a virtual server slot.  See the Gnus manual for details.")
 
 (defvoo nnml-nov-is-evil nil
   "If non-nil, Gnus will never generate and use nov databases for mail groups.
@@ -61,13 +67,19 @@ 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 `nnml-generate-nov-databases' command.  The function will go
 through all nnml directories and generate nov databases for them
-all.  This may very well take some time.")
+all.  This may very well take some time.
+
+This variable is a virtual server slot.  See the Gnus manual for details.")
 
 (defvoo nnml-prepare-save-mail-hook nil
-  "Hook run narrowed to an article before saving.")
+  "Hook run narrowed to an article before saving.
+
+This variable is a virtual server slot.  See the Gnus manual for details.")
 
 (defvoo nnml-inhibit-expiry nil
-  "If non-nil, inhibit expiry.")
+  "If non-nil, inhibit expiry.
+
+This variable is a virtual server slot.  See the Gnus manual for details.")
 
 
 \f
@@ -88,6 +100,9 @@ all.  This may very well take some time.")
 (defvoo nnml-generate-active-function 'nnml-generate-active-info)
 
 (defvar nnml-nov-buffer-file-name nil)
+(defvar nnml-check-directory-twice t
+  "If t, to make sure nothing went wrong when reading over NFS --
+check twice.")
 
 (defvoo nnml-file-coding-system nnmail-file-coding-system)
 
@@ -102,11 +117,16 @@ all.  This may very well take some time.")
     (save-excursion
       (set-buffer nntp-server-buffer)
       (erase-buffer)
-      (let ((file nil)
-           (number (length sequence))
-           (count 0)
-           (file-name-coding-system nnmail-pathname-coding-system)
-           beg article)
+      (let* ((file nil)
+            (number (length sequence))
+            (count 0)
+            (file-name-coding-system nnmail-pathname-coding-system)
+            beg article
+            (nnml-check-directory-twice
+             (and nnml-check-directory-twice
+                  ;; To speed up, disable it in some case.
+                  (or (not (numberp nnmail-large-newsgroup))
+                      (<= number nnmail-large-newsgroup)))))
        (if (stringp (car sequence))
            'headers
          (if (nnml-retrieve-headers-with-nov sequence fetch-old)
@@ -158,9 +178,9 @@ all.  This may very well take some time.")
                     server nnml-directory)
     t)))
 
-(defun nnml-request-regenerate (server)
+(deffoo nnml-request-regenerate (server)
   (nnml-possibly-change-directory nil server)
-  (nnml-generate-nov-databases)
+  (nnml-generate-nov-databases server)
   t)
 
 (deffoo nnml-request-article (id &optional group server buffer)
@@ -323,8 +343,8 @@ all.  This may very well take some time.")
     (and
      (nnml-deletable-article-p group article)
      (nnml-request-article article group server)
-     (let (nnml-current-directory 
-          nnml-current-group 
+     (let (nnml-current-directory
+          nnml-current-group
           nnml-article-file-alist)
        (save-excursion
         (set-buffer buf)
@@ -371,6 +391,9 @@ all.  This may very well take some time.")
         (nnml-save-nov))))
     result))
 
+(deffoo nnml-request-post (&optional server)
+  (nnmail-do-request-post 'nnml-request-accept-article server))
+
 (deffoo nnml-request-replace-article (article group buffer)
   (nnml-possibly-change-directory group)
   (save-excursion
@@ -419,7 +442,8 @@ all.  This may very well take some time.")
           (directory-files
            nnml-current-directory t
            (concat nnheader-numerical-short-files
-                   "\\|" (regexp-quote nnml-nov-file-name) "$")))
+                   "\\|" (regexp-quote nnml-nov-file-name) "$"
+                   "\\|" (regexp-quote nnml-marks-file-name) "$")))
          article)
       (while articles
        (setq article (pop articles))
@@ -457,6 +481,10 @@ all.  This may very well take some time.")
       (let ((overview (concat old-dir nnml-nov-file-name)))
        (when (file-exists-p overview)
          (rename-file overview (concat new-dir nnml-nov-file-name))))
+      ;; Move .marks file.
+      (let ((marks (concat old-dir nnml-marks-file-name)))
+       (when (file-exists-p marks)
+         (rename-file marks (concat new-dir nnml-marks-file-name))))
       (when (<= (length (directory-files old-dir)) 2)
        (ignore-errors (delete-directory old-dir)))
       ;; That went ok, so we change the internal structures.
@@ -489,13 +517,14 @@ all.  This may very well take some time.")
   (let (file)
     (if (setq file (cdr (assq article nnml-article-file-alist)))
        (expand-file-name file nnml-current-directory)
+      (if nnml-check-directory-twice
       ;; Just to make sure nothing went wrong when reading over NFS --
-      ;; check once more.
-      (when (file-exists-p
-            (setq file (expand-file-name (number-to-string article)
-                                         nnml-current-directory)))
-       (nnml-update-file-alist t)
-       file))))
+         ;; check once more.
+         (when (file-exists-p
+                (setq file (expand-file-name (number-to-string article)
+                                             nnml-current-directory)))
+           (nnml-update-file-alist t)
+           file)))))
 
 (defun nnml-deletable-article-p (group article)
   "Say whether ARTICLE in GROUP can be deleted."
@@ -517,7 +546,7 @@ all.  This may very well take some time.")
       ;; likely that the article we are looking for is in that group.
       (if (setq number (nnml-find-id nnml-current-group id))
          (cons nnml-current-group number)
-       ;; It wasn't there, so we look through the other groups as well.
+      ;; It wasn't there, so we look through the other groups as well.
        (while (and (not number)
                    alist)
          (or (string= (caar alist) nnml-current-group)
@@ -709,13 +738,14 @@ all.  This may very well take some time.")
       (setq nnml-nov-buffer-alist (cdr nnml-nov-buffer-alist)))))
 
 ;;;###autoload
-(defun nnml-generate-nov-databases ()
+(defun nnml-generate-nov-databases (&optional server)
   "Generate NOV databases in all nnml directories."
-  (interactive)
+  (interactive (list (or (nnoo-current-server 'nnml) "")))
   ;; Read the active file to make sure we don't re-use articles
   ;; numbers in empty groups.
   (nnmail-activate 'nnml)
-  (nnml-open-server (or (nnoo-current-server 'nnml) ""))
+  (unless (nnml-server-opened server)
+    (nnml-open-server server))
   (setq nnml-directory (expand-file-name nnml-directory))
   ;; Recurse down the directories.
   (nnml-generate-nov-databases-1 nnml-directory nil t)
@@ -754,15 +784,18 @@ all.  This may very well take some time.")
 (eval-when-compile (defvar files))
 (defun nnml-generate-active-info (dir)
   ;; Update the active info for this group.
-  (let ((group (nnheader-file-to-group
-               (directory-file-name dir) nnml-directory)))
-    (setq nnml-group-alist
-         (delq (assoc group nnml-group-alist) nnml-group-alist))
+  (let* ((group (nnheader-file-to-group
+                (directory-file-name dir) nnml-directory))
+        (entry (assoc group nnml-group-alist))
+        (last (or (caadr entry) 0)))
+    (setq nnml-group-alist (delq entry nnml-group-alist))
     (push (list group
-               (cons (caar files)
-                     (let ((f files))
-                       (while (cdr f) (setq f (cdr f)))
-                       (caar f))))
+               (cons (or (caar files) (1+ last))
+                     (max last
+                          (or (let ((f files))
+                                (while (cdr f) (setq f (cdr f)))
+                                (caar f))
+                              0))))
          nnml-group-alist)))
 
 (defun nnml-generate-nov-file (dir files)
@@ -825,6 +858,85 @@ all.  This may very well take some time.")
     (setq nnml-article-file-alist
          (nnheader-article-to-file-alist nnml-current-directory))))
 
+(defvoo nnml-marks-file-name ".marks")
+(defvoo nnml-marks-is-evil nil)
+(defvoo nnml-marks nil)
+
+(deffoo nnml-request-set-mark (group actions &optional server)
+  (nnml-possibly-change-directory group server)
+  (unless nnml-marks-is-evil
+    (nnml-open-marks group server)
+    (dolist (action actions)
+      (let ((range (nth 0 action))
+           (what  (nth 1 action))
+           (marks (nth 2 action)))
+       (assert (or (eq what 'add) (eq what 'del)) t
+               "Unknown request-set-mark action: %s" what)
+       (dolist (mark marks)
+         (setq nnml-marks (nnimap-update-alist-soft
+                           mark
+                           (funcall (if (eq what 'add) 'gnus-range-add
+                                      'gnus-remove-from-range)
+                                    (cdr (assoc mark nnml-marks)) range)
+                           nnml-marks)))))
+    (nnml-save-marks group server)))
+
+(deffoo nnml-request-update-info (group info &optional server)
+  (nnml-possibly-change-directory group server)
+  (unless nnml-marks-is-evil
+    (nnml-open-marks group server)
+    ;; Update info using `nnml-marks'.
+    (mapcar (lambda (pred)
+             (gnus-info-set-marks
+              info
+              (nnimap-update-alist-soft
+               (cdr pred)
+               (cdr (assq (cdr pred) nnml-marks))
+               (gnus-info-marks info))
+              t))
+           gnus-article-mark-lists)
+    (let ((seen (cdr (assq 'read nnml-marks))))
+      (gnus-info-set-read info
+                         (if (and (integerp (car seen))
+                                  (null (cdr seen)))
+                             (list (cons (car seen) (car seen)))
+                           seen))))
+  info)
+
+(defun nnml-save-marks (group server)
+  (let ((file-name-coding-system nnmail-pathname-coding-system)
+       (file (expand-file-name nnml-marks-file-name
+                               (nnmail-group-pathname group nnml-directory))))
+    (gnus-make-directory (file-name-directory file))
+    (with-temp-file file
+      (erase-buffer)
+      (princ nnml-marks (current-buffer))
+      (insert "\n"))))
+
+(defun nnml-open-marks (group server)
+  (with-temp-buffer
+    (let ((file (expand-file-name 
+                nnml-marks-file-name 
+                (nnmail-group-pathname group nnml-directory))))
+      (if (file-exists-p file)
+         (setq nnml-marks (condition-case err
+                              (progn
+                                (nnheader-insert-file-contents file)
+                                (read (current-buffer)))
+                            (error (or (gnus-yes-or-no-p
+                                        (format "Error reading nnml marks file %s (%s).  Continuing will use marks from .newsrc.eld.  Continue? " file err))
+                                       (error "Cannot read nnml marks file %s (%s)" file err)))))
+       ;; User didn't have a .marks file.  Probably first time
+       ;; user of the .marks stuff.  Bootstrap it from .newsrc.eld.
+       (let ((info (gnus-get-info
+                    (gnus-group-prefixed-name
+                     group
+                     (gnus-server-to-method (format "nnml:%s" server))))))
+         (nnheader-message 6 "Boostrapping nnml marks...")
+         (setq nnml-marks (gnus-info-marks info))
+         (push (cons 'read (gnus-info-read info)) nnml-marks)
+         (nnml-save-marks group server))))))
+
 (provide 'nnml)
 
 ;;; nnml.el ends here