Import No Gnus v0.4.
[elisp/gnus.git-] / lisp / nnvirtual.el
index a6e92f5..751e6be 100644 (file)
@@ -1,9 +1,11 @@
 ;;; nnvirtual.el --- virtual newsgroups access for Gnus
-;; Copyright (C) 1994,95,96,97,98 Free Software Foundation, Inc.
+
+;; Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+;;   2003, 2004, 2005, 2006 Free Software Foundation, Inc.
 
 ;; Author: David Moore <dmoore@ucsd.edu>
 ;;     Lars Magne Ingebrigtsen <larsi@gnus.org>
-;;     Masanobu UMEDA <umerin@flab.flab.fujitsu.junet>
+;;     Masanobu UMEDA <umerin@flab.flab.fujitsu.junet>
 ;; Keywords: news
 
 ;; This file is part of GNU Emacs.
@@ -20,8 +22,8 @@
 
 ;; You should have received a copy of the GNU General Public License
 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
-;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-;; Boston, MA 02111-1307, USA.
+;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+;; Boston, MA 02110-1301, USA.
 
 ;;; Commentary:
 
 (require 'gnus-start)
 (require 'gnus-sum)
 (require 'gnus-msg)
-(require 'cl)
+(eval-when-compile (require 'cl))
 
 (nnoo-declare nnvirtual)
 
 (defvoo nnvirtual-always-rescan t
-  "*If non-nil, always scan groups for unread articles when entering a group.
-If this variable is nil (which is the default) and you read articles
-in a component group after the virtual group has been activated, the
-read articles from the component group will show up when you enter the
-virtual group.")
+  "If non-nil, always scan groups for unread articles when entering a group.
+If this variable is nil and you read articles in a component group
+after the virtual group has been activated, the read articles from the
+component group will show up when you enter the virtual group.")
 
 (defvoo nnvirtual-component-regexp nil
-  "*Regexp to match component groups.")
+  "Regexp to match component groups.")
 
 (defvoo nnvirtual-component-groups nil
   "Component group in this nnvirtual group.")
@@ -63,8 +64,7 @@ virtual group.")
 (defvoo nnvirtual-current-group nil)
 
 (defvoo nnvirtual-mapping-table nil
-  "Table of rules on how to map between component group and article number
-to virtual article number.")
+  "Table of rules on how to map between component group and article number 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.")
@@ -122,47 +122,47 @@ to virtual article number.")
                       (let ((gnus-use-cache t))
                         (setq result (gnus-retrieve-headers
                                       articles cgroup nil))))
-           (set-buffer nntp-server-buffer)
-           ;; If we got HEAD headers, we convert them into NOV
-           ;; headers.  This is slow, inefficient and, come to think
-           ;; of it, downright evil.  So sue me.  I couldn't be
-           ;; bothered to write a header parse routine that could
-           ;; parse a mixed HEAD/NOV buffer.
-           (when (eq result 'headers)
-             (nnvirtual-convert-headers))
-           (goto-char (point-min))
-           (while (not (eobp))
-             (delete-region (point)
-                            (progn
-                              (setq carticle (read nntp-server-buffer))
-                              (point)))
-
-             ;; We remove this article from the articles list, if
-             ;; anything is left in the articles list after going through
-             ;; the entire buffer, then those articles have been
-             ;; expired or canceled, so we appropriately update the
-             ;; 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
-                 ;; was an extra article reference returned by nntp.
-                 (progn
-                   (beginning-of-line)
-                   (delete-region (point) (progn (forward-line 1) (point))))
-               ;; Otherwise insert the virtual article number,
-               ;; and clean up the xrefs.
-               (princ article nntp-server-buffer)
-               (nnvirtual-update-xref-header cgroup carticle
-                                             prefix system-name)
-               (forward-line 1))
-             )
-
-           (set-buffer vbuf)
-           (goto-char (point-max))
-           (insert-buffer-substring nntp-server-buffer))
+             (set-buffer nntp-server-buffer)
+             ;; If we got HEAD headers, we convert them into NOV
+             ;; headers.  This is slow, inefficient and, come to think
+             ;; of it, downright evil.  So sue me.  I couldn't be
+             ;; bothered to write a header parse routine that could
+             ;; parse a mixed HEAD/NOV buffer.
+             (when (eq result 'headers)
+               (nnvirtual-convert-headers))
+             (goto-char (point-min))
+             (while (not (eobp))
+               (delete-region (point)
+                              (progn
+                                (setq carticle (read nntp-server-buffer))
+                                (point)))
+
+               ;; We remove this article from the articles list, if
+               ;; anything is left in the articles list after going through
+               ;; the entire buffer, then those articles have been
+               ;; expired or canceled, so we appropriately update the
+               ;; 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
+                   ;; was an extra article reference returned by nntp.
+                   (progn
+                     (beginning-of-line)
+                     (delete-region (point) (progn (forward-line 1) (point))))
+                 ;; Otherwise insert the virtual article number,
+                 ;; and clean up the xrefs.
+                 (princ article nntp-server-buffer)
+                 (nnvirtual-update-xref-header cgroup carticle
+                                               prefix system-name)
+                 (forward-line 1))
+               )
+
+             (set-buffer vbuf)
+             (goto-char (point-max))
+             (insert-buffer-substring nntp-server-buffer))
            ;; Anything left in articles is expired or canceled.
            ;; Could be smart and not tell it about articles already known?
            (when articles
@@ -199,8 +199,9 @@ to virtual article number.")
          (save-excursion
            (when buffer
              (set-buffer buffer))
-           (let ((method (gnus-find-method-for-group
-                          nnvirtual-last-accessed-component-group)))
+           (let* ((gnus-override-method nil)
+                  (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.
@@ -219,7 +220,9 @@ to virtual article number.")
          (if buffer
              (save-excursion
                (set-buffer buffer)
-               (gnus-request-article-this-buffer (cdr amap) cgroup))
+               ;; We bind this here to avoid double decoding.
+               (let ((gnus-article-decode-hook nil))
+                 (gnus-request-article-this-buffer (cdr amap) cgroup)))
            (gnus-request-article (cdr amap) cgroup))))))))
 
 
@@ -283,11 +286,11 @@ to virtual article number.")
 
 (deffoo nnvirtual-request-update-mark (group article mark)
   (let* ((nart (nnvirtual-map-article article))
-        (cgroup (car nart))
-        ;; The component group might be a virtual group.
-        (nmark (gnus-request-update-mark cgroup (cdr nart) mark)))
+        (cgroup (car nart)))
     (when (and nart
-              (= mark nmark)
+              (memq mark gnus-auto-expirable-marks)
+              ;; The component group might be a virtual group.
+              (= mark (gnus-request-update-mark cgroup (cdr nart) mark))
               (gnus-group-auto-expirable-p cgroup))
       (setq mark gnus-expirable-mark)))
   mark)
@@ -336,9 +339,9 @@ to virtual article number.")
     (let ((gnus-group-marked (copy-sequence nnvirtual-component-groups))
          (gnus-expert-user t))
       ;; Make sure all groups are activated.
-      (mapcar
+      (mapc
        (lambda (g)
-        (when (not (numberp (car (gnus-gethash g gnus-newsrc-hashtb))))
+        (when (not (numberp (gnus-group-unread g)))
           (gnus-activate-group g)))
        nnvirtual-component-groups)
       (save-excursion
@@ -359,19 +362,33 @@ to virtual article number.")
                       (cdr gnus-message-group-art)))))
       (gnus-request-post (gnus-find-method-for-group group)))))
 
