From 7c13cb18da8d917ec6688dd63face28caf457158 Mon Sep 17 00:00:00 2001 From: Lars Magne Ingebrigtsen Date: Wed, 5 Mar 1997 00:19:30 +0000 Subject: [PATCH] *** empty log message *** --- lisp/ChangeLog | 65 ++++ lisp/Makefile | 5 +- lisp/article.el | 4 +- lisp/dgnushack.el | 28 +- lisp/gnus-async.el | 7 +- lisp/gnus-demon.el | 19 ++ lisp/gnus-group.el | 12 +- lisp/gnus-move.el | 7 +- lisp/gnus-spec.el | 54 ++-- lisp/gnus-start.el | 2 +- lisp/gnus-sum.el | 18 +- lisp/gnus-undo.el | 2 +- lisp/gnus.el | 5 +- lisp/nnmail.el | 1 - lisp/nnml.el | 5 +- lisp/nntp.el | 60 ++-- lisp/nnvirtual.el | 727 ++++++++++++++++++++++++++++++++------------- lisp/nnweb.el | 4 +- lisp/smiley.el | 5 +- texi/gnus.texi | 8 +- 20 files changed, 743 insertions(+), 295 deletions(-) diff --git a/lisp/ChangeLog b/lisp/ChangeLog index b966d5b72..17d1de910 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog @@ -1,3 +1,68 @@ +Tue Nov 12 17:55:17 1996 Lars Magne Ingebrigtsen + + * gnus-group.el (gnus-group-set-timestamp): Removed reference to + free variable `group'. + +Mon Nov 11 16:29:00 1996 David Moore + + * gnus-group.el (gnus-group-timestamp-delta): New function. + + * gnus-demon.el (gnus-demon-add-scan-timestamps, + gnus-demon-scan-timestamps): New functions. + +Mon Nov 11 05:27:20 1996 Lars Magne Ingebrigtsen + + * article.el (gnus-emphasis-alist): Added ":" as sentence-end. + +Mon Nov 11 05:14:02 1996 David Moore + + * nnvirtual.el: New version. + +Mon Nov 11 05:09:14 1996 Lars Magne Ingebrigtsen + + * article.el (gnus-emphasis-underline-bold): Renamed. + +Mon Nov 11 05:05:09 1996 Alexandre Oliva + + * nntp.el (nntp-possibly-change-group): Bind + `nnheader-callback-function' to nil. + +Sun Nov 10 12:13:08 1996 Lars Magne Ingebrigtsen + + * gnus-start.el (gnus-parse-active): Give correct answer. + + * nntp.el (nntp-snarf-error-message): Massage the message. + +Sun Nov 10 11:49:33 1996 Joe Wells + + * dgnushack.el (require): Load .el files only. + +Sun Nov 10 10:06:12 1996 Lars Magne Ingebrigtsen + + * gnus-move.el (gnus-move-group-to-server): Would pass wrong + params to `gnus-retrieve-headers'. + + * nntp.el (nntp-wait-for): Accept a `discard' param. + (nntp-open-connection): Would mix it up when establishing asynch + connections. + + * nnml.el (nnml-find-id): Would report false positives. + + * gnus-spec.el (gnus-update-format-specifications): Do all + computations in the right buffer. + + * nnweb.el (nnweb-type-definition): Moved search engine. + (nnweb-fetch-form): Use "POST" instead of `POST'. + + * gnus-undo.el (gnus-undo-register): Entered malformed undo + statements. + + * smiley.el (smiley-nosey-regexp-alist): Add a devilish face. + +Sun Nov 10 06:38:38 1996 Lars Magne Ingebrigtsen + + * gnus.el: Red Gnus v0.60 is released. + Sun Nov 10 06:31:36 1996 Lars Magne Ingebrigtsen * gnus.el: Red Gnus v0.59 is released. diff --git a/lisp/Makefile b/lisp/Makefile index fa3acedb0..10dc29d75 100644 --- a/lisp/Makefile +++ b/lisp/Makefile @@ -2,9 +2,12 @@ SHELL = /bin/sh EMACS=emacs FLAGS=-batch -q -no-site-file -l ./dgnushack.el -all: +total: rm -f *.elc ; $(EMACS) $(FLAGS) -f dgnushack-compile +all: + $(EMACS) $(FLAGS) -f dgnushack-compile + some: $(EMACS) $(FLAGS) -f dgnushack-recompile diff --git a/lisp/article.el b/lisp/article.el index d2f1b511d..4730abb56 100644 --- a/lisp/article.el +++ b/lisp/article.el @@ -120,7 +120,7 @@ asynchronously. The compressed face will be piped to this command." (defcustom gnus-emphasis-alist (let ((format - "\\(\\s-\\|^\\)\\(%s\\(\\w+\\(\\s-+\\w+\\)*\\)%s\\)\\(\\s-\\|[?!.,;)]\\)") + "\\(\\s-\\|^\\)\\(%s\\(\\w+\\(\\s-+\\w+\\)*\\)%s\\)\\(\\s-\\|[?!.,;:\")]\\)") (types '(("_" "_" underline) ("/" "/" italic) @@ -166,7 +166,7 @@ is the face used for highlighting." "Face used for displaying underlined emphasized text (_word_)." :group 'article) -(defface gnus-emphasis-bold-underline '((t (:bold t :underline t))) +(defface gnus-emphasis-underline-bold '((t (:bold t :underline t))) "Face used for displaying underlined bold emphasized text (_*word*_)." :group 'article) diff --git a/lisp/dgnushack.el b/lisp/dgnushack.el index 0b8b548d9..2f71ea366 100644 --- a/lisp/dgnushack.el +++ b/lisp/dgnushack.el @@ -37,6 +37,22 @@ (defalias 'efs-re-read-dir 'ignore) (defalias 'ange-ftp-re-read-dir 'ignore) +(defadvice require (before require-avoid-compiled activate) + ;; (feature filename) + "Avoid loading .elc files." + ;; Ensure a second argument to require is supplied that explicitly + ;; specifies loading the .el version of the file. + (let ((filename (ad-get-arg 1))) + (or filename (setq filename (symbol-name (ad-get-arg 0)))) + (save-match-data + (cond ((string-match "\\.el\\'" filename) + nil) + ((string-match "\\.elc\\'" filename) + (setq filename (replace-match ".el" t t filename))) + (t + (setq filename (concat filename ".el"))))) + (ad-set-arg 1 filename))) + (eval-and-compile (unless (string-match "XEmacs" emacs-version) (fset 'get-popup-menu-response 'ignore) @@ -53,9 +69,7 @@ (condition-case () (require 'w3-forms) (error (setq files (delete "nnweb.el" files)))) - (while files - (setq file (car files) - files (cdr files)) + (while (setq file (pop files)) (cond ((or (string= file "custom.el") (string= file "browse-url.el")) (setq byte-compile-warnings nil)) @@ -69,9 +83,11 @@ "messagexmas.el" "nnheaderxm.el" "smiley.el"))) xemacs) - (condition-case () - (byte-compile-file file) - (error nil)))))) + (when (or (not (file-exists-p (concat file "c"))) + (file-newer-than-file-p file (concat file "c"))) + (condition-case () + (byte-compile-file file) + (error nil))))))) (defun dgnushack-recompile () (require 'gnus) diff --git a/lisp/gnus-async.el b/lisp/gnus-async.el index ed6f47101..27f657c81 100644 --- a/lisp/gnus-async.el +++ b/lisp/gnus-async.el @@ -235,9 +235,10 @@ It should return non-nil if the article is to be prefetched." (defun gnus-async-delete-prefected-entry (entry) "Delete ENTRY from buffer and alist." - (delete-region (cadr entry) (caddr entry)) - (set-marker (cadr entry) nil) - (set-marker (caddr entry) nil) + (ignore-errors + (delete-region (cadr entry) (caddr entry)) + (set-marker (cadr entry) nil) + (set-marker (caddr entry) nil)) (gnus-async-with-semaphore (setq gnus-async-article-alist (delq entry gnus-async-article-alist)))) diff --git a/lisp/gnus-demon.el b/lisp/gnus-demon.el index 6cb07b623..015f4f05e 100644 --- a/lisp/gnus-demon.el +++ b/lisp/gnus-demon.el @@ -256,6 +256,25 @@ time Emacs has been idle for IDLE `gnus-demon-timestep's." (set-buffer gnus-group-buffer) (gnus-group-get-new-news))))) +(defun gnus-demon-add-scan-timestamps () + "Add daemonic updating of timestamps in empty newgroups." + (gnus-demon-add-handler 'gnus-demon-scan-timestamps nil 30)) + +(defun gnus-demon-scan-timestamps () + "Set the timestamp on all newsgroups with no unread and no ticked articles." + (when (gnus-alive-p) + (let ((cur-time (current-time)) + (newsrc (cdr gnus-newsrc-alist)) + info group unread has-ticked) + (while (setq info (pop newsrc)) + (setq group (gnus-info-group info) + unread (gnus-group-unread group) + has-ticked (cdr (assq 'tick (gnus-info-marks info)))) + (when (and (numberp unread) + (= unread 0) + (not has-ticked)) + (gnus-group-set-parameter group 'timestamp cur-time)))))) + (provide 'gnus-demon) ;;; gnus-demon.el ends here diff --git a/lisp/gnus-group.el b/lisp/gnus-group.el index d1c6b64d5..783b13d9c 100644 --- a/lisp/gnus-group.el +++ b/lisp/gnus-group.el @@ -1274,6 +1274,7 @@ If REGEXP, only list groups matching REGEXP." (level (or (gnus-info-level info) 9)) (score (or (gnus-info-score info) 0)) (ticked (gnus-range-length (cdr (assq 'tick marked)))) + (group-age (gnus-group-timestamp-delta group)) (inhibit-read-only t)) ;; Eval the cars of the lists until we find a match. (while (and list @@ -3390,7 +3391,8 @@ and the second element is the address." (defun gnus-group-set-timestamp () "Change the timestamp of the current group to the current time. -This function can be used in hooks like `gnus-select-group-hook'." +This function can be used in hooks like `gnus-select-group-hook' +or `gnus-group-catchup-group-hook'." (let ((time (current-time))) (setcdr (cdr time) nil) (gnus-group-set-parameter gnus-newsgroup-name 'timestamp time))) @@ -3399,6 +3401,14 @@ This function can be used in hooks like `gnus-select-group-hook'." "Return the timestamp for GROUP." (gnus-group-get-parameter group 'timestamp)) +(defun gnus-group-timestamp-delta (group) + "Return the offset in seconds from the timestamp for GROUP to the current time, as a floating point number." + (let* ((time (or (gnus-group-timestamp group) + (list 0 0))) + (delta (gnus-time-minus (current-time) time))) + (+ (* (nth 0 delta) 65536.0) + (nth 1 delta)))) + (defun gnus-group-timestamp-string (group) "Return a string of the timestamp for GROUP." (let ((time (gnus-group-timestamp group))) diff --git a/lisp/gnus-move.el b/lisp/gnus-move.el index 6bdf14cf1..dd39935a2 100644 --- a/lisp/gnus-move.el +++ b/lisp/gnus-move.el @@ -66,7 +66,7 @@ Update the .newsrc.eld file to reflect the change of nntp server." hashtb (make-vector 1023 0)) ;; Fetch the headers from the `to-server'. (when (setq type (gnus-retrieve-headers - (car to-active) (cdr to-active))) + (gnus-uncompress-range to-active) group to-server)) ;; Convert HEAD headers. I don't care. (when (eq type 'headers) (nnvirtual-convert-headers)) @@ -85,8 +85,9 @@ Update the .newsrc.eld file to reflect the change of nntp server." (when (and (gnus-request-group group nil from-server) (gnus-active group) (setq type (gnus-retrieve-headers - (car (gnus-active group)) - (cdr (gnus-active group))))) + (gnus-uncompress-range + (gnus-active group)) + group from-server))) ;; Make it easier to map marks. (let ((mark-lists (gnus-info-marks info)) ms type m) diff --git a/lisp/gnus-spec.el b/lisp/gnus-spec.el index bc2d4618d..f5bf13b6a 100644 --- a/lisp/gnus-spec.el +++ b/lisp/gnus-spec.el @@ -184,33 +184,33 @@ (buffer-name (get-buffer val))) (set-buffer (get-buffer val))) (setq new-format (symbol-value - (intern (format "gnus-%s-line-format" type)))))) - (setq entry (cdr (assq type gnus-format-specs))) - (if (and (car entry) - (equal (car entry) new-format)) - ;; Use the old format. - (set (intern (format "gnus-%s-line-format-spec" type)) - (cadr entry)) - ;; This is a new format. - (setq val - (if (not (stringp new-format)) - ;; This is a function call or something. - new-format - ;; This is a "real" format. - (gnus-parse-format - new-format - (symbol-value - (intern (format "gnus-%s-line-format-alist" - (if (eq type 'article-mode) - 'summary-mode type)))) - (not (string-match "mode$" (symbol-name type)))))) - ;; Enter the new format spec into the list. - (if entry - (progn - (setcar (cdr entry) val) - (setcar entry new-format)) - (push (list type new-format val) gnus-format-specs)) - (set (intern (format "gnus-%s-line-format-spec" type)) val)))) + (intern (format "gnus-%s-line-format" type))))) + (setq entry (cdr (assq type gnus-format-specs))) + (if (and (car entry) + (equal (car entry) new-format)) + ;; Use the old format. + (set (intern (format "gnus-%s-line-format-spec" type)) + (cadr entry)) + ;; This is a new format. + (setq val + (if (not (stringp new-format)) + ;; This is a function call or something. + new-format + ;; This is a "real" format. + (gnus-parse-format + new-format + (symbol-value + (intern (format "gnus-%s-line-format-alist" + (if (eq type 'article-mode) + 'summary-mode type)))) + (not (string-match "mode$" (symbol-name type)))))) + ;; Enter the new format spec into the list. + (if entry + (progn + (setcar (cdr entry) val) + (setcar entry new-format)) + (push (list type new-format val) gnus-format-specs)) + (set (intern (format "gnus-%s-line-format-spec" type)) val))))) (unless (assq 'version gnus-format-specs) (push (cons 'version emacs-version) gnus-format-specs))) diff --git a/lisp/gnus-start.el b/lisp/gnus-start.el index 2f3dc6a64..61f35dc9e 100644 --- a/lisp/gnus-start.el +++ b/lisp/gnus-start.el @@ -1462,7 +1462,7 @@ newsgroup." ;; Parse the result we got from `gnus-request-group'. (when (looking-at "[0-9]+ [0-9]+ \\([0-9]+\\) [0-9]+") (goto-char (match-beginning 1)) - (cons (read (current-buffer)) + (cons (1+ (read (current-buffer))) (read (current-buffer)))))) (defun gnus-make-articles-unread (group articles) diff --git a/lisp/gnus-sum.el b/lisp/gnus-sum.el index 2bc9ef98a..092372c4b 100644 --- a/lisp/gnus-sum.el +++ b/lisp/gnus-sum.el @@ -8148,8 +8148,7 @@ save those articles instead." (gnus-summary-position-point) ;; If all commands are to be bunched up on one line, we collect ;; them here. - (if gnus-view-pseudos-separately - () + (unless gnus-view-pseudos-separately (let ((ps (setq pslist (sort pslist 'gnus-pseudos<))) files action) (while ps @@ -8160,17 +8159,16 @@ save those articles instead." (or (cdr (assq 'action (cadr ps))) "2"))) (push (cdr (assq 'name (cadr ps))) files) (setcdr ps (cddr ps))) - (if (not files) - () + (when files (when (not (string-match "%s" action)) (push " " files)) (push " " files) - (and (assq 'execute (car ps)) - (setcdr (assq 'execute (car ps)) - (funcall (if (string-match "%s" action) - 'format 'concat) - action - (mapconcat (lambda (f) f) files " "))))) + (when (assq 'execute (car ps)) + (setcdr (assq 'execute (car ps)) + (funcall (if (string-match "%s" action) + 'format 'concat) + action + (mapconcat (lambda (f) f) files " "))))) (setq ps (cdr ps))))) (if (and gnus-view-pseudos (not not-view)) (while pslist diff --git a/lisp/gnus-undo.el b/lisp/gnus-undo.el index 1beab7e3a..d37a22426 100644 --- a/lisp/gnus-undo.el +++ b/lisp/gnus-undo.el @@ -125,7 +125,7 @@ FORMS may use backtick quote syntax." (when gnus-undo-mode (gnus-undo-register-1 `(lambda () - ,@form)))) + ,form)))) (put 'gnus-undo-register 'lisp-indent-function 0) (put 'gnus-undo-register 'edebug-form-spec '(body)) diff --git a/lisp/gnus.el b/lisp/gnus.el index a43bcd193..3678eaf19 100644 --- a/lisp/gnus.el +++ b/lisp/gnus.el @@ -42,7 +42,7 @@ "Score and kill file handling." :group 'gnus ) -(defconst gnus-version-number "0.60" +(defconst gnus-version-number "0.61" "Version number for this version of Gnus.") (defconst gnus-version (format "Red Gnus v%s" gnus-version-number) @@ -1057,6 +1057,7 @@ gnus-newsrc-hashtb should be kept so that both hold the same information.") gnus-Folder-save-name gnus-folder-save-name) ("gnus-mh" :interactive t gnus-summary-save-in-folder) ("gnus-demon" gnus-demon-add-nocem gnus-demon-add-scanmail + gnus-demon-add-rescan gnus-demon-add-scan-timestamps gnus-demon-add-disconnection gnus-demon-add-handler gnus-demon-remove-handler) ("gnus-demon" :interactive t @@ -1947,7 +1948,7 @@ Disallow illegal group names." group) (while (not group) (when (string-match - "[ `'\"/]" + "[ `'\"/]\\|^$" (setq group (read-string (concat prefix prompt) "" 'gnus-group-history))) (setq prefix (format "Illegal group name: \"%s\". " group) diff --git a/lisp/nnmail.el b/lisp/nnmail.el index 2fc05415b..e840e236c 100644 --- a/lisp/nnmail.el +++ b/lisp/nnmail.el @@ -1077,7 +1077,6 @@ See the documentation for the variable `nnmail-split-fancy' for documentation." (apply 'concat (nreverse expanded)) newtext))) - ;; Get a list of spool files to read. (defun nnmail-get-spool-files (&optional group) (if (null nnmail-spool-file) diff --git a/lisp/nnml.el b/lisp/nnml.el index 0ca7a94a6..3f8b7eebc 100644 --- a/lisp/nnml.el +++ b/lisp/nnml.el @@ -509,8 +509,9 @@ all. This may very well take some time.") (while (and (not found) (search-forward id nil t)) ; We find the ID. ;; And the id is in the fourth field. - (when (search-backward - "\t" (save-excursion (beginning-of-line) (point)) t 4) + (if (not (and (search-backward "\t" nil t 4) + (not (search-backward"\t" (gnus-point-at-bol) t)))) + (forward-line 1) (beginning-of-line) (setq found t) ;; We return the article number. diff --git a/lisp/nntp.el b/lisp/nntp.el index 0debdd7f5..dabf74312 100644 --- a/lisp/nntp.el +++ b/lisp/nntp.el @@ -575,18 +575,18 @@ It will prompt for a password." (funcall nntp-open-connection-function pbuffer)))) (when process (process-kill-without-query process) - (nntp-wait-for process "^.*\n" buffer) + (nntp-wait-for process "^.*\n" buffer nil t) (if (memq (process-status process) '(open run)) (prog1 - (caar (push (list process buffer nil) - nntp-connection-alist)) + (caar (push (list process buffer nil) nntp-connection-alist)) (push process nntp-connection-list) (save-excursion - (set-buffer nntp-server-buffer) + (set-buffer pbuffer) (nntp-read-server-type) - (run-hooks 'nntp-server-opened-hook) - (set-buffer buffer) - (erase-buffer))) + (erase-buffer) + (set-buffer nntp-server-buffer) + (let ((nnheader-callback-function nil)) + (run-hooks 'nntp-server-opened-hook)))) (when (buffer-name (process-buffer process)) (kill-buffer (process-buffer process))) nil)))) @@ -682,7 +682,7 @@ It will prompt for a password." "Send STRING to PROCESS." (process-send-string process (concat string nntp-end-of-line))) -(defun nntp-wait-for (process wait-for buffer &optional decode) +(defun nntp-wait-for (process wait-for buffer &optional decode discard) "Wait for WAIT-FOR to arrive from PROCESS." (save-excursion (set-buffer (process-buffer process)) @@ -704,18 +704,24 @@ It will prompt for a password." (nntp-accept-process-output process) (goto-char (point-max))) (nntp-decode-text (not decode)) - (save-excursion - (set-buffer buffer) - (goto-char (point-max)) - (insert-buffer-substring (process-buffer process)) - ;; Nix out "nntp reading...." message. - (message "") - t)) - (erase-buffer)))) + (unless discard + (save-excursion + (set-buffer buffer) + (goto-char (point-max)) + (insert-buffer-substring (process-buffer process)) + ;; Nix out "nntp reading...." message. + (message "") + t))) + (unless discard + (erase-buffer))))) (defun nntp-snarf-error-message () "Save the error message in the current buffer." - (setq nntp-status-string (buffer-string))) + (let ((message (buffer-string))) + (while (string-match "[\r\n]+" message) + (setq message (replace-match " " t t message))) + (nnheader-report 'nntp message) + message)) (defun nntp-accept-process-output (process) "Wait for output from PROCESS and message some dots." @@ -732,18 +738,18 @@ It will prompt for a password." (nntp-accept-process-output (nntp-find-connection nntp-server-buffer))) (defun nntp-possibly-change-group (group server &optional connectionless) - (when server - (or (nntp-server-opened server) - (nntp-open-server server nil connectionless))) + (let ((nnheader-callback-function nil)) + (when server + (or (nntp-server-opened server) + (nntp-open-server server nil connectionless))) - (unless connectionless - (or (nntp-find-connection nntp-server-buffer) - (nntp-open-connection nntp-server-buffer))) + (unless connectionless + (or (nntp-find-connection nntp-server-buffer) + (nntp-open-connection nntp-server-buffer))) - (when group - (let ((entry (nntp-find-connection-entry nntp-server-buffer))) - (when (not (equal group (caddr entry))) - (let ((nnheader-callback-function nil)) + (when group + (let ((entry (nntp-find-connection-entry nntp-server-buffer))) + (when (not (equal group (caddr entry))) (nntp-request-group group) (save-excursion (set-buffer nntp-server-buffer) diff --git a/lisp/nnvirtual.el b/lisp/nnvirtual.el index ce264d4c7..58754266c 100644 --- a/lisp/nnvirtual.el +++ b/lisp/nnvirtual.el @@ -1,7 +1,8 @@ ;;; nnvirtual.el --- virtual newsgroups access for Gnus ;; Copyright (C) 1994,95,96 Free Software Foundation, Inc. -;; Author: Lars Magne Ingebrigtsen +;; Author: David Moore +;; Lars Magne Ingebrigtsen ;; Masanobu UMEDA ;; Keywords: news @@ -56,10 +57,28 @@ virtual group.") -(defconst nnvirtual-version "nnvirtual 1.0") +(defconst nnvirtual-version "nnvirtual 1.1") (defvoo nnvirtual-current-group nil) -(defvoo nnvirtual-mapping nil) + +(defvoo nnvirtual-mapping-table nil + "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.") + +(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.") + +(defvoo nnvirtual-mapping-marks nil + "Compressed marks alist for the virtual group as computed from the +marks of individual component groups.") (defvoo nnvirtual-status-string "") @@ -72,6 +91,7 @@ virtual group.") (nnoo-define-basics nnvirtual) + (deffoo nnvirtual-retrieve-headers (articles &optional newsgroup server fetch-old) (when (nnvirtual-possibly-change-server server) @@ -82,78 +102,71 @@ virtual group.") 'headers (let ((vbuf (nnheader-set-temp-buffer (get-buffer-create " *virtual headers*"))) - (unfetched (mapcar (lambda (g) (list g)) - nnvirtual-component-groups)) + (carticles (nnvirtual-partition-sequence articles)) (system-name (system-name)) - cgroup article result prefix) - (while articles - (setq article (assq (pop articles) nnvirtual-mapping)) - (when (and (setq cgroup (cadr article)) + cgroup carticle article result prefix) + (while carticles + (setq cgroup (caar carticles)) + (setq articles (cdar carticles)) + (pop carticles) + (when (and articles (gnus-check-server (gnus-find-method-for-group cgroup) t) - (gnus-request-group cgroup t)) - (setq prefix (gnus-group-real-prefix cgroup)) - (when (setq result (gnus-retrieve-headers - (list (caddr article)) cgroup nil)) - (set-buffer nntp-server-buffer) - (if (zerop (buffer-size)) - (nconc (assq cgroup unfetched) (list (caddr article))) - ;; 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 (read nntp-server-buffer) (point))) - (princ (car article) (current-buffer)) + (gnus-request-group cgroup t) + (setq prefix (gnus-group-real-prefix cgroup)) + ;; FIX FIX FIX we want to check the cache! + ;; This is probably evil if people have set + ;; gnus-use-cache to nil themselves, but I + ;; have no way of finding the true value of it. + (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) - (looking-at - "[^\t]*\t[^\t]*\t[^\t]*\t[^\t]*\t[^\t]*\t[^\t]*\t[^\t]*\t") - (goto-char (match-end 0)) - (or (search-forward - "\t" (save-excursion (end-of-line) (point)) t) - (end-of-line)) - (while (= (char-after (1- (point))) ? ) - (forward-char -1) - (delete-char 1)) - (if (eolp) - (progn - (end-of-line) - (or (= (char-after (1- (point))) ?\t) - (insert ?\t)) - (insert "Xref: " system-name " " cgroup ":") - (princ (caddr article) (current-buffer)) - (insert "\t")) - (insert "Xref: " system-name " " cgroup ":") - (princ (caddr article) (current-buffer)) - (insert " ") - (when (not (string= "" prefix)) - (while (re-search-forward - "[^ ]+:[0-9]+" - (save-excursion (end-of-line) (point)) t) - (save-excursion - (goto-char (match-beginning 0)) - (insert prefix)))) - (end-of-line) - (or (= (char-after (1- (point))) ?\t) - (insert ?\t))) - (forward-line 1)) - (set-buffer vbuf) - (goto-char (point-max)) - (insert-buffer-substring nntp-server-buffer))))) - - ;; In case some of the articles have expired or been - ;; cancelled, we have to mark them as read in the - ;; component group. - (while unfetched - (when (cdar unfetched) - (gnus-group-make-articles-read - (caar unfetched) (sort (cdar unfetched) '<))) - (setq unfetched (cdr unfetched))) + (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 + (gnus-group-make-articles-read cgroup articles)) + ) ;; The headers are ready for reading, so they are inserted into ;; the nntp-server-buffer, which is where Gnus expects to find @@ -163,14 +176,20 @@ virtual group.") (set-buffer nntp-server-buffer) (erase-buffer) (insert-buffer-substring vbuf) + ;; FIX FIX FIX, we should be able to sort faster than + ;; this if needed, since each cgroup is sorted, we just + ;; need to merge + (sort-numeric-fields 1 (point-min) (point-max)) 'nov) (kill-buffer vbuf))))))) + + (deffoo nnvirtual-request-article (article &optional group server buffer) (when (and (nnvirtual-possibly-change-server server) (numberp article)) - (let* ((amap (assq article nnvirtual-mapping)) - (cgroup (cadr amap))) + (let* ((amap (nnvirtual-map-article article)) + (cgroup (car amap))) (cond ((not amap) (nnheader-report 'nnvirtual "No such article: %s" article)) @@ -183,8 +202,9 @@ virtual group.") (if buffer (save-excursion (set-buffer buffer) - (gnus-request-article-this-buffer (caddr amap) cgroup)) - (gnus-request-article (caddr amap) cgroup))))))) + (gnus-request-article-this-buffer (cdr amap) cgroup)) + (gnus-request-article (cdr amap) cgroup))))))) + (deffoo nnvirtual-open-server (server &optional defs) (unless (assq 'nnvirtual-component-regexp defs) @@ -193,7 +213,11 @@ virtual group.") (nnoo-change-server 'nnvirtual server defs) (if nnvirtual-component-groups t - (setq nnvirtual-mapping nil) + (setq nnvirtual-mapping-table nil + nnvirtual-mapping-offsets nil + nnvirtual-mapping-len 0 + nnvirtual-mapping-reads nil + nnvirtual-mapping-marks nil) (when nnvirtual-component-regexp ;; Go through the newsrc alist and find all component groups. (let ((newsrc (cdr gnus-newsrc-alist)) @@ -207,6 +231,7 @@ virtual group.") (nnheader-report 'nnvirtual "No component groups: %s" server) t))) + (deffoo nnvirtual-request-group (group &optional server dont-check) (nnvirtual-possibly-change-server server) (setq nnvirtual-component-groups @@ -220,101 +245,83 @@ virtual group.") nnvirtual-always-rescan) (nnvirtual-create-mapping)) (setq nnvirtual-current-group group) - (let ((len (length nnvirtual-mapping))) - (nnheader-insert "211 %d 1 %d %s\n" len len group))))) + (nnheader-insert "211 %d 1 %d %s\n" + nnvirtual-mapping-len nnvirtual-mapping-len group)))) + (deffoo nnvirtual-request-type (group &optional article) (if (not article) 'unknown - (let ((mart (assq article nnvirtual-mapping))) + (let ((mart (nnvirtual-map-article article))) (when mart - (gnus-request-type (cadr mart) (car mart)))))) + (gnus-request-type (car mart) (cdr mart)))))) (deffoo nnvirtual-request-update-mark (group article mark) - (let* ((nart (assq article nnvirtual-mapping)) - (cgroup (cadr nart)) + (let* ((nart (nnvirtual-map-article article)) + (cgroup (car nart)) ;; The component group might be a virtual group. - (nmark (gnus-request-update-mark cgroup (caddr nart) mark))) + (nmark (gnus-request-update-mark cgroup (cdr nart) mark))) (when (and nart (= mark nmark) (gnus-group-auto-expirable-p cgroup)) (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 articles. + ;; Copy (un)read status and marks back to component groups. (nnvirtual-update-reads) - ;; We copy the marks from this group to the component - ;; groups here. - (nnvirtual-update-marked)) + (nnvirtual-update-marked t)) t) + (deffoo nnvirtual-request-list (&optional server) (nnheader-report 'nnvirtual "LIST is not implemented.")) + (deffoo nnvirtual-request-newgroups (date &optional server) (nnheader-report 'nnvirtual "NEWGROUPS is not supported.")) + (deffoo nnvirtual-request-list-newsgroups (&optional server) (nnheader-report 'nnvirtual "LIST NEWSGROUPS is not implemented.")) + (deffoo nnvirtual-request-update-info (group info &optional server) (when (nnvirtual-possibly-change-server server) - (let ((map nnvirtual-mapping) - (marks (mapcar (lambda (m) (list (cdr m))) gnus-article-mark-lists)) - reads mr m op) - ;; Go through the mapping. - (while map - (unless (nth 3 (setq m (pop map))) - ;; Read article. - (push (car m) reads)) - ;; Copy marks. - (when (setq mr (nth 4 m)) - (while mr - (setcdr (setq op (assq (pop mr) marks)) (cons (car m) (cdr op)))))) - ;; Compress the marks and the reads. - (setq mr marks) - (while mr - (setcdr (car mr) (gnus-compress-sequence (sort (cdr (pop mr)) '<)))) - (setcar (cddr info) (gnus-compress-sequence (nreverse reads))) - ;; Remove empty marks lists. - (while (and marks (not (cdar marks))) - (setq marks (cdr marks))) - (setq mr marks) - (while (cdr mr) - (if (cdadr mr) - (setq mr (cdr mr)) - (setcdr mr (cddr mr)))) - - ;; Enter these new marks into the info of the group. - (if (nthcdr 3 info) - (setcar (nthcdr 3 info) marks) - ;; Add the marks lists to the end of the info. - (when marks - (setcdr (nthcdr 2 info) (list marks)))) - t))) + ;; 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)))) + t)) + (deffoo nnvirtual-catchup-group (group &optional server all) - (nnvirtual-possibly-change-server server) - (let ((gnus-group-marked (copy-sequence nnvirtual-component-groups)) - (gnus-expert-user t)) - ;; Make sure all groups are activated. - (mapcar - (lambda (g) - (when (not (numberp (car (gnus-gethash g gnus-newsrc-hashtb)))) - (gnus-activate-group g))) - nnvirtual-component-groups) - (save-excursion - (set-buffer gnus-group-buffer) - (gnus-group-catchup-current nil 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) + ;; do a catchup on all component groups + (let ((gnus-group-marked (copy-sequence nnvirtual-component-groups)) + (gnus-expert-user t)) + ;; Make sure all groups are activated. + (mapcar + (lambda (g) + (when (not (numberp (car (gnus-gethash g gnus-newsrc-hashtb)))) + (gnus-activate-group g))) + nnvirtual-component-groups) + (save-excursion + (set-buffer gnus-group-buffer) + (gnus-group-catchup-current nil all))))) + (deffoo nnvirtual-find-group-art (group article) "Return the real group and article for virtual GROUP and ARTICLE." - (let ((mart (assq article nnvirtual-mapping))) - (when mart - (cons (cadr mart) (caddr mart))))) + (nnvirtual-map-article article)) ;;; Internal functions. @@ -330,87 +337,405 @@ virtual group.") (while (setq header (pop headers)) (nnheader-insert-nov header))))) + +(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." + ;; Move to beginning of Xref field, creating a slot if needed. + (beginning-of-line) + (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) + (insert "\t")) + + ;; Remove any spaces at the beginning of the Xref field. + (while (= (char-after (1- (point))) ? ) + (forward-char -1) + (delete-char 1)) + + (insert "Xref: " system-name " " group ":") + (princ article (current-buffer)) + + ;; If there were existing xref lines, clean them up to have the correct + ;; component server prefix. + (let ((xref-end (save-excursion + (search-forward "\t" (gnus-point-at-eol) 'move) + (point))) + (len (length prefix))) + (unless (= (point) xref-end) + (insert " ") + (when (not (string= "" prefix)) + (while (re-search-forward "[^ ]+:[0-9]+" xref-end t) + (save-excursion + (goto-char (match-beginning 0)) + (insert prefix)) + (setq xref-end (+ xref-end len))) + ))) + + ;; Ensure a trailing \t. + (end-of-line) + (or (= (char-after (1- (point))) ?\t) + (insert ?\t))) + + (defun nnvirtual-possibly-change-server (server) (or (not server) (nnoo-current-server-p 'nnvirtual server) (nnvirtual-open-server server))) -(defun nnvirtual-update-marked () - "Copy marks from the virtual group to the component groups." - (let ((mark-lists gnus-article-mark-lists) - (marks (gnus-info-marks (gnus-get-info (nnvirtual-current-group)))) - type list mart cgroups) - (while (setq type (cdr (pop mark-lists))) - (setq list (gnus-uncompress-range (cdr (assq type marks)))) - (setq cgroups - (mapcar (lambda (g) (list g)) nnvirtual-component-groups)) - (while list - (nconc (assoc (cadr (setq mart (assq (pop list) nnvirtual-mapping))) - cgroups) - (list (caddr mart)))) - (while cgroups - (gnus-add-marked-articles - (caar cgroups) type (cdar cgroups) nil t) - (gnus-group-update-group (car (pop cgroups)) t))))) (defun nnvirtual-update-reads () - "Copy (un)reads from the current group to the component groups." - (let ((groups (mapcar (lambda (g) (list g)) nnvirtual-component-groups)) - (articles (gnus-list-of-unread-articles - (nnvirtual-current-group))) - m) - (while articles - (setq m (assq (pop articles) nnvirtual-mapping)) - (nconc (assoc (nth 1 m) groups) (list (nth 2 m)))) + "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) + "Copy marks from the virtual group to the component groups. +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 - (gnus-update-read-articles (caar groups) (cdr (pop 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 () "Return the prefixed name of the current nnvirtual group." (concat "nnvirtual:" nnvirtual-current-group)) -(defsubst nnvirtual-marks (article marks) - "Return a list of mark types for ARTICLE." - (let (out) - (while marks - (when (memq article (cdar marks)) - (push (caar marks) out)) - (setq marks (cdr marks))) - out)) + + +;;; This is currently O(kn^2) to merge n lists of length k. +;;; You could do it in O(knlogn), but we have a small n, and the +;;; overhead of the other approach is probably greater. +(defun nnvirtual-merge-sorted-lists (&rest lists) + "Merge many sorted lists of numbers." + (if (null (cdr lists)) + (car lists) + (apply 'nnvirtual-merge-sorted-lists + (merge 'list (car lists) (cadr lists) '<) + (cddr lists)))) + + + +;;; 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: +;;; +;;; 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 + +;;; To compute these mappings we generate a couple tables and then +;;; do some fast operations on them. Tables for the example above: +;;; +;;; Offsets - [(A 0) (B -3) (C -1)] +;;; +;;; a b c d e +;;; Mapping - ([ 3 0 1 3 0 ] +;;; [ 6 3 2 9 3 ] +;;; [ 8 6 3 15 9 ]) +;;; +;;; (note column 'e' is different in real algorithm, which is slightly +;;; different than described here, but this gives you the methodology.) +;;; +;;; The basic idea is this, when going from component->virtual, apply +;;; the appropriate offset to the article number. Then search the first +;;; column of the table for a row where 'a' is less than or equal to the +;;; modified number. You can see that only group A can therefore go to +;;; the first row, groups A and B to the second, and all to the last. +;;; The third column of the table is telling us the number of groups +;;; which might be able to reach that row (it might increase by more than +;;; 1 if several groups have the same size). +;;; Then column 'b' provides an additional offset you apply when you have +;;; found the correct row. You then multiply by 'c' and add on the groups +;;; _position_ in the offset table. The basic idea here is that on +;;; any given row we are going to map back and forth using X'=X*c+Y and +;;; X=(X'/c), Y=(X' mod c). Then once you've done this transformation, +;;; you apply a final offset from column 'e' to give the virtual article. +;;; +;;; Going the other direction, you instead search on column 'd' instead +;;; of 'a', and apply everything in reverse order. + +;;; Convert component -> virtual: +;;; set num = num - Offset(group) +;;; find first row in Mapping where num <= 'a' +;;; num = (num-'b')*c + Position(group) + 'e' + +;;; Convert virtual -> component: +;;; find first row in Mapping where num <= 'd' +;;; num = num - 'e' +;;; group_pos = num mod 'c' +;;; num = (num / 'c') + 'b' + Offset(group_pos) + +;;; Easy no? :) +;;; +;;; Well actually, you need to keep column e offset smaller by the 'c' +;;; column for that line, and always add 1 more when going from +;;; component -> virtual. Otherwise you run into a problem with +;;; unique reverse mapping. + +(defun nnvirtual-map-article (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 + (> article (aref (car table) 3))) + (setq table (cdr table))) + (when (and table + (> article 0)) + (setq entry (car table)) + (setq article (- article (aref entry 4) 1)) + (setq group-pos (mod article (aref entry 2))) + (cons (car (aref nnvirtual-mapping-offsets group-pos)) + (+ (/ article (aref entry 2)) + (aref entry 1) + (cdr (aref nnvirtual-mapping-offsets group-pos))) + )) + )) + + + +(defun nnvirtual-reverse-map-article (group article) + "Return the virtual article number corresponding to the given +component GROUP and ARTICLE." + (let ((table nnvirtual-mapping-table) + (group-pos 0) + entry) + (while (not (string= group (car (aref nnvirtual-mapping-offsets + group-pos)))) + (setq group-pos (1+ group-pos))) + (setq article (- article (cdr (aref nnvirtual-mapping-offsets + group-pos)))) + (while (and table + (> article (aref (car table) 0))) + (setq table (cdr table))) + (setq entry (car table)) + (when (and entry + (> article 0) + (< group-pos (aref entry 2))) ; article not out of range below + (+ (aref entry 4) + group-pos + (* (- article (aref entry 1)) + (aref entry 2)) + 1)) + )) + + +(defun nnvirtual-reverse-map-sequence (group articles) + "Return list of virtual article numbers for all ARTICLES in GROUP. +The ARTICLES should be sorted, and can be a compressed sequence. +If any of the article numbers has no corresponding virtual article, +then it is left out of the result." + (when (numberp (cdr-safe articles)) + (setq articles (list articles))) + (let (result a i j new-a) + (while (setq a (pop articles)) + (if (atom a) + (setq i a + j a) + (setq i (car a) + j (cdr a))) + (while (<= i j) + ;; If this is slow, you can optimize by moving article checking + ;; into here. You don't have to recompute the group-pos, + ;; nor scan the table every time. + (when (setq new-a (nnvirtual-reverse-map-article group i)) + (push new-a result)) + (setq i (1+ i)))) + (nreverse 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." + (when (numberp (cdr-safe articles)) + (setq articles (list articles))) + (let ((carticles (mapcar (lambda (g) (list g)) + nnvirtual-component-groups)) + a i j article entry) + (while (setq a (pop articles)) + (if (atom a) + (setq i a + j a) + (setq i (car a) + j (cdr a))) + (while (<= i j) + (when (setq article (nnvirtual-map-article i)) + (setq entry (assoc (car article) carticles)) + (setcdr entry (cons (cdr article) (cdr entry)))) + (setq i (1+ i)))) + (mapc '(lambda (x) (setcdr x (nreverse (cdr x)))) + carticles) + carticles)) + (defun nnvirtual-create-mapping () - "Create an article mapping for the current group." - (let* ((div nil) - m unreads marks active - (map (sort - (apply - 'nconc - (mapcar - (lambda (g) - (when (and (setq active (gnus-activate-group g)) - (>= (cdr active) (car active))) - (setq unreads (gnus-list-of-unread-articles g) - marks (gnus-uncompress-marks - (gnus-info-marks (gnus-get-info g)))) - (when gnus-use-cache - (push (cons 'cache (gnus-cache-articles-in-group g)) - marks)) - (setq div (/ (float (car active)) - (if (zerop (cdr active)) - 1 (cdr active)))) - (mapcar (lambda (n) - (list (* div (- n (car active))) - g n (and (memq n unreads) t) - (inline (nnvirtual-marks n marks)))) - (gnus-uncompress-range active)))) - nnvirtual-component-groups)) - (lambda (m1 m2) - (< (car m1) (car m2))))) - (i 0)) - (setq nnvirtual-mapping map) - ;; Set the virtual article numbers. - (while (setq m (pop map)) - (setcar m (incf i))))) + "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) + (i 0) + actives all-unreads all-marks + active min max size unreads marks + next-M next-tot + reads beg) + ;; Ok, we loop over all component groups and collect a lot of + ;; information: + ;; Into actives we place (g size max), where size is max-min+1. + ;; Into all-unreads we put (g unreads). + ;; Into all-marks we put (g marks). + ;; We also increment cnt and tot here, and compute M (max of sizes). + (mapc (lambda (g) + (setq active (gnus-activate-group g) + min (car active) + max (cdr active)) + (when (and active (>= max min)) + ;; store active information + (push (list g (- max min -1) max) actives) + ;; collect unread/mark info for later + (setq unreads (gnus-list-of-unread-articles g)) + (setq marks (gnus-info-marks (gnus-get-info g))) + (when gnus-use-cache + (push (cons 'cache + (gnus-cache-articles-in-group g)) + marks)) + (push (cons g unreads) all-unreads) + (push (cons g marks) all-marks) + ;; count groups, total #articles, and max size + (setq size (- max min -1)) + (setq cnt (1+ cnt) + tot (+ tot size) + M (max M size)))) + nnvirtual-component-groups) + + ;; Number of articles in the virtual group. + (setq nnvirtual-mapping-len tot) + + + ;; 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 + (nreverse + (mapcar (lambda (entry) + (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)) + (while actives + (setq size (car actives)) + (setq next-M (- M size)) + (setq next-tot (- tot (* cnt size))) + ;; make current row in table + (push (vector M next-M cnt tot (- next-tot cnt)) + nnvirtual-mapping-table) + ;; update M and tot + (setq M next-M) + (setq tot next-tot) + ;; subtract the current size from all entries. + (setq actives (mapcar (lambda (x) (- x size)) actives)) + ;; remove anything that went to 0. + (while (and actives + (= (car actives) 0)) + (pop actives) + (setq cnt (- cnt 1)))) + + + ;; Now that the mapping tables are generated, we can convert + ;; and combine the separate component unreads and marks lists + ;; into single lists of virtual article numbers. + (setq unreads (apply 'nnvirtual-merge-sorted-lists + (mapcar (lambda (x) + (nnvirtual-reverse-map-sequence + (car x) (cdr x))) + all-unreads))) + (setq marks (mapcar + (lambda (type) + (cons (cdr type) + (gnus-compress-sequence + (apply + 'nnvirtual-merge-sorted-lists + (mapcar (lambda (x) + (nnvirtual-reverse-map-sequence + (car x) + (cdr (assq (cdr type) (cdr x))))) + all-marks))))) + gnus-article-mark-lists)) + + ;; Remove any empty marks lists, and store. + (setq nnvirtual-mapping-marks (delete-if-not 'cdr marks)) + + ;; We need to convert the unreads to reads. We compress the + ;; sequence as we go, otherwise it could be huge. + (while (and (<= (incf i) nnvirtual-mapping-len) + unreads) + (if (= i (car unreads)) + (setq unreads (cdr unreads)) + ;; try to get a range. + (setq beg i) + (while (and (<= (incf i) nnvirtual-mapping-len) + (not (= i (car unreads))))) + (setq i (- i 1)) + (if (= i beg) + (push i reads) + (push (cons beg i) reads)) + )) + (when (<= i nnvirtual-mapping-len) + (if (= i nnvirtual-mapping-len) + (push i reads) + (push (cons i nnvirtual-mapping-len) reads))) + + ;; Store the reads list for later use. + (setq nnvirtual-mapping-reads (nreverse reads)) + )) (provide 'nnvirtual) diff --git a/lisp/nnweb.el b/lisp/nnweb.el index 5162cff1e..f6549a2a3 100644 --- a/lisp/nnweb.el +++ b/lisp/nnweb.el @@ -49,7 +49,7 @@ (article . nnweb-dejanews-wash-article) (map . nnweb-dejanews-create-mapping) (search . nnweb-dejanews-search) - (address . "http://search.dejanews.com/dnquery.xp")) + (address . "http://xp9.dejanews.com/dnquery.xp")) (reference (article . nnweb-reference-wash-article) (map . nnweb-reference-create-mapping) @@ -322,7 +322,7 @@ (defun nnweb-fetch-form (url pairs) (let ((url-request-data (nnweb-encode-www-form-urlencoded pairs)) - (url-request-method 'POST) + (url-request-method "POST") (url-request-extra-headers '(("Content-type" . "application/x-www-form-urlencoded")))) (url-insert-file-contents url) diff --git a/lisp/smiley.el b/lisp/smiley.el index 9c18458a6..012365441 100644 --- a/lisp/smiley.el +++ b/lisp/smiley.el @@ -58,7 +58,7 @@ ("\\(:-*p\\)\\W" 1 "FaceTalking.xpm") ("\\(:-*d\\)\\W" 1 "FaceTasty.xpm") ("\\(;-*[>)}»]+\\)\\W" 1 "FaceWinking.xpm") - ("\\(:-*[Vvµ]\\)\\W" 1 "FaceWry.xpm") + ("\\(:-*[Vvµ]\\)\\W" 1 "FaceWry.xpm") ("\\([:|]-*P\\)\\W" 1 "FaceYukky.xpm")) "Normal and deformed faces for smilies.") @@ -67,7 +67,7 @@ ("\\(:-+\\]+\\)\\W" 1 "FaceGoofy.xpm") ("\\(:-+D\\)\\W" 1 "FaceGrinning.xpm") ("\\(:-+[}»]+\\)\\W" 1 "FaceHappy.xpm") - ("\\(:-*)+\\)\\W" 1 "FaceHappy.xpm") ;; The exception that confirms the rule + ("\\(:-*)+\\)\\W" 1 "FaceHappy.xpm") ("\\(:-+[/\\\"]+\\)\\W" 1 "FaceIronic.xpm") ("\\([8|]-+[|Oo%]\\)\\W" 1 "FaceKOed.xpm") ("\\([:|]-+#+\\)\\W" 1 "FaceNyah.xpm") @@ -78,6 +78,7 @@ ("\\(:-+d\\)\\W" 1 "FaceTasty.xpm") ("\\(;-+[>)}»]+\\)\\W" 1 "FaceWinking.xpm") ("\\(:-+[Vvµ]\\)\\W" 1 "FaceWry.xpm") + ("\\(][:8B]-[)>]\\)\\W" 1 "FaceDevilish.xpm") ("\\([:|]-+P\\)\\W" 1 "FaceYukky.xpm")) "Smileys with noses. These get less false matches.") diff --git a/texi/gnus.texi b/texi/gnus.texi index 7bc8a3b83..b6a8065ad 100644 --- a/texi/gnus.texi +++ b/texi/gnus.texi @@ -1,7 +1,7 @@ \input texinfo @c -*-texinfo-*- @setfilename gnus -@settitle Red Gnus 0.60 Manual +@settitle Red Gnus 0.61 Manual @synindex fn cp @synindex vr cp @synindex pg cp @@ -287,7 +287,7 @@ into another language, under the above conditions for modified versions. @tex @titlepage -@title Red Gnus 0.60 Manual +@title Red Gnus 0.61 Manual @author by Lars Magne Ingebrigtsen @page @@ -13594,6 +13594,9 @@ Hallvard B Furuseth---various bits and pieces, especially dealing with @item Brian Edmonds---@file{gnus-bbdb.el}. +@item +David Moore---rewrite of @file{nnvirtual.el} and many other things. + @item Ricardo Nassif, Mark Borges, and Jost Krieger---proof-reading. @@ -13661,7 +13664,6 @@ Shlomo Mahlab, Nat Makarevitch, Timo Metzemakers, Richard Mlynarik, -David Moore, Lantz Moore, Morioka Tomohiko, @c Morioka Hrvoje Niksic, -- 2.25.1