*** empty log message ***
[gnus] / lisp / nnvirtual.el
index 5875426..eb41838 100644 (file)
@@ -1,5 +1,5 @@
 ;;; nnvirtual.el --- virtual newsgroups access for Gnus
-;; Copyright (C) 1994,95,96 Free Software Foundation, Inc.
+;; Copyright (C) 1994,95,96,97 Free Software Foundation, Inc.
 
 ;; Author: David Moore <dmoore@ucsd.edu>
 ;;     Lars Magne Ingebrigtsen <larsi@ifi.uio.no>
@@ -66,19 +66,19 @@ virtual group.")
 to virtual article number.")
 
 (defvoo nnvirtual-mapping-offsets nil
-  "Table indexed by component group to an offset to be applied to article
-numbers in that group.")
+  "Table indexed by component group to an offset to be applied to article numbers in that group.")
 
 (defvoo nnvirtual-mapping-len 0
   "Number of articles in this virtual group.")
 
 (defvoo nnvirtual-mapping-reads nil
-  "Compressed sequence of read articles on the virtual group as computed
-from the unread status of individual component groups.")
+  "Compressed sequence of read articles on the virtual group as computed from the unread status of individual component groups.")
 
 (defvoo nnvirtual-mapping-marks nil
-  "Compressed marks alist for the virtual group as computed from the
-marks of individual component groups.")
+  "Compressed marks alist for the virtual group as computed from the marks of individual component groups.")
+
+(defvoo nnvirtual-info-installed nil
+  "T if we have already installed the group info for this group, and shouldn't blast over it again.")
 
 (defvoo nnvirtual-status-string "")
 
@@ -100,7 +100,7 @@ marks of individual component groups.")
       (erase-buffer)
       (if (stringp (car articles))
          'headers
-       (let ((vbuf (nnheader-set-temp-buffer 
+       (let ((vbuf (nnheader-set-temp-buffer
                     (get-buffer-create " *virtual headers*")))
              (carticles (nnvirtual-partition-sequence articles))
              (system-name (system-name))
@@ -143,7 +143,7 @@ marks of individual component groups.")
              ;; component group below.  They should be coming up
              ;; generally in order, so this shouldn't be slow.
              (setq articles (delq carticle articles))
-             
+
              (setq article (nnvirtual-reverse-map-article cgroup carticle))
              (if (null article)
                  ;; This line has no reverse mapping, that means it
@@ -158,7 +158,7 @@ marks of individual component groups.")
                                              prefix system-name)
                (forward-line 1))
              )
-           
+
            (set-buffer vbuf)
            (goto-char (point-max))
            (insert-buffer-substring nntp-server-buffer))
@@ -184,26 +184,42 @@ marks of individual component groups.")
            (kill-buffer vbuf)))))))
 
 