+
+(deffoo nnvirtual-request-expire-articles (articles group
+                                                   &optional server force)
+  (nnvirtual-possibly-change-server server)
+  (setq nnvirtual-component-groups
+       (delete (nnvirtual-current-group) nnvirtual-component-groups))
+  (let (unexpired)
+    (dolist (group nnvirtual-component-groups)
+      (setq unexpired (nconc unexpired
+                            (mapcar
+                             #'(lambda (article)
+                                 (nnvirtual-reverse-map-article
+                                  group article))
+                             (gnus-uncompress-range
+                              (gnus-group-expire-articles-1 group))))))
+    (sort (delq nil unexpired) '<)))
+
 \f
 ;;; Internal functions.
 
 (defun nnvirtual-convert-headers ()
   "Convert HEAD headers into NOV headers."
-  (save-excursion
-    (set-buffer nntp-server-buffer)
+  (with-current-buffer nntp-server-buffer
     (let* ((dependencies (make-vector 100 0))
-          (headers (gnus-get-newsgroup-headers dependencies))
-          header)
+          (headers (gnus-get-newsgroup-headers dependencies)))
       (erase-buffer)
-      (while (setq header (pop headers))
-       (nnheader-insert-nov header)))))
+      (mapc 'nnheader-insert-nov headers))))
 
 
 (defun nnvirtual-update-xref-header (group article prefix system-name)
@@ -381,11 +398,11 @@ to virtual article number.")
   (looking-at
    "[^\t]*\t[^\t]*\t[^\t]*\t[^\t]*\t[^\t]*\t[^\t]*\t[^\t]*\t")
   (goto-char (match-end 0))
-  (unless (search-forward "\t" (gnus-point-at-eol) 'move)
+  (unless (search-forward "\t" (point-at-eol) 'move)
     (insert "\t"))
 
   ;; Remove any spaces at the beginning of the Xref field.
-  (while (= (char-after (1- (point))) ? )
+  (while (eq (char-after (1- (point))) ? )
     (forward-char -1)
     (delete-char 1))
 
@@ -397,8 +414,8 @@ to virtual article number.")
   ;; component server prefix.
   (save-restriction
     (narrow-to-region (point)
-                     (or (search-forward "\t" (gnus-point-at-eol) t)
-                         (gnus-point-at-eol)))
+                     (or (search-forward "\t" (point-at-eol) t)
+                         (point-at-eol)))
     (goto-char (point-min))
     (when (re-search-forward "Xref: *[^\n:0-9 ]+ *" nil t)
       (replace-match "" t t))
@@ -407,7 +424,7 @@ to virtual article number.")
           (concat (regexp-quote (gnus-group-real-name group)) ":[0-9]+")
           nil t)
       (replace-match "" t t))
-    (unless (= (point) (point-max))
+    (unless (eobp)
       (insert " ")
       (when (not (string= "" prefix))
        (while (re-search-forward "[^ ]+:[0-9]+" nil t)
@@ -417,7 +434,7 @@ to virtual article number.")
 
   ;; Ensure a trailing \t.
   (end-of-line)
-  (or (= (char-after (1- (point))) ?\t)
+  (or (eq (char-after (1- (point))) ?\t)
       (insert ?\t)))
 
 
@@ -445,21 +462,21 @@ If UPDATE-P is not nil, call gnus-group-update-group on the components."
                                   (nnvirtual-partition-sequence (cdr ml)))))
                         (gnus-info-marks (gnus-get-info
                                           (nnvirtual-current-group))))))
-         mark type groups carticles info entry)
+         type groups info)
 
       ;; 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.
       (progn
        ;; move (un)read
-       (let ((gnus-newsgroup-active nil)) ;workaround guns-update-read-articles
-         (while (setq entry (pop unreads))
+       ;; bind for workaround guns-update-read-articles
+       (let ((gnus-newsgroup-active nil))
+         (dolist (entry 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)))
+       (dolist (group nnvirtual-component-groups)
+         (when (and (setq info (gnus-get-info group))
                     (gnus-info-marks info))
            (gnus-info-set-marks
             info
@@ -470,18 +487,17 @@ If UPDATE-P is not nil, call gnus-group-update-group on the components."
        ;; 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))
+       (dolist (mark type-marks)
          (setq type (car mark))
          (setq groups (cdr mark))
-         (while (setq carticles (pop groups))
+         (dolist (carticles 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))))))
+       (dolist (group nnvirtual-component-groups)
+         (gnus-group-update-group group t))))))
 
 
 (defun nnvirtual-current-group ()
@@ -501,14 +517,15 @@ If UPDATE-P is not nil, call gnus-group-update-group on the components."
 
 
 ;;; We map between virtual articles and real articles in a manner
-;;; which keeps the size of the virtual active list the same as
-;;; the sum of the component active lists.
-;;; To achieve fair mixing of the groups, the last article in
-;;; each of N component groups will be in the the last N articles
-;;; in the virtual group.
-
-;;; If you have 3 components A, B and C, with articles 1-8, 1-5, and 6-7
-;;; resprectively, then the virtual article numbers look like:
+;;; which keeps the size of the virtual active list the same as the
+;;; sum of the component active lists.
+
+;;; To achieve fair mixing of the groups, the last article in each of
+;;; N component groups will be in the last N articles in the virtual
+;;; group.
+
+;;; If you have 3 components A, B and C, with articles 1-8, 1-5, and
+;;; 6-7 respectively, then the virtual article numbers look like:
 ;;;
 ;;;  1  2  3  4  5  6  7  8  9  10 11 12 13 14 15
 ;;;  A1 A2 A3 A4 B1 A5 B2 A6 B3 A7 B4 C6 A8 B5 C7
@@ -579,7 +596,7 @@ If UPDATE-P is not nil, call gnus-group-update-group on the components."
               (aref entry 1)
               (cdr (aref nnvirtual-mapping-offsets group-pos)))
            ))
