;;; 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>
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 "")
(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))
;; 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
prefix system-name)
(forward-line 1))
)
-
+
(set-buffer vbuf)
(goto-char (point-max))
(insert-buffer-substring nntp-server-buffer))
(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)
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))
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))))
(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."))
(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))
(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
(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 ()
;;; 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
(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)
(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))
(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)
(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
;; 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
(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))
;; 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)