+(defvoo nnvirtual-last-accessed-component-group nil)
 
 (deffoo nnvirtual-request-article (article &optional group server buffer)
-  (when (and (nnvirtual-possibly-change-server server)
-            (numberp article))
-    (let* ((amap (nnvirtual-map-article article))
-          (cgroup (car amap)))
-      (cond
-       ((not amap)
-       (nnheader-report 'nnvirtual "No such article: %s" article))
-       ((not (gnus-check-group cgroup))
-       (nnheader-report
-        'nnvirtual "Can't open server where %s exists" cgroup))
-       ((not (gnus-request-group cgroup t))
-       (nnheader-report 'nnvirtual "Can't open component group %s" cgroup))
-       (t
-       (if buffer 
-           (save-excursion
-             (set-buffer buffer)
-             (gnus-request-article-this-buffer (cdr amap) cgroup))
-         (gnus-request-article (cdr amap) cgroup)))))))
+  (when (nnvirtual-possibly-change-server server)
+    (if (stringp article)
+       ;; This is a fetch by Message-ID.
+       (cond
+        ((not nnvirtual-last-accessed-component-group)
+         (nnheader-report
+          'nnvirtual "Don't know what server to request from"))
+        (t
+         (save-excursion
+           (when buffer
+             (set-buffer buffer))
+           (let ((method (gnus-find-method-for-group
+                          nnvirtual-last-accessed-component-group)))
+             (funcall (gnus-get-function method 'request-article)
+                      article nil (nth 1 method) buffer)))))
+      ;; This is a fetch by number.
+      (let* ((amap (nnvirtual-map-article article))
+            (cgroup (car amap)))
+       (cond
+        ((not amap)
+         (nnheader-report 'nnvirtual "No such article: %s" article))
+        ((not (gnus-check-group cgroup))
+         (nnheader-report
+          'nnvirtual "Can't open server where %s exists" cgroup))
+        ((not (gnus-request-group cgroup t))
+         (nnheader-report 'nnvirtual "Can't open component group %s" cgroup))
+        (t
+         (setq nnvirtual-last-accessed-component-group cgroup)
+         (if buffer
+             (save-excursion
+               (set-buffer buffer)
+               (gnus-request-article-this-buffer (cdr amap) cgroup))
+           (gnus-request-article (cdr amap) cgroup))))))))
 
 
 (deffoo nnvirtual-open-server (server &optional defs)
@@ -217,7 +233,8 @@ marks of individual component groups.")
          nnvirtual-mapping-offsets nil
          nnvirtual-mapping-len 0
          nnvirtual-mapping-reads nil
-         nnvirtual-mapping-marks nil)
+         nnvirtual-mapping-marks nil
+         nnvirtual-info-installed nil)
     (when nnvirtual-component-regexp
       ;; Go through the newsrc alist and find all component groups.
       (let ((newsrc (cdr gnus-newsrc-alist))
@@ -245,7 +262,7 @@ marks of individual component groups.")
              nnvirtual-always-rescan)
       (nnvirtual-create-mapping))
     (setq nnvirtual-current-group group)
-    (nnheader-insert "211 %d 1 %d %s\n" 
+    (nnheader-insert "211 %d 1 %d %s\n"
                     nnvirtual-mapping-len nnvirtual-mapping-len group))))
 
 
@@ -267,15 +284,13 @@ marks of individual component groups.")
       (setq mark gnus-expirable-mark)))
   mark)
 
-    
+
 (deffoo nnvirtual-close-group (group &optional server)
   (when (and (nnvirtual-possibly-change-server server)
             (not (gnus-ephemeral-group-p (nnvirtual-current-group))))
-    ;; Copy (un)read status and marks back to component groups.
-    (nnvirtual-update-reads)
-    (nnvirtual-update-marked t))
+    (nnvirtual-update-read-and-marked t t))
   t)
-    
+
 
 (deffoo nnvirtual-request-list (&optional server)
   (nnheader-report 'nnvirtual "LIST is not implemented."))
@@ -290,21 +305,25 @@ marks of individual component groups.")
 
 
 (deffoo nnvirtual-request-update-info (group info &optional server)
-  (when (nnvirtual-possibly-change-server server)
-    ;; Install the lists.
-    (setcar (cddr info) nnvirtual-mapping-reads)
-    (if (nthcdr 3 info)
-       (setcar (nthcdr 3 info) nnvirtual-mapping-marks)
-      (when nnvirtual-mapping-marks
-       (setcdr (nthcdr 2 info) (list nnvirtual-mapping-marks))))
+  (when (and (nnvirtual-possibly-change-server server)
+            (not nnvirtual-info-installed))
+    ;; Install the precomputed lists atomically, so the virtual group
+    ;; is not left in a half-way state in case of C-g.
+    (gnus-atomic-progn
+      (setcar (cddr info) nnvirtual-mapping-reads)
+      (if (nthcdr 3 info)
+         (setcar (nthcdr 3 info) nnvirtual-mapping-marks)
+       (when nnvirtual-mapping-marks
+         (setcdr (nthcdr 2 info) (list nnvirtual-mapping-marks))))
+      (setq nnvirtual-info-installed t))
     t))
-      
+
 
 (deffoo nnvirtual-catchup-group (group &optional server all)
   (when (and (nnvirtual-possibly-change-server server)
             (not (gnus-ephemeral-group-p (nnvirtual-current-group))))
     ;; copy over existing marks first, in case they set anything
-    (nnvirtual-update-marked nil)
+    (nnvirtual-update-read-and-marked nil nil)
     ;; do a catchup on all component groups
     (let ((gnus-group-marked (copy-sequence nnvirtual-component-groups))
          (gnus-expert-user t))
@@ -339,8 +358,7 @@ marks of individual component groups.")
 
 
 (defun nnvirtual-update-xref-header (group article prefix system-name)
-  "Edit current NOV header in current buffer to have an xref to the
-component group, and also server prefix any existing xref lines."
+  "Edit current NOV header in current buffer to have an xref to the component group, and also server prefix any existing xref lines."
   ;; Move to beginning of Xref field, creating a slot if needed.
   (beginning-of-line)
   (looking-at
@@ -385,49 +403,53 @@ component group, and also server prefix any existing xref lines."
       (nnvirtual-open-server server)))
 
 
-(defun nnvirtual-update-reads ()
-  "Copy (un)read status from the virtual group to the component groups."
-  (let ((unreads (nnvirtual-partition-sequence (gnus-list-of-unread-articles
-                                               (nnvirtual-current-group))))
-       entry)
-    (while (setq entry (pop unreads))
-      (gnus-update-read-articles (car entry) (cdr entry)))))
-
-
-(defun nnvirtual-update-marked (update-p)
+(defun nnvirtual-update-read-and-marked (read-p update-p)
   "Copy marks from the virtual group to the component groups.
+If READ-P is not nil, update the (un)read status of the components.
 If UPDATE-P is not nil, call gnus-group-update-group on the components."
-  (let ((type-marks (mapcar (lambda (ml)
-                             (cons (car ml)
-                                   (nnvirtual-partition-sequence (cdr ml))))
-                           (gnus-info-marks (gnus-get-info
-                                             (nnvirtual-current-group)))))
-       mark type groups carticles info)
-
-    ;; clear all existing marks on the component groups, since
-    ;; we install new versions below.
-    (setq groups nnvirtual-component-groups)
-    (while groups
-      (when (and (setq info (gnus-get-info (pop groups)))
-                (gnus-info-marks info))
-       (gnus-info-set-marks info nil)))
-
-    ;; Ok, currently type-marks is an assq list with keys of a mark type,
-    ;; with data of an assq list with keys of component group names
-    ;; and the articles which correspond to that key/group pair.
-    (while (setq mark (pop type-marks))
-      (setq type (car mark))
-      (setq groups (cdr mark))
-      (while (setq carticles (pop groups))
-       (gnus-add-marked-articles (car carticles) type (cdr carticles) 
-                                 nil t)))
-      
-    ;; possibly update the display, it is really slow
-    (when update-p
-      (setq groups nnvirtual-component-groups)
-      (while groups
-       (gnus-group-update-group (pop groups) t)))
-    ))
+  (when nnvirtual-current-group
+    (let ((unreads (and read-p
+                       (nnvirtual-partition-sequence
+                        (gnus-list-of-unread-articles
+                         (nnvirtual-current-group)))))
+         (type-marks (mapcar (lambda (ml)
+                               (cons (car ml)
+                                     (nnvirtual-partition-sequence (cdr ml))))
+                             (gnus-info-marks (gnus-get-info
+                                               (nnvirtual-current-group)))))
+         mark type groups carticles info entry)
+
+      ;; Ok, atomically move all of the (un)read info, clear any old
+      ;; marks, and move all of the current marks.  This way if someone
+      ;; hits C-g, you won't leave the component groups in a half-way state.
+      (gnus-atomic-progn
+       ;; move (un)read
+       (let ((gnus-newsgroup-active nil)) ;workaround guns-update-read-articles
+         (while (setq entry (pop unreads))
+           (gnus-update-read-articles (car entry) (cdr entry))))
+
+       ;; clear all existing marks on the component groups
+       (setq groups nnvirtual-component-groups)
+       (while groups
+         (when (and (setq info (gnus-get-info (pop groups)))
+                    (gnus-info-marks info))
+           (gnus-info-set-marks info nil)))
+
+       ;; Ok, currently type-marks is an assq list with keys of a mark type,
+       ;; with data of an assq list with keys of component group names
+       ;; and the articles which correspond to that key/group pair.
+       (while (setq mark (pop type-marks))
+         (setq type (car mark))
+         (setq groups (cdr mark))
+         (while (setq carticles (pop groups))
+           (gnus-add-marked-articles (car carticles) type (cdr carticles)
+                                     nil t))))
+
+      ;; possibly update the display, it is really slow
+      (when update-p
+       (setq groups nnvirtual-component-groups)
+       (while groups
+         (gnus-group-update-group (pop groups) t))))))
 
 
 (defun nnvirtual-current-group ()
@@ -512,8 +534,7 @@ If UPDATE-P is not nil, call gnus-group-update-group on the components."
 ;;; unique reverse mapping.
 
 (defun nnvirtual-map-article (article)
-  "Return a cons of the component group and article corresponding to
-the given virtual ARTICLE."
+  "Return a cons of the component group and article corresponding to the given virtual ARTICLE."
   (let ((table nnvirtual-mapping-table)
        entry group-pos)
     (while (and table
@@ -534,8 +555,7 @@ the given virtual ARTICLE."
 
 
 (defun nnvirtual-reverse-map-article (group article)
-  "Return the virtual article number corresponding to the given
-component GROUP and ARTICLE."
+  "Return the virtual article number corresponding to the given component GROUP and ARTICLE."
   (let ((table nnvirtual-mapping-table)
        (group-pos 0)
        entry)
@@ -584,11 +604,12 @@ then it is left out of the result."
 
 
 (defun nnvirtual-partition-sequence (articles)
-  "Return an association list of component article numbers, indexed
-by elements of nnvirtual-component-groups, based on the sequence
-ARTICLES of virtual article numbers.  ARTICLES should be sorted,
-and can be a compressed sequence. If any of the article numbers has
-no corresponding component article, then it is left out of the result."
+  "Return an association list of component article numbers.
+These are indexed by elements of nnvirtual-component-groups, based on
+the sequence ARTICLES of virtual article numbers.  ARTICLES should be
+sorted, and can be a compressed sequence. If any of the article
+numbers has no corresponding component article, then it is left out of
+the result."
   (when (numberp (cdr-safe articles))
     (setq articles (list articles)))
   (let ((carticles (mapcar (lambda (g) (list g))
@@ -611,9 +632,9 @@ no corresponding component article, then it is left out of the result."
 
 
 (defun nnvirtual-create-mapping ()
-  "Build the tables necessary to map between component (group, article)
-to virtual article.  Generate the set of read messages and marks for
-the virtual group based on the marks on the component groups."
+  "Build the tables necessary to map between component (group, article) to virtual article.
+Generate the set of read messages and marks for the virtual group
+based on the marks on the component groups."
   (let ((cnt 0)
        (tot 0)
        (M 0)
@@ -632,7 +653,7 @@ the virtual group based on the marks on the component groups."
            (setq active (gnus-activate-group g)
                  min (car active)
                  max (cdr active))
-           (when (and active (>= max min))
+           (when (and active (>= max min) (not (zerop max)))
              ;; store active information
              (push (list g (- max min -1) max) actives)
              ;; collect unread/mark info for later
@@ -657,7 +678,7 @@ the virtual group based on the marks on the component groups."
 
     ;; We want the actives list sorted by size, to build the tables.
     (setq actives (sort actives (lambda (g1 g2) (< (nth 1 g1) (nth 1 g2)))))
-    
+
     ;; Build the offset table.  Largest sized groups are at the front.
     (setq nnvirtual-mapping-offsets
          (vconcat
@@ -666,7 +687,7 @@ the virtual group based on the marks on the component groups."
                      (cons (nth 0 entry)
                            (- (nth 2 entry) M)))
                    actives))))
-    
+
     ;; Build the mapping table.
     (setq nnvirtual-mapping-table nil)
     (setq actives (mapcar (lambda (entry) (nth 1 entry)) actives))
@@ -735,6 +756,9 @@ the virtual group based on the marks on the component groups."
 
     ;; Store the reads list for later use.
     (setq nnvirtual-mapping-reads (nreverse reads))
+
+    ;; Throw flag to show we changed the info.
+    (setq nnvirtual-info-installed nil)
     ))
 
 (provide 'nnvirtual)