-      ))
+    ))
 
 
 
@@ -637,13 +654,12 @@ 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
+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))
-                          nnvirtual-component-groups))
+  (let ((carticles (mapcar 'list nnvirtual-component-groups))
        a i j article entry)
     (while (setq a (pop articles))
       (if (atom a)
@@ -656,8 +672,8 @@ the result."
          (setq entry (assoc (car article) carticles))
          (setcdr entry (cons (cdr article) (cdr entry))))
        (setq i (1+ i))))
-    (mapcar (lambda (x) (setcdr x (nreverse (cdr x))))
-           carticles)
+    (mapc (lambda (x) (setcdr x (nreverse (cdr x))))
+         carticles)
     carticles))
 
 
@@ -701,7 +717,7 @@ based on the marks on the component groups."
                    tot (+ tot size)
                    M (max M size))))
          nnvirtual-component-groups)
-
+    
     ;; Number of articles in the virtual group.
     (setq nnvirtual-mapping-len tot)
 
@@ -763,10 +779,9 @@ based on the marks on the component groups."
 
     ;; Remove any empty marks lists, and store.
     (setq nnvirtual-mapping-marks nil)
-    (while marks
-      (if (cdr (car marks))
-         (push (car marks) nnvirtual-mapping-marks))
-      (setq marks (cdr marks)))
+    (dolist (mark marks)
+      (when (cdr mark)
+       (push mark nnvirtual-mapping-marks)))
 
     ;; We need to convert the unreads to reads.  We compress the
     ;; sequence as we go, otherwise it could be huge.
@@ -797,4 +812,5 @@ based on the marks on the component groups."
 
 (provide 'nnvirtual)
 
+;;; arch-tag: ca8c8ad9-1bd8-4b0f-9722-90dc645a45f5
 ;;; nnvirtual.el ends here