X-Git-Url: http://git.chise.org/gitweb/?a=blobdiff_plain;f=lisp%2Fgnus-agent.el;h=7d40b8219812205036ef325b998e6636734af31c;hb=3e59b4eab4dc5aadb05830e6b055c6a468656286;hp=a23249363854ec4242839e70f5171d8b70b7e0ca;hpb=0a3c83b64e8c32294def8d9465cfb57b583f92a1;p=elisp%2Fgnus.git- diff --git a/lisp/gnus-agent.el b/lisp/gnus-agent.el index a232493..7d40b82 100644 --- a/lisp/gnus-agent.el +++ b/lisp/gnus-agent.el @@ -1,9 +1,7 @@ -;;; gnus-agent.el --- unplugged support for Gnus -;; Copyright (C) 1997 Free Software Foundation, Inc. - -;; Author: Lars Magne Ingebrigtsen -;; Keywords: news +;;; gnus-agent.el --- unplugged support for Semi-gnus +;; Copyright (C) 1997,98 Free Software Foundation, Inc. +;; Author: Lars Magne Ingebrigtsen ;; This file is part of GNU Emacs. ;; GNU Emacs is free software; you can redistribute it and/or modify @@ -51,8 +49,36 @@ :group 'gnus-agent :type 'integer) +(defcustom gnus-agent-expire-days 7 + "Read articles older than this will be expired." + :group 'gnus-agent + :type 'integer) + +(defcustom gnus-agent-expire-all nil + "If non-nil, also expire unread, ticked and dormant articles. +If nil, only read articles will be expired." + :group 'gnus-agent + :type 'boolean) + +(defcustom gnus-agent-group-mode-hook nil + "Hook run in Agent group minor modes." + :group 'gnus-agent + :type 'hook) + +(defcustom gnus-agent-summary-mode-hook nil + "Hook run in Agent summary minor modes." + :group 'gnus-agent + :type 'hook) + +(defcustom gnus-agent-server-mode-hook nil + "Hook run in Agent summary minor modes." + :group 'gnus-agent + :type 'hook) + ;;; 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) @@ -115,7 +141,8 @@ (defsubst gnus-agent-directory () "Path of the Gnus agent directory." - (nnheader-concat gnus-agent-directory (gnus-agent-method) "/")) + (nnheader-concat gnus-agent-directory + (nnheader-translate-file-chars (gnus-agent-method)) "/")) (defun gnus-agent-lib-file (file) "The full path of the Gnus agent library FILE." @@ -177,7 +204,8 @@ buffer)))) minor-mode-map-alist)) (gnus-agent-toggle-plugged gnus-plugged) - (run-hooks 'gnus-agent-mode-hook))) + (gnus-run-hooks 'gnus-agent-mode-hook + (intern (format "gnus-agent-%s-mode-hook" buffer))))) (defvar gnus-agent-group-mode-map (make-sparse-keymap)) (gnus-define-keys gnus-agent-group-mode-map @@ -239,12 +267,13 @@ (interactive (list (not gnus-plugged))) (if plugged (progn - (run-hooks 'gnus-agent-plugged-hook) + (setq gnus-plugged plugged) + (gnus-run-hooks 'gnus-agent-plugged-hook) (setcar (cdr gnus-agent-mode-status) " Plugged")) (gnus-agent-close-connections) - (run-hooks 'gnus-agent-unplugged-hook) + (setq gnus-plugged plugged) + (gnus-run-hooks 'gnus-agent-unplugged-hook) (setcar (cdr gnus-agent-mode-status) " Unplugged")) - (setq gnus-plugged plugged) (set-buffer-modified-p t)) (defun gnus-agent-close-connections () @@ -254,13 +283,20 @@ (gnus-close-server (pop methods))))) ;;;###autoload -(defun gnus-ungplugged () +(defun gnus-unplugged () "Start Gnus unplugged." (interactive) (setq gnus-plugged nil) (gnus)) ;;;###autoload +(defun gnus-plugged () + "Start Gnus plugged." + (interactive) + (setq gnus-plugged t) + (gnus)) + +;;;###autoload (defun gnus-agentize () "Allow Gnus to be an offline newsreader. The normal usage of this command is to put the following as the @@ -296,19 +332,34 @@ agent minor mode in all Gnus buffers." (re-search-forward (concat "^" (regexp-quote mail-header-separator) "\n")) (replace-match "\n") + (gnus-agent-insert-meta-information 'mail) (gnus-request-accept-article "nndraft:queue"))) +(defun gnus-agent-insert-meta-information (type &optional method) + "Insert meta-information into the message that says how it's to be posted. +TYPE can be either `mail' or `news'. If the latter METHOD can +be a select method." + (save-excursion + (message-remove-header gnus-agent-meta-information-header) + (goto-char (point-min)) + (insert gnus-agent-meta-information-header ": " + (symbol-name type) " " (format "%S" method) + "\n") + (forward-char -1) + (while (search-backward "\n" nil t) + (replace-match "\\n" t t)))) + ;;; ;;; Group mode commands ;;; (defun gnus-agent-fetch-groups (n) - "Put all new articles in the current groups into the agent." + "Put all new articles in the current groups into the Agent." (interactive "P") (gnus-group-iterate n 'gnus-agent-fetch-group)) (defun gnus-agent-fetch-group (group) - "Put all new articles in GROUP into the agent." + "Put all new articles in GROUP into the Agent." (interactive (list (gnus-group-group-name))) (unless group (error "No group on the current line")) @@ -352,7 +403,7 @@ agent minor mode in all Gnus buffers." (error "Server already in the agent program")) (push method gnus-agent-covered-methods) (gnus-agent-write-servers) - (message "Entered %s into the agent" server))) + (message "Entered %s into the Agent" server))) (defun gnus-agent-remove-server (server) "Remove SERVER from the agent program." @@ -478,10 +529,26 @@ the actual number of articles toggled is returned." (when (file-exists-p (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) + (let* ((gnus-command-method method) + (file (gnus-agent-lib-file "active"))) + (gnus-make-directory (file-name-directory file)) + (nnheader-temp-write file + (insert-file-contents file) + (goto-char (point-min)) + (when (re-search-forward (concat "^" (regexp-quote group) " ") nil t) + (gnus-delete-line)) + (insert group " " (number-to-string (cdr active)) " " + (number-to-string (car active)) "\n"))))) + (defun gnus-agent-group-path (group) "Translate GROUP into a path." - (nnheader-translate-file-chars - (nnheader-replace-chars-in-string group ?. ?/))) + (if nnmail-use-long-file-names + (gnus-group-real-name group) + (nnheader-replace-chars-in-string + (nnheader-translate-file-chars (gnus-group-real-name group)) + ?. ?/))) @@ -536,7 +603,7 @@ the actual number of articles toggled is returned." (goto-char (point-max)) (insert id "\t" (number-to-string date) "\t") (while group-arts - (insert (caar group-arts) "/" (number-to-string (cdr (pop group-arts))) + (insert (caar group-arts) " " (number-to-string (cdr (pop group-arts))) " ")) (insert "\n"))) @@ -562,7 +629,7 @@ the actual number of articles toggled is returned." ;;; (defun gnus-agent-fetch-articles (group articles) - "Fetch ARTICLES from GROUP and put them into the agent." + "Fetch ARTICLES from GROUP and put them into the Agent." (when articles ;; Prune off articles that we have already fetched. (while (and articles @@ -595,7 +662,7 @@ the actual number of articles toggled is returned." (insert-buffer-substring nntp-server-buffer))) (copy-to-buffer nntp-server-buffer (point-min) (point-max)) (setq pos (nreverse pos))))) - ;; Then save these articles into the agent. + ;; Then save these articles into the Agent. (save-excursion (set-buffer nntp-server-buffer) (while pos @@ -619,7 +686,8 @@ the actual number of articles toggled is returned." (if (not (re-search-forward "^Message-ID: *<\\([^>\n]+\\)>" nil t)) (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)) + (let ((coding-system-for-write + gnus-agent-article-file-coding-system)) (write-region (point-min) (point-max) (concat dir (number-to-string (caar pos))) nil 'silent)) @@ -651,7 +719,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))) @@ -692,6 +760,17 @@ the actual number of articles toggled is returned." (set-buffer nntp-server-buffer) (unless (eq 'nov (gnus-retrieve-headers articles group)) (nnvirtual-convert-headers)) + ;; + ;; To gnus-agent-expire work fine with no Xref field in .overview + ;; Tatsuya Ichikawa + (goto-char (point-min)) + (while (not (eobp)) + (goto-char (point-at-eol)) + (insert "\t") + (forward-line 1)) + ;; Tatsuya Ichikawa + ;; To gnus-agent-expire work fine with no Xref field in .overview + ;; ;; Save these headers for later processing. (copy-to-buffer gnus-agent-overview-buffer (point-min) (point-max)) (let (file) @@ -720,7 +799,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-min)) (if (or (= (point-min) (point-max)) (progn @@ -795,12 +874,15 @@ the actual number of articles toggled is returned." groups group gnus-command-method) (save-excursion (while methods - (setq gnus-command-method (car methods) - groups (nreverse (gnus-groups-from-server (pop methods)))) - (gnus-agent-with-fetch - (while (setq group (pop groups)) - (when (<= (gnus-group-level group) gnus-agent-handle-level) - (gnus-agent-fetch-group-1 group gnus-command-method))))) + (setq gnus-command-method (car methods)) + (when (or (gnus-server-opened gnus-command-method) + (gnus-open-server gnus-command-method)) + (setq groups (gnus-groups-from-server (car methods))) + (gnus-agent-with-fetch + (while (setq group (pop groups)) + (when (<= (gnus-group-level group) gnus-agent-handle-level) + (gnus-agent-fetch-group-1 group gnus-command-method))))) + (pop methods)) (gnus-message 6 "Finished fetching articles into the Gnus agent")))) (defun gnus-agent-fetch-group-1 (group method) @@ -811,7 +893,8 @@ the actual number of articles toggled is returned." gnus-use-cache articles score arts category predicate info marks score-param) ;; Fetch headers. - (when (and (setq articles (gnus-list-of-unread-articles group)) + (when (and (or (gnus-active group) (gnus-activate-group group)) + (setq articles (gnus-list-of-unread-articles group)) (gnus-agent-fetch-headers group articles)) ;; Parse them and see which articles we want to fetch. (setq gnus-newsgroup-dependencies @@ -926,7 +1009,7 @@ the actual number of articles toggled is returned." ["Edit groups" gnus-category-edit-groups t] ["Exit" gnus-category-exit t])) - (run-hooks 'gnus-category-menu-hook))) + (gnus-run-hooks 'gnus-category-menu-hook))) (defun gnus-category-mode () "Major mode for listing and editing agent categories. @@ -952,7 +1035,7 @@ The following commands are available: (buffer-disable-undo (current-buffer)) (setq truncate-lines t) (setq buffer-read-only t) - (run-hooks 'gnus-category-mode-hook)) + (gnus-run-hooks 'gnus-category-mode-hook)) (defalias 'gnus-category-position-point 'gnus-goto-colon) @@ -1122,7 +1205,7 @@ The following commands are available: (defun gnus-agent-high-scored-p () "Say whether an article has a high score or not." - (> gnus-score gnus-agent-low-score)) + (> gnus-score gnus-agent-high-score)) (defun gnus-category-make-function (cat) "Make a function from category CAT." @@ -1181,88 +1264,116 @@ The following commands are available: "Expire all old articles." (interactive) (let ((methods gnus-agent-covered-methods) - (alist (cdr gnus-newsrc-alist)) - gnus-command-method ofiles info method file group) - (while (setq gnus-command-method (pop methods)) - (setq ofiles (nconc ofiles (gnus-agent-expire-directory - (gnus-agent-directory))))) - (while (setq info (pop alist)) - (when (and (gnus-agent-method-p - (setq gnus-command-method - (gnus-find-method-for-group - (setq group (gnus-info-group info))))) - (member - (setq file - (concat - (gnus-agent-directory) - (gnus-agent-group-path group) "/.overview")) - ofiles)) - (setq ofiles (delete file ofiles)) - (gnus-agent-expire-group file group))) - (while ofiles - (gnus-agent-expire-group (pop ofiles))))) - -(defun gnus-agent-expire-directory (dir) - "Expire all groups in DIR recursively." - (when (file-directory-p dir) - (let ((files (directory-files dir t)) - file ofiles) - (while (setq file (pop files)) - (cond - ((member (file-name-nondirectory file) '("." "..")) - ;; Do nothing. - ) - ((file-directory-p file) - ;; Recurse. - (setq ofiles (nconc ofiles (gnus-agent-expire-directory file)))) - ((string-match "\\.overview$" file) - ;; Expire group. - (push file ofiles)))) - ofiles))) - -(defun gnus-agent-expire-group (overview &optional group) - "Expire articles in OVERVIEW." - (gnus-message 5 "Expiring %s..." overview) - (let ((odate (- (gnus-time-to-day (current-time)) 4)) - (dir (file-name-directory overview)) - (info (when group (gnus-get-info group))) - headers article file point unreads) - (gnus-agent-load-alist nil dir) - (when info - (setq unreads - (nconc - (gnus-list-of-unread-articles group) - (gnus-uncompress-range - (cdr (assq 'tick (gnus-info-marks info)))) - (gnus-uncompress-range - (cdr (assq 'dormant (gnus-info-marks info))))))) - (nnheader-temp-write overview - (insert-file-contents overview) - (goto-char (point-min)) - (while (not (eobp)) - (setq point (point)) - (condition-case () - (setq headers (inline (nnheader-parse-nov))) - (error - (goto-char point) - (gnus-delete-line) - (setq headers nil))) - (when headers - (unless (memq (setq article (mail-header-number headers)) unreads) - (if (not (< (inline - (gnus-time-to-day - (inline (nnmail-date-to-time - (mail-header-date headers))))) - odate)) - (forward-line 1) - (gnus-delete-line) - (setq gnus-agent-article-alist - (delq (assq article gnus-agent-article-alist) - gnus-agent-article-alist)) - (when (file-exists-p - (setq file (concat dir (number-to-string article)))) - (delete-file file)))))) - (gnus-agent-save-alist nil nil nil dir)))) + (day (- (gnus-time-to-day (current-time)) gnus-agent-expire-days)) + (expiry-hashtb (gnus-make-hashtable 1023)) + gnus-command-method sym group articles + history overview file histories elem art nov-file low info + unreads marked article) + (save-excursion + (setq overview (get-buffer-create " *expire overview*")) + (while (setq gnus-command-method (pop methods)) + (gnus-agent-open-history) + (set-buffer + (setq gnus-agent-current-history + (setq history (gnus-agent-history-buffer)))) + (goto-char (point-min)) + (when (> (buffer-size) 1) + (goto-char (point-min)) + (while (not (eobp)) + (skip-chars-forward "^\t") + (if (> (read (current-buffer)) day) + ;; New article; we don't expire it. + (forward-line 1) + ;; Old article. Schedule it for possible nuking. + (while (not (eolp)) + (setq sym (let ((obarray expiry-hashtb)) + (read (current-buffer)))) + (if (boundp sym) + (set sym (cons (cons (read (current-buffer)) (point)) + (symbol-value sym))) + (set sym (list (cons (read (current-buffer)) (point))))) + (skip-chars-forward " ")) + (forward-line 1))) + ;; We now have all articles that can possibly be expired. + (mapatoms + (lambda (sym) + (setq group (symbol-name sym) + articles (sort (symbol-value sym) 'car-less-than-car) + low (car (gnus-active group)) + info (gnus-get-info group) + unreads (ignore-errors (gnus-list-of-unread-articles group)) + marked (nconc (gnus-uncompress-range + (cdr (assq 'ticked (gnus-info-marks info)))) + (gnus-uncompress-range + (cdr (assq 'dormant + (gnus-info-marks info))))) + nov-file (gnus-agent-article-name ".overview" group)) + (gnus-agent-load-alist group) + (gnus-message 5 "Expiring articles in %s" group) + (set-buffer overview) + (erase-buffer) + (when (file-exists-p nov-file) + (nnheader-insert-file-contents nov-file)) + (goto-char (point-min)) + (setq article 0) + (while (setq elem (pop articles)) + (setq article (car elem)) + (when (or (null low) + (< article low) + gnus-agent-expire-all + (and (not (memq article unreads)) + (not (memq article marked)))) + ;; Find and nuke the NOV line. + (while (and (not (eobp)) + (or (not (numberp + (setq art (read (current-buffer))))) + (< art article))) + (if (file-exists-p + (gnus-agent-article-name + (number-to-string article) group)) + (forward-line 1) + ;; Remove old NOV lines that have no articles. + (gnus-delete-line))) + (if (or (eobp) + (/= art article)) + (beginning-of-line) + (gnus-delete-line)) + ;; Nuke the article. + (when (file-exists-p (setq file (gnus-agent-article-name + (number-to-string article) + group))) + (delete-file file)) + ;; Schedule the history line for nuking. + (push (cdr elem) histories))) + (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)) + (while (and alist + (<= (caar alist) article)) + (if (or (not (cdar alist)) + (not (file-exists-p + (gnus-agent-article-name + (number-to-string + (caar alist)) + group)))) + (setcdr prev (setq alist (cdr alist))) + (setq prev alist + alist (cdr alist)))) + (setq gnus-agent-article-alist (cdr first))) + (gnus-agent-save-alist group)) + expiry-hashtb) + (set-buffer history) + (setq histories (nreverse (sort histories '<))) + (while histories + (goto-char (pop histories)) + (gnus-delete-line)) + (gnus-agent-save-history) + (gnus-agent-close-history)) + (gnus-message 4 "Expiry...done"))))) ;;;###autoload (defun gnus-agent-batch ()