*** empty log message ***
authorLars Magne Ingebrigtsen <larsi@gnus.org>
Sun, 14 Sep 1997 19:20:58 +0000 (19:20 +0000)
committerLars Magne Ingebrigtsen <larsi@gnus.org>
Sun, 14 Sep 1997 19:20:58 +0000 (19:20 +0000)
18 files changed:
lisp/ChangeLog
lisp/Makefile
lisp/gnus-agent.el
lisp/gnus-bcklg.el
lisp/gnus-draft.el [new file with mode: 0644]
lisp/gnus-int.el
lisp/gnus-msg.el
lisp/gnus-start.el
lisp/gnus-sum.el
lisp/gnus-win.el
lisp/gnus.el
lisp/message.el
lisp/nnagent.el
lisp/nndraft.el
lisp/nnmh.el
lisp/nnml.el
texi/gnus.texi
texi/message.texi

index 748e70f..155a397 100644 (file)
@@ -1,3 +1,37 @@
+Sun Sep 14 21:17:34 1997  Lars Magne Ingebrigtsen  <larsi@menja.ifi.uio.no>
+
+       * gnus.el: Quassia Gnus v0.3 is released.
+
+Sun Sep 14 01:51:45 1997  Lars Magne Ingebrigtsen  <larsi@ifi.uio.no>
+
+       * gnus-agent.el (gnus-agent-short-article): New variables.
+
+       * message.el (message-set-auto-save-file-name): Use drafts.
+
+       * nndraft.el (nndraft-request-expire-articles): Use it.
+
+       * nnmh.el (nnmh-deletable-article-p): Change.
+       (nnmh-allow-delete-final): New variable.
+
+       * gnus-msg.el (gnus-summary-send-draft): Removed.
+
+       * gnus.el (gnus-article-mark-lists): Save unsendable marks.
+
+       * gnus-sum.el (gnus-newsgroup-unsendable): New variable.
+
+       * gnus-draft.el: New file.
+
+       * gnus-sum.el (gnus-unsendable-mark): New variable.
+
+       * nndraft.el (nndraft-execute-nnmh-command): Cleanup.
+
+       * message.el (message-send-news): Use `gnus-request-post'.
+
+       * gnus-agent.el (gnus-agentize): New command.
+
+       * gnus-bcklg.el (gnus-backlog-remove-article): Remove the ident
+       from the list.
+
 Sun Sep 14 00:26:47 1997  Lars Magne Ingebrigtsen  <larsi@menja.ifi.uio.no>
 
        * gnus.el: Quassia Gnus v0.2 is released.
index 571c263..b949400 100644 (file)
@@ -12,7 +12,7 @@ clever:
        $(EMACS) $(FLAGS) -f dgnushack-compile
 
 some:
-       $(EMACS) $(FLAGS) -f dgnushack-recompile
+       $(EMACS) $(FLAGS) -f dgnushack-compile
 
 tags:
        etags *.el
index daa2868..a856b76 100644 (file)
 
 ;;; Commentary:
 
-;; (add-hook 'gnus-before-startup-hook 'gnus-open-agent)
-
 ;;; Code:
 
 (require 'gnus)
 (require 'gnus-cache)
 (require 'nnvirtual)
 (require 'gnus-sum)
+(eval-when-compile (require 'cl))
 
 (defcustom gnus-agent-directory (nnheader-concat gnus-directory "agent/")
   "Where the Gnus agent will store its files."
@@ -61,6 +60,7 @@
 (defvar gnus-category-group-cache nil)
 (defvar gnus-agent-spam-hashtb nil)
 (defvar gnus-agent-file-name nil)
+(defvar gnus-agent-send-mail-function nil)
 
 (defvar gnus-plugged t
   "Whether Gnus is plugged or not.")
   (setq gnus-plugged nil)
   (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
+last form in your `.gnus.el' file:
+
+\(gnus-agentize)
+
+This will modify the `gnus-before-startup-hook', `gnus-post-method',
+and `message-send-mail-function' variables, and install the Gnus
+agent minor mode in all Gnus buffers."
+  (interactive)
+  (add-hook 'gnus-before-startup-hook 'gnus-open-agent)
+  (setq gnus-agent-send-mail-function message-send-mail-function
+       message-send-mail-function 'gnus-agent-send-mail))
+
+(defun gnus-agent-send-mail ()
+  (if gnus-plugged
+      (funcall gnus-agent-send-mail-function)
+    (goto-char (point-min))
+    (re-search-forward
+     (concat "^" (regexp-quote mail-header-separator) "\n"))
+    (replace-match "\n")
+    (gnus-request-accept-article "nndraft:drafts")))
+
 ;;;
 ;;; Group mode commands
 ;;;
@@ -793,6 +818,19 @@ the actual number of articles toggled is returned."
 (defvar gnus-category-mode-line-format "Gnus: %%b"
   "The format specification for the category mode line.")
 
+(defvar gnus-agent-short-article 100
+  "Articles that have fewer lines than this are short.")
+
+(defvar gnus-agent-long-article 200
+  "Articles that have more lines than this are long.")
+
+(defvar gnus-agent-low-score 0
+  "Articles that have a score lower than this have a low score.")
+
+(defvar gnus-agent-high-score 0
+  "Articles that have a score higher than this have a high score.")
+
+
 ;;; Internal variables.
 
 (defvar gnus-category-buffer "*Agent Category*")
@@ -1027,20 +1065,20 @@ The following commands are available:
        (gnus-sethash string t gnus-agent-spam-hashtb)))))
 
 (defun gnus-agent-short-p ()
-  "Say whether an article is short (less than 100 lines) or not."
-  (< (mail-header-lines gnus-headers) 100))
+  "Say whether an article is short or not."
+  (< (mail-header-lines gnus-headers) gnus-agent-short-article))
 
 (defun gnus-agent-long-p ()
-  "Say whether an article is long (more than 200 lines) or not."
-  (> (mail-header-lines gnus-headers) 200))
+  "Say whether an article is long or not."
+  (> (mail-header-lines gnus-headers) gnus-agent-long-article))
 
 (defun gnus-agent-low-scored-p ()
   "Say whether an article has a low score or not."
-  (< gnus-score gnus-summary-default-score))
+  (< gnus-score gnus-agent-low-score))
 
 (defun gnus-agent-high-scored-p ()
   "Say whether an article has a high score or not."
-  (> gnus-score gnus-summary-default-score))
+  (> gnus-score gnus-agent-low-score))
 
 (defun gnus-category-make-function (cat)
   "Make a function from category CAT."
index ead87fe..d3f2ac5 100644 (file)
                     (1+ beg) 'gnus-backlog (current-buffer) (point-max)))
              (delete-region beg end)
              ;; Return success.
-             t)))))))
+             t))
+         (setq gnus-backlog-articles (delq ident gnus-backlog-articles)))))))
 
 (defun gnus-backlog-request-article (group number buffer)
   (when (numberp number)
diff --git a/lisp/gnus-draft.el b/lisp/gnus-draft.el
new file mode 100644 (file)
index 0000000..fadd0aa
--- /dev/null
@@ -0,0 +1,148 @@
+;;; gnus-draft.el --- draft message support for Gnus
+;; Copyright (C) 1997 Free Software Foundation, Inc.
+
+;; Author: Lars Magne Ingebrigtsen <larsi@ifi.uio.no>
+;; Keywords: news
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 2, or (at your option)
+;; any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.         See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs; see the file COPYING.  If not, write to the
+;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
+
+;;; Commentary:
+
+;;; Code:
+
+(require 'gnus)
+(require 'gnus-sum)
+(require 'message)
+(require 'gnus-msg)
+(eval-when-compile (require 'cl))
+
+;;; Draft minor mode
+
+(defvar gnus-draft-mode nil
+  "Minor mode for providing a draft summary buffers.")
+
+(defvar gnus-draft-mode-map nil)
+
+(unless gnus-draft-mode-map
+  (setq gnus-draft-mode-map (make-sparse-keymap))
+
+  (gnus-define-keys gnus-draft-mode-map
+    "Dt" gnus-draft-toggle-sending
+    "De" gnus-draft-edit-message
+    "Ds" gnus-draft-send-message
+    "DS" gnus-draft-send-all-messages))
+
+(defun gnus-draft-make-menu-bar ()
+  (unless (boundp 'gnus-draft-menu)
+    (easy-menu-define
+     gnus-draft-menu gnus-draft-mode-map ""
+     '("Drafts"
+       ["Toggle whether to send" gnus-draft-toggle-sending t]))))
+
+(defun gnus-draft-mode (&optional arg)
+  "Minor mode for providing a draft summary buffers.
+
+\\{gnus-draft-mode-map}"
+  (interactive "P")
+  (when (eq major-mode 'gnus-summary-mode)
+    (if (not (set (make-local-variable 'gnus-draft-mode)
+                 (if (null arg) (not gnus-draft-mode)
+                   (> (prefix-numeric-value arg) 0))))
+       (remove-hook 'gnus-message-setup-hook 'gnus-draft-setup-message)
+      ;; Set up the menu.
+      (when (gnus-visual-p 'draft-menu 'menu)
+       (gnus-draft-make-menu-bar))
+      (gnus-add-minor-mode 'gnus-draft-mode " Draft" gnus-draft-mode-map)
+      (run-hooks 'gnus-draft-mode-hook))))
+
+;;; Commands
+
+(defun gnus-draft-toggle-sending (article)
+  "Toggle whether to send an article or not."
+  (interactive (list (gnus-summary-article-number)))
+  (if (gnus-draft-article-sendable-p article)
+      (progn
+       (push article gnus-newsgroup-unsendable)
+       (gnus-summary-mark-article article gnus-unsendable-mark))
+    (setq gnus-newsgroup-unsendable
+         (delq article gnus-newsgroup-unsendable))
+    (gnus-summary-mark-article article gnus-unread-mark))
+  (gnus-summary-position-point))
+
+(defun gnus-draft-edit-message ()
+  "Enter a mail/post buffer to edit and send the draft."
+  (interactive)
+  (gnus-set-global-variables)
+  (let ((article (gnus-summary-article-number)))
+    (gnus-draft-setup article)
+    (push
+     `((lambda ()
+        (when (buffer-name (get-buffer ,gnus-summary-buffer))
+          (save-excursion
+            (set-buffer (get-buffer ,gnus-summary-buffer))
+            (gnus-cache-possibly-remove-article ,article nil nil nil t)
+            (gnus-summary-mark-as-read ,article gnus-canceled-mark)))))
+     message-send-actions)))
+
+(defun gnus-draft-send-message (&optional n)
+  "Send the current draft."
+  (interactive "P")
+  (gnus-set-global-variables)
+  (let ((articles (gnus-summary-work-articles n))
+       article)
+    (while (setq article (pop articles))
+      (gnus-summary-remove-process-mark article)
+      (unless (memq article gnus-newsgroup-unsendable)
+       (gnus-draft-send article)
+       (gnus-summary-mark-article article gnus-canceled-mark)))))
+
+(defun gnus-draft-send (article)
+  "Send message ARTICLE."
+  (gnus-draft-setup article)
+  (message-send-and-exit))
+
+(defun gnus-draft-send-all-messages ()
+  "Send all the sendable drafts."
+  (interactive)
+  (gnus-uu-mark-buffer)
+  (gnus-draft-send-message))
+
+;;; Utility functions
+
+(defun gnus-draft-setup (article)
+  (gnus-setup-message 'forward
+    (message-mail)
+    (erase-buffer)
+    (if (not (gnus-request-restore-buffer article gnus-newsgroup-name))
+       (error "Couldn't restore the article")
+      ;; Insert the separator.
+      (goto-char (point-min))
+      (search-forward "\n\n")
+      (forward-char -1)
+      (insert mail-header-separator)
+      (forward-line 1)
+      (save-buffer 0))))
+
+(defun gnus-draft-article-sendable-p (article)
+  "Say whether ARTICLE is sendable."
+  (not (memq article gnus-newsgroup-unsendable)))
+
+(provide 'gnus-draft)
+
+;;; gnus-draft.el ends here
+
index c4e85f1..1fc205b 100644 (file)
@@ -391,7 +391,7 @@ If GROUP is nil, all groups on GNUS-COMMAND-METHOD are scanned."
             article (gnus-group-real-name group)
             (nth 1 gnus-command-method) accept-function last)))
 
-(defun gnus-request-accept-article (group gnus-command-method &optional last)
+(defun gnus-request-accept-article (group &optional gnus-command-method last)
   ;; Make sure there's a newline at the end of the article.
   (when (stringp gnus-command-method)
     (setq gnus-command-method (gnus-server-to-method gnus-command-method)))
index fc94bb2..2cd789b 100644 (file)
@@ -1046,27 +1046,6 @@ this is a reply."
                (insert " ")))
            (insert "\n")))))))
 
-(defun gnus-summary-send-draft ()
-  "Enter a mail/post buffer to edit and send the draft."
-  (interactive)
-  (gnus-set-global-variables)
-  (let (buf)
-    (if (not (setq buf (gnus-request-restore-buffer
-                       (gnus-summary-article-number) gnus-newsgroup-name)))
-       (error "Couldn't restore the article")
-      (switch-to-buffer buf)
-      (when (eq major-mode 'news-reply-mode)
-       (local-set-key "\C-c\C-c" 'gnus-inews-news))
-      ;; Insert the separator.
-      (goto-char (point-min))
-      (search-forward "\n\n")
-      (forward-char -1)
-      (insert mail-header-separator)
-      ;; Configure windows.
-      (let ((gnus-draft-buffer (current-buffer)))
-       (gnus-configure-windows 'draft t)
-       (goto-char (point))))))
-
 (gnus-add-shutdown 'gnus-inews-close 'gnus)
 
 (defun gnus-inews-close ()
index 6931a6f..89dd8a7 100644 (file)
@@ -690,6 +690,7 @@ prompt the user for the name of an NNTP server to use."
 
          ;; Do the actual startup.
          (gnus-setup-news nil level dont-connect)
+         (gnus-draft-setup)
          ;; Generate the group buffer.
          (gnus-group-list-groups level)
          (gnus-group-first-unread-group)
@@ -697,6 +698,15 @@ prompt the user for the name of an NNTP server to use."
          (gnus-group-set-mode-line)
          (run-hooks 'gnus-started-hook))))))
 
+(defun gnus-draft-setup ()
+  "Make sure the draft group exists."
+  (unless (gnus-gethash "nndraft:draft" gnus-newsrc-hashtb)
+    (gnus-request-create-group "draft" '(nndraft ""))
+    (let ((gnus-level-default-subscribed 1))
+      (gnus-subscribe-group "nndraft:draft" nil '(nndraft "")))
+    (gnus-group-set-parameter
+     "nndraft:draft" 'gnus-dummy '((gnus-draft-mode)))))
+
 ;;;###autoload
 (defun gnus-unload ()
   "Unload all Gnus features."
index 54770d0..9489614 100644 (file)
@@ -423,6 +423,11 @@ It uses the same syntax as the `gnus-split-methods' variable."
   :group 'gnus-summary-marks
   :type 'character)
 
+(defcustom gnus-unsendable-mark ?=
+  "*Mark used for articles that won't be sent."
+  :group 'gnus-summary-marks
+  :type 'character)
+
 (defcustom gnus-score-over-mark ?+
   "*Score mark used for articles with high scores."
   :group 'gnus-summary-marks
@@ -886,6 +891,9 @@ variable (string, integer, character, etc).")
 (defvar gnus-newsgroup-undownloaded nil
   "List of articles in the current newsgroup that haven't been downloaded..")
 
+(defvar gnus-newsgroup-unsendable nil
+  "List of articles in the current newsgroup that won't be sent.")
+
 (defvar gnus-newsgroup-bookmarks nil
   "List of articles in the current newsgroup that have bookmarks.")
 
@@ -926,6 +934,7 @@ variable (string, integer, character, etc).")
     gnus-newsgroup-replied gnus-newsgroup-expirable
     gnus-newsgroup-processable gnus-newsgroup-killed
     gnus-newsgroup-downloadable gnus-newsgroup-undownloaded
+    gnus-newsgroup-unsendable
     gnus-newsgroup-bookmarks gnus-newsgroup-dormant
     gnus-newsgroup-headers gnus-newsgroup-threads
     gnus-newsgroup-prepared gnus-summary-highlight-line-function
@@ -2170,6 +2179,7 @@ This is all marks except unread, ticked, dormant, and expirable."
 
 (defmacro gnus-article-mark (number)
   `(cond
+    ((memq ,number gnus-newsgroup-unsendable) gnus-unsendable-mark)
     ((memq ,number gnus-newsgroup-undownloaded) gnus-undownloaded-mark)
     ((memq ,number gnus-newsgroup-downloadable) gnus-downloadable-mark)
     ((memq ,number gnus-newsgroup-unreads) gnus-unread-mark)
index 87d7bb1..d66d0c5 100644 (file)
      (vertical 1.0
               (article 0.5)
               (message 1.0 point)))
-    (draft
-     (vertical 1.0
-              (draft 1.0 point)))
     (pipe
      (vertical 1.0
               (summary 0.25 point)
index 7c7ab92..7470a71 100644 (file)
@@ -244,7 +244,7 @@ is restarted, and sometimes reloaded."
   :link '(custom-manual "(gnus)Exiting Gnus")
   :group 'gnus)
 
-(defconst gnus-version-number  "0.2"
+(defconst gnus-version-number "0.3"
   "Version number for this version of Gnus.")
 
 (defconst gnus-version (format "Quassia Gnus v%s" gnus-version-number)
@@ -1445,7 +1445,8 @@ want."
     (expirable . expire) (killed . killed)
     (bookmarks . bookmark) (dormant . dormant)
     (scored . score) (saved . save)
-    (cached . cache) (downloadable . download)))
+    (cached . cache) (downloadable . download)
+    (unsendable . unsend)))
 
 (defvar gnus-headers-retrieved-by nil)
 (defvar gnus-article-reply nil)
@@ -1696,11 +1697,12 @@ gnus-newsrc-hashtb should be kept so that both hold the same information.")
       gnus-async-prefetch-article gnus-async-prefetch-remove-group
       gnus-async-halt-prefetch)
      ("gnus-agent" gnus-open-agent gnus-agent-get-function
-      gnus-agent-save-groups gnus-agent-save-active)
+      gnus-agent-save-groups gnus-agent-save-active gnus-agent-method-p)
      ("gnus-agent" :interactive t
       gnus-unplugged)
      ("gnus-vm" :interactive t gnus-summary-save-in-vm
-      gnus-summary-save-article-vm))))
+      gnus-summary-save-article-vm)
+     ("gnus-draft" :interactive t gnus-draft-mode))))
 
 ;;; gnus-sum.el thingies
 
index 161ac26..1a6b555 100644 (file)
@@ -268,10 +268,10 @@ If t, use `message-user-organization-file'."
   :type 'file
   :group 'message-headers)
 
-(defcustom message-autosave-directory "~/"
-  ; (concat (file-name-as-directory message-directory) "drafts/")
-  "*Directory where message autosaves buffers.
-If nil, message won't autosave."
+(defcustom message-autosave-directory
+  (nnheader-concat message-directory "drafts/")
+  "*Directory where Message autosaves buffers.
+If nil, Message won't autosave."
   :group 'message-buffers
   :type 'directory)
 
@@ -805,6 +805,7 @@ The cdr of ech entry is a function for applying the face to a region.")
 (defvar message-buffer-list nil)
 (defvar message-this-is-news nil)
 (defvar message-this-is-mail nil)
+(defvar message-draft-article nil)
 
 ;; Byte-compiler warning
 (defvar gnus-active-hashtb)
@@ -1238,6 +1239,7 @@ C-c C-r  message-caesar-buffer-body (rot13 the message body)."
   (make-local-variable 'message-exit-actions) 
   (make-local-variable 'message-kill-actions)
   (make-local-variable 'message-postpone-actions)
+  (make-local-variable 'message-draft-article)
   (set-syntax-table message-mode-syntax-table)
   (use-local-map message-mode-map)
   (setq local-abbrev-table message-mode-abbrev-table)
@@ -1286,6 +1288,7 @@ C-c C-r  message-caesar-buffer-body (rot13 the message body)."
     (if (fboundp 'mail-abbrevs-setup)
        (mail-abbrevs-setup)
       (funcall (intern "mail-aliases-setup"))))
+  (message-set-auto-save-file-name)
   (run-hooks 'text-mode-hook 'message-mode-hook))
 
 \f
@@ -1777,10 +1780,10 @@ the user from the mailer."
        ;; (mail-hist-put-headers-into-history))
        (run-hooks 'message-sent-hook)
        (message "Sending...done")
-       ;; If buffer has no file, mark it as unmodified and delete autosave.
-       (unless buffer-file-name
-         (set-buffer-modified-p nil)
-         (delete-auto-save-file-if-necessary t))
+       ;; Mark the buffer as unmodified and delete autosave.
+       (set-buffer-modified-p nil)
+       (delete-auto-save-file-if-necessary t)
+       (message-disassociate-draft)
        ;; Delete other mail buffers and stuff.
        (message-do-send-housekeeping)
        (message-do-actions message-send-actions)
@@ -2040,12 +2043,14 @@ to find out how to use this."
              (replace-match "\n")
              (backward-char 1))
            (run-hooks 'message-send-news-hook)
-           (require (car method))
-           (funcall (intern (format "%s-open-server" (car method)))
-                    (cadr method) (cddr method))
-           (setq result
-                 (funcall (intern (format "%s-request-post" (car method)))
-                          (cadr method))))
+           ;;(require (car method))
+           ;;(funcall (intern (format "%s-open-server" (car method)))
+           ;;(cadr method) (cddr method))
+           ;;(setq result
+           ;;    (funcall (intern (format "%s-request-post" (car method)))
+           ;;             (cadr method)))
+           (gnus-open-server method)
+           (setq result (gnus-request-post method)))
        (kill-buffer tembuf))
       (set-buffer messbuf)
       (if result
@@ -2998,7 +3003,6 @@ Headers already prepared in the buffer are not modified."
                   (copy-sequence message-required-mail-headers))))))
   (run-hooks 'message-signature-setup-hook)
   (message-insert-signature)
-  (message-set-auto-save-file-name)
   (save-restriction
     (message-narrow-to-headers)
     (run-hooks 'message-header-setup-hook))
@@ -3011,25 +3015,13 @@ Headers already prepared in the buffer are not modified."
 (defun message-set-auto-save-file-name ()
   "Associate the message buffer with a file in the drafts directory."
   (when message-autosave-directory
-    (unless (file-exists-p message-autosave-directory)
-      (make-directory message-autosave-directory t))
-    (let ((name (make-temp-name
-                (expand-file-name
-                 (concat (file-name-as-directory message-autosave-directory)
-                         "msg."
-                         (nnheader-replace-chars-in-string
-                          (nnheader-replace-chars-in-string
-                           (buffer-name) ?* ?.)
-                          ?/ ?-))))))
-      (setq buffer-auto-save-file-name
-           (save-excursion
-             (prog1
-                 (progn
-                   (set-buffer (get-buffer-create " *draft tmp*"))
-                   (setq buffer-file-name name)
-                   (make-auto-save-file-name))
-               (kill-buffer (current-buffer)))))
-      (clear-visited-file-modtime))))
+    (setq message-draft-article (nndraft-request-associate-buffer "drafts"))
+    (clear-visited-file-modtime)))
+
+(defun message-disassociate-draft ()
+  "Disassociate the message buffer from the drafts directory."
+  (nndraft-request-expire-articles
+   (list message-draft-article) "drafts" nil t))
 
 \f
 
@@ -3702,7 +3694,8 @@ regexp varstr."
        (regexp "^gnus\\|^nn\\|^message"))
     (mapcar
      (lambda (local)
-       (when (and (car local)
+       (when (and (consp local)
+                 (car local)
                  (string-match regexp (symbol-name (car local))))
         (ignore-errors
           (set (make-local-variable (car local))
index b5d967f..6b46765 100644 (file)
 (deffoo nnagent-request-newgroups (date server)
   nil)
 
+(deffoo nnagent-request-post (&optional server)
+  (gnus-request-accept-article "nndraft:draft"))
+
 ;; Use nnml functions for just about everything.
 (nnoo-import nnagent
   (nnml))
index 5f2cb9a..59e54a7 100644 (file)
 ;;; Code:
 
 (require 'nnheader)
+(require 'nnmail)
+(require 'gnus-start)
 (require 'nnmh)
 (require 'nnoo)
-(eval-and-compile (require 'cl))
+(eval-when-compile (require 'cl))
 
-(nnoo-declare nndraft)
+(nnoo-declare nndraft
+  nnmh)
 
-(eval-and-compile
-  (autoload 'mail-send-and-exit "sendmail"))
-
-(defvoo nndraft-directory nil
-  "Where nndraft will store its directory.")
+(defvoo nndraft-directory (nnheader-concat message-directory "drafts/")
+  "Where nndraft will store its files."
+  nnmh-current-directory)
 
 \f
 
+(defvoo nndraft-current-group "" nil nnmh-current-group)
+(defvoo nndraft-top-directory nil nil nnmh-directory)
+(defvoo nndraft-get-new-mail nil nil nnmh-get-new-mail)
+
 (defconst nndraft-version "nndraft 1.0")
-(defvoo nndraft-status-string "")
+(defvoo nndraft-status-string "" nnmh-status-string)
 
 \f
 
 
 (nnoo-define-basics nndraft)
 
+(deffoo nndraft-open-server (server &optional defs)
+  (push `(nndraft-current-group
+         ,(file-name-nondirectory (directory-file-name nndraft-directory)))
+       defs)
+  (push `(nndraft-top-directory
+         ,(file-name-directory (directory-file-name nndraft-directory)))
+       defs)
+  (nnoo-change-server 'nndraft server defs)
+  (cond
+   ((not (file-exists-p nndraft-directory))
+    (nndraft-close-server)
+    (nnheader-report 'nndraft "No such file or directory: %s"
+                    nndraft-directory))
+   ((not (file-directory-p (file-truename nndraft-directory)))
+    (nndraft-close-server)
+    (nnheader-report 'nndraft "Not a directory: %s" nndraft-directory))
+   (t
+    (nnheader-report 'nndraft "Opened server %s using directory %s"
+                    server nndraft-directory)
+    t)))
+
 (deffoo nndraft-retrieve-headers (articles &optional group server fetch-old)
   (save-excursion
     (set-buffer nntp-server-buffer)
        (nnheader-fold-continuation-lines)
        'headers))))
 
-(deffoo nndraft-open-server (server &optional defs)
-  (nnoo-change-server 'nndraft server defs)
-  (unless (assq 'nndraft-directory defs)
-    (setq nndraft-directory server))
-  (cond
-   ((not (file-exists-p nndraft-directory))
-    (nndraft-close-server)
-    (nnheader-report 'nndraft "No such file or directory: %s"
-                    nndraft-directory))
-   ((not (file-directory-p (file-truename nndraft-directory)))
-    (nndraft-close-server)
-    (nnheader-report 'nndraft "Not a directory: %s" nndraft-directory))
-   (t
-    (nnheader-report 'nndraft "Opened server %s using directory %s"
-                    server nndraft-directory)
-    t)))
-
 (deffoo nndraft-request-article (id &optional group server buffer)
   (when (numberp id)
     ;; We get the newest file of the auto-saved file and the
 
 (deffoo nndraft-request-restore-buffer (article &optional group server)
   "Request a new buffer that is restored to the state of ARTICLE."
-  (let ((file (nndraft-article-filename article ".state"))
-       nndraft-point nndraft-mode nndraft-buffer-name)
-    (when (file-exists-p file)
-      (load file t t t)
-      (when nndraft-buffer-name
-       (set-buffer (get-buffer-create
-                    (generate-new-buffer-name nndraft-buffer-name)))
-       (nndraft-request-article article group server (current-buffer))
-       (funcall nndraft-mode)
-       (let ((gnus-verbose-backends nil))
-         (nndraft-request-expire-articles (list article) group server t))
-       (goto-char nndraft-point))
-      nndraft-buffer-name)))
+  (when (nndraft-request-article article group server (current-buffer))
+    (let ((gnus-verbose-backends nil))
+      (nndraft-request-expire-articles (list article) group server t))
+    t))
 
 (deffoo nndraft-request-update-info (group info &optional server)
   (setcar (cddr info) nil)
-  (when (nth 3 info)
-    (setcar (nthcdr 3 info) nil))
+  (let (marks)
+    (when (setq marks (nth 3 info))
+      (setcar (nthcdr 3 info)
+             (if (assq 'unsend marks)
+                 (list (assq 'unsend marks))
+               nil))))
   t)
 
 (deffoo nndraft-request-associate-buffer (group)
   "Associate the current buffer with some article in the draft group."
-  (let* ((gnus-verbose-backends nil)
-        (article (cdr (nndraft-request-accept-article
-                       group (nnoo-current-server 'nndraft) t 'noinsert)))
-        (file (nndraft-article-filename article)))
+  (let ((gnus-verbose-backends nil)
+       (buf (current-buffer))
+        article file)
+    (nnheader-temp-write nil
+      (insert-buffer buf)
+      (setq article (cdr (nndraft-request-accept-article
+                         group (nnoo-current-server 'nndraft) t 'noinsert)))
+      (setq file (nndraft-article-filename article)))
     (setq buffer-file-name file)
     (setq buffer-auto-save-file-name (make-auto-save-file-name))
     (clear-visited-file-modtime)
     article))
 
-(deffoo nndraft-request-group (group &optional server dont-check)
-  (prog1
-      (nndraft-execute-nnmh-command
-       `(nnmh-request-group group "" ,dont-check))
-    (nnheader-report 'nndraft nnmh-status-string)))
-
-(deffoo nndraft-request-list (&optional server dir)
-  (nndraft-execute-nnmh-command
-   `(nnmh-request-list nil ,dir)))
-
-(deffoo nndraft-request-newgroups (date &optional server)
-  (nndraft-execute-nnmh-command
-   `(nnmh-request-newgroups ,date ,server)))
-
-(deffoo nndraft-request-expire-articles
-  (articles group &optional server force)
-  (let ((res (nndraft-execute-nnmh-command
-             `(nnmh-request-expire-articles
-               ',articles group ,server ,force)))
-       article)
+(deffoo nndraft-request-expire-articles (articles group &optional server force)
+  (let* ((nnmh-allow-delete-final t)
+        (res (nndraft-execute-nnmh-command
+              `(nnmh-request-expire-articles
+                ',articles group ,server ,force)))
+        article)
     ;; Delete all the "state" files of articles that have been expired.
     (while articles
       (unless (memq (setq article (pop articles)) res)
-       (let ((file (nndraft-article-filename article ".state"))
-             (auto (nndraft-auto-save-file-name
+       (let ((auto (nndraft-auto-save-file-name
                     (nndraft-article-filename article))))
-         (when (file-exists-p file)
-           (funcall nnmail-delete-file-function file))
          (when (file-exists-p auto)
            (funcall nnmail-delete-file-function auto)))))
     res))
 
 (deffoo nndraft-request-accept-article (group &optional server last noinsert)
-  (let* ((point (point))
-        (mode major-mode)
-        (name (buffer-name))
-        (gnus-verbose-backends nil)
-        (gart (nndraft-execute-nnmh-command
-               `(nnmh-request-accept-article group ,server ,last noinsert)))
-        (state
-         (nndraft-article-filename (cdr gart) ".state")))
-    ;; Write the "state" file.
-    (save-excursion
-      (nnheader-set-temp-buffer " *draft state*")
-      (insert (format "%S\n" `(setq nndraft-mode (quote ,mode)
-                                   nndraft-point ,point
-                                   nndraft-buffer-name ,name)))
-      (write-region (point-min) (point-max) state nil 'silent)
-      (kill-buffer (current-buffer)))
-    gart))
-
-(deffoo nndraft-close-group (group &optional server)
-  t)
+  (let ((gnus-verbose-backends nil))
+    (nndraft-execute-nnmh-command
+     `(nnmh-request-accept-article group ,server ,last noinsert))))
 
 (deffoo nndraft-request-create-group (group &optional server args)
   (if (file-exists-p nndraft-directory)
 ;;; Low-Level Interface
 
 (defun nndraft-execute-nnmh-command (command)
-  (let ((dir (expand-file-name nndraft-directory)))
-    (when (string-match "/$" dir)
-      (setq dir (substring dir 0 (match-beginning 0))))
-    (string-match "/[^/]+$" dir)
-    (let ((group (substring dir (1+ (match-beginning 0))))
-          (nnmh-directory (substring dir 0 (1+ (match-beginning 0))))
-         (nnmail-keep-last-article nil)
-         (nnmh-get-new-mail nil))
-      (eval command))))
+  (let* ((dir (directory-file-name (expand-file-name nndraft-directory)))
+        (group (file-name-nondirectory dir))
+        (nnmh-directory (file-name-directory dir))
+        (nnmail-keep-last-article nil)
+        (nnmh-get-new-mail nil))
+    (eval command)))
 
 (defun nndraft-article-filename (article &rest args)
   (apply 'concat
          (make-auto-save-file-name))
       (kill-buffer (current-buffer)))))
 
+(nnoo-map-functions nndraft
+  (nnmh-retrieve-headers 0 nndraft-current-group 0 0)
+  (nnmh-request-group nndraft-current-group 0 0)
+  (nnmh-close-group nndraft-current-group 0)
+  (nnmh-request-list (nnoo-current-server 'nndraft) nndraft-directory)
+  (nnmh-request-newsgroups (nnoo-current-server 'nndraft) nndraft-directory))
+
 (provide 'nndraft)
 
 ;;; nndraft.el ends here
index f91bed5..d5bd4f0 100644 (file)
@@ -60,6 +60,7 @@
 
 (defvoo nnmh-status-string "")
 (defvoo nnmh-group-alist nil)
+(defvoo nnmh-allow-delete-final nil)
 
 \f
 
@@ -76,8 +77,6 @@
           (large (and (numberp nnmail-large-newsgroup)
                       (> number nnmail-large-newsgroup)))
           (count 0)
-          ;; 1997/8/12 by MORIOKA Tomohiko
-          ;;   for XEmacs/mule.
           (pathname-coding-system 'binary)
           beg article)
       (nnmh-possibly-change-directory newsgroup server)
   (let ((file (if (stringp id)
                  nil
                (concat nnmh-current-directory (int-to-string id))))
-       ;; 1997/8/12 by MORIOKA Tomohiko
-       ;;      for XEmacs/mule.
        (pathname-coding-system 'binary)
        (nntp-server-buffer (or buffer nntp-server-buffer)))
     (and (stringp file)
 
 (deffoo nnmh-request-group (group &optional server dont-check)
   (let ((pathname (nnmail-group-pathname group nnmh-directory))
-       ;; 1997/8/12 by MORIOKA Tomohiko
-       ;;      for XEmacs/mule.
        (pathname-coding-system 'binary)
        dir)
     (cond
     (nnmh-open-server server))
   (when newsgroup
     (let ((pathname (nnmail-group-pathname newsgroup nnmh-directory))
-         ;; 1997/8/12 by MORIOKA Tomohiko
-         ;;    for XEmacs/mule.
          (pathname-coding-system 'binary))
       (if (file-directory-p pathname)
          (setq nnmh-current-directory pathname)
   (let ((path (concat nnmh-current-directory (int-to-string article))))
     ;; Writable.
     (and (file-writable-p path)
-        ;; We can never delete the last article in the group.
-        (not (eq (cdr (nth 1 (assoc group nnmh-group-alist)))
-                 article)))))
+        (or
+         ;; We can never delete the last article in the group.
+         (not (eq (cdr (nth 1 (assoc group nnmh-group-alist)))
+                  article))
+         ;; Well, we can.
+         nnmh-allow-delete-final))))
 
 (provide 'nnmh)
 
index 7a0773e..ab03007 100644 (file)
@@ -194,8 +194,6 @@ all.  This may very well take some time.")
            (string-to-int (file-name-nondirectory path)))))))
 
 (deffoo nnml-request-group (group &optional server dont-check)
-  ;; 1997/8/12 by MORIOKA Tomohiko
-  ;;   for XEmacs/mule.
   (let ((pathname-coding-system 'binary))
     (cond
      ((not (nnml-possibly-change-directory group server))
index d340b06..14963c9 100644 (file)
@@ -1,7 +1,7 @@
 \input texinfo                  @c -*-texinfo-*-
 
 @setfilename gnus
-@settitle Quassia Gnus 0.2 Manual
+@settitle Quassia Gnus 0.3 Manual
 @synindex fn cp
 @synindex vr cp
 @synindex pg cp
@@ -309,7 +309,7 @@ into another language, under the above conditions for modified versions.
 @tex
 
 @titlepage
-@title Quassia Gnus 0.2 Manual
+@title Quassia Gnus 0.3 Manual
 
 @author by Lars Magne Ingebrigtsen
 @page
@@ -345,7 +345,7 @@ can be gotten by any nefarious means you can think of---@sc{nntp}, local
 spool or your mbox file.  All at the same time, if you want to push your
 luck.
 
-This manual corresponds to Quassia Gnus 0.2.
+This manual corresponds to Quassia Gnus 0.3.
 
 @end ifinfo
 
@@ -7689,9 +7689,8 @@ to make Gnus try to post using the foreign server.
 * Posting Server::       What server should you post via?
 * Mail and Post::        Mailing and posting at the same time.
 * Archived Messages::    Where Gnus stores the messages you've sent.
-@c * Posting Styles::       An easier way to configure some key elements.
-@c * Drafts::               Postponing messages and rejected messages.
-@c * Rejected Articles::    What happens if the server doesn't like your article?
+* Drafts::               Postponing messages and rejected messages.
+* Rejected Articles::    What happens if the server doesn't like your article?
 @end menu
 
 Also see @pxref{Canceling and Superseding} for information on how to
@@ -8015,90 +8014,90 @@ but the latter is the preferred method.
 @c           (signature . "~/.mail-signature"))))
 @c @end lisp
 
-@c @node Drafts
-@c @section Drafts
-@c @cindex drafts
-@c 
-@c If you are writing a message (mail or news) and suddenly remember that
-@c you have a steak in the oven (or some pesto in the food processor, you
-@c craazy vegetarians), you'll probably wish there was a method to save the
-@c message you are writing so that you can continue editing it some other
-@c day, and send it when you feel its finished.
-@c 
-@c Well, don't worry about it.  Whenever you start composing a message of
-@c some sort using the Gnus mail and post commands, the buffer you get will
-@c automatically associate to an article in a special @dfn{draft} group.
-@c If you save the buffer the normal way (@kbd{C-x C-s}, for instance), the
-@c article will be saved there.  (Auto-save files also go to the draft
-@c group.) 
-@c 
-@c @cindex nndraft
-@c @vindex gnus-draft-group-directory
-@c The draft group is a special group (which is implemented as an
-@c @code{nndraft} group, if you absolutely have to know) called
-@c @samp{nndraft:drafts}.  The variable @code{gnus-draft-group-directory}
-@c controls both the name of the group and the location---the leaf element
-@c in the path will be used as the name of the group.  What makes this
-@c group special is that you can't tick any articles in it or mark any
-@c articles as read---all articles in the group are permanently unread.
-@c 
-@c If the group doesn't exist, it will be created and you'll be subscribed
-@c to it.
-@c 
-@c @findex gnus-dissociate-buffer-from-draft
-@c @kindex C-c M-d (Mail)
-@c @kindex C-c M-d (Post)
-@c @findex gnus-associate-buffer-with-draft
-@c @kindex C-c C-d (Mail)
-@c @kindex C-c C-d (Post)
-@c If you're writing some super-secret message that you later want to
-@c encode with PGP before sending, you may wish to turn the auto-saving
-@c (and association with the draft group) off.  You never know who might be
-@c interested in reading all your extremely valuable and terribly horrible
-@c and interesting secrets.  The @kbd{C-c M-d}
-@c (@code{gnus-dissociate-buffer-from-draft}) command does that for you.
-@c If you change your mind and want to turn the auto-saving back on again,
-@c @kbd{C-c C-d} (@code{gnus-associate-buffer-with-draft} does that.
-@c 
-@c @vindex gnus-use-draft
-@c To leave association with the draft group off by default, set
-@c @code{gnus-use-draft} to @code{nil}.  It is @code{t} by default. 
-@c 
-@c @findex gnus-summary-send-draft
-@c @kindex S D c (Summary)
-@c When you want to continue editing the article, you simply enter the
-@c draft group and push @kbd{S D c} (@code{gnus-summary-send-draft}) to do
-@c that.  You will be placed in a buffer where you left off.
-@c 
-@c Rejected articles will also be put in this draft group (@pxref{Rejected
-@c Articles}).
-@c 
-@c @findex gnus-summary-send-all-drafts
-@c If you have lots of rejected messages you want to post (or mail) without
-@c doing further editing, you can use the @kbd{S D a} command
-@c (@code{gnus-summary-send-all-drafts}).  This command understands the
-@c process/prefix convention (@pxref{Process/Prefix}).  
-@c 
-@c 
-@c @node Rejected Articles
-@c @section Rejected Articles
-@c @cindex rejected articles
-@c 
-@c Sometimes a news server will reject an article.  Perhaps the server
-@c doesn't like your face.  Perhaps it just feels miserable.  Perhaps
-@c @emph{there be demons}.  Perhaps you have included too much cited text.
-@c Perhaps the disk is full.  Perhaps the server is down.
-@c 
-@c These situations are, of course, totally beyond the control of Gnus.
-@c (Gnus, of course, loves the way you look, always feels great, has angels
-@c fluttering around inside of it, doesn't care about how much cited text
-@c you include, never runs full and never goes down.)  So Gnus saves these
-@c articles until some later time when the server feels better.
-@c 
-@c The rejected articles will automatically be put in a special draft group
-@c (@pxref{Drafts}).  When the server comes back up again, you'd then
-@c typically enter that group and send all the articles off.
-@c 
+@node Drafts
+@section Drafts
+@cindex drafts
+
+If you are writing a message (mail or news) and suddenly remember that
+you have a steak in the oven (or some pesto in the food processor, you
+craazy vegetarians), you'll probably wish there was a method to save the
+message you are writing so that you can continue editing it some other
+day, and send it when you feel its finished.
+
+Well, don't worry about it.  Whenever you start composing a message of
+some sort using the Gnus mail and post commands, the buffer you get will
+automatically associate to an article in a special @dfn{draft} group.
+If you save the buffer the normal way (@kbd{C-x C-s}, for instance), the
+article will be saved there.  (Auto-save files also go to the draft
+group.) 
+
+@cindex nndraft
+@vindex gnus-draft-group-directory
+The draft group is a special group (which is implemented as an
+@code{nndraft} group, if you absolutely have to know) called
+@samp{nndraft:drafts}.  The variable @code{gnus-draft-group-directory}
+controls both the name of the group and the location---the leaf element
+in the path will be used as the name of the group.  What makes this
+group special is that you can't tick any articles in it or mark any
+articles as read---all articles in the group are permanently unread.
+
+If the group doesn't exist, it will be created and you'll be subscribed
+to it.
+
+@findex gnus-dissociate-buffer-from-draft
+@kindex C-c M-d (Mail)
+@kindex C-c M-d (Post)
+@findex gnus-associate-buffer-with-draft
+@kindex C-c C-d (Mail)
+@kindex C-c C-d (Post)
+If you're writing some super-secret message that you later want to
+encode with PGP before sending, you may wish to turn the auto-saving
+(and association with the draft group) off.  You never know who might be
+interested in reading all your extremely valuable and terribly horrible
+and interesting secrets.  The @kbd{C-c M-d}
+(@code{gnus-dissociate-buffer-from-draft}) command does that for you.
+If you change your mind and want to turn the auto-saving back on again,
+@kbd{C-c C-d} (@code{gnus-associate-buffer-with-draft} does that.
+
+@vindex gnus-use-draft
+To leave association with the draft group off by default, set
+@code{gnus-use-draft} to @code{nil}.  It is @code{t} by default. 
+
+@findex gnus-summary-send-draft
+@kindex S D c (Summary)
+When you want to continue editing the article, you simply enter the
+draft group and push @kbd{S D c} (@code{gnus-summary-send-draft}) to do
+that.  You will be placed in a buffer where you left off.
+
+Rejected articles will also be put in this draft group (@pxref{Rejected
+Articles}).
+
+@findex gnus-summary-send-all-drafts
+If you have lots of rejected messages you want to post (or mail) without
+doing further editing, you can use the @kbd{S D a} command
+(@code{gnus-summary-send-all-drafts}).  This command understands the
+process/prefix convention (@pxref{Process/Prefix}).  
+
+
+@node Rejected Articles
+@section Rejected Articles
+@cindex rejected articles
+
+Sometimes a news server will reject an article.  Perhaps the server
+doesn't like your face.  Perhaps it just feels miserable.  Perhaps
+@emph{there be demons}.  Perhaps you have included too much cited text.
+Perhaps the disk is full.  Perhaps the server is down.
+
+These situations are, of course, totally beyond the control of Gnus.
+(Gnus, of course, loves the way you look, always feels great, has angels
+fluttering around inside of it, doesn't care about how much cited text
+you include, never runs full and never goes down.)  So Gnus saves these
+articles until some later time when the server feels better.
+
+The rejected articles will automatically be put in a special draft group
+(@pxref{Drafts}).  When the server comes back up again, you'd then
+typically enter that group and send all the articles off.
+
 
 @node Select Methods
 @chapter Select Methods
@@ -8137,6 +8136,7 @@ The different methods all have their peculiarities, of course.
 * Getting Mail::          Reading your personal mail with Gnus.
 * Other Sources::         Reading directories, files, SOUP packets.
 * Combined Groups::       Combining groups into one group.
+* Gnus Unplugged::        Reading news and mail offline.
 @end menu
 
 
@@ -10716,6 +10716,434 @@ Articles marked as read in the @code{nnkiboze} group will have
 their @sc{nov} lines removed from the @sc{nov} file.
 
 
+@node Gnus Unplugged
+@section Gnus Unplugged
+@cindex offline
+@cindex unplugged
+@cindex Agent
+@cindex Gnus Agent
+@cindex Gnus Unplugged
+
+In olden times (ca. February '88), people used to run their newsreaders
+on big machines with permanent connections to the net.  News transport
+was dealt with by news servers, and all the newsreaders had to do was to
+read news.  Believe it or not.
+
+Nowadays most people read news and mail at home, and use some sort of
+modem to connect to the net.  To avoid running up huge phone bills, it
+would be nice to have a way to slurp down all the news and mail, hang up
+the phone, read for several hours, and then upload any responses you
+have to make.  And then you repeat the procedure.
+
+Of course, you can use news servers for doing this as well.  I've used
+@code{inn} together with @code{slurp}, @code{pop} and @code{sendmail}
+for some years, but doing that's a bore.  Moving the news server
+functionality up to the newsreader makes sense if you're the only person
+reading news on a machine.
+
+Using Gnus as an ``offline'' newsreader is quite simple.
+
+@itemize @bullet
+@item
+First, set ut Gnus as you would do if you were running it on a machine
+that has full connection to the net.  Go ahead.  I'll still be waiting
+here.
+
+@item
+Then, put the following magical incantation at the end of your
+@file{.gnus.el} file:
+
+@lisp
+(gnus-agentize)
+@end lisp
+@end itemize
+
+That's it.  Gnus is now an ``offline'' newsreader.
+
+Of course, to use it as such, you have to learn a few new commands.
+
+@menu
+* Agent Basics::           How it all is supposed to work.
+* Agent Categories::       How to tell the Gnus Agent what to download.
+* Agent Commands::         New commands for all the buffers.
+* Agent Variables::        Customizing is fun.
+@end menu
+
+
+@node Agent Basics
+@subsection Agent Basics
+
+First, let's get some terminilogy out of the way.
+
+The Gnus Agent is said to be @dfn{unplugged} when you have severed the
+connection to the net (and notified the Agent that this is the case).
+When the connection to the net is up again (and Gnus knows this), the
+Agent is @dfn{plugged}.
+
+The @dfn{local} machine is the one you're running on, and which isn't
+connected to the net continously.
+
+@dfn{Downloading} means fetching things from the net to your local
+machine.  @dfn{Uploading} is doing the opposite.
+
+Let's take a typical Gnus session using the Agent.
+
+@itemize @bullet
+
+@item
+You start Gnus with @code{gnus-unplugged}.  This brings up the Gnus
+Agent in a disconnected state.  You can read all the news that you have
+already fetched while in this mode.
+
+@item
+You then decide to see whether any new news has arrived.  You connect
+your machine to the net (using PPP or whatever), and then hit @kbd{J j}
+to make Gnus become @dfn{plugged}.
+
+@item
+You can then read the new news immediately, or you can download the news
+onto your local machine.  If you want to do the latter, you press @kbd{J
+s} to fetch all the eligible articles in all the groups.  (To let Gnus
+know which articles you want to download, @pxref{Agent Categories}.)
+
+@item
+After fetching the articles, you press @kbd{J j} to make Gnus become
+unplugged again, and you shut down the PPP thing (or whatever).  And
+then you read the news offline.
+
+@item
+And then you go to step 2.
+@end itemize
+
+
+@node Agent Categories
+@subsection Agent Categories
+
+On of the main reasons to integrate the news transport layer into the
+newsreader is to allow greater control over what articles to download.
+There's not much point in downloading huge amounts of articles, just to
+find out that you're not interested in reading any of them.  It's better
+to be somewhat more conservative in choosing what to download, and then
+mark the articles for downloading manually if it should turn out that
+you're interested in the articles anyway.
+
+The main way to control what is to be downloaded is to create a
+@dfn{category} and then assign some (or all) groups to this category. 
+Gnus has its own buffer for creating and managing categories.
+
+@menu
+* Category Syntax::       What a category looks like.
+* The Category Buffer::   A buffer for maintaining categories.
+* Category Variables::    Customize'r'Us.
+@end menu
+
+
+@node Category Syntax
+@subsubsection Category Syntax
+
+A category consists of two things.
+
+@enumerate
+@item
+A predicate which (generally) gives a rough outline of which articles
+are eligible for downloading; and
+
+@item
+a score rule which (generally) gives you a finer granularity when
+deciding what articles to download.  (Note that this @dfn{download
+score} is wholly unrelated to normal scores.)
+@end enumerate
+
+A predicate consists of predicates with logical operators sprinkled in
+between.
+
+Perhaps some examples are in order.
+
+Here's a simple predicate.  (It's the default predicate, in fact, used
+for all groups that don't belong to any other category.)
+
+@lisp
+short
+@end lisp
+
+Quite simple, eh?  This predicate is true if and only if the article is
+short (for some value of ``short'').
+
+Here's a more complex predicate:
+
+@lisp
+(or high
+    (and
+     (not low)
+     (not long)))
+@end lisp
+
+This means that an article should be downloaded if it has a high score,
+or if the score is not low and the article is not long.  You get the
+drift.
+
+The available logical operators are @code{or}, @code{and} and
+@code{not}.  (If you prefer, you can use the more ``C''-ish operators
+@samp{|}, @code{&} and @code{!} instead.)
+
+The following predicates are pre-defined, but if none of these fit what
+you want to do, you can write your own.
+
+@table @code
+@item short
+True iff the article is shorter than @code{gnus-agent-short-article}
+lines; default 100.
+
+@item long
+True iff the article is longer than @code{gnus-agent-long-article}
+lines; default 200.
+
+@item low
+True iff the article has a download score less than
+@code{gnus-agent-low-score}; default 0.
+
+@item high
+True iff the article has a download score greater than
+@code{gnus-agent-high-score}; default 0.
+
+@item spam
+True iff the Gnus Agent guesses that the article is spam.  The
+heuristics may change over time, but at present it just computes a
+checksum and see whether articles match.
+
+@item true
+Always true.
+
+@item false
+Always false.
+@end table
+
+If you want to create your own predicate function, here's what you have
+to know:  The functions are called with no parameters, but the
+@code{gnus-headers} and @code{gnus-score} dynamic variables are bound to
+useful values.
+
+Now, the syntax of the download score is the same as the syntax of
+normal score files, except that all elements that require actually
+seeing the article itself is verboten.  This means that only the
+following headers can be scored on: @code{From}, @code{Subject},
+@code{Date}, @code{Xref}, @code{Lines}, @code{Chars}, @code{Message-ID},
+and @code{References}.
+
+
+@node The Category Buffer
+@subsubsection The Category Buffer
+
+You'd normally do all category maintenance from the category buffer.
+When you enter it for the first time (with the @kbd{J c} command from
+the group buffer), you'll only see the @code{default} category.
+
+The following commands are available in this buffer:
+
+@table @kbd
+@item q
+@kindex q (Category)
+@findex gnus-category-exit
+Return to the group buffer (@code{gnus-category-exit}).
+
+@item k
+@kindex k (Category)
+@findex gnus-category-kill
+Kill the current category (@code{gnus-category-kill}).
+
+@item c
+@kindex c (Category)
+@findex gnus-category-copy
+Copy the current category (@code{gnus-category-copy}).
+
+@item a
+@kindex a (Category)
+@findex gnus-category-add
+Add a new category (@code{gnus-category-add}).
+
+@item p
+@kindex p (Category)
+@findex gnus-category-edit-predicate
+Edit the predicate of the current category
+(@code{gnus-category-edit-predicate}).
+
+@item g
+@kindex g (Category)
+@findex gnus-category-edit-groups
+Edit the list of groups belonging to the current category
+(@code{gnus-category-edit-groups}).
+
+@item s
+@kindex s (Category)
+@findex gnus-category-edit-score
+Edit the download score rule of the current category
+(@code{gnus-category-edit-score}).
+
+@item l
+@kindex l (Category)
+@findex gnus-category-list
+List all the categories (@code{gnus-category-list}).
+@end table
+
+
+@node Category Variables
+@subsubsection Category Variables
+
+@table @code
+@item gnus-category-mode-hook
+@vindex gnus-category-mode-hook
+Hook run in category buffers.
+
+@item gnus-category-line-format
+@vindex gnus-category-line-format
+Format of the lines in the category buffer (@pxref{Formatting
+Variables}).  Legal elements are:
+
+@table @samp
+@item c
+The name of the category.
+
+@item g
+The number of groups in the category.
+@end table
+
+@item gnus-category-mode-line-format
+@vindex gnus-category-mode-line-format
+Format of the category mode line.
+
+@item gnus-agent-short-article 
+@vindex gnus-agent-short-article 
+Articles that have fewer lines than this are short.  Default 100.
+
+@item gnus-agent-long-article 
+@vindex gnus-agent-long-article 
+Articles that have more lines than this are long.  Default 200.
+
+@item gnus-agent-low-score 
+@vindex gnus-agent-low-score 
+Articles that have a score lower than this have a low score.  Default
+0. 
+
+@item gnus-agent-high-score 
+@vindex gnus-agent-high-score 
+Articles that have a score higher than this have a high score.  Default
+0. 
+
+@end table
+
+
+@node Agent Commands
+@subsection Agent Commands
+
+All the Gnus Agent commands is on the @kbd{J} submap.  The @kbd{J j}
+(@code{gnus-agent-toggle-plugged} command works in all modes, and
+toggles the plugged/unplugged state of the Gnus Agent.
+
+
+@menu
+* Group Agent Commands::
+* Summary Agent Commands::
+* Server Agent Commands::
+@end menu
+
+
+@node Group Agent Commands
+@subsubsection Group Agent Commands
+
+@table @kbd
+@item J u 
+@kindex J u (Agent Group)
+@findex gnus-agent-fetch-group
+Fetch all eligible articles in the current group
+(@code{gnus-agent-fetch-group}).
+
+@item J c
+@kindex J c (Agent Group)
+@findex gnus-enter-category-buffer
+Enter the Agent category buffer (@code{gnus-enter-category-buffer}).
+
+@item J s
+@kindex J s (Agent Group)
+@findex gnus-agent-fetch-session
+Fetch all eligible articles in all groups
+(@code{gnus-agent-fetch-session}).
+
+@item J a
+@kindex J a (Agent Group)
+@findex gnus-agent-add-group
+Add the current group to an Agent category
+(@code{gnus-agent-add-group}).
+
+@end table
+
+
+@node Summary Agent Commands
+@subsubsection Summary Agent Commands
+
+@table @kbd
+@item J #
+@kindex J # (Agent Summary)
+@findex gnus-agent-mark-article
+Mark the article for downloading (@code{gnus-agent-mark-article}).
+
+@item J M-#
+@kindex J M-# (Agent Summary)
+@findex gnus-agent-unmark-article
+Remove the downloading mark from the article
+(@code{gnus-agent-unmark-article}).
+
+@item @@
+@kindex @@ (Agent Summary)
+@findex gnus-agent-toggle-mark
+Toggle whether to download the article (@code{gnus-agent-toggle-mark}).
+
+@item J c
+@kindex J c (Agent Summary)
+@findex gnus-agent-catchup
+Mark all undownloaded articles as read (@code{gnus-agent-catchup}).
+
+@end table
+
+
+@node Server Agent Commands
+@subsubsection Server Agent Commands
+
+@table @kbd
+@item J a
+@kindex J a (Agent Server)
+@findex gnus-agent-add-server
+Add the current server to the list of servers covered by the Gnus Agent
+(@code{gnus-agent-add-server}).
+
+@item J r
+@kindex J r (Agent Server)
+@findex gnus-agent-remove-server
+Remove the current server from the list of servers covered by the Gnus
+Agent (@code{gnus-agent-remove-server}).
+
+@end table
+
+
+@node Agent Variables
+@subsection Agent Variables
+
+@table @code
+@item gnus-agent-directory
+@vindex gnus-agent-directory
+Where the Gnus Agent will store its files.  The default is
+@file{~/News/agent/}.
+
+@item gnus-agent-plugged-hook
+@vindex gnus-agent-plugged-hook
+Hook run when connecting to the network.
+
+@item gnus-agent-unplugged-hook
+@vindex gnus-agent-unplugged-hook
+Hook run when disconnecting from the network.
+
+@end table
+
+
 @node Scoring
 @chapter Scoring
 @cindex scoring
index 83acd94..4aae155 100644 (file)
@@ -1,7 +1,7 @@
 \input texinfo                  @c -*-texinfo-*-
 
 @setfilename message
-@settitle Message 0.2 Manual
+@settitle Message 0.3 Manual
 @synindex fn cp
 @synindex vr cp
 @synindex pg cp
@@ -39,7 +39,7 @@ into another language, under the above conditions for modified versions.
 @tex
 
 @titlepage
-@title Message 0.2 Manual
+@title Message 0.3 Manual
 
 @author by Lars Magne Ingebrigtsen
 @page
@@ -79,7 +79,7 @@ buffers.
 * Key Index::         List of Message mode keys.
 @end menu
 
-This manual corresponds to Message 0.2.  Message is distributed with
+This manual corresponds to Message 0.3.  Message is distributed with
 the Gnus distribution bearing the same version number as this manual
 has.