X-Git-Url: https://cgit.sxemacs.org/?a=blobdiff_plain;f=lisp%2Fgnus-agent.el;h=efe869af4abf73e866d167a6211b96e6cec5fce4;hb=5b8ecce52d86ed7352e6e5b5d768c34321a4c58d;hp=5128fc318c058b45e71286e71373e326946eb66c;hpb=807e3ebcecfdb28c8ea4876813070d13f174d20f;p=gnus diff --git a/lisp/gnus-agent.el b/lisp/gnus-agent.el index 5128fc318..efe869af4 100644 --- a/lisp/gnus-agent.el +++ b/lisp/gnus-agent.el @@ -1,5 +1,5 @@ ;;; gnus-agent.el --- unplugged support for Gnus -;; Copyright (C) 1997,98 Free Software Foundation, Inc. +;; Copyright (C) 1997,98,99 Free Software Foundation, Inc. ;; Author: Lars Magne Ingebrigtsen ;; This file is part of GNU Emacs. @@ -27,7 +27,9 @@ (require 'gnus-cache) (require 'nnvirtual) (require 'gnus-sum) -(eval-when-compile (require 'cl)) +(eval-when-compile + (require 'cl) + (require 'gnus-score)) (defcustom gnus-agent-directory (nnheader-concat gnus-directory "agent/") "Where the Gnus agent will store its files." @@ -77,8 +79,6 @@ If nil, only read articles will be expired." ;;; Internal variables -(defvar gnus-agent-meta-information-header "X-Gnus-Agent-Meta-Information") - (defvar gnus-agent-history-buffers nil) (defvar gnus-agent-buffer-alist nil) (defvar gnus-agent-article-alist nil) @@ -92,7 +92,11 @@ If nil, only read articles will be expired." (defvar gnus-agent-spam-hashtb nil) (defvar gnus-agent-file-name nil) (defvar gnus-agent-send-mail-function nil) -(defvar gnus-agent-article-file-coding-system 'no-conversion) +(defvar gnus-agent-file-coding-system 'binary) + +(defconst gnus-agent-scoreable-headers + '("subject" "from" "date" "message-id" "references" "chars" "lines" "xref") + "Headers that are considered when scoring articles for download via the Agent.") ;; Dynamic variables (defvar gnus-headers) @@ -108,6 +112,8 @@ If nil, only read articles will be expired." (gnus-category-read) (setq gnus-agent-overview-buffer (gnus-get-buffer-create " *Gnus agent overview*")) + (with-current-buffer gnus-agent-overview-buffer + (mm-enable-multibyte)) (add-hook 'gnus-group-mode-hook 'gnus-agent-mode) (add-hook 'gnus-summary-mode-hook 'gnus-agent-mode) (add-hook 'gnus-server-mode-hook 'gnus-agent-mode)) @@ -127,9 +133,9 @@ If nil, only read articles will be expired." (defun gnus-agent-read-file (file) "Load FILE and do a `read' there." - (nnheader-temp-write nil + (with-temp-buffer (ignore-errors - (insert-file-contents file) + (nnheader-insert-file-contents file) (goto-char (point-min)) (read (current-buffer))))) @@ -215,7 +221,8 @@ If nil, only read articles will be expired." "Jj" gnus-agent-toggle-plugged "Js" gnus-agent-fetch-session "JS" gnus-group-send-drafts - "Ja" gnus-agent-add-group) + "Ja" gnus-agent-add-group + "Jr" gnus-agent-remove-group) (defun gnus-agent-group-make-menu-bar () (unless (boundp 'gnus-agent-group-menu) @@ -311,7 +318,7 @@ agent minor mode in all Gnus buffers." (interactive) (gnus-open-agent) (add-hook 'gnus-setup-news-hook 'gnus-agent-queue-setup) - (unless gnus-agent-send-mail-function + (unless gnus-agent-send-mail-function (setq gnus-agent-send-mail-function message-send-mail-function message-send-mail-function 'gnus-agent-send-mail)) (unless gnus-agent-covered-methods @@ -334,7 +341,7 @@ agent minor mode in all Gnus buffers." (concat "^" (regexp-quote mail-header-separator) "\n")) (replace-match "\n") (gnus-agent-insert-meta-information 'mail) - (gnus-request-accept-article "nndraft:queue"))) + (gnus-request-accept-article "nndraft:queue" nil t t))) (defun gnus-agent-insert-meta-information (type &optional method) "Insert meta-information into the message that says how it's to be posted. @@ -357,11 +364,15 @@ be a select method." (defun gnus-agent-fetch-groups (n) "Put all new articles in the current groups into the Agent." (interactive "P") + (unless gnus-plugged + (error "Groups can't be fetched when Gnus is unplugged")) (gnus-group-iterate n 'gnus-agent-fetch-group)) (defun gnus-agent-fetch-group (group) "Put all new articles in GROUP into the Agent." (interactive (list (gnus-group-group-name))) + (unless gnus-plugged + (error "Groups can't be fetched when Gnus is unplugged")) (unless group (error "No group on the current line")) (let ((gnus-command-method (gnus-find-method-for-group group))) @@ -390,6 +401,16 @@ be a select method." (setf (cadddr cat) (nconc (cadddr cat) groups)) (gnus-category-write))) +(defun gnus-agent-remove-group (arg) + "Remove the current group from its agent category, if any." + (interactive "P") + (let (c) + (gnus-group-iterate arg + (lambda (group) + (when (cadddr (setq c (gnus-group-category group))) + (setf (cadddr c) (delete group (cadddr c)))))) + (gnus-category-write))) + ;;; ;;; Server mode commands ;;; @@ -427,7 +448,8 @@ be a select method." (defun gnus-agent-write-servers () "Write the alist of covered servers." - (nnheader-temp-write (nnheader-concat gnus-agent-directory "lib/servers") + (gnus-make-directory (nnheader-concat gnus-agent-directory "lib")) + (with-temp-file (nnheader-concat gnus-agent-directory "lib/servers") (prin1 gnus-agent-covered-methods (current-buffer)))) ;;; @@ -492,12 +514,23 @@ the actual number of articles toggled is returned." (when (and (not gnus-plugged) (gnus-agent-method-p gnus-command-method)) (gnus-agent-load-alist gnus-newsgroup-name) - (let ((articles gnus-newsgroup-unreads) + ;; First mark all undownloaded articles as undownloaded. + (let ((articles (append gnus-newsgroup-unreads + gnus-newsgroup-marked + gnus-newsgroup-dormant)) article) (while (setq article (pop articles)) (unless (or (cdr (assq article gnus-agent-article-alist)) - (memq article gnus-newsgroup-downloadable)) - (push article gnus-newsgroup-undownloaded))))))) + (memq article gnus-newsgroup-downloadable)) + (push article gnus-newsgroup-undownloaded)))) + ;; Then mark downloaded downloadable as not-downloadable, + ;; if you get my drift. + (let ((articles gnus-newsgroup-downloadable) + article) + (while (setq article (pop articles)) + (when (cdr (assq article gnus-agent-article-alist)) + (setq gnus-newsgroup-downloadable + (delq article gnus-newsgroup-downloadable)))))))) (defun gnus-agent-catchup () "Mark all undownloaded articles as read." @@ -517,7 +550,7 @@ the actual number of articles toggled is returned." (let* ((gnus-command-method method) (file (gnus-agent-lib-file "active"))) (gnus-make-directory (file-name-directory file)) - (let ((coding-system-for-write gnus-agent-article-file-coding-system)) + (let ((coding-system-for-write gnus-agent-file-coding-system)) (write-region (point-min) (point-max) file nil 'silent)) (when (file-exists-p (gnus-agent-lib-file "groups")) (delete-file (gnus-agent-lib-file "groups")))))) @@ -526,9 +559,10 @@ the actual number of articles toggled is returned." (let* ((gnus-command-method method) (file (gnus-agent-lib-file "groups"))) (gnus-make-directory (file-name-directory file)) - (write-region (point-min) (point-max) file nil 'silent)) + (let ((coding-system-for-write gnus-agent-file-coding-system)) + (write-region (point-min) (point-max) file nil 'silent)) (when (file-exists-p (gnus-agent-lib-file "active")) - (delete-file (gnus-agent-lib-file "active")))) + (delete-file (gnus-agent-lib-file "active"))))) (defun gnus-agent-save-group-info (method group active) (when (gnus-agent-method-p method) @@ -537,9 +571,9 @@ the actual number of articles toggled is returned." (gnus-agent-lib-file "active") (gnus-agent-lib-file "groups")))) (gnus-make-directory (file-name-directory file)) - (nnheader-temp-write file + (with-temp-file file (when (file-exists-p file) - (insert-file-contents file)) + (nnheader-insert-file-contents file)) (goto-char (point-min)) (if nntp-server-list-active-group (progn @@ -547,8 +581,9 @@ the actual number of articles toggled is returned." (concat "^" (regexp-quote group) " ") nil t) (gnus-delete-line)) (insert group " " (number-to-string (cdr active)) " " - (number-to-string (car active)) "\n")) - (when (re-search-forward (concat (regexp-quote group) " ") nil t) + (number-to-string (car active)) " y\n")) + (when (re-search-forward + (concat (regexp-quote group) "\\($\\| \\)") nil t) (gnus-delete-line)) (insert-buffer-substring nntp-server-buffer)))))) @@ -597,8 +632,9 @@ the actual number of articles toggled is returned." (save-excursion (set-buffer gnus-agent-current-history) (gnus-make-directory (file-name-directory gnus-agent-file-name)) - (write-region (1+ (point-min)) (point-max) - gnus-agent-file-name nil 'silent))) + (let ((coding-system-for-write gnus-agent-file-coding-system)) + (write-region (1+ (point-min)) (point-max) + gnus-agent-file-name nil 'silent)))) (defun gnus-agent-close-history () (when (gnus-buffer-live-p gnus-agent-current-history) @@ -654,7 +690,7 @@ the actual number of articles toggled is returned." (let ((dir (concat (gnus-agent-directory) (gnus-agent-group-path group) "/")) - (date (gnus-time-to-day (current-time))) + (date (time-to-days (current-time))) (case-fold-search t) pos crosses id elem) (gnus-make-directory dir) @@ -662,7 +698,7 @@ the actual number of articles toggled is returned." ;; Fetch the articles from the backend. (if (gnus-check-backend-function 'retrieve-articles group) (setq pos (gnus-retrieve-articles articles group)) - (nnheader-temp-write nil + (with-temp-buffer (let (article) (while (setq article (pop articles)) (when (gnus-request-article article group) @@ -696,7 +732,7 @@ the actual number of articles toggled is returned." (setq id "No-Message-ID-in-article") (setq id (buffer-substring (match-beginning 1) (match-end 1)))) (let ((coding-system-for-write - gnus-agent-article-file-coding-system)) + gnus-agent-file-coding-system)) (write-region (point-min) (point-max) (concat dir (number-to-string (caar pos))) nil 'silent)) @@ -728,7 +764,7 @@ the actual number of articles toggled is returned." (when (= (point-max) (point-min)) (push (cons group (current-buffer)) gnus-agent-buffer-alist) (ignore-errors - (insert-file-contents + (nnheader-insert-file-contents (gnus-agent-article-name ".overview" group)))) (nnheader-find-nov-line (string-to-number (cdar crosses))) (insert (string-to-number (cdar crosses))) @@ -739,44 +775,57 @@ the actual number of articles toggled is returned." (save-excursion (while gnus-agent-buffer-alist (set-buffer (cdar gnus-agent-buffer-alist)) - (write-region (point-min) (point-max) - (gnus-agent-article-name ".overview" - (caar gnus-agent-buffer-alist)) - nil 'silent) + (let ((coding-system-for-write + gnus-agent-file-coding-system)) + (write-region (point-min) (point-max) + (gnus-agent-article-name ".overview" + (caar gnus-agent-buffer-alist)) + nil 'silent)) (pop gnus-agent-buffer-alist)) (while gnus-agent-group-alist - (nnheader-temp-write (caar gnus-agent-group-alist) + (with-temp-file (caar gnus-agent-group-alist) (princ (cdar gnus-agent-group-alist)) (insert "\n")) (pop gnus-agent-group-alist)))) (defun gnus-agent-fetch-headers (group &optional force) - (when (gnus-agent-load-alist group) - (let ((articles (gnus-uncompress-range - (cons (1+ (caar (last (gnus-agent-load-alist group)))) - (cdr (gnus-active group)))))) - ;; Fetch them. - (when articles - (gnus-message 7 "Fetching headers for %s..." group) - (save-excursion - (set-buffer nntp-server-buffer) - (unless (eq 'nov (gnus-retrieve-headers articles group)) - (nnvirtual-convert-headers)) - ;; Save these headers for later processing. - (copy-to-buffer gnus-agent-overview-buffer (point-min) (point-max)) - (let (file) - (when (file-exists-p - (setq file (gnus-agent-article-name ".overview" group))) - (gnus-agent-braid-nov group articles file)) - (gnus-make-directory (nnheader-translate-file-chars - (file-name-directory file))) - (write-region (point-min) (point-max) file nil 'silent) - (gnus-agent-save-alist group articles nil) - (gnus-agent-enter-history - "last-header-fetched-for-session" - (list (cons group (nth (- (length articles) 1) articles))) - (gnus-time-to-day (current-time))) - articles)))))) + (let ((articles (gnus-list-of-unread-articles group)) + (gnus-decode-encoded-word-function 'identity) + (file (gnus-agent-article-name ".overview" group))) + ;; add article with marks to list of article headers we want to fetch + (dolist (arts (gnus-info-marks (gnus-get-info group))) + (setq articles (union (gnus-uncompress-sequence (cdr arts)) + articles))) + (setq articles (sort articles '<)) + ;; remove known articles + (when (gnus-agent-load-alist group) + (setq articles (gnus-sorted-intersection + articles + (gnus-uncompress-range + (cons (1+ (caar (last gnus-agent-article-alist))) + (cdr (gnus-active group))))))) + ;; Fetch them. + (gnus-make-directory (nnheader-translate-file-chars + (file-name-directory file))) + (when articles + (gnus-message 7 "Fetching headers for %s..." group) + (save-excursion + (set-buffer nntp-server-buffer) + (unless (eq 'nov (gnus-retrieve-headers articles group)) + (nnvirtual-convert-headers)) + ;; Save these headers for later processing. + (copy-to-buffer gnus-agent-overview-buffer (point-min) (point-max)) + (when (file-exists-p file) + (gnus-agent-braid-nov group articles file)) + (let ((coding-system-for-write + gnus-agent-file-coding-system)) + (write-region (point-min) (point-max) file nil 'silent)) + (gnus-agent-save-alist group articles nil) + (gnus-agent-enter-history + "last-header-fetched-for-session" + (list (cons group (nth (- (length articles) 1) articles))) + (time-to-days (current-time))) + articles)))) (defsubst gnus-agent-copy-nov-line (article) (let (b e) @@ -795,7 +844,7 @@ the actual number of articles toggled is returned." (goto-char (point-min)) (set-buffer nntp-server-buffer) (erase-buffer) - (insert-file-contents file) + (nnheader-insert-file-contents file) (goto-char (point-max)) (if (or (= (point-min) (point-max)) (progn @@ -837,9 +886,9 @@ the actual number of articles toggled is returned." (defun gnus-agent-save-alist (group &optional articles state dir) "Save the article-state alist for GROUP." - (nnheader-temp-write (if dir - (concat dir ".agentview") - (gnus-agent-article-name ".agentview" group)) + (with-temp-file (if dir + (concat dir ".agentview") + (gnus-agent-article-name ".agentview" group)) (princ (setq gnus-agent-article-alist (nconc gnus-agent-article-alist (mapcar (lambda (article) (cons article state)) @@ -884,6 +933,7 @@ the actual number of articles toggled is returned." (defun gnus-agent-fetch-group-1 (group method) "Fetch GROUP." (let ((gnus-command-method method) + (gnus-newsgroup-name group) gnus-newsgroup-dependencies gnus-newsgroup-headers gnus-newsgroup-scored gnus-headers gnus-score gnus-use-cache articles arts @@ -894,27 +944,64 @@ the actual number of articles toggled is returned." ;; Parse them and see which articles we want to fetch. (setq gnus-newsgroup-dependencies (make-vector (length articles) 0)) - (setq gnus-newsgroup-headers - (gnus-get-newsgroup-headers-xover articles nil nil group)) + ;; No need to call `gnus-get-newsgroup-headers-xover' with + ;; the entire .overview for group as we still have the just + ;; downloaded headers in `gnus-agent-overview-buffer'. + (let ((nntp-server-buffer gnus-agent-overview-buffer)) + (setq gnus-newsgroup-headers + (gnus-get-newsgroup-headers-xover articles nil nil group))) (setq category (gnus-group-category group)) (setq predicate - (gnus-get-predicate - (or (gnus-group-get-parameter group 'agent-predicate) + (gnus-get-predicate + (or (gnus-group-get-parameter group 'agent-predicate t) (cadr category)))) - (setq score-param - (or (gnus-group-get-parameter group 'agent-score) - (caddr category))) - (when score-param - (gnus-score-headers (list (list score-param)))) - (setq arts nil) - (while (setq gnus-headers (pop gnus-newsgroup-headers)) - (setq gnus-score - (or (cdr (assq (mail-header-number gnus-headers) - gnus-newsgroup-scored)) - gnus-summary-default-score)) - (when (funcall predicate) - (push (mail-header-number gnus-headers) - arts))) + ;; Do we want to download everything, or nothing? + (if (or (eq (caaddr predicate) 'gnus-agent-true) + (eq (caaddr predicate) 'gnus-agent-false)) + ;; Yes. + (setq arts (symbol-value + (cadr (assoc (caaddr predicate) + '((gnus-agent-true articles) + (gnus-agent-false nil)))))) + ;; No, we need to decide what we want. + (setq score-param + (let ((score-method + (or + (gnus-group-get-parameter group 'agent-score t) + (caddr category)))) + (when score-method + (require 'gnus-score) + (if (eq score-method 'file) + (let ((entries + (gnus-score-load-files + (gnus-all-score-files group))) + list score-file) + (while (setq list (car entries)) + (push (car list) score-file) + (setq list (cdr list)) + (while list + (when (member (caar list) + gnus-agent-scoreable-headers) + (push (car list) score-file)) + (setq list (cdr list))) + (setq score-param + (append score-param (list (nreverse score-file))) + score-file nil entries (cdr entries))) + (list score-param)) + (if (stringp (car score-method)) + score-method + (list (list score-method))))))) + (when score-param + (gnus-score-headers score-param)) + (setq arts nil) + (while (setq gnus-headers (pop gnus-newsgroup-headers)) + (setq gnus-score + (or (cdr (assq (mail-header-number gnus-headers) + gnus-newsgroup-scored)) + gnus-summary-default-score)) + (when (funcall predicate) + (push (mail-header-number gnus-headers) + arts)))) ;; Fetch the articles. (when arts (gnus-agent-fetch-articles group arts))) @@ -925,7 +1012,11 @@ the actual number of articles toggled is returned." (gnus-agent-fetch-articles group (gnus-uncompress-range (cdr arts))) (setq marks (delq arts (gnus-info-marks info))) - (gnus-info-set-marks info marks)))) + (gnus-info-set-marks info marks) + (gnus-dribble-enter + (concat "(gnus-group-set-info '" + (gnus-prin1-to-string info) + ")"))))) ;;; ;;; Agent Category Mode @@ -1027,7 +1118,7 @@ The following commands are available: (gnus-set-default-directory) (setq mode-line-process nil) (use-local-map gnus-category-mode-map) - (buffer-disable-undo (current-buffer)) + (buffer-disable-undo) (setq truncate-lines t) (setq buffer-read-only t) (gnus-run-hooks 'gnus-category-mode-hook)) @@ -1079,12 +1170,13 @@ The following commands are available: (or (gnus-agent-read-file (nnheader-concat gnus-agent-directory "lib/categories")) (list (list 'default 'short nil nil))))) - + (defun gnus-category-write () "Write the category alist." (setq gnus-category-predicate-cache nil gnus-category-group-cache nil) - (nnheader-temp-write (nnheader-concat gnus-agent-directory "lib/categories") + (gnus-make-directory (nnheader-concat gnus-agent-directory "lib")) + (with-temp-file (nnheader-concat gnus-agent-directory "lib/categories") (prin1 gnus-category-alist (current-buffer)))) (defun gnus-category-edit-predicate (category) @@ -1097,7 +1189,7 @@ The following commands are available: (setf (cadr (assq ',category gnus-category-alist)) predicate) (gnus-category-write) (gnus-category-list))))) - + (defun gnus-category-edit-score (category) "Edit the score expression for CATEGORY." (interactive (list (gnus-category-name))) @@ -1145,7 +1237,7 @@ The following commands are available: (interactive "SCategory name: ") (when (assq category gnus-category-alist) (error "Category %s already exists" category)) - (push (list category 'true nil nil) + (push (list category 'false nil nil) gnus-category-alist) (gnus-category-write) (gnus-category-list)) @@ -1212,7 +1304,7 @@ The following commands are available: (defun gnus-agent-false () "Return nil." nil) - + (defun gnus-category-make-function-1 (cat) "Make a function from category CAT." (cond @@ -1258,7 +1350,7 @@ The following commands are available: "Expire all old articles." (interactive) (let ((methods gnus-agent-covered-methods) - (day (- (gnus-time-to-day (current-time)) gnus-agent-expire-days)) + (day (- (time-to-days (current-time)) gnus-agent-expire-days)) gnus-command-method sym group articles history overview file histories elem art nov-file low info unreads marked article) @@ -1307,7 +1399,7 @@ The following commands are available: (set-buffer overview) (erase-buffer) (when (file-exists-p nov-file) - (insert-file-contents nov-file)) + (nnheader-insert-file-contents nov-file)) (goto-char (point-min)) (setq article 0) (while (setq elem (pop articles)) @@ -1340,13 +1432,16 @@ The following commands are available: ;; Schedule the history line for nuking. (push (cdr elem) histories))) (gnus-make-directory (file-name-directory nov-file)) - (write-region (point-min) (point-max) nov-file nil 'silent) + (let ((coding-system-for-write + gnus-agent-file-coding-system)) + (write-region (point-min) (point-max) nov-file nil 'silent)) ;; Delete the unwanted entries in the alist. (setq gnus-agent-article-alist (sort gnus-agent-article-alist 'car-less-than-car)) (let* ((alist gnus-agent-article-alist) (prev (cons nil alist)) - (first prev)) + (first prev) + expired) (while (and alist (<= (caar alist) article)) (if (or (not (cdar alist)) @@ -1355,22 +1450,36 @@ The following commands are available: (number-to-string (caar alist)) group)))) - (setcdr prev (setq alist (cdr alist))) + (progn + (push (caar alist) expired) + (setcdr prev (setq alist (cdr alist)))) (setq prev alist alist (cdr alist)))) (setq gnus-agent-article-alist (cdr first)) - ;;; Mark all articles up to the first article - ;;; in `gnus-article-alist' as read. - (when (caar gnus-agent-article-alist) + (gnus-agent-save-alist group) + ;; Mark all articles up to the first article + ;; in `gnus-article-alist' as read. + (when (and info (caar gnus-agent-article-alist)) (setcar (nthcdr 2 info) (gnus-range-add (nth 2 info) (cons 1 (- (caar gnus-agent-article-alist) 1))))) + ;; Maybe everything has been expired from `gnus-article-alist' + ;; and so the above marking as read could not be conducted, + ;; or there are expired article within the range of the alist. + (when (and info + expired + (or (not (caar gnus-agent-article-alist)) + (> (car expired) + (caar gnus-agent-article-alist)))) + (setcar (nthcdr 2 info) + (gnus-add-to-range + (nth 2 info) + (nreverse expired)))) (gnus-dribble-enter (concat "(gnus-group-set-info '" (gnus-prin1-to-string info) - ")")) - (gnus-agent-save-alist group))) + ")")))) expiry-hashtb) (set-buffer history) (setq histories (nreverse (sort histories '<)))