Mercurial > emacs
comparison lisp/nnvirtual.el @ 15511:530d0d516a42
New version.
author | Lars Magne Ingebrigtsen <larsi@gnus.org> |
---|---|
date | Tue, 25 Jun 1996 22:21:39 +0000 |
parents | 83f275dcd93a |
children |
comparison
equal
deleted
inserted
replaced
15510:db0f45c7f885 | 15511:530d0d516a42 |
---|---|
1 ;;; nnvirtual.el --- virtual newsgroups access for Gnus | 1 ;;; nnvirtual.el --- virtual newsgroups access for Gnus |
2 | 2 ;; Copyright (C) 1994,95,96 Free Software Foundation, Inc. |
3 ;; Copyright (C) 1994,95 Free Software Foundation, Inc. | |
4 | 3 |
5 ;; Author: Lars Magne Ingebrigtsen <larsi@ifi.uio.no> | 4 ;; Author: Lars Magne Ingebrigtsen <larsi@ifi.uio.no> |
6 ;; Masanobu UMEDA <umerin@flab.flab.fujitsu.junet> | 5 ;; Masanobu UMEDA <umerin@flab.flab.fujitsu.junet> |
7 ;; Keywords: news | 6 ;; Keywords: news |
8 | 7 |
32 ;;; Code: | 31 ;;; Code: |
33 | 32 |
34 (require 'nntp) | 33 (require 'nntp) |
35 (require 'nnheader) | 34 (require 'nnheader) |
36 (require 'gnus) | 35 (require 'gnus) |
36 (require 'nnoo) | |
37 (eval-when-compile (require 'cl)) | |
38 | |
39 (nnoo-declare nnvirtual) | |
40 | |
41 (defvoo nnvirtual-always-rescan nil | |
42 "*If non-nil, always scan groups for unread articles when entering a group. | |
43 If this variable is nil (which is the default) and you read articles | |
44 in a component group after the virtual group has been activated, the | |
45 read articles from the component group will show up when you enter the | |
46 virtual group.") | |
47 | |
48 (defvoo nnvirtual-component-regexp nil | |
49 "*Regexp to match component groups.") | |
37 | 50 |
38 | 51 |
39 | 52 |
40 (defconst nnvirtual-version "nnvirtual 1.0" | 53 (defconst nnvirtual-version "nnvirtual 1.0") |
41 "Version number of this version of nnvirtual.") | 54 |
42 | 55 (defvoo nnvirtual-current-group nil) |
43 (defvar nnvirtual-group-alist nil) | 56 (defvoo nnvirtual-component-groups nil) |
44 (defvar nnvirtual-current-group nil) | 57 (defvoo nnvirtual-mapping nil) |
45 (defvar nnvirtual-current-groups nil) | 58 |
46 (defvar nnvirtual-current-mapping nil) | 59 (defvoo nnvirtual-status-string "") |
47 | 60 |
48 (defvar nnvirtual-do-not-open nil) | 61 (eval-and-compile |
49 | 62 (autoload 'gnus-cache-articles-in-group "gnus-cache")) |
50 (defvar nnvirtual-status-string "") | |
51 | 63 |
52 | 64 |
53 | 65 |
54 ;;; Interface functions. | 66 ;;; Interface functions. |
55 | 67 |
56 (defun nnvirtual-retrieve-headers (sequence &optional newsgroup server) | 68 (nnoo-define-basics nnvirtual) |
57 "Retrieve the headers for the articles in SEQUENCE." | 69 |
58 (nnvirtual-possibly-change-newsgroups newsgroup server t) | 70 (deffoo nnvirtual-retrieve-headers (articles &optional newsgroup |
59 (save-excursion | 71 server fetch-old) |
60 (set-buffer (get-buffer-create "*virtual headers*")) | 72 (when (nnvirtual-possibly-change-server server) |
61 (buffer-disable-undo (current-buffer)) | |
62 (erase-buffer) | |
63 (if (stringp (car sequence)) | |
64 'headers | |
65 (let ((map nnvirtual-current-mapping) | |
66 (offset 0) | |
67 articles beg group active top article result prefix | |
68 fetched-articles group-method) | |
69 (while sequence | |
70 (while (< (car (car map)) (car sequence)) | |
71 (setq offset (car (car map))) | |
72 (setq map (cdr map))) | |
73 (setq top (car (car map))) | |
74 (setq group (nth 1 (car map))) | |
75 (setq prefix (gnus-group-real-prefix group)) | |
76 (setq active (nth 2 (car map))) | |
77 (setq articles nil) | |
78 (while (and sequence (<= (car sequence) top)) | |
79 (setq articles (cons (- (+ active (car sequence)) offset) | |
80 articles)) | |
81 (setq sequence (cdr sequence))) | |
82 (setq articles (nreverse articles)) | |
83 (if (and articles | |
84 (setq result | |
85 (progn | |
86 (setq group-method | |
87 (gnus-find-method-for-group group)) | |
88 (and (or (gnus-server-opened group-method) | |
89 (gnus-open-server group-method)) | |
90 (gnus-request-group group t) | |
91 (gnus-retrieve-headers articles group))))) | |
92 (save-excursion | |
93 (set-buffer nntp-server-buffer) | |
94 ;; If we got HEAD headers, we convert them into NOV | |
95 ;; headers. This is slow, inefficient and, come to think | |
96 ;; of it, downright evil. So sue me. I couldn't be | |
97 ;; bothered to write a header parse routine that could | |
98 ;; parse a mixed HEAD/NOV buffer. | |
99 (and (eq result 'headers) (nnvirtual-convert-headers)) | |
100 (goto-char (point-min)) | |
101 (setq fetched-articles nil) | |
102 (while (not (eobp)) | |
103 (setq beg (point) | |
104 article (read nntp-server-buffer) | |
105 fetched-articles (cons article fetched-articles)) | |
106 (delete-region beg (point)) | |
107 (insert (int-to-string (+ (- article active) offset))) | |
108 (beginning-of-line) | |
109 (looking-at | |
110 "[^\t]*\t[^\t]*\t[^\t]*\t[^\t]*\t[^\t]*\t[^\t]*\t[^\t]*\t") | |
111 (goto-char (match-end 0)) | |
112 (or (search-forward | |
113 "\t" (save-excursion (end-of-line) (point)) t) | |
114 (end-of-line)) | |
115 (while (= (char-after (1- (point))) ? ) | |
116 (forward-char -1) | |
117 (delete-char 1)) | |
118 (if (eolp) | |
119 (progn | |
120 (end-of-line) | |
121 (or (= (char-after (1- (point))) ?\t) | |
122 (insert ?\t)) | |
123 (insert (format "Xref: %s %s:%d\t" (system-name) | |
124 group article))) | |
125 (if (not (string= "" prefix)) | |
126 (while (re-search-forward | |
127 "[^ ]+:[0-9]+" | |
128 (save-excursion (end-of-line) (point)) t) | |
129 (save-excursion | |
130 (goto-char (match-beginning 0)) | |
131 (insert prefix)))) | |
132 (end-of-line) | |
133 (or (= (char-after (1- (point))) ?\t) | |
134 (insert ?\t))) | |
135 (forward-line 1)))) | |
136 (goto-char (point-max)) | |
137 (insert-buffer-substring nntp-server-buffer) | |
138 ;; We have now massaged and inserted the headers from one | |
139 ;; group. In case some of the articles have expired or been | |
140 ;; cancelled, we have to mark them as read in the component | |
141 ;; group. | |
142 (let ((unfetched (gnus-sorted-complement | |
143 articles (nreverse fetched-articles)))) | |
144 (and unfetched | |
145 (gnus-group-make-articles-read group unfetched nil)))) | |
146 ;; The headers are ready for reading, so they are inserted into | |
147 ;; the nntp-server-buffer, which is where Gnus expects to find | |
148 ;; them. | |
149 (prog1 | |
150 (save-excursion | |
151 (set-buffer nntp-server-buffer) | |
152 (erase-buffer) | |
153 (insert-buffer-substring "*virtual headers*") | |
154 'nov) | |
155 (kill-buffer (current-buffer))))))) | |
156 | |
157 (defun nnvirtual-open-server (newsgroups &optional something) | |
158 "Open a virtual newsgroup that contains NEWSGROUPS." | |
159 (nnheader-init-server-buffer)) | |
160 | |
161 (defun nnvirtual-close-server (&rest dum) | |
162 "Close news server." | |
163 t) | |
164 | |
165 (defun nnvirtual-request-close () | |
166 (setq nnvirtual-current-group nil | |
167 nnvirtual-current-groups nil | |
168 nnvirtual-current-mapping nil | |
169 nnvirtual-group-alist nil) | |
170 t) | |
171 | |
172 (defun nnvirtual-server-opened (&optional server) | |
173 "Return server process status, T or NIL. | |
174 If the stream is opened, return T, otherwise return NIL." | |
175 (and nntp-server-buffer | |
176 (get-buffer nntp-server-buffer))) | |
177 | |
178 (defun nnvirtual-status-message (&optional server) | |
179 "Return server status response as string." | |
180 nnvirtual-status-string) | |
181 | |
182 (defun nnvirtual-request-article (article &optional newsgroup server buffer) | |
183 "Select article by message number." | |
184 (nnvirtual-possibly-change-newsgroups newsgroup server t) | |
185 (and (numberp article) | |
186 (let ((map nnvirtual-current-mapping) | |
187 (offset 0) | |
188 group-method) | |
189 (while (< (car (car map)) article) | |
190 (setq offset (car (car map))) | |
191 (setq map (cdr map))) | |
192 (setq group-method (gnus-find-method-for-group (nth 1 (car map)))) | |
193 (or (gnus-server-opened group-method) | |
194 (gnus-open-server group-method)) | |
195 (gnus-request-group (nth 1 (car map)) t) | |
196 (gnus-request-article (- (+ (nth 2 (car map)) article) offset) | |
197 (nth 1 (car map)) buffer)))) | |
198 | |
199 (defun nnvirtual-request-group (group &optional server dont-check) | |
200 "Make GROUP the current newsgroup." | |
201 (nnvirtual-possibly-change-newsgroups group server dont-check) | |
202 (let ((map nnvirtual-current-mapping)) | |
203 (save-excursion | 73 (save-excursion |
204 (set-buffer nntp-server-buffer) | 74 (set-buffer nntp-server-buffer) |
205 (erase-buffer) | 75 (erase-buffer) |
206 (if map | 76 (if (stringp (car articles)) |
207 (progn | 77 'headers |
208 (while (cdr map) | 78 (let ((vbuf (nnheader-set-temp-buffer |
209 (setq map (cdr map))) | 79 (get-buffer-create " *virtual headers*"))) |
210 (insert (format "211 %d 1 %d %s\n" (car (car map)) | 80 (unfetched (mapcar (lambda (g) (list g)) |
211 (car (car map)) group)) | 81 nnvirtual-component-groups)) |
212 t) | 82 (system-name (system-name)) |
213 (setq nnvirtual-status-string "No component groups") | 83 cgroup article result prefix) |
214 (setq nnvirtual-current-group nil) | 84 (while articles |
215 nil)))) | 85 (setq article (assq (pop articles) nnvirtual-mapping)) |
86 (when (and (setq cgroup (cadr article)) | |
87 (gnus-check-server | |
88 (gnus-find-method-for-group cgroup) t) | |
89 (gnus-request-group cgroup t)) | |
90 (setq prefix (gnus-group-real-prefix cgroup)) | |
91 (when (setq result (gnus-retrieve-headers | |
92 (list (caddr article)) cgroup nil)) | |
93 (set-buffer nntp-server-buffer) | |
94 (if (zerop (buffer-size)) | |
95 (nconc (assq cgroup unfetched) (list (caddr article))) | |
96 ;; If we got HEAD headers, we convert them into NOV | |
97 ;; headers. This is slow, inefficient and, come to think | |
98 ;; of it, downright evil. So sue me. I couldn't be | |
99 ;; bothered to write a header parse routine that could | |
100 ;; parse a mixed HEAD/NOV buffer. | |
101 (when (eq result 'headers) | |
102 (nnvirtual-convert-headers)) | |
103 (goto-char (point-min)) | |
104 (while (not (eobp)) | |
105 (delete-region | |
106 (point) (progn (read nntp-server-buffer) (point))) | |
107 (princ (car article) (current-buffer)) | |
108 (beginning-of-line) | |
109 (looking-at | |
110 "[^\t]*\t[^\t]*\t[^\t]*\t[^\t]*\t[^\t]*\t[^\t]*\t[^\t]*\t") | |
111 (goto-char (match-end 0)) | |
112 (or (search-forward | |
113 "\t" (save-excursion (end-of-line) (point)) t) | |
114 (end-of-line)) | |
115 (while (= (char-after (1- (point))) ? ) | |
116 (forward-char -1) | |
117 (delete-char 1)) | |
118 (if (eolp) | |
119 (progn | |
120 (end-of-line) | |
121 (or (= (char-after (1- (point))) ?\t) | |
122 (insert ?\t)) | |
123 (insert "Xref: " system-name " " cgroup ":") | |
124 (princ (caddr article) (current-buffer)) | |
125 (insert "\t")) | |
126 (insert "Xref: " system-name " " cgroup ":") | |
127 (princ (caddr article) (current-buffer)) | |
128 (insert " ") | |
129 (if (not (string= "" prefix)) | |
130 (while (re-search-forward | |
131 "[^ ]+:[0-9]+" | |
132 (save-excursion (end-of-line) (point)) t) | |
133 (save-excursion | |
134 (goto-char (match-beginning 0)) | |
135 (insert prefix)))) | |
136 (end-of-line) | |
137 (or (= (char-after (1- (point))) ?\t) | |
138 (insert ?\t))) | |
139 (forward-line 1)) | |
140 (set-buffer vbuf) | |
141 (goto-char (point-max)) | |
142 (insert-buffer-substring nntp-server-buffer))))) | |
143 | |
144 ;; In case some of the articles have expired or been | |
145 ;; cancelled, we have to mark them as read in the | |
146 ;; component group. | |
147 (while unfetched | |
148 (when (cdar unfetched) | |
149 (gnus-group-make-articles-read | |
150 (caar unfetched) (sort (cdar unfetched) '<))) | |
151 (setq unfetched (cdr unfetched))) | |
152 | |
153 ;; The headers are ready for reading, so they are inserted into | |
154 ;; the nntp-server-buffer, which is where Gnus expects to find | |
155 ;; them. | |
156 (prog1 | |
157 (save-excursion | |
158 (set-buffer nntp-server-buffer) | |
159 (erase-buffer) | |
160 (insert-buffer-substring vbuf) | |
161 'nov) | |
162 (kill-buffer vbuf))))))) | |
163 | |
164 (deffoo nnvirtual-request-article (article &optional group server buffer) | |
165 (when (and (nnvirtual-possibly-change-server server) | |
166 (numberp article)) | |
167 (let* ((amap (assq article nnvirtual-mapping)) | |
168 (cgroup (cadr amap))) | |
169 (cond | |
170 ((not amap) | |
171 (nnheader-report 'nnvirtual "No such article: %s" article)) | |
172 ((not (gnus-check-group cgroup)) | |
173 (nnheader-report | |
174 'nnvirtual "Can't open server where %s exists" cgroup)) | |
175 ((not (gnus-request-group cgroup t)) | |
176 (nnheader-report 'nnvirtual "Can't open component group %s" cgroup)) | |
177 (t | |
178 (if buffer | |
179 (save-excursion | |
180 (set-buffer buffer) | |
181 (gnus-request-article-this-buffer (caddr amap) cgroup)) | |
182 (gnus-request-article (caddr amap) cgroup))))))) | |
183 | |
184 (deffoo nnvirtual-open-server (server &optional defs) | |
185 (unless (assq 'nnvirtual-component-regexp defs) | |
186 (push `(nnvirtual-component-regexp ,server) | |
187 defs)) | |
188 (nnoo-change-server 'nnvirtual server defs) | |
189 (if nnvirtual-component-groups | |
190 t | |
191 (setq nnvirtual-mapping nil) | |
192 ;; Go through the newsrc alist and find all component groups. | |
193 (let ((newsrc (cdr gnus-newsrc-alist)) | |
194 group) | |
195 (while (setq group (car (pop newsrc))) | |
196 (when (string-match nnvirtual-component-regexp group) ; Match | |
197 ;; Add this group to the list of component groups. | |
198 (setq nnvirtual-component-groups | |
199 (cons group (delete group nnvirtual-component-groups)))))) | |
200 (if (not nnvirtual-component-groups) | |
201 (nnheader-report 'nnvirtual "No component groups: %s" server) | |
202 t))) | |
203 | |
204 (deffoo nnvirtual-request-group (group &optional server dont-check) | |
205 (nnvirtual-possibly-change-server server) | |
206 (setq nnvirtual-component-groups | |
207 (delete (nnvirtual-current-group) nnvirtual-component-groups)) | |
208 (cond | |
209 ((null nnvirtual-component-groups) | |
210 (setq nnvirtual-current-group nil) | |
211 (nnheader-report 'nnvirtual "No component groups in %s" group)) | |
212 (t | |
213 (unless dont-check | |
214 (nnvirtual-create-mapping)) | |
215 (setq nnvirtual-current-group group) | |
216 (let ((len (length nnvirtual-mapping))) | |
217 (nnheader-insert "211 %d 1 %d %s\n" len len group))))) | |
218 | |
219 (deffoo nnvirtual-request-type (group &optional article) | |
220 (if (not article) | |
221 'unknown | |
222 (let ((mart (assq article nnvirtual-mapping))) | |
223 (when mart | |
224 (gnus-request-type (cadr mart) (car mart)))))) | |
225 | |
226 (deffoo nnvirtual-request-update-mark (group article mark) | |
227 (let* ((nart (assq article nnvirtual-mapping)) | |
228 (cgroup (cadr nart)) | |
229 ;; The component group might be a virtual group. | |
230 (nmark (gnus-request-update-mark cgroup (caddr nart) mark))) | |
231 (when (and nart | |
232 (= mark nmark) | |
233 (gnus-group-auto-expirable-p cgroup)) | |
234 (setq mark gnus-expirable-mark))) | |
235 mark) | |
216 | 236 |
217 (defun nnvirtual-close-group (group &optional server) | 237 (deffoo nnvirtual-close-group (group &optional server) |
218 (if (not nnvirtual-current-group) | 238 (when (nnvirtual-possibly-change-server server) |
219 () | 239 ;; Copy (un)read articles. |
220 (nnvirtual-possibly-change-newsgroups group server t) | 240 (nnvirtual-update-reads) |
221 (nnvirtual-update-marked) | 241 ;; We copy the marks from this group to the component |
222 (setq nnvirtual-current-group nil | 242 ;; groups here. |
223 nnvirtual-current-groups nil | 243 (nnvirtual-update-marked)) |
224 nnvirtual-current-mapping nil) | 244 t) |
225 (setq nnvirtual-group-alist | 245 |
226 (delq (assoc group nnvirtual-group-alist) nnvirtual-group-alist)))) | 246 (deffoo nnvirtual-request-list (&optional server) |
227 | 247 (nnheader-report 'nnvirtual "LIST is not implemented.")) |
228 (defun nnvirtual-request-list (&optional server) | 248 |
229 (setq nnvirtual-status-string "nnvirtual: LIST is not implemented.") | 249 (deffoo nnvirtual-request-newgroups (date &optional server) |
230 nil) | 250 (nnheader-report 'nnvirtual "NEWGROUPS is not supported.")) |
231 | 251 |
232 (defun nnvirtual-request-newgroups (date &optional server) | 252 (deffoo nnvirtual-request-list-newsgroups (&optional server) |
233 "List new groups." | 253 (nnheader-report 'nnvirtual "LIST NEWSGROUPS is not implemented.")) |
234 (setq nnvirtual-status-string "NEWGROUPS is not supported.") | 254 |
235 nil) | 255 (deffoo nnvirtual-request-update-info (group info &optional server) |
236 | 256 (when (nnvirtual-possibly-change-server server) |
237 (defun nnvirtual-request-list-newsgroups (&optional server) | 257 (let ((map nnvirtual-mapping) |
238 (setq nnvirtual-status-string | 258 (marks (mapcar (lambda (m) (list (cdr m))) gnus-article-mark-lists)) |
239 "nnvirtual: LIST NEWSGROUPS is not implemented.") | 259 reads mr m op) |
240 nil) | 260 ;; Go through the mapping. |
241 | |
242 (defalias 'nnvirtual-request-post 'nntp-request-post) | |
243 | |
244 (defun nnvirtual-request-post-buffer | |
245 (post group subject header article-buffer info follow-to respect-poster) | |
246 (nntp-request-post-buffer post "" subject header article-buffer | |
247 info follow-to respect-poster)) | |
248 | |
249 | |
250 ;;; Internal functions. | |
251 | |
252 ;; Convert HEAD headers into NOV headers. | |
253 (defun nnvirtual-convert-headers () | |
254 (save-excursion | |
255 (set-buffer nntp-server-buffer) | |
256 (let* ((gnus-newsgroup-dependencies (make-vector 100 0)) | |
257 (headers (gnus-get-newsgroup-headers)) | |
258 header) | |
259 (erase-buffer) | |
260 (while headers | |
261 (setq header (car headers) | |
262 headers (cdr headers)) | |
263 (insert (int-to-string (mail-header-number header)) "\t" | |
264 (or (mail-header-subject header) "") "\t" | |
265 (or (mail-header-from header) "") "\t" | |
266 (or (mail-header-date header) "") "\t" | |
267 (or (mail-header-id header) "") "\t" | |
268 (or (mail-header-references header) "") "\t" | |
269 (int-to-string (or (mail-header-chars header) 0)) "\t" | |
270 (int-to-string (or (mail-header-lines header) 0)) "\t" | |
271 (if (mail-header-xref header) | |
272 (concat "Xref: " (mail-header-xref header) "\t") | |
273 "") "\n"))))) | |
274 | |
275 (defun nnvirtual-possibly-change-newsgroups (group regexp &optional check) | |
276 (let ((inf t)) | |
277 (or (not group) | |
278 (and nnvirtual-current-group | |
279 (string= group nnvirtual-current-group)) | |
280 (and (setq inf (assoc group nnvirtual-group-alist)) | |
281 (string= (nth 3 inf) regexp) | |
282 (progn | |
283 (setq nnvirtual-current-group (car inf)) | |
284 (setq nnvirtual-current-groups (nth 1 inf)) | |
285 (setq nnvirtual-current-mapping (nth 2 inf))))) | |
286 (if (or (not check) (not inf)) | |
287 (progn | |
288 (and inf (setq nnvirtual-group-alist | |
289 (delq inf nnvirtual-group-alist))) | |
290 (setq nnvirtual-current-mapping nil) | |
291 (setq nnvirtual-current-group group) | |
292 (let ((newsrc gnus-newsrc-alist) | |
293 (virt-group (gnus-group-prefixed-name | |
294 nnvirtual-current-group '(nnvirtual "")))) | |
295 (setq nnvirtual-current-groups nil) | |
296 (while newsrc | |
297 (and (string-match regexp (car (car newsrc))) | |
298 (not (string= (car (car newsrc)) virt-group)) | |
299 (setq nnvirtual-current-groups | |
300 (cons (car (car newsrc)) nnvirtual-current-groups))) | |
301 (setq newsrc (cdr newsrc)))) | |
302 (if nnvirtual-current-groups | |
303 (progn | |
304 (nnvirtual-create-mapping group) | |
305 (setq nnvirtual-group-alist | |
306 (cons (list group nnvirtual-current-groups | |
307 nnvirtual-current-mapping regexp) | |
308 nnvirtual-group-alist))) | |
309 (setq nnvirtual-status-string | |
310 (format | |
311 "nnvirtual: No newsgroups for this virtual newsgroup")))))) | |
312 nnvirtual-current-groups) | |
313 | |
314 (defun nnvirtual-create-mapping (group) | |
315 (let* ((group (gnus-group-prefixed-name group (list 'nnvirtual ""))) | |
316 (info (nth 2 (gnus-gethash group gnus-newsrc-hashtb))) | |
317 (groups nnvirtual-current-groups) | |
318 (offset 0) | |
319 reads unread igroup itotal ireads) | |
320 ;; The virtual group doesn't exist. (?) | |
321 (or info (error "No such group: %s" group)) | |
322 (setq nnvirtual-current-mapping nil) | |
323 (while groups | |
324 ;; Added by Sudish Joseph <joseph@cis.ohio-state.edu>. | |
325 (setq igroup (car groups)) | |
326 (let ((info (nth 2 (gnus-gethash igroup gnus-newsrc-hashtb))) | |
327 (active (gnus-gethash igroup gnus-active-hashtb))) | |
328 ;; See if the group has had its active list read this session | |
329 ;; if not, we do it now. | |
330 (if (null active) | |
331 (if (gnus-activate-group igroup) | |
332 (progn | |
333 (gnus-get-unread-articles-in-group | |
334 info (gnus-gethash igroup gnus-active-hashtb)) | |
335 (setq active (gnus-gethash igroup gnus-active-hashtb))) | |
336 (message "Couldn't open component group %s" igroup))) | |
337 (if (null active) | |
338 () | |
339 ;; And then we do the mapping for this component group. If | |
340 ;; you feel tempted to cast your eyes to the soup below - | |
341 ;; don't. It'll hurt your soul. Suffice to say that it | |
342 ;; assigns ranges of nnvirtual article numbers to the | |
343 ;; different component groups. To get the article number | |
344 ;; from the nnvirtual number, one does something like | |
345 ;; (+ (- number offset) (car active)), where `offset' is the | |
346 ;; slice the mess below assigns, and active is the lowest | |
347 ;; active article in the component group. | |
348 (setq itotal (1+ (- (cdr active) (car active)))) | |
349 (if (setq ireads (nth 2 info)) | |
350 (let ((itreads | |
351 (if (not (listp (cdr ireads))) | |
352 (setq ireads (list (cons (car ireads) (cdr ireads)))) | |
353 (setq ireads (copy-alist ireads))))) | |
354 (if (< (or (and (numberp (car ireads)) (car ireads)) | |
355 (cdr (car ireads))) (car active)) | |
356 (setq ireads (setq itreads (cdr ireads)))) | |
357 (if (and ireads (< (or (and (numberp (car ireads)) | |
358 (car ireads)) | |
359 (car (car ireads))) (car active))) | |
360 (setcar (or (and (numberp (car ireads)) ireads) | |
361 (car ireads)) (1+ (car active)))) | |
362 (while itreads | |
363 (setcar (or (and (numberp (car itreads)) itreads) | |
364 (car itreads)) | |
365 (+ (max | |
366 1 (- (if (numberp (car itreads)) | |
367 (car itreads) | |
368 (car (car itreads))) | |
369 (car active))) | |
370 offset)) | |
371 (if (not (numberp (car itreads))) | |
372 (setcdr (car itreads) | |
373 (+ (- (cdr (car itreads)) (car active)) offset))) | |
374 (setq itreads (cdr itreads))) | |
375 (setq reads (nconc reads ireads)))) | |
376 (setq offset (+ offset (1- itotal))) | |
377 (setq nnvirtual-current-mapping | |
378 (cons (list offset igroup (car active)) | |
379 nnvirtual-current-mapping))) | |
380 (setq groups (cdr groups)))) | |
381 (setq nnvirtual-current-mapping | |
382 (nreverse nnvirtual-current-mapping)) | |
383 ;; Set Gnus active info. | |
384 (gnus-sethash group (cons 1 (1- offset)) gnus-active-hashtb) | |
385 ;; Set Gnus read info. | |
386 (setcar (nthcdr 2 info) reads) | |
387 | |
388 ;; Then we deal with the marks. | |
389 (let ((map nnvirtual-current-mapping) | |
390 (marks '(tick dormant reply expire score)) | |
391 (offset 0) | |
392 tick dormant reply expire score marked active) | |
393 (while map | 261 (while map |
394 (setq igroup (nth 1 (car map))) | 262 (unless (nth 3 (setq m (pop map))) |
395 (setq active (nth 2 (car map))) | 263 ;; Read article. |
396 (setq marked (nth 3 (nth 2 (gnus-gethash igroup gnus-newsrc-hashtb)))) | 264 (push (car m) reads)) |
397 (let ((m marks)) | 265 ;; Copy marks. |
398 (while m | 266 (when (setq mr (nth 4 m)) |
399 (and (assq (car m) marked) | 267 (while mr |
400 (set (car m) | 268 (setcdr (setq op (assq (pop mr) marks)) (cons (car m) (cdr op)))))) |
401 (nconc (mapcar | 269 ;; Compress the marks and the reads. |
402 (lambda (art) | 270 (setq mr marks) |
403 (if (numberp art) | 271 (while mr |
404 (if (< art active) | 272 (setcdr (car mr) (gnus-compress-sequence (sort (cdr (pop mr)) '<)))) |
405 0 (+ (- art active) offset)) | 273 (setcar (cddr info) (gnus-compress-sequence (nreverse reads))) |
406 (cons (+ (- (car art) active) offset) | 274 ;; Remove empty marks lists. |
407 (cdr art)))) | 275 (while (and marks (not (cdar marks))) |
408 (cdr (assq (car m) marked))) | |
409 (symbol-value (car m))))) | |
410 (setq m (cdr m)))) | |
411 (setq offset (car (car map))) | |
412 (setq map (cdr map))) | |
413 ;; Put the list of marked articles in the info of the virtual group. | |
414 (let ((m marks) | |
415 marked) | |
416 (while m | |
417 (and (symbol-value (car m)) | |
418 (setq marked (cons (cons (car m) (symbol-value (car m))) | |
419 marked))) | |
420 (setq m (cdr m))) | |
421 (if (nthcdr 3 info) | |
422 (setcar (nthcdr 3 info) marked) | |
423 (setcdr (nthcdr 2 info) (list marked))))))) | |
424 | |
425 (defun nnvirtual-update-marked () | |
426 (let ((mark-lists '((gnus-newsgroup-marked . tick) | |
427 (gnus-newsgroup-dormant . dormant) | |
428 (gnus-newsgroup-expirable . expire) | |
429 (gnus-newsgroup-replied . reply))) | |
430 marks art-group group-alist g) | |
431 (while mark-lists | |
432 (setq marks (symbol-value (car (car mark-lists)))) | |
433 ;; Find out what groups the mark belong to. | |
434 (while marks | |
435 (setq art-group (nnvirtual-art-group (car marks))) | |
436 (if (setq g (assoc (car art-group) group-alist)) | |
437 (nconc g (list (cdr art-group))) | |
438 (setq group-alist (cons (list (car art-group) (cdr art-group)) | |
439 group-alist))) | |
440 (setq marks (cdr marks))) | 276 (setq marks (cdr marks))) |
441 ;; The groups that don't have marks must have no marks. (Yup.) | 277 (setq mr marks) |
442 (let ((groups nnvirtual-current-groups)) | 278 (while (cdr mr) |
443 (while groups | 279 (if (cdadr mr) |
444 (or (assoc (car groups) group-alist) | 280 (setq mr (cdr mr)) |
445 (setq group-alist (cons (list (car groups)) group-alist))) | 281 (setcdr mr (cddr mr)))) |
446 (setq groups (cdr groups)))) | 282 |
447 ;; The we update the list of marks. | 283 ;; Enter these new marks into the info of the group. |
448 (while group-alist | 284 (if (nthcdr 3 info) |
449 (gnus-add-marked-articles | 285 (setcar (nthcdr 3 info) marks) |
450 (car (car group-alist)) (cdr (car mark-lists)) | 286 ;; Add the marks lists to the end of the info. |
451 (cdr (car group-alist)) nil t) | 287 (when marks |
452 (gnus-group-update-group (car (car group-alist)) t) | 288 (setcdr (nthcdr 2 info) (list marks)))) |
453 (setq group-alist (cdr group-alist))) | 289 t))) |
454 (setq mark-lists (cdr mark-lists))))) | 290 |
455 | 291 (deffoo nnvirtual-catchup-group (group &optional server all) |
456 (defun nnvirtual-art-group (article) | 292 (nnvirtual-possibly-change-server server) |
457 (let ((map nnvirtual-current-mapping) | 293 (let ((gnus-group-marked (copy-sequence nnvirtual-component-groups)) |
458 (offset 0)) | |
459 (while (< (car (car map)) (if (numberp article) article (car article))) | |
460 (setq offset (car (car map)) | |
461 map (cdr map))) | |
462 (cons (nth 1 (car map)) | |
463 (if (numberp article) | |
464 (- (+ article (nth 2 (car map))) offset) | |
465 (cons (- (+ (car article) (nth 2 (car map))) offset) | |
466 (cdr article)))))) | |
467 | |
468 (defun nnvirtual-catchup-group (group &optional server all) | |
469 (nnvirtual-possibly-change-newsgroups group server) | |
470 (let ((gnus-group-marked nnvirtual-current-groups) | |
471 (gnus-expert-user t)) | 294 (gnus-expert-user t)) |
295 ;; Make sure all groups are activated. | |
296 (mapcar | |
297 (lambda (g) | |
298 (when (not (numberp (car (gnus-gethash g gnus-newsrc-hashtb)))) | |
299 (gnus-activate-group g))) | |
300 nnvirtual-component-groups) | |
472 (save-excursion | 301 (save-excursion |
473 (set-buffer gnus-group-buffer) | 302 (set-buffer gnus-group-buffer) |
474 (gnus-group-catchup-current nil all)))) | 303 (gnus-group-catchup-current nil all)))) |
475 | 304 |
305 (deffoo nnvirtual-find-group-art (group article) | |
306 "Return the real group and article for virtual GROUP and ARTICLE." | |
307 (let ((mart (assq article nnvirtual-mapping))) | |
308 (when mart | |
309 (cons (cadr mart) (caddr mart))))) | |
310 | |
311 | |
312 ;;; Internal functions. | |
313 | |
314 (defun nnvirtual-convert-headers () | |
315 "Convert HEAD headers into NOV headers." | |
316 (save-excursion | |
317 (set-buffer nntp-server-buffer) | |
318 (let* ((dependencies (make-vector 100 0)) | |
319 (headers (gnus-get-newsgroup-headers dependencies)) | |
320 header) | |
321 (erase-buffer) | |
322 (while (setq header (pop headers)) | |
323 (nnheader-insert-nov header))))) | |
324 | |
325 (defun nnvirtual-possibly-change-server (server) | |
326 (or (not server) | |
327 (nnoo-current-server-p 'nnvirtual server) | |
328 (nnvirtual-open-server server))) | |
329 | |
330 (defun nnvirtual-update-marked () | |
331 "Copy marks from the virtual group to the component groups." | |
332 (let ((mark-lists gnus-article-mark-lists) | |
333 (marks (gnus-info-marks (gnus-get-info (nnvirtual-current-group)))) | |
334 type list mart cgroups) | |
335 (while (setq type (cdr (pop mark-lists))) | |
336 (setq list (gnus-uncompress-range (cdr (assq type marks)))) | |
337 (setq cgroups | |
338 (mapcar (lambda (g) (list g)) nnvirtual-component-groups)) | |
339 (while list | |
340 (nconc (assoc (cadr (setq mart (assq (pop list) nnvirtual-mapping))) | |
341 cgroups) | |
342 (list (caddr mart)))) | |
343 (while cgroups | |
344 (gnus-add-marked-articles | |
345 (caar cgroups) type (cdar cgroups) nil t) | |
346 (gnus-group-update-group (car (pop cgroups)) t))))) | |
347 | |
348 (defun nnvirtual-update-reads () | |
349 "Copy (un)reads from the current group to the component groups." | |
350 (let ((groups (mapcar (lambda (g) (list g)) nnvirtual-component-groups)) | |
351 (articles (gnus-list-of-unread-articles | |
352 (nnvirtual-current-group))) | |
353 m) | |
354 (while articles | |
355 (setq m (assq (pop articles) nnvirtual-mapping)) | |
356 (nconc (assoc (nth 1 m) groups) (list (nth 2 m)))) | |
357 (while groups | |
358 (gnus-update-read-articles (caar groups) (cdr (pop groups)))))) | |
359 | |
360 (defun nnvirtual-current-group () | |
361 "Return the prefixed name of the current nnvirtual group." | |
362 (concat "nnvirtual:" nnvirtual-current-group)) | |
363 | |
364 (defsubst nnvirtual-marks (article marks) | |
365 "Return a list of mark types for ARTICLE." | |
366 (let (out) | |
367 (while marks | |
368 (when (memq article (cdar marks)) | |
369 (push (caar marks) out)) | |
370 (setq marks (cdr marks))) | |
371 out)) | |
372 | |
373 (defun nnvirtual-create-mapping () | |
374 "Create an article mapping for the current group." | |
375 (let* ((div nil) | |
376 m marks list article unreads marks active | |
377 (map (sort | |
378 (apply | |
379 'nconc | |
380 (mapcar | |
381 (lambda (g) | |
382 (when (and (setq active (gnus-activate-group g)) | |
383 (> (cdr active) (car active))) | |
384 (setq unreads (gnus-list-of-unread-articles g) | |
385 marks (gnus-uncompress-marks | |
386 (gnus-info-marks (gnus-get-info g)))) | |
387 (when gnus-use-cache | |
388 (push (cons 'cache (gnus-cache-articles-in-group g)) | |
389 marks)) | |
390 (setq div (/ (float (car active)) | |
391 (if (zerop (cdr active)) | |
392 1 (cdr active)))) | |
393 (mapcar (lambda (n) | |
394 (list (* div (- n (car active))) | |
395 g n (and (memq n unreads) t) | |
396 (inline (nnvirtual-marks n marks)))) | |
397 (gnus-uncompress-range active)))) | |
398 nnvirtual-component-groups)) | |
399 (lambda (m1 m2) | |
400 (< (car m1) (car m2))))) | |
401 (i 0)) | |
402 (setq nnvirtual-mapping map) | |
403 ;; Set the virtual article numbers. | |
404 (while (setq m (pop map)) | |
405 (setcar m (setq article (incf i)))))) | |
406 | |
476 (provide 'nnvirtual) | 407 (provide 'nnvirtual) |
477 | 408 |
478 ;;; nnvirtual.el ends here | 409 ;;; nnvirtual.el ends here |