*** empty log message ***
authorLars Magne Ingebrigtsen <larsi@gnus.org>
Tue, 4 Mar 1997 01:50:18 +0000 (01:50 +0000)
committerLars Magne Ingebrigtsen <larsi@gnus.org>
Tue, 4 Mar 1997 01:50:18 +0000 (01:50 +0000)
14 files changed:
lisp/ChangeLog
lisp/gnus-kill.el [new file with mode: 0644]
lisp/gnus-uu.el
lisp/gnus-visual.el
lisp/gnus.el
lisp/nnfolder.el
lisp/nnmail.el
lisp/nnmh.el
lisp/nnml.el
lisp/nnspool.el
lisp/nntp.el
lisp/nnvirtual.el
readme
texi/gnus.texi

index 6b47af7..ff7e238 100644 (file)
@@ -1,3 +1,301 @@
+Sun Apr 16 00:02:02 1995  Lars Magne Ingebrigtsen  <larsi@holmenkollen.ifi.uio.no>
+
+       * gnus.el (gnus-summary-update-line): Don't mark ancient and read
+       articles as low-scored.
+       (gnus-inews-article): Would insert headers one line too early. 
+
+Sat Apr 15 22:16:26 1995  Lars Magne Ingebrigtsen  <larsi@holmenkollen.ifi.uio.no>
+
+       * gnus.el (gnus-score-headers): Changed to allow score alists
+       returned from function in...
+       (gnus-score-find-score-files-function): Wider semantics.
+       (gnus-parse-options-lines): Don't parse options if there aren't
+       any. 
+       (gnus-group-default-list-level): New variable.
+       (gnus-use-long-file-name): Extended semantics.
+       (gnus-group-list-groups): Use new variable.
+       (gnus-group-jump-to-group): Bux fix.
+
+Fri Apr 14 08:05:42 1995  Lars Ingebrigtsen  <lars@eyesore.no>
+
+       * gnus.el: Installed Fabrice Popineau's XEmacs patches.
+
+       * nnfolder.el: Installed Scott Byer's version.
+
+       * nntp.el (nntp-request-group): Use LIST ACTIVE group if the
+       server supports it.
+
+Fri Apr 14 17:14:44 1995  Lars Magne Ingebrigtsen  <larsi@menja.ifi.uio.no>
+
+       * gnus.el (gnus-post-news): Set gnus-newsgroup-name when posting. 
+
+       * nnfolder.el (nnfolder-request-expire-articles): Setcar the wrong
+       thing. 
+
+       * gnus.el (gnus-summary-catchup): Would catchup no matter what
+       answer the user gave.
+
+       * nnfolder.el (nnfolder-request-close): New function to remove
+       nnfolder buffers.
+
+Fri Apr 14 17:09:40 1995  Lars Magne Ingebrigtsen  <larsi@gymir.ifi.uio.no>
+
+        * gnus.el: 0.49 is released.
+
+Fri Apr 14 00:29:43 1995  Lars Magne Ingebrigtsen  <larsi@gymir.ifi.uio.no>
+
+       * gnus.el (gnus-list-active-group): New function.
+       (gnus-browse-server-mode-map): New keystrokes: `l' and 
+       `L', which both return to the group buffer.
+
+       * nntp.el (nntp-list-active-group): New function.
+
+       * gnus.el: New spec for current score file.
+       (gnus-simplify-subject-fuzzy): Made fuzzier on white space.
+       (gnus-summary-cancel-article): Heade headers after replying, etc. 
+       (gnus-user-mail-address): New variable.
+       (gnus-score-orphans): Orphan functions added.
+
+Wed Apr 12 23:13:17 1995  Lars Ingebrigtsen  <lars@eyesore.no>
+
+       * gnus.el (gnus-score-body): New function.
+       (gnus-summary-raise-by-body): All the raise/lower functions and
+       keystrokes are added.
+
+Wed Apr 12 17:23:32 1995  Lars Magne Ingebrigtsen  <larsi@menja.ifi.uio.no>
+
+       * gnus.el (gnus-mail-reply-using-mail): Allow reply-to function to
+       return a list of headers to insert.
+       (gnus-summary-save-article): Don't re-request articles before
+       saving. 
+       (gnus-summary-read-group): If all articles have been expunged on
+       accound of low scores, display all articles.
+       (gnus-score-check-syntax): Don't choke on empty score entries.
+
+Wed Apr 12 00:23:01 1995  Lars Ingebrigtsen  <lars@eyesore.no>
+
+       * gnus.el: Doc fix.
+       (gnus-score-integer): New function.
+       (gnus-score-date): New function.
+
+       * nntp.el (nntp-accept-response): Give a better error message.
+
+       * nnvirtual.el (nnvirtual-update-marked): Removal of article marks
+       now propagates to the source groups.
+
+       * gnus.el (gnus-select-newsgroup): Adjust marked lists after
+       really entering group.
+
+Tue Apr 11 23:08:25 1995  Lars Ingebrigtsen  <lars@eyesore.no>
+
+       * gnus.el (gnus-summary-catchup-and-goto-next-group): New command
+       and keystroke.
+       (gnus-summary-toggle-header): Set point at the start up the buffer
+       when toggling the header.
+       (gnus-score-transform-old-to-new): Would rewrite 'files atoms
+       incorrectly. 
+
+       * nnmail.el (nnmail-request-post-buffer): Bind buffer-read-only to
+       nil before attempting to change the buffer.
+
+       * gnus-uu.el (gnus-uu-save-files): Don't choke on non-existing
+       files. 
+
+       * gnus.el (gnus-score-save): Make sure that the directory that the
+       score file is written to actually exists.
+
+       * gnus-kill.el (gnus-kill-file-raise-followups-to-author):
+       Misleading message.
+
+       * gnus.el (gnus-summary-save-article): Remove any X-Gnus header
+       lines before saving.
+
+Tue Apr 11 00:03:35 1995  Lars Magne Ingebrigtsen  <larsi@gjalp.ifi.uio.no>
+
+       * gnus.el (gnus-summary-number-of-articles-in-thread): New
+       function. 
+       (gnus-summary-score-entry): A slightly more elaborate prompt.
+       (gnus-group-first-unread-group): New function and keystroke.
+
+Mon Apr 10 20:41:55 1995  Lars Magne Ingebrigtsen  <larsi@gjalp.ifi.uio.no>
+
+       * gnus.el (gnus-summary-mark-as-read-forward): Overwrite E marks. 
+       (gnus-group-jump-to-group): Allow jumping to groups not in the
+       active file.
+       (gnus-summary-line-format-alist): New spec: number of articles in
+       the current subthread.
+
+       * nnml.el (nnml-possibly-create-directory): Create directories on
+       the fly instead of creating all possible directories at startup.
+
+       * nnmail.el (nnmail-article-group): Allow nnmail-split-methods to
+       be a function to be called.
+
+       * gnus.el (gnus-nov-parse-line): Allow articles without
+       message-ids to pass through. Fudge temporary ids.
+
+       * nnml.el (nnml-make-nov-line): Create dummy message-ids for
+       articles that do not have them.
+
+       * gnus.el (gnus-group-make-group): Refuse to create groups that
+       already exist.
+       (gnus-group-change-level): Don't enter foreign groups into killed
+       lists. 
+       (gnus-parse-n-options): Handle options -n lines as the were
+       supposed to - sequentially.
+       (gnus-newsrc-options-n-yes, gnus-newsrc-options-n-no): Obsolete
+       variables. 
+       (gnus-newsrc-options-n): New variable.
+       (gnus-matches-options-n): New function.
+       (gnus-summary-next-group): Kill summary buffer even when C-g'ing
+       while choosing the next group after n'ing.
+       (gnus-summary-mode-line-format-alist): New format spec added.
+       (gnus-short-group-name): New function.
+       (gnus-mail-forward-using-mail): Use From line instead of grup name
+       in the Subject header when forwarding.
+       (gnus-summary-mode-line-format-alist): Added user-defined spec to
+       the mode line alists. 
+       (gnus-score-save): Would set `gnus-score-cache' to nil.
+
+       * gnus-uu.el (gnus-uu-mark-sparse): Did not create hashtb before
+       using it.
+
+       * gnus.el (gnus-mail-other-window-using-mail): Used lisp keymap.
+
+Mon Apr 10 20:29:26 1995  Lars Magne Ingebrigtsen  <larsi@surt.ifi.uio.no>
+
+       * gnus.el (gnus-summary-move-article): Didn't remove articles from
+       list of unreads.
+
+Mon Apr 10 14:59:49 1995  Lars Magne Ingebrigtsen  <larsi@menja.ifi.uio.no>
+
+       * gnus.el, gnus-uu.el: Changed all instances of
+       mail-header-separator to use regexp-quote and anchors.
+
+       * gnus.el (gnus-nov-parse-line): Don't choke on malformed NOV
+       lines. 
+
+Sun Apr  2 13:16:03 1995  Lars Magne Ingebrigtsen  <larsi@mimir.ifi.uio.no>
+
+       * gnus.el (gnus-inews-insert-headers): Didn't check new
+       -gather-limit correctly. 
+       (gnus-summary-prepare-threads): Print subjects if `fuzzy' was
+       used, but subjects aren't equal.
+
+Sun Apr  2 12:11:17 1995  Lars Magne Ingebrigtsen  <larsi@mimir.ifi.uio.no>
+
+       * gnus.el: 0.47 & 0.48 is released.
+
+       * nnmh.el (nnmh-article-pathname): Wouldn't find groups that were
+       located in directories that had "." in the directory names.
+
+       * gnus.el (gnus-score-load-file): Changing score alists would have
+       no effect. 
+
+Sat Apr  1 16:45:14 1995  Lars Ingebrigtsen  <lars@eyesore.no>
+
+       * gnus-visual.el (gnus-visual-highlight-selected-summary): Would
+       mess up selected face when no mouse highlights were used.
+
+       * nnml.el (nnml-request-create-group): Really create groups that
+       are created.
+
+       * gnus.el (gnus-setup-news): If the local server can't be
+       contacted, just ignore it and offer to continue.
+       (gnus-group-post-news): Set newsgroup name to nil before offering
+       to post.
+       (gnus-summary-read-group): Summary buffer wouldn't be killed when
+       `n'-ing to a group that had all its articles expired.
+
+       * nntp.el (nntp-open-server): Allow quitting when setting up
+       connection to a server.
+
+       * gnus.el (gnus-articles-to-read): Ticked articles would become
+       read when newsgroups were entered with C-u SPC.
+       (gnus-inews-check-post): Check outgoing post for long lines.
+       (gnus-score-load-score-alist): Ignore empty score files. 
+       (gnus-score-check-syntax): Check score file syntax.
+
+Sat Apr  1 10:41:11 1995  Lars Magne Ingebrigtsen  <larsi@nain.ifi.uio.no>
+
+       * gnus.el (gnus-summary-move-article): Did not remove ticked and
+       dormant articles from relevant lists.
+
+Fri Mar 31 11:49:44 1995  Lars Magne Ingebrigtsen  <larsi@menja.ifi.uio.no>
+
+       * gnus-kill.el (gnus-apply-kill-file-internal): Did not add kill
+       buffers to the buffer list for later killing.
+
+       * nnml.el (nnml-request-expire-articles): Would bug out on empty
+       groups. 
+
+Wed Mar 29 13:34:45 1995  Lars Ingebrigtsen  <lars@eyesore.no>
+
+       * nnspool.el (nnspool-request-newgroups): Use floats instead of
+       fudging. 
+
+       * gnus.el (gnus-adjust-marked-articles): Remove expired reply
+       marks. 
+
+       * nnvirtual.el (nnvirtual-create-mapping): Ignore marks on
+       articles that are expired.
+
+       * gnus.el (gnus-gather-threads): Allow fuzzy comparisons.
+       (gnus-simplify-subject-fuzzy): New function.
+
+       * nnml.el (nnml-request-create-group): New function.
+
+       * gnus.el (gnus-group-make-group): Create nnml groups when
+       requested. 
+       (gnus-request-create-group): New function.
+
+       * nntp.el (nntp-request-article): Avoid obsolete concating of
+       numbers-as-strings.
+
+Wed Mar 29 10:21:00 1995  Lars Magne Ingebrigtsen  <larsi@gjalp.ifi.uio.no>
+
+        * gnus.el: 0.46 is released.
+
+Wed Mar 29 09:55:15 1995  Lars Magne Ingebrigtsen  <larsi@gjalp.ifi.uio.no>
+
+       * gnus.el (gnus-score-load-file): Use different method for
+       figuring out whether score file names are relative.
+
+Wed Mar 29 08:54:25 1995  Lars Magne Ingebrigtsen  <larsi@surt.ifi.uio.no>
+
+       * gnus.el (gnus-group-set-info): Bugged out on lists instead of
+       ranges. 
+
+       * nntp.el (nntp-open-server): Would try to send MODE READER even
+       when opening was unsuccessful.
+
+Wed Mar 29 03:56:05 1995  Lars Magne Ingebrigtsen  <larsi@mimir.ifi.uio.no>
+
+       * gnus.el (gnus-group-faq-directory): Change in value.
+
+Tue Mar 28 11:06:18 1995  Lars Magne Ingebrigtsen  <larsi@maud.ifi.uio.no>
+
+       * gnus.el (gnus-group-archive-directory): New variable.
+       (gnus-group-make-archive-group): New command and keystroke.
+       (gnus-get-unread-articles): Did not properly activate nnvirtual
+       groups. 
+       (gnus-summary-insert-pseudos): Didn't initialize hashtb before
+       inserting pseudos.
+
+Mon Mar 27 20:58:05 1995  Lars Magne Ingebrigtsen  <larsi@surt.ifi.uio.no>
+
+       * gnus.el (gnus-summary-sort): If used in summary-prepare-hook,
+       would lead to infinite recursion.
+
+Mon Mar 27 19:09:35 1995  Lars Magne Ingebrigtsen  <larsi@gymir.ifi.uio.no>
+
+       * gnus.el (gnus-mail-other-window-using-mail): Would give wrong
+       parameters to sendamil function.
+
+Mon Mar 27 19:06:58 1995  Lars Magne Ingebrigtsen  <larsi@menja.ifi.uio.no>
+
+       * gnus.el: 0.45 is released.
+
 Mon Mar 27 18:31:05 1995  Lars Magne Ingebrigtsen  <larsi@gymir.ifi.uio.no>
 
        * gnus.el (gnus-summary-catchup-to-here): Would mark everything as
diff --git a/lisp/gnus-kill.el b/lisp/gnus-kill.el
new file mode 100644 (file)
index 0000000..a76233e
--- /dev/null
@@ -0,0 +1,548 @@
+;;; gnus-kill --- kill commands for Gnus
+;; Copyright (C) 1995 Free Software Foundation, Inc.
+
+;; Author: Masanobu UMEDA <umerin@flab.flab.fujitsu.junet>
+;;     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, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+;;; Commentary:
+
+;;; Code:
+
+(require 'gnus)
+
+(defvar gnus-kill-file-mode-hook nil
+  "A hook for Gnus kill file mode.")
+
+(defvar gnus-winconf-kill-file nil)
+
+\f
+
+(defmacro gnus-raise (field expression level)
+  (` (gnus-kill (, field) (, expression)
+               (function (gnus-summary-raise-score (, level))) t)))
+
+(defmacro gnus-lower (field expression level)
+  (` (gnus-kill (, field) (, expression)
+               (function (gnus-summary-raise-score (- (, level)))) t)))
+
+;;;
+;;; Gnus Kill File Mode
+;;;
+
+(defvar gnus-kill-file-mode-map nil)
+
+(if gnus-kill-file-mode-map
+    nil
+  (setq gnus-kill-file-mode-map (copy-keymap emacs-lisp-mode-map))
+  (define-key gnus-kill-file-mode-map 
+    "\C-c\C-k\C-s" 'gnus-kill-file-kill-by-subject)
+  (define-key gnus-kill-file-mode-map
+    "\C-c\C-k\C-a" 'gnus-kill-file-kill-by-author)
+  (define-key gnus-kill-file-mode-map
+    "\C-c\C-k\C-a" 'gnus-kill-file-kill-by-thread)
+  (define-key gnus-kill-file-mode-map 
+    "\C-c\C-k\C-a" 'gnus-kill-file-kill-by-xref)
+  (define-key gnus-kill-file-mode-map
+    "\C-c\C-a" 'gnus-kill-file-apply-buffer)
+  (define-key gnus-kill-file-mode-map
+    "\C-c\C-e" 'gnus-kill-file-apply-last-sexp)
+  (define-key gnus-kill-file-mode-map 
+    "\C-c\C-c" 'gnus-kill-file-exit))
+
+(defun gnus-kill-file-mode ()
+  "Major mode for editing kill files.
+
+If you are using this mode - you probably shouldn't.  Kill files
+perform badly and paint with a pretty broad brush.  Score files, on
+the other hand, are vastly faster (40x speedup) and give you more
+control over what to do.
+
+In addition to Emacs-Lisp Mode, the following commands are available:
+
+\\{gnus-kill-file-mode-map}
+
+  A kill file contains Lisp expressions to be applied to a selected
+newsgroup.  The purpose is to mark articles as read on the basis of
+some set of regexps.  A global kill file is applied to every newsgroup,
+and a local kill file is applied to a specified newsgroup.  Since a
+global kill file is applied to every newsgroup, for better performance
+use a local one.
+
+  A kill file can contain any kind of Emacs Lisp expressions expected
+to be evaluated in the Summary buffer.  Writing Lisp programs for this
+purpose is not so easy because the internal working of Gnus must be
+well-known.  For this reason, Gnus provides a general function which
+does this easily for non-Lisp programmers.
+
+  The `gnus-kill' function executes commands available in Summary Mode
+by their key sequences. `gnus-kill' should be called with FIELD,
+REGEXP and optional COMMAND and ALL.  FIELD is a string representing
+the header field or an empty string.  If FIELD is an empty string, the
+entire article body is searched for.  REGEXP is a string which is
+compared with FIELD value. COMMAND is a string representing a valid
+key sequence in Summary mode or Lisp expression. COMMAND defaults to
+'(gnus-summary-mark-as-read nil \"X\").  Make sure that COMMAND is
+executed in the Summary buffer.  If the second optional argument ALL
+is non-nil, the COMMAND is applied to articles which are already
+marked as read or unread.  Articles which are marked are skipped over
+by default.
+
+  For example, if you want to mark articles of which subjects contain
+the string `AI' as read, a possible kill file may look like:
+
+       (gnus-kill \"Subject\" \"AI\")
+
+  If you want to mark articles with `D' instead of `X', you can use
+the following expression:
+
+       (gnus-kill \"Subject\" \"AI\" \"d\")
+
+In this example it is assumed that the command
+`gnus-summary-mark-as-read-forward' is assigned to `d' in Summary Mode.
+
+  It is possible to delete unnecessary headers which are marked with
+`X' in a kill file as follows:
+
+       (gnus-expunge \"X\")
+
+  If the Summary buffer is empty after applying kill files, Gnus will
+exit the selected newsgroup normally.  If headers which are marked
+with `D' are deleted in a kill file, it is impossible to read articles
+which are marked as read in the previous Gnus sessions.  Marks other
+than `D' should be used for articles which should really be deleted.
+
+Entry to this mode calls emacs-lisp-mode-hook and
+gnus-kill-file-mode-hook with no arguments, if that value is non-nil."
+  (interactive)
+  (kill-all-local-variables)
+  (use-local-map gnus-kill-file-mode-map)
+  (set-syntax-table emacs-lisp-mode-syntax-table)
+  (setq major-mode 'gnus-kill-file-mode)
+  (setq mode-name "Kill")
+  (lisp-mode-variables nil)
+  (run-hooks 'emacs-lisp-mode-hook 'gnus-kill-file-mode-hook))
+
+(defun gnus-kill-file-edit-file (newsgroup)
+  "Begin editing a kill file for NEWSGROUP.
+If NEWSGROUP is nil, the global kill file is selected."
+  (interactive "sNewsgroup: ")
+  (let ((file (gnus-newsgroup-kill-file newsgroup)))
+    (gnus-make-directory (file-name-directory file))
+    ;; Save current window configuration if this is first invocation.
+    (or (and (get-file-buffer file)
+            (get-buffer-window (get-file-buffer file)))
+       (setq gnus-winconf-kill-file (current-window-configuration)))
+    ;; Hack windows.
+    (let ((buffer (find-file-noselect file)))
+      (cond ((get-buffer-window buffer)
+            (pop-to-buffer buffer))
+           ((eq major-mode 'gnus-group-mode)
+            (gnus-configure-windows '(1 0 0)) ;Take all windows.
+            (pop-to-buffer gnus-group-buffer)
+            ;; Fix by sachs@SLINKY.CS.NYU.EDU (Jay Sachs).
+            (let ((gnus-summary-buffer buffer))
+              (gnus-configure-windows '(1 1 0))) ;Split into two.
+            (pop-to-buffer buffer))
+           ((eq major-mode 'gnus-summary-mode)
+            (gnus-configure-windows 'article)
+            (pop-to-buffer gnus-article-buffer)
+            (bury-buffer gnus-article-buffer)
+            (switch-to-buffer buffer))
+           (t                          ;No good rules.
+            (find-file-other-window file))))
+    (gnus-kill-file-mode)))
+
+;; Fix by Sudish Joseph <joseph@cis.ohio-state.edu>.
+(defun gnus-kill-set-kill-buffer ()
+  (let* ((file (gnus-newsgroup-kill-file gnus-newsgroup-name))
+        (buffer (find-file-noselect file)))
+    (set-buffer buffer)
+    (gnus-kill-file-mode)
+    (bury-buffer buffer)))
+
+(defun gnus-kill-file-enter-kill (field regexp)
+  ;; Enter kill file entry.
+  ;; FIELD: String containing the name of the header field to kill.
+  ;; REGEXP: The string to kill.
+  (save-excursion
+    (let (string)
+      (gnus-kill-set-kill-buffer)
+      (goto-char (point-max))
+      (insert (setq string (format "(gnus-kill %S %S)\n" field regexp)))
+      (gnus-kill-file-apply-string string))))
+    
+(defun gnus-kill-file-kill-by-subject ()
+  "Kill by subject."
+  (interactive)
+  (gnus-kill-file-enter-kill
+   "Subject" 
+   (regexp-quote 
+    (gnus-simplify-subject (header-subject gnus-current-headers)))))
+  
+(defun gnus-kill-file-kill-by-author ()
+  "Kill by author."
+  (interactive)
+  (gnus-kill-file-enter-kill
+   "From" (regexp-quote (header-from gnus-current-headers))))
+(defun gnus-kill-file-kill-by-thread ()
+  "Kill by author."
+  (interactive "p")
+  (gnus-kill-file-enter-kill
+   "References" (regexp-quote (header-id gnus-current-headers))))
+(defun gnus-kill-file-kill-by-xref ()
+  "Kill by Xref."
+  (interactive)
+  (let ((xref (header-xref gnus-current-headers))
+       (start 0)
+       group)
+    (if xref
+       (while (string-match " \\([^ \t]+\\):" xref start)
+         (setq start (match-end 0))
+         (if (not (string= 
+                   (setq group 
+                         (substring xref (match-beginning 1) (match-end 1)))
+                   gnus-newsgroup-name))
+             (gnus-kill-file-enter-kill 
+              "Xref" (concat " " (regexp-quote group) ":"))))
+      (gnus-kill-file-enter-kill "Xref" ""))))
+
+(defun gnus-kill-file-raise-followups-to-author (level)
+  "Raise score for all followups to the current author."
+  (interactive "p")
+  (let ((name (header-from gnus-current-headers))
+       string)
+    (save-excursion
+      (gnus-kill-set-kill-buffer)
+      (goto-char (point-min))
+      (setq name (read-string (concat "Add " level
+                                     " to followup articles to: ")
+                             (regexp-quote name)))
+      (setq 
+       string
+       (format
+       "(gnus-kill %S %S '(gnus-summary-temporarily-raise-by-thread %S))\n"
+       "From" name level))
+      (insert string)
+      (gnus-kill-file-apply-string string))
+    (message "Added temporary score file entry for followups to %s." name)))
+
+(defun gnus-kill-file-apply-buffer ()
+  "Apply current buffer to current newsgroup."
+  (interactive)
+  (if (and gnus-current-kill-article
+          (get-buffer gnus-summary-buffer))
+      ;; Assume newsgroup is selected.
+      (gnus-kill-file-apply-string (buffer-string))
+    (ding) (message "No newsgroup is selected.")))
+
+(defun gnus-kill-file-apply-string (string)
+  "Apply STRING to current newsgroup."
+  (interactive)
+  (let ((string (concat "(progn \n" string "\n)")))
+    (save-excursion
+      (save-window-excursion
+       (pop-to-buffer gnus-summary-buffer)
+       (eval (car (read-from-string string)))))))
+
+(defun gnus-kill-file-apply-last-sexp ()
+  "Apply sexp before point in current buffer to current newsgroup."
+  (interactive)
+  (if (and gnus-current-kill-article
+          (get-buffer gnus-summary-buffer))
+      ;; Assume newsgroup is selected.
+      (let ((string
+            (buffer-substring
+             (save-excursion (forward-sexp -1) (point)) (point))))
+       (save-excursion
+         (save-window-excursion
+           (pop-to-buffer gnus-summary-buffer)
+           (eval (car (read-from-string string))))))
+    (ding) (message "No newsgroup is selected.")))
+
+(defun gnus-kill-file-exit ()
+  "Save a kill file, then return to the previous buffer."
+  (interactive)
+  (save-buffer)
+  (let ((killbuf (current-buffer)))
+    ;; We don't want to return to article buffer.
+    (and (get-buffer gnus-article-buffer)
+        (bury-buffer gnus-article-buffer))
+    ;; Delete the KILL file windows.
+    (delete-windows-on killbuf)
+    ;; Restore last window configuration if available.
+    (and gnus-winconf-kill-file
+        (set-window-configuration gnus-winconf-kill-file))
+    (setq gnus-winconf-kill-file nil)
+    ;; Kill the KILL file buffer.  Suggested by tale@pawl.rpi.edu.
+    (kill-buffer killbuf)))
+
+;; For kill files
+
+(defun gnus-Newsgroup-kill-file (newsgroup)
+  "Return the name of a kill file for NEWSGROUP.
+If NEWSGROUP is nil, return the global kill file instead."
+  (cond ((or (null newsgroup)
+            (string-equal newsgroup ""))
+        ;; The global kill file is placed at top of the directory.
+        (expand-file-name gnus-kill-file-name
+                          (or gnus-kill-files-directory "~/News")))
+       (gnus-use-long-file-name
+        ;; Append ".KILL" to capitalized newsgroup name.
+        (expand-file-name (concat (gnus-capitalize-newsgroup newsgroup)
+                                  "." gnus-kill-file-name)
+                          (or gnus-kill-files-directory "~/News")))
+       (t
+        ;; Place "KILL" under the hierarchical directory.
+        (expand-file-name (concat (gnus-newsgroup-directory-form newsgroup)
+                                  "/" gnus-kill-file-name)
+                          (or gnus-kill-files-directory "~/News")))))
+
+(defalias 'gnus-expunge 'gnus-summary-remove-lines-marked-with)
+
+(defun gnus-apply-kill-file-internal ()
+  "Apply a kill file to the current newsgroup.
+Returns the number of articles marked as read."
+  (let* ((kill-files (list (gnus-newsgroup-kill-file nil)
+                          (gnus-newsgroup-kill-file gnus-newsgroup-name)))
+        (unreads (length gnus-newsgroup-unreads))
+        (gnus-summary-inhibit-highlight t)
+        (mark-below (or gnus-summary-mark-below gnus-summary-default-score 0))
+        (expunge-below gnus-summary-expunge-below)
+        form beg)
+    (setq gnus-newsgroup-kill-headers nil)
+    (or gnus-newsgroup-headers-hashtb-by-number
+       (gnus-make-headers-hashtable-by-number))
+    ;; If there are any previously scored articles, we remove these
+    ;; from the `gnus-newsgroup-headers' list that the score functions
+    ;; will see. This is probably pretty wasteful when it comes to
+    ;; conses, but is, I think, faster than having to assq in every
+    ;; single score funtion.
+    (let ((files kill-files))
+      (while files
+       (if (file-exists-p (car files))
+           (let ((headers gnus-newsgroup-headers))
+             (if gnus-kill-killed
+                 (setq gnus-newsgroup-kill-headers
+                       (mapcar (lambda (header) (header-number header))
+                               headers))
+               (while headers
+                 (or (gnus-member-of-range 
+                      (header-number (car headers)) 
+                      gnus-newsgroup-killed)
+                     (setq gnus-newsgroup-kill-headers 
+                           (cons (header-number (car headers))
+                                 gnus-newsgroup-kill-headers)))
+                 (setq headers (cdr headers))))
+             (setq files nil))
+         (setq files (cdr files)))))
+    (if gnus-newsgroup-kill-headers
+       (save-excursion
+         (while kill-files
+           (if (file-exists-p (car kill-files))
+               (progn
+                 (message "Processing kill file %s..." (car kill-files))
+                 (find-file (car kill-files))
+                 (gnus-kill-file-mode)
+                 (gnus-add-current-to-buffer-list)
+                 (goto-char (point-min))
+                 (while (progn
+                          (setq beg (point))
+                          (setq form (condition-case nil 
+                                         (read (current-buffer)) 
+                                       (error nil))))
+                   (if (or (eq (car form) 'gnus-kill)
+                           (eq (car form) 'gnus-raise)
+                           (eq (car form) 'gnus-lower))
+                       (progn
+                         (delete-region beg (point))
+                         (insert (or (eval form) "")))
+                     (condition-case ()
+                         (eval form)
+                       (error nil))))
+                 (and (buffer-modified-p) (save-buffer))
+                 (message "Processing kill file %s...done" (car kill-files))))
+           (setq kill-files (cdr kill-files)))))
+    (if beg
+       (let ((nunreads (- unreads (length gnus-newsgroup-unreads))))
+         (or (eq nunreads 0)
+             (message "Marked %d articles as read" nunreads))
+         nunreads)
+      0)))
+
+;; Kill changes and new format by suggested by JWZ and Sudish Joseph
+;; <joseph@cis.ohio-state.edu>.  
+(defun gnus-kill (field regexp &optional exe-command all)
+  "If FIELD of an article matches REGEXP, execute COMMAND.
+Optional 1st argument COMMAND is default to
+       (gnus-summary-mark-as-read nil \"X\").
+If optional 2nd argument ALL is non-nil, articles marked are also applied to.
+If FIELD is an empty string (or nil), entire article body is searched for.
+COMMAND must be a lisp expression or a string representing a key sequence."
+  ;; We don't want to change current point nor window configuration.
+  (save-excursion
+    (save-window-excursion
+      ;; Selected window must be summary buffer to execute keyboard
+      ;; macros correctly. See command_loop_1.
+      (switch-to-buffer gnus-summary-buffer 'norecord)
+      (goto-char (point-min))          ;From the beginning.
+      (let ((kill-list regexp)
+           (date (current-time-string))
+           (command (or exe-command '(gnus-summary-mark-as-read 
+                                      nil gnus-kill-file-mark)))
+           kill kdate prev)
+       (if (listp kill-list)
+           ;; It is a list.
+           (if (not (consp (cdr kill-list)))
+               ;; It's on the form (regexp . date).
+               (if (zerop (gnus-execute field (car kill-list) 
+                                        command nil (not all)))
+                   (if (> (gnus-days-between date (cdr kill-list))
+                          gnus-score-expiry-days)
+                       (setq regexp nil))
+                 (setcdr kill-list date))
+             (while (setq kill (car kill-list))
+               (if (consp kill)
+                   ;; It's a temporary kill.
+                   (progn
+                     (setq kdate (cdr kill))
+                     (if (zerop (gnus-execute 
+                                 field (car kill) command nil (not all)))
+                         (if (> (gnus-days-between date kdate)
+                                gnus-score-expiry-days)
+                             ;; Time limit has been exceeded, so we
+                             ;; remove the match.
+                             (if prev
+                                 (setcdr prev (cdr kill-list))
+                               (setq regexp (cdr regexp))))
+                       ;; Successful kill. Set the date to today.
+                       (setcdr kill date)))
+                 ;; It's a permanent kill.
+                 (gnus-execute field kill command nil (not all)))
+               (setq prev kill-list)
+               (setq kill-list (cdr kill-list))))
+         (gnus-execute field kill-list command nil (not all))))))
+  (if (and (eq major-mode 'gnus-kill-file-mode) regexp)
+      (gnus-pp-gnus-kill
+       (nconc (list 'gnus-kill field 
+                   (if (consp regexp) (list 'quote regexp) regexp))
+             (if (or exe-command all) (list (list 'quote exe-command)))
+             (if all (list t) nil)))))
+
+(defun gnus-pp-gnus-kill (object)
+  (if (or (not (consp (nth 2 object)))
+         (not (consp (cdr (nth 2 object))))
+         (and (eq 'quote (car (nth 2 object)))
+              (not (consp (cdr (car (cdr (nth 2 object))))))))
+      (concat "\n" (prin1-to-string object))
+    (save-excursion
+      (set-buffer (get-buffer-create "*Gnus PP*"))
+      (buffer-disable-undo (current-buffer))
+      (erase-buffer)
+      (insert (format "\n(%S %S\n  '(" (nth 0 object) (nth 1 object)))
+      (let ((klist (car (cdr (nth 2 object))))
+           (first t))
+       (while klist
+         (insert (if first (progn (setq first nil) "")  "\n    ")
+                 (prin1-to-string (car klist)))
+         (setq klist (cdr klist))))
+      (insert ")")
+      (and (nth 3 object)
+          (insert "\n  " 
+                  (if (and (consp (nth 3 object))
+                           (not (eq 'quote (car (nth 3 object))))) 
+                      "'" "")
+                  (prin1-to-string (nth 3 object))))
+      (and (nth 4 object)
+          (insert "\n  t"))
+      (insert ")")
+      (prog1
+         (buffer-substring (point-min) (point-max))
+       (kill-buffer (current-buffer))))))
+
+(defun gnus-execute-1 (function regexp form header)
+  (save-excursion
+    (let (did-kill)
+      (if (null header)
+         nil                           ;Nothing to do.
+       (if function
+           ;; Compare with header field.
+           (let (value)
+             (and header
+                  (progn
+                    (setq value (funcall function header))
+                    ;; Number (Lines:) or symbol must be converted to string.
+                    (or (stringp value)
+                        (setq value (prin1-to-string value)))
+                    (setq did-kill (string-match regexp value)))
+                  (if (stringp form)   ;Keyboard macro.
+                      (execute-kbd-macro form)
+                    (funcall form))))
+         ;; Search article body.
+         (let ((gnus-current-article nil) ;Save article pointer.
+               (gnus-last-article nil)
+               (gnus-break-pages nil)  ;No need to break pages.
+               (gnus-mark-article-hook nil)) ;Inhibit marking as read.
+           (message "Searching for article: %d..." (header-number header))
+           (gnus-article-setup-buffer)
+           (gnus-article-prepare (header-number header) t)
+           (if (save-excursion
+                 (set-buffer gnus-article-buffer)
+                 (goto-char (point-min))
+                 (setq did-kill (re-search-forward regexp nil t)))
+               (if (stringp form)      ;Keyboard macro.
+                   (execute-kbd-macro form)
+                 (eval form))))))
+      did-kill)))
+
+(defun gnus-execute (field regexp form &optional backward ignore-marked)
+  "If FIELD of article header matches REGEXP, execute lisp FORM (or a string).
+If FIELD is an empty string (or nil), entire article body is searched for.
+If optional 1st argument BACKWARD is non-nil, do backward instead.
+If optional 2nd argument IGNORE-MARKED is non-nil, articles which are
+marked as read or ticked are ignored."
+  (save-excursion
+    (let ((killed-no 0)
+         function header article)
+      (if (or (null field) (string-equal field ""))
+         (setq function nil)
+       ;; Get access function of header filed.
+       (setq function (intern-soft (concat "gnus-header-" (downcase field))))
+       (if (and function (fboundp function))
+           (setq function (symbol-function function))
+         (error "Unknown header field: \"%s\"" field))
+       ;; Make FORM funcallable.
+       (if (and (listp form) (not (eq (car form) 'lambda)))
+           (setq form (list 'lambda nil form))))
+      ;; Starting from the current article.
+      (while (or (and (not article)
+                     (setq article (gnus-summary-article-number))
+                     t)
+                (setq article 
+                      (gnus-summary-search-subject 
+                       backward (not ignore-marked))))
+       (and (or (null gnus-newsgroup-kill-headers)
+                (memq article gnus-newsgroup-kill-headers))
+            (gnus-execute-1 function regexp form 
+                            (gnus-get-header-by-number article))
+            (setq killed-no (1+ killed-no))))
+      killed-no)))
+
index e6c8046..c02aaeb 100644 (file)
@@ -424,13 +424,10 @@ so I simply dropped them.")
     (setq gnus-newsgroup-processable nil)
     (save-excursion
       (while marked
-       (setq subject (header-subject 
-                      (gnus-gethash 
-                       (int-to-string (car marked))
-                       gnus-newsgroup-headers-hashtb-by-number)))
-       (setq articles (gnus-uu-find-articles-matching 
-                       (gnus-uu-reginize-string subject)))
-       (setq total (nconc total articles))
+       (setq subject (header-subject (gnus-get-header-by-number (car marked)))
+             articles (gnus-uu-find-articles-matching 
+                       (gnus-uu-reginize-string subject))
+             total (nconc total articles))
        (while articles
          (gnus-summary-set-process-mark (car articles))
          (setcdr marked (delq (car articles) (cdr marked)))
@@ -472,10 +469,10 @@ so I simply dropped them.")
        file)
     (while files
       (setq file (cdr (assq 'name (car files))))
-      (copy-file file (if (file-directory-p dir)
-                         (concat dir (file-name-nondirectory file))
-                       dir)
-                t)
+      (and (file-exists-p file)
+          (copy-file file (if (file-directory-p dir)
+                              (concat dir (file-name-nondirectory file)) dir)
+                     1 t))
       (setq files (cdr files)))
     (message "Saved %d file%s" len (if (> len 1) "s" ""))))
 
@@ -1510,10 +1507,10 @@ to post the entire encoded files.
   (setq mode-name "Gnus UU News")
   (make-local-variable 'paragraph-separate)
   (make-local-variable 'paragraph-start)
-  (setq paragraph-start (concat "^" mail-header-separator "$\\|"
-                               paragraph-start))
-  (setq paragraph-separate (concat "^" mail-header-separator "$\\|"
-                                  paragraph-separate))
+  (setq paragraph-start (concat "^" (regexp-quote mail-header-separator)
+                               "$\\|" paragraph-start))
+  (setq paragraph-separate (concat "^" (regexp-quote mail-header-separator)
+                                  "$\\|" paragraph-separate))
   (run-hooks 'text-mode-hook 'gnus-uu-post-reply-mode-hook))
 
 (defun gnus-uu-post-news ()
@@ -1571,7 +1568,7 @@ The user will be asked for a file name."
   (save-restriction
     (set-buffer gnus-post-news-buffer)
     (goto-char 1)
-    (re-search-forward mail-header-separator)
+    (re-search-forward (regexp-quote mail-header-separator))
     (beginning-of-line)
     (forward-line -1)
     (narrow-to-region 1 (point))
@@ -1673,7 +1670,7 @@ If no file has been included, the user will be asked for a file."
     (if (not (re-search-forward 
              (if gnus-uu-post-separate-description 
                  gnus-uu-post-binary-separator 
-               mail-header-separator) nil t))
+               (concat "^" (regexp-quote mail-header-separator) "$")) nil t))
        (error "Internal error: No binary/header separator"))
     (beginning-of-line)
     (forward-line 1)
@@ -1695,7 +1692,8 @@ If no file has been included, the user will be asked for a file."
     (kill-region (point) (point-max))
 
     (goto-char 1)
-    (search-forward mail-header-separator nil t)
+    (re-search-forward 
+     (concat "^" (regexp-quote mail-header-separator) "$") nil t)
     (beginning-of-line)
     (setq header (buffer-substring 1 (point)))
 
@@ -1757,7 +1755,8 @@ If no file has been included, the user will be asked for a file."
        (setq beg end)
        (setq i (1+ i))
        (goto-char 1)
-       (re-search-forward mail-header-separator nil t)
+       (re-search-forward
+        (concat "^" (regexp-quote mail-header-separator) "$") nil t)
        (beginning-of-line)
        (forward-line 2)
        (if (re-search-forward gnus-uu-post-binary-separator nil t)
index 7e53af0..9a397a4 100644 (file)
 ;;; Code:
 
 (require 'gnus)
-(require 'easymenu)
+(require (if gnus-xemacs 'auc-menu 'easymenu))
 
 (defvar gnus-summary-selected-face 'underline
-  "Face used for highlighting the selected article in the Summary buffer.")
+  "Face used for highlighting the current article in the summary buffer.")
 
 (defvar gnus-visual-summary-highlight
   '(((> score default) . bold)
     ((< score default) . italic))
-  "Alist of (FORM . FACE).
+  "Alist of `(FORM . FACE)'.
 Summary lines are highlighted with the FACE for the first FORM which
-evaluate to non-nil.  
+evaluate to a non-nil value.  
 
-When FORM is evaluated point will be at the beginning of the line, and
-the following free variable can be used for convenience:
+Point will be at the beginning of the line when FORM is evaluated.
+The following can be used for convenience:
 
-score:   (gnus-summary-interest)
-default: gnus-summary-default-interest
+score:   (gnus-summary-article-score)
+default: gnus-summary-default-score
 below:   gnus-summary-mark-below
 
-To check for marks, e.g. to underline replied articles, use `looking-at':
+To check for marks, e.g. to underline replied articles, use
+`gnus-summary-article-mark': 
 
-   ((looking-at \".R\") . underline)
-
-This will match all lines where the second character is `R'.  
-The `.' will match any character.")
+   ((= (gnus-summary-article-mark) gnus-replied-mark) . underline)")
 
 (eval-and-compile
   (autoload 'nnkiboze-generate-groups "nnkiboze"))
@@ -83,27 +81,29 @@ The `.' will match any character.")
    gnus-group-mode-map
    ""
    '("Groups"
+     ("Listing"
+      ["List subscribed groups" gnus-group-list-groups t]
+      ["List all groups" gnus-group-list-all-groups t]
+      ["List groups matching..." gnus-group-list-matching t]
+      ["List killed groups" gnus-group-list-killed t]
+      ["List zombie groups" gnus-group-list-zombies t]
+      ["Describe all groups" gnus-group-describe-all-groups t]
+      ["Group apropos" gnus-group-apropos t]
+      ["Group and description apropos" gnus-group-description-apropos t]
+      ["List groups matching..." gnus-group-list-matching t])
+     ("Subscribe"
+      ["Subscribe to random group" gnus-group-unsubscribe-group t]
+      ["Kill all newsgroups in region" gnus-group-kill-region t]
+      ["Kill all zombie groups" gnus-group-kill-all-zombies t])
+     ("Foreign groups"
+      ["Make a foreign group" gnus-group-make-group t]
+      ["Edit a group entry" gnus-group-edit-group t]
+      ["Add a directory group" gnus-group-make-directory-group t]
+      ["Add the help group" gnus-group-make-help-group t]
+      ["Add the archive group" gnus-group-make-archive-group t]
+      ["Make a kiboze group" gnus-group-make-kiboze-group t])
      ["Jump to group" gnus-group-jump-to-group t]
      ["Best unread group" gnus-group-best-unread-group t]
-     ["List subscribed groups" gnus-group-list-groups t]
-     ["List all groups" gnus-group-list-all-groups t]
-     ["List groups matching..." gnus-group-list-matching t]
-     ["Sort group buffer" gnus-group-sort-groups t]
-     ["Subscribe to random group" gnus-group-unsubscribe-group t]
-     ["Describe all groups" gnus-group-describe-all-groups t]
-     ["Group apropos" gnus-group-apropos t]
-     ["Group and description apropos" gnus-group-description-apropos t]
-     ["List groups matching..." gnus-group-list-matching t]
-     ["Add a foreign group" gnus-group-add-group t]
-     ["Edit a group entry" gnus-group-edit-group t]
-     ["Add a directory group" gnus-group-make-directory-group t]
-     ["Add the help group" gnus-group-make-help-group t]
-     ["Make a kiboze group" gnus-group-make-kiboze-group t]
-     ["Kill all newsgroups in region" gnus-group-kill-region t]
-     ["Kill all zombie groups" gnus-group-kill-all-zombies t]
-     ["List killed groups" gnus-group-list-killed t]
-     ["List zombie groups" gnus-group-list-zombies t]
-     ["Edit global KILL file" gnus-group-edit-global-kill t]
      ))
 
   (easy-menu-define
@@ -135,6 +135,8 @@ The `.' will match any character.")
      ["Clear dribble buffer" gnus-group-clear-dribble t]
      ["Exit from Gnus" gnus-group-exit t]
      ["Exit without saving" gnus-group-quit t]
+     ["Sort group buffer" gnus-group-sort-groups t]
+     ["Edit global KILL file" gnus-group-edit-global-kill t]
      ))
 
   )
@@ -265,7 +267,8 @@ The `.' will match any character.")
       ["Sort by date" gnus-summary-sort-by-date t])
      ["Fetch group FAQ" gnus-summary-fetch-faq t]
      ["Filter articles" gnus-summary-execute-command t]
-     ["Mark all read and exit" gnus-summary-catchup-and-exit t]
+     ["Catchup and exit" gnus-summary-catchup-and-exit t]
+     ["Catchup and goto next" gnus-summary-catchup-and-goto-next-group t]
      ["Toggle line truncation" gnus-summary-toggle-truncation t]
      ["Expire expirable articles" gnus-summary-expire-articles t]
      ["Show dormant articles" gnus-summary-show-all-dormant t]
@@ -350,7 +353,7 @@ The `.' will match any character.")
 (defun gnus-article-make-menu-bar ()
 
  (easy-menu-define
-   gnus-article-mode-menu
+   gnus-article-article-menu
    gnus-article-mode-map
    ""
    '("Article"
@@ -364,7 +367,7 @@ The `.' will match any character.")
      ))
 
  (easy-menu-define
-   gnus-article-mode-menu
+   gnus-article-treatment-menu
    gnus-article-mode-map
    ""
    '("Treatment"
@@ -377,46 +380,94 @@ The `.' will match any character.")
      ))
  )
 
-(defun gnus-visual-highlight-selected-summary ()
-  ;; Added by Per Abrahamsen <amanda@iesd.auc.dk>.
-  ;; Highlight selected article in summary buffer
-  (if gnus-summary-selected-face
-      (save-excursion
-       (let* ((beg (progn (beginning-of-line) (point)))
-              (end (progn (end-of-line) (point)))
-              (from (or
-                     (next-single-property-change beg 'mouse-face nil end)
-                     beg))
-              (to (or (next-single-property-change from 'mouse-face nil end)
-                      end)))
-         (if gnus-newsgroup-selected-overlay
-             (move-overlay gnus-newsgroup-selected-overlay 
-                           from to (current-buffer))
-           (setq gnus-newsgroup-selected-overlay (make-overlay from to))
-           (overlay-put gnus-newsgroup-selected-overlay 'face 
-                        gnus-summary-selected-face))))))
-
+(if gnus-xemacs
+    (defun gnus-visual-highlight-selected-summary ()
+      (if gnus-summary-selected-face
+         (save-excursion
+           (let* ((beg (progn (beginning-of-line) (point)))
+                  (end (progn (end-of-line) (point)))
+                  (from (or
+                         (next-single-property-change beg 'mouse-face nil end)
+                         beg))
+                  (to (or (next-single-property-change from 'mouse-face nil end)
+                          end)))
+             (if gnus-newsgroup-selected-overlay
+                 (move-overlay gnus-newsgroup-selected-overlay 
+                               from to (current-buffer))
+               (setq gnus-newsgroup-selected-overlay (make-overlay from to))
+               (overlay-put gnus-newsgroup-selected-overlay 'face 
+                            gnus-summary-selected-face))))))
+
+  (defun gnus-visual-highlight-selected-summary ()
+    ;; Added by Per Abrahamsen <amanda@iesd.auc.dk>.
+    ;; Highlight selected article in summary buffer
+    (if gnus-summary-selected-face
+       (save-excursion
+         (let* ((beg (progn (beginning-of-line) (point)))
+                (end (progn (end-of-line) (point)))
+                (to (max 1 (1- (previous-single-property-change
+                                end 'mouse-face nil beg))))
+                (from (1+ (previous-single-property-change 
+                           to 'mouse-face nil beg))))
+           (if gnus-newsgroup-selected-overlay
+               (move-overlay gnus-newsgroup-selected-overlay 
+                             from to (current-buffer))
+             (setq gnus-newsgroup-selected-overlay (make-overlay from to))
+             (overlay-put gnus-newsgroup-selected-overlay 'face 
+                          gnus-summary-selected-face)))))))
+
+;; New implementation by Christian Limpach <Christian.Limpach@nice.ch>.
 (defun gnus-visual-summary-highlight-line ()
   "Highlight current line according to `gnus-visual-summary-highlight'."
-  (let ((list gnus-visual-summary-highlight)
-       (score (gnus-summary-article-score))
-       (default gnus-summary-default-score)
-       (inhibit-read-only t))
-    (save-excursion
-      (beginning-of-line)
-      (while (and list (not (eval (car (car list)))))
-       (setq list (cdr list)))
-      (let ((face (and list (cdr (car list)))))
-       (save-excursion
-         ;; BUG! For some reason the text properties of the first
-         ;; characters get mangled. 
-         (forward-char 10)
-         (if (eq face (get-text-property (point) 'face))
-             ()
-           (put-text-property (save-excursion (beginning-of-line 1) (point))
-                              (save-excursion (end-of-line 1) (point))
-                              'face face)))))))
+  (let* ((list gnus-visual-summary-highlight)
+        (p (point))
+        (end (progn (end-of-line) (point)))
+        ;; now find out where the line starts and leave point there.
+        (beg (progn (beginning-of-line) (point)))
+        (score (or (cdr (assq (or (get-text-property beg 'gnus-number)
+                                  gnus-current-article)
+                              gnus-newsgroup-scored))
+                   gnus-summary-default-score 0))
+        (default gnus-summary-default-score)
+        (mark (get-text-property beg 'gnus-mark))
+        (inhibit-read-only t))
+    (while (and list (not (eval (car (car list)))))
+      (setq list (cdr list)))
+    (let ((face (and list (cdr (car list)))))
+      ;; BUG! For some reason the text properties of the first
+      ;; characters get mangled.
+      (or (eq face (get-text-property (+ beg 10) 'face))
+         (put-text-property beg end 'face face)))
+    (goto-char p)))
+
+(if (not gnus-xemacs)
+    ()
+  (setq gnus-group-mode-hook
+       (cons
+        (lambda ()
+          (easy-menu-add gnus-group-reading-menu)
+          (easy-menu-add gnus-group-group-menu)
+          (easy-menu-add gnus-group-post-menu)
+          (easy-menu-add gnus-group-misc-menu)) 
+        gnus-group-mode-hook))
+  (setq gnus-summary-mode-hook
+       (cons
+        (lambda ()
+          (easy-menu-add gnus-summary-mark-menu)
+          (easy-menu-add gnus-summary-move-menu)
+          (easy-menu-add gnus-summary-article-menu)
+          (easy-menu-add gnus-summary-thread-menu)
+          (easy-menu-add gnus-summary-misc-menu)
+          (easy-menu-add gnus-summary-post-menu)
+          (easy-menu-add gnus-summary-kill-menu))
+        gnus-summary-mode-hook))
+  (setq gnus-article-mode-hook
+       (cons
+        (lambda ()
+          (easy-menu-add gnus-article-article-menu)
+          (easy-menu-add gnus-article-treatment-menu)) 
+        gnus-article-mode-hook)))
 
 (provide 'gnus-visual)
 
-;;; gnus-visual.el ends here
+;;; gnus-visual.el ends here
\ No newline at end of file
index 7a6ae12..f599500 100644 (file)
@@ -93,7 +93,7 @@ This is a list where each element is a complete select methdod (see
 If, for instance, you want to read your mail with the nnml backend,
 you could set this variable:
 
-(setq gnus-secondary-select-methods '((nnml ""))")
+(setq gnus-secondary-select-methods '((nnml \"\"))")
 
 (defvar gnus-secondary-servers nil
   "List of NNTP servers that the user can choose between interactively.
@@ -136,11 +136,16 @@ be used instead.")
 If a file with the .el or .elc suffixes exist, it will be read
 instead.") 
 
-(defvar gnus-group-faq-directory "/ftp@rtfm.mit.edu:/pub/usenet-by-group/"
+(defvar gnus-group-faq-directory
+  "/anonymous@rtfm.mit.edu:/pub/usenet-by-group/"
   "Directory where the group FAQs are stored.
 This will most commonly be on a remote machine, and the file will be
 fetched by ange-ftp.")
 
+(defvar gnus-group-archive-directory
+  "/ftp@sina.tcamc.uh.edu:/pub/emacs/ding-list/" 
+  "The address of the (ding) archives.")
+
 (defvar gnus-default-subscribed-newsgroups nil
   "This variable lists what newsgroups should be susbcribed the first time Gnus is used.
 It should be a list of strings.
@@ -193,13 +198,23 @@ Here's an example `gnus-followup-to-function':
 (defvar gnus-reply-to-function nil
   "A variable that contains a function that returns a reply address.
 See the `gnus-followup-to-function' variable for an explanation of how
-this variable is used.")
+this variable is used.
+
+This function should return a string that will be used to fill in the
+header.  This function may also return a list.  In that case, every
+list element should be a cons where the first car should be a string
+with the header name, and the cdr should be a string with the header
+value.")
 
 (defvar gnus-large-newsgroup 200
   "The number of articles which indicates a large newsgroup.
 If the number of articles in a newsgroup is greater than this value,
 confirmation is required for selecting the newsgroup.")
 
+;; Suggested by Andrew Eskilsson <pi92ae@lelle.pt.hk-r.se>.
+(defvar gnus-no-groups-message "No news is horrible news"
+  "Message displayed by Gnus when no groups are available.")
+
 (defvar gnus-author-copy (getenv "AUTHORCOPY")
   "Save outgoing articles in this file.
 Initialized from the AUTHORCOPY environment variable.
@@ -227,7 +242,13 @@ mailbox format.")
 
 (defvar gnus-use-long-file-name (not (memq system-type '(usg-unix-v xenix)))
   "Non-nil means that the default name of a file to save articles in is the group name.
-If it's nil, the directory form of the group name is used instead.")
+If it's nil, the directory form of the group name is used instead.
+
+If this variable is a list, and the list contains the element
+`not-score', long file names will not be used for score files, if it
+contains the element `not-save', long file names will not be used for
+saving, and if it contains the element `not-kill', long file names
+will not be used for kill files.")
 
 (defvar gnus-article-save-directory (or (getenv "SAVEDIR") "~/News/")
   "Name of the directory articles will be saved in (default \"~/News\").
@@ -339,7 +360,10 @@ Use nil to compare full subjects.  Setting this variable to a low
 number will help gather threads that have been corrupted by
 newsreaders chopping off subject lines, but it might also mean that
 unrelated articles that have subject that happen to begin with the
-same few characters will be incorrectly gathered.")
+same few characters will be incorrectly gathered.
+
+If this variable is `fuzzy', Gnus will use a fuzzy algortihm when
+comparing subjects.")
 
 ;; Added by Per Abrahamsen <amanda@iesd.auc.dk>.
 (defvar gnus-summary-same-subject ""
@@ -375,7 +399,13 @@ check for bogus newsgroups with \\<gnus-group-mode-map>\\[gnus-group-check-bogus
 
 (defvar gnus-read-active-file t
   "Non-nil means that Gnus will read the entire active file at startup.
-If this variable is nil, Gnus will only read parts of the active file.")
+If this variable is nil, Gnus will only know about the groups in your
+`.newsrc' file.
+
+If you set this variable to nil, you probably still want to be told
+about new newsgroups that arrive.  To do that, set
+`gnus-check-new-newsgroups' to `ask-server'.  This may not work
+properly with all servers.") 
 
 (defvar gnus-activate-foreign-newsgroups nil
   "If nil, Gnus will not check foreign newsgroups at startup.
@@ -452,6 +482,9 @@ score files in the \"/ftp.some-where:/pub/score\" directory.
   "Default article score level.
 If this variable is nil, scoring will be disabled.")
 
+(defvar gnus-group-default-list-level 5
+  "Default listing level.")
+
 (defvar gnus-user-login-name nil
   "The login name of the user.
 Got from the function `user-login-name' if undefined.")
@@ -460,6 +493,21 @@ Got from the function `user-login-name' if undefined.")
   "The full name of the user.
 Got from the NAME environment variable if undefined.")
 
+(defvar gnus-user-from-line nil
+  "Your full, complete e-mail address.  
+Overrides the other Gnus variables if it is non-nil.
+
+Here are two example values of this variable:
+
+ \"Lars Magne Ingebrigtsen <larsi@ifi.uio.no>\"
+
+and
+
+ \"larsi@ifi.uio.no (Lars Magne Ingebrigtsen)\"
+
+The first version is recommended, but the name has to be quoted if it
+contains non-alphanumerical characters.")
+
 (defvar gnus-show-mime nil
   "*If non-ni, do mime processing of articles.
 The articles will simply be fed to the function given by
@@ -630,7 +678,7 @@ alphabetic order; `gnus-subscribe-hierarchically' inserts new groups
 in hierarchical newsgroup order; `gnus-subscribe-interactively' asks
 for your decision.")
 
-;; Suggested by a bug report by Hallvard B Furuseth
+;; Suggested by a bug report by Hallvard B Furuseth.
 ;; <h.b.furuseth@usit.uio.no>. 
 (defvar gnus-subscribe-options-newsgroup-method
   (function gnus-subscribe-alphabetically)
@@ -773,6 +821,7 @@ with some simple extensions.
 %<   Spaces of length (- 20 thread-level) (string)
 %i   Article score (number)
 %z   Article zcore (character)
+%t   Number of articles under the current thread.
 %u   User defined specifier. The next character in the format string should
      be a letter.  Gnus will call the function gnus-user-format-function-X,
      where X is the letter following %u. The function will be passed the
@@ -848,6 +897,9 @@ No mouse highlights will be done if `gnus-visual' is nil.")
 This variable is local to each summary buffer and usually set by the
 score file.")  
 
+(defvar gnus-orphan-score nil
+  "All orphans get this score added. Set in the score file.")
+
 (defvar gnus-thread-sort-functions '(gnus-thread-sort-by-number)
   "List of functions used for sorting threads in the summary buffer.
 By default, threads are sorted by article number.
@@ -884,7 +936,11 @@ gnus-score-find-single: Only apply the group's own SCORE file.
 gnus-score-find-hierarchical: Also apply SCORE files from parent groups.
 gnus-score-find-bnews: Apply SCORE files whose names matches.
 
-See the documentation to these functions for more information.")
+See the documentation to these functions for more information.
+
+This variable can also be a list of functions to be called.  Each
+function should either return a list of score files, or the functions
+may also return lists of score alists.")
 
 (defvar gnus-options-subscribe nil
   "All new groups matching this regexp will be subscribed unconditionally.
@@ -1102,6 +1158,8 @@ If stringp, use this; if non-nil, use no host name (user name only).")
 (defvar gnus-internal-global-score-files nil)
 (defvar gnus-current-score-file nil)
 
+(defvar gnus-current-move-group nil)
+
 (defvar gnus-score-alist nil
   "Alist containing score information.
 The keys can be symbols or strings.  The following symbols are defined. 
@@ -1173,6 +1231,7 @@ of the last succesful match.")
        (list ?i 'score ?s)
        (list ?z 'score-char ?c)
        (list ?U 'unread ?c)
+       (list ?t '(gnus-summary-number-of-articles-in-thread thread) ?d)
        (list ?u 'user-defined ?s))
   "An alist of format specifications that can appear in summary lines,
 and what variables they correspond with, along with the type of the
@@ -1180,27 +1239,32 @@ variable (string, integer, character, etc).")
 
 (defconst gnus-summary-dummy-line-format-alist
   (list (list ?S 'subject ?s)
-       (list ?N 'number ?d)))
+       (list ?N 'number ?d)
+       (list ?u 'user-defined ?s)))
 
 (defconst gnus-summary-mode-line-format-alist 
   (list (list ?G 'group-name ?s)
+       (list ?g '(gnus-short-group-name group-name))
        (list ?A 'article-number ?d)
        (list ?Z 'unread-and-unselected ?s)
        (list ?V 'gnus-version ?s)
        (list ?U 'unread ?d)
        (list ?S 'subject ?s)
-       (list ?u 'unselected ?d)))
+       (list ?e 'unselected ?d)
+       (list ?u 'user-defined ?s)
+       (list ?s 'gnus-current-score-file ?s)))
 
 (defconst gnus-group-mode-line-format-alist 
   (list (list ?S 'news-server ?s)
-       (list ?M 'news-method ?s)))
+       (list ?M 'news-method ?s)
+       (list ?u 'user-defined ?s)))
 
 (defvar gnus-have-read-active-file nil)
 
 (defconst gnus-maintainer "Lars Magne Ingebrigtsen <larsi@ifi.uio.no>"
   "The mail address of the Gnus maintainer.")
 
-(defconst gnus-version "(ding) Gnus v0.44"
+(defconst gnus-version "(ding) Gnus v0.50"
   "Version number for this version of Gnus.")
 
 (defvar gnus-info-nodes
@@ -1220,8 +1284,7 @@ variable (string, integer, character, etc).")
   "Gnus buffers that should be killed on exit.")
 
 (defvar gnus-variable-list
-  '(gnus-newsrc-options 
-    gnus-newsrc-options-n-yes gnus-newsrc-options-n-no
+  '(gnus-newsrc-options gnus-newsrc-options-n
     gnus-newsrc-last-checked-date
     gnus-newsrc-assoc gnus-killed-list gnus-zombie-list)
   "Gnus variables saved in the quick startup file.")
@@ -1235,11 +1298,8 @@ It is a list of `(original overload &optional file)'.")
 (defvar gnus-newsrc-options nil
   "Options line in the .newsrc file.")
 
-(defvar gnus-newsrc-options-n-yes nil
-  "Regexp representing groups to be subscribed to unconditionally.")
-
-(defvar gnus-newsrc-options-n-no nil
-  "Regexp representing group to be ignored unconditionally.")
+(defvar gnus-newsrc-options-n nil
+  "List of regexps representing groups to be subscribed/ignored unconditionally.") 
 
 (defvar gnus-newsrc-last-checked-date nil
   "Date Gnus last asked server for new newsgroups.")
@@ -1446,17 +1506,32 @@ gnus-newsrc-hashtb should be kept so that both hold the same information.")
   (autoload 'mail-extract-address-components "mail-extr")
   )
 
-(put 'gnus-group-mode 'mode-class 'special)
-(put 'gnus-summary-mode 'mode-class 'special)
-(put 'gnus-article-mode 'mode-class 'special)
-
 \f
 
 ;; Fix by Hallvard B Furuseth <h.b.furuseth@usit.uio.no>.
-(defun gnus-summary-position-cursor () nil)
-(defun gnus-group-position-cursor () nil)
-(fset 'gnus-summary-position-cursor 'gnus-goto-colon)
-(fset 'gnus-group-position-cursor 'gnus-goto-colon)
+;; If you want the cursor to go somewhere else, set these two
+;; functions in some startup hook to whatever you want.
+(defalias 'gnus-summary-position-cursor 'gnus-goto-colon)
+(defalias 'gnus-group-position-cursor 'gnus-goto-colon)
+
+;; Cruft to make Gnus work under GNU XEmacs.
+(defvar gnus-xemacs nil
+  "Non-nil if Gnus is running under GNU XEmacs.")
+
+(if (not (string-match "XEmacs\\|Lucid" emacs-version))
+    ()
+  (setq gnus-xemacs t)
+  (eval
+   '((or (memq 'underline (list-faces))
+        (make-face 'underline))
+     (or (face-differs-from-default-p 'underline)
+        (set-face-underline-p 'underline t))
+     
+     (defun set-text-properties (start end props &optional buffer)
+       (if props
+          (put-text-property start end (car props) (cadr props) buffer)
+        (remove-text-properties start end ()))))))
+
 
 (defmacro gnus-eval-in-buffer-window (buffer &rest forms)
   "Pop to BUFFER, evaluate FORMS, and then returns to original window."
@@ -1600,7 +1675,8 @@ Optional argument HASHSIZE specifies the table size."
 
 (defun gnus-set-mouse-face (string)
   ;; Set mouse face property on STRING.
-  (put-text-property 0 (length string) 'mouse-face gnus-mouse-face string)
+  (or gnus-xemacs
+      (put-text-property 0 (length string) 'mouse-face gnus-mouse-face string))
   string)
 
 (defun gnus-parse-format (format spec-alist)
@@ -1689,7 +1765,9 @@ Optional argument HASHSIZE specifies the table size."
 ;; Suggested by Brian Edmonds <edmonds@cs.ubc.ca>.
 (defun gnus-read-init-file ()
   (and gnus-init-file
-       (or (file-exists-p gnus-init-file)
+       (or (and (file-exists-p gnus-init-file) 
+               ;; Don't try to load a directory.
+               (not (file-directory-p gnus-init-file)))
           (file-exists-p (concat gnus-init-file ".el"))
           (file-exists-p (concat gnus-init-file ".elc")))
        (load gnus-init-file nil t)))
@@ -1702,7 +1780,7 @@ If variable `gnus-use-long-file-name' is nil, it is ~/News/News.group/num.
 Otherwise, it is like ~/News/news/group/num."
   (let ((default
          (expand-file-name
-          (concat (if gnus-use-long-file-name
+          (concat (if (gnus-use-long-file-name 'not-save)
                       (gnus-capitalize-newsgroup newsgroup)
                     (gnus-newsgroup-directory-form newsgroup))
                   "/" (int-to-string (header-number headers)))
@@ -1720,7 +1798,7 @@ If variable `gnus-use-long-file-name' is nil, it is ~/News/news.group/num.
 Otherwise, it is like ~/News/news/group/num."
   (let ((default
          (expand-file-name
-          (concat (if gnus-use-long-file-name
+          (concat (if (gnus-use-long-file-name 'not-save)
                       newsgroup
                     (gnus-newsgroup-directory-form newsgroup))
                   "/" (int-to-string (header-number headers)))
@@ -1738,7 +1816,7 @@ If variable `gnus-use-long-file-name' is nil, it is ~/News/News.group.
 Otherwise, it is like ~/News/news/group/news."
   (or last-file
       (expand-file-name
-       (if gnus-use-long-file-name
+       (if (gnus-use-long-file-name 'not-save)
           (gnus-capitalize-newsgroup newsgroup)
         (concat (gnus-newsgroup-directory-form newsgroup) "/news"))
        (or gnus-article-save-directory "~/News"))))
@@ -1749,7 +1827,7 @@ If variable `gnus-use-long-file-name' is nil, it is ~/News/news.group.
 Otherwise, it is like ~/News/news/group/news."
   (or last-file
       (expand-file-name
-       (if gnus-use-long-file-name
+       (if (gnus-use-long-file-name 'not-save)
           newsgroup
         (concat (gnus-newsgroup-directory-form newsgroup) "/news"))
        (or gnus-article-save-directory "~/News"))))
@@ -1924,12 +2002,33 @@ If optional argument RE-ONLY is non-nil, strip `Re:' only."
        (while (string-match "[ \t\n]*([^()]*)[ \t\n]*\\'" subject)
          (setq subject (substring subject 0 (match-beginning 0)))))
     ;; Return subject string.
-    subject
-    ))
+    subject))
+
+(defun gnus-simplify-subject-fuzzy (subject)
+  (let ((case-fold-search t))
+    (and (string-match "^re:[ \t]*" subject)
+        (setq subject (substring subject (match-end 0))))
+    (while (string-match "[ \t\n]*([^()]*)[ \t\n]*\\'" subject)
+      (setq subject (substring subject 0 (match-beginning 0))))
+    (let ((beg 0)
+         (osubject ""))
+      (while (string-match "[ \t]+" subject beg)
+       (setq osubject
+             (concat osubject (substring 
+                               subject beg (match-beginning 0)) " ")
+             beg (match-end 0)))
+      (setq osubject (concat osubject (substring subject beg)))
+      (and (string-match " \\'" osubject) 
+          (setq osubject (substring osubject 0 (match-beginning 0))))
+      osubject)))
 
 (defun gnus-add-current-to-buffer-list ()
   (setq gnus-buffer-list (cons (current-buffer) gnus-buffer-list)))
 
+(defun gnus-string> (s1 s2)
+  (not (or (string< s1 s2)
+          (string= s1 s2))))
+
 ;; Functions accessing headers.
 ;; Functions are more convenient than macros in some cases.
 
@@ -2154,7 +2253,7 @@ or not."
   (erase-buffer)
   (mail-setup gnus-maintainer "[Gnus Bug Report] " nil nil nil nil)
   (goto-char (point-min))
-  (search-forward mail-header-separator)
+  (re-search-forward (concat "^" (regexp-quote mail-header-separator) "$"))
   (forward-line 1)
   (insert (format "%s\n%s\n\n" (gnus-version) (emacs-version)))
   (gnus-debug)
@@ -2269,6 +2368,12 @@ If nothing is specified, use the variable gnus-overload-functions."
      (gnus-number-base-x 
       (% num (expt base pos)) (1- pos) base))))
 
+;; Check whether to use long file names.
+(defun gnus-use-long-file-name (symbol)
+  (and gnus-use-long-file-name
+       (or (not (listp gnus-use-long-file-name))
+          (not (memq 'symbol gnus-use-long-file-name)))))
+
 ;; List and range functions
 
 (defun gnus-last-element (list)
@@ -2308,7 +2413,7 @@ Both lists have to be sorted over <."
            (t
             (setq out (cons (car list2) out))
             (setq list2 (cdr list2)))))
-    (append (or list1 list2) out)))
+    (nreverse (append (or list1 list2) out))))
 
 (defun gnus-intersection (list1 list2)      
   (let ((result nil))
@@ -2330,7 +2435,7 @@ Both lists have to be sorted over <."
             (setq list1 (cdr list1)))
            (t
             (setq list2 (cdr list2)))))
-    out))
+    (nreverse out)))
 
 (defun gnus-set-sorted-intersection (list1 list2)
   ;; LIST1 and LIST2 have to be sorted over <.
@@ -2551,6 +2656,8 @@ Note: LIST has to be sorted over `<'."
 (defvar gnus-group-mode-map nil)
 (defvar gnus-group-make-map nil)
 (defvar gnus-group-list-map nil)
+(defvar gnus-group-sub-map nil)
+(put 'gnus-group-mode 'mode-class 'special)
 
 (if gnus-group-mode-map
     nil
@@ -2568,6 +2675,7 @@ Note: LIST has to be sorted over `<'."
   (define-key gnus-group-mode-map "\M-n" 'gnus-group-next-unread-group-same-level)
   (define-key gnus-group-mode-map "\M-p" 'gnus-group-prev-unread-group-same-level)
   (define-key gnus-group-mode-map "," 'gnus-group-best-unread-group)
+  (define-key gnus-group-mode-map "." 'gnus-group-first-unread-group)
   (define-key gnus-group-mode-map "u" 'gnus-group-unsubscribe-current-group)
   (define-key gnus-group-mode-map "U" 'gnus-group-unsubscribe-group)
   (define-key gnus-group-mode-map "c" 'gnus-group-catchup-current)
@@ -2592,13 +2700,11 @@ Note: LIST has to be sorted over `<'."
   (define-key gnus-group-mode-map "\C-k" 'gnus-group-kill-group)
   (define-key gnus-group-mode-map "\C-y" 'gnus-group-yank-group)
   (define-key gnus-group-mode-map "\C-w" 'gnus-group-kill-region)
-  (define-key gnus-group-mode-map "\M-z" 'gnus-group-kill-all-zombies)
   (define-key gnus-group-mode-map "\C-x\C-t" 'gnus-group-transpose-groups)
   (define-key gnus-group-mode-map "\C-c\C-l" 'gnus-group-list-killed)
   (define-key gnus-group-mode-map "\C-c\C-x" 'gnus-group-expire-articles)
   (define-key gnus-group-mode-map "\C-c\M-\C-x" 'gnus-group-expire-all-groups)
   (define-key gnus-group-mode-map "V" 'gnus-version)
-  (define-key gnus-group-mode-map "S" 'gnus-group-set-current-level)
   (define-key gnus-group-mode-map "s" 'gnus-group-save-newsrc)
   (define-key gnus-group-mode-map "z" 'gnus-group-suspend)
   (define-key gnus-group-mode-map "Z" 'gnus-group-clear-dribble)
@@ -2608,12 +2714,14 @@ Note: LIST has to be sorted over `<'."
   (define-key gnus-group-mode-map "?" 'gnus-group-describe-briefly)
   (define-key gnus-group-mode-map "\C-c\C-i" 'gnus-info-find-node)
   (define-key gnus-group-mode-map "\M-e" 'gnus-group-edit-group)
-  (define-key gnus-group-mode-map [mouse-2] 'gnus-mouse-pick-group)
+  (define-key gnus-group-mode-map
+    (if gnus-xemacs [button2] [mouse-2]) 'gnus-mouse-pick-group)
 
   (define-prefix-command 'gnus-group-make-map)
   (define-key gnus-group-mode-map "M" 'gnus-group-make-map)
   (define-key gnus-group-make-map "d" 'gnus-group-make-directory-group)
   (define-key gnus-group-make-map "h" 'gnus-group-make-help-group)
+  (define-key gnus-group-make-map "a" 'gnus-group-make-archive-group)
   (define-key gnus-group-make-map "k" 'gnus-group-make-kiboze-group)
   (define-key gnus-group-make-map "m" 'gnus-group-make-group)
   (define-key gnus-group-make-map "e" 'gnus-group-edit-group)
@@ -2628,7 +2736,16 @@ Note: LIST has to be sorted over `<'."
   (define-key gnus-group-list-map "d" 'gnus-group-description-apropos)
   (define-key gnus-group-list-map "m" 'gnus-group-list-matching)
   (define-key gnus-group-list-map "M" 'gnus-group-list-all-matching)
-  )
+
+  (define-prefix-command 'gnus-group-sub-map)
+  (define-key gnus-group-mode-map "S" 'gnus-group-sub-map)
+  (define-key gnus-group-sub-map "l" 'gnus-group-set-current-level)
+  (define-key gnus-group-sub-map "t" 'gnus-group-unsubscribe-current-group)
+  (define-key gnus-group-sub-map "s" 'gnus-group-unsubscribe-group)
+  (define-key gnus-group-sub-map "k" 'gnus-group-kill-group)
+  (define-key gnus-group-sub-map "y" 'gnus-group-yank-group)
+  (define-key gnus-group-sub-map "w" 'gnus-group-kill-region)
+  (define-key gnus-group-sub-map "z" 'gnus-group-kill-all-zombies))
 
 (defun gnus-group-mode ()
   "Major mode for reading news.
@@ -2696,15 +2813,18 @@ prompt the user for the name of an NNTP server to use."
        (gnus-group-get-new-news))
     (gnus-clear-system)
     (gnus-read-init-file)
-    (let ((level (and arg (numberp arg) (> arg 0) arg)))
+    (let ((level (and arg (numberp arg) (> arg 0) arg))
+         did-connect)
       (unwind-protect
          (progn
            (switch-to-buffer (get-buffer-create gnus-group-buffer))
            (gnus-add-current-to-buffer-list)
            (gnus-group-mode)
-           (or dont-connect (gnus-start-news-server (and arg (not level)))))
+           (or dont-connect 
+               (setq did-connect
+                     (gnus-start-news-server (and arg (not level))))))
        (if (and (not dont-connect) 
-                (not (gnus-server-opened gnus-select-method)))
+                (not did-connect))
            (gnus-group-quit)
          (run-hooks 'gnus-startup-hook)
          ;; NNTP server is successfully open. 
@@ -2720,7 +2840,7 @@ prompt the user for the name of an NNTP server to use."
          (or t (not gnus-novice-user)
              gnus-expert-user
              (gnus-group-describe-briefly)) ;Show brief help message.
-         (gnus-group-list-groups (or level 5)))))))
+         (gnus-group-list-groups level))))))
 
 (defun gnus-group-startup-message (&optional x y)
   "Insert startup message in current buffer."
@@ -2761,14 +2881,13 @@ prompt the user for the name of an NNTP server to use."
 Default is 5, which lists all subscribed groups.
 If argument UNREAD is non-nil, groups with no unread articles are also listed."
   (interactive "P")
-  (setq level (or level 5))
+  (setq level (or level gnus-group-default-list-level 5))
   (gnus-group-setup-buffer)    ;May call from out of group buffer
   (let ((case-fold-search nil)
        (group (gnus-group-group-name)))
     (funcall gnus-group-prepare-function level unread nil)
     (if (zerop (buffer-size))
-       ;; Suggested by Andrew Eskilsson <pi92ae@lelle.pt.hk-r.se>.
-       (message "No news is horrible news")
+       (message gnus-no-groups-message)
       (goto-char (point-min))
       (if (not group)
          ;; Go to the first group with unread articles.
@@ -2894,8 +3013,7 @@ If REGEXP, only list groups matching REGEXP."
        (while marked
          (or (eq 'score (car (car marked)))
              (eq 'bookmark (car (car marked)))
-             (not (or (atom (cdr (cdr (car marked))))
-                      (not (atom (car (cdr (car marked)))))))
+             (eq 'killed (car (car marked)))
              (setcdr (car marked) 
                      (gnus-uncompress-range (cdr (car marked)))))
          (setq marked (cdr marked)))))
@@ -3107,10 +3225,11 @@ If FIRST-TOO, the current line is also eligeble as a target."
                            (let ((unread 
                                   (get-text-property (point) 'gnus-unread)))
                              (or (eq unread t) (and unread (> unread 0))))
-                           (< (get-text-property (point) 'gnus-level) 6)))
+                           (let ((lev (get-text-property (point) 'gnus-level)))
+                             (and lev (< (get-text-property (point) 'gnus-level) 6)))))
                       (or (not level)
                           (let ((lev (get-text-property (point) 'gnus-level)))
-                            (if (<= lev level)
+                            (if (and lev (<= lev level))
                                 t
                               (if (< lev low)
                                   (progn
@@ -3156,18 +3275,26 @@ If argument ALL is non-nil, already read articles become readable."
 
 (defun gnus-group-jump-to-group (group)
   "Jump to newsgroup GROUP."
-  (interactive
-   (list 
-    (completing-read "Group: " gnus-active-hashtb nil t)))
-  (let (b)
-    ;; Either go to the line in the group buffer...
-    (or (and (setq b (text-property-any (point-min) (point-max) 
-                                       'gnus-group (intern group)))
-            (goto-char b))
-       ;; ... or insert the line.
-       (progn (gnus-group-update-group group)
-              (goto-char (text-property-any (point-min) (point-max) 
-                                            'gnus-group (intern group))))))
+  (interactive (list (completing-read "Group: " gnus-active-hashtb nil
+                                     (not (not gnus-read-active-file)))))
+
+  (if (equal group "")
+      (error "empty group name"))
+
+  (let ((b (text-property-any (point-min) (point-max) 
+                             'gnus-group (intern group))))
+    (if b
+       ;; Either go to the line in the group buffer...
+       (goto-char b)
+      ;; ... or insert the line.
+      (or
+       (gnus-gethash group gnus-active-hashtb)
+       (gnus-activate-newsgroup group)
+       (error "%s error: %s" group (gnus-status-message group)))
+
+      (gnus-group-update-group group)
+      (goto-char (text-property-any (point-min) (point-max) 
+                                   'gnus-group (intern group)))))
   ;; Adjust cursor point.
   (gnus-group-position-cursor))
 
@@ -3229,8 +3356,9 @@ done."
   (gnus-group-next-unread-group (- n) t (gnus-group-group-level))
   (gnus-group-position-cursor))
 
-(defun gnus-group-best-unread-group ()
-  "Go to the group with the highest level."
+(defun gnus-group-best-unread-group (&optional exclude-group)
+  "Go to the group with the highest level.
+If EXCLUDE-GROUP, do not go to that group."
   (interactive)
   (goto-char (point-min))
   (let ((best 10)
@@ -3239,7 +3367,9 @@ done."
       (if (and (numberp unread) (> unread 0))
          (progn
            (or best-point (setq best-point (point)))
-           (if (< (get-text-property (point) 'gnus-level) best)
+           (if (and (< (get-text-property (point) 'gnus-level) best)
+                    (or (not exclude-group)
+                        (not (equal exclude-group (gnus-group-group-name)))))
                (progn 
                  (setq best (get-text-property (point) 'gnus-level))
                  (setq best-point (point))))))
@@ -3248,6 +3378,12 @@ done."
     (gnus-summary-position-cursor)
     (and best-point (gnus-group-group-name))))
 
+(defun gnus-group-first-unread-group ()
+  "Go to the first group with unread articles."
+  (interactive)
+  (goto-char (point-min))
+  (gnus-group-next-unread-group 1))
+
 (defun gnus-group-make-group (name method address)
   "Add a new newsgroup.
 The user will be prompted for a NAME, for a select METHOD, and an
@@ -3265,6 +3401,8 @@ ADDRESS."
              "")))))
   (let ((nname (gnus-group-prefixed-name name (list (intern method) address)))
        info)
+    (and (gnus-gethash nname gnus-active-hashtb)
+        (error "Group %s already exists" nname))
     (gnus-group-change-level 
      (setq info (list t nname 3 nil nil (list (intern method) address)))
      3 9 (gnus-gethash (or (gnus-group-group-name) "dummy.group")
@@ -3272,7 +3410,10 @@ ADDRESS."
     (gnus-sethash nname '(0 . 0) gnus-active-hashtb)
     (gnus-dribble-enter 
      (concat "(gnus-group-set-info '" (prin1-to-string (cdr info)) ")"))
-    (gnus-group-insert-group-line-info nname)))
+    (gnus-group-insert-group-line-info nname)
+
+    (and (gnus-check-backend-function 'request-create-group nname)
+        (gnus-request-create-group nname))))
 
 (defun gnus-group-edit-group (group)
   (interactive (list (gnus-group-group-name)))
@@ -3295,6 +3436,7 @@ ADDRESS."
        (while marked
          (or (eq 'score (car (car marked)))
              (eq 'bookmark (car (car marked)))
+             (eq 'killed (car (car marked)))
              (setcdr (car marked) 
                      (gnus-compress-sequence (sort (cdr (car marked)) '<) t)))
          (setq marked (cdr marked))))
@@ -3330,6 +3472,15 @@ ADDRESS."
      (concat (file-name-as-directory (car path)) "doc.txt"))
     (gnus-group-position-cursor)))
 
+(defun gnus-group-make-archive-group ()
+  "Create the (ding) Gnus archive group."
+  (interactive)
+  (and (gnus-gethash (gnus-group-prefixed-name "ding.archives" '(nndir ""))
+                    gnus-newsrc-hashtb)
+       (error "Archive group already exists"))
+  (gnus-group-make-group "ding.archives" "nndir" gnus-group-archive-directory)
+  (gnus-group-position-cursor))
+
 (defun gnus-group-make-directory-group (dir)
   "Create an nndir group.
 The user will be prompted for a directory. The contents of this
@@ -3387,7 +3538,7 @@ score file entries for articles to include in the group."
        (sort (cdr gnus-newsrc-assoc) gnus-group-sort-function))
   (gnus-make-hashtable-from-newsrc-alist)
   (gnus-get-unread-articles 6)
-  (gnus-group-list-groups 5))
+  (gnus-group-list-groups nil))
 
 (defun gnus-group-sort-by-alphabet (info1 info2)
   (string< (car info1) (car info2)))
@@ -3675,7 +3826,7 @@ specify which levels you are interested in re-scanning."
        (gnus-get-unread-articles (or arg 6)))
     (let ((gnus-read-active-file nil))
       (gnus-get-unread-articles (or arg 6))))
-  (gnus-group-list-groups 5 gnus-have-all-newsgroups))
+  (gnus-group-list-groups nil gnus-have-all-newsgroups))
 
 (defun gnus-group-get-new-news-this-group (n)
   "Check for newly arrived news in the current group (and the N-1 next groups).
@@ -3841,7 +3992,7 @@ If LOWEST, don't list groups with level lower than LOWEST."
   (interactive "P")
   (gnus-save-newsrc-file)
   (gnus-setup-news 'force)
-  (gnus-group-list-groups (or arg 5) gnus-have-all-newsgroups))
+  (gnus-group-list-groups arg gnus-have-all-newsgroups))
 
 (defun gnus-group-read-init-file ()
   "Read the Gnus elisp init file."
@@ -3852,7 +4003,7 @@ If LOWEST, don't list groups with level lower than LOWEST."
   "Check bogus newsgroups."
   (interactive)
   (gnus-check-bogus-newsgroups (not gnus-expert-user)) ;Require confirmation.
-  (gnus-group-list-groups 5 gnus-have-all-newsgroups))
+  (gnus-group-list-groups nil gnus-have-all-newsgroups))
 
 (defun gnus-group-mail ()
   "Start composing a mail."
@@ -3920,6 +4071,7 @@ The hook `gnus-exit-gnus-hook' is called before actually exiting."
        (gnus-clear-system))))
 
 (defun gnus-close-backends ()
+  ;; Send a close request to all backends that support such a request. 
   (let ((methods gnus-valid-select-methods)
        func)
     (while methods
@@ -3976,6 +4128,7 @@ and the second element is the address."
 
 (defvar gnus-browse-server-mode-hook nil)
 (defvar gnus-browse-server-mode-map nil)
+(put 'gnus-browse-server-mode 'mode-class 'special)
 
 (if gnus-browse-server-mode-map
     nil
@@ -3992,6 +4145,8 @@ and the second element is the address."
   (define-key gnus-browse-server-mode-map "\M-p" 'gnus-browse-prev-group)
   (define-key gnus-browse-server-mode-map "\r" 'gnus-browse-read-group)
   (define-key gnus-browse-server-mode-map "u" 'gnus-browse-unsubscribe-current-group)
+  (define-key gnus-browse-server-mode-map "l" 'gnus-browse-exit)
+  (define-key gnus-browse-server-mode-map "L" 'gnus-browse-exit)
   (define-key gnus-browse-server-mode-map "q" 'gnus-browse-exit)
   (define-key gnus-browse-server-mode-map "Q" 'gnus-browse-exit)
   (define-key gnus-browse-server-mode-map "\C-c\C-c" 'gnus-browse-exit)
@@ -4006,8 +4161,11 @@ and the second element is the address."
   (let ((gnus-select-method method)
        groups group)
     (message "Connecting to %s..." (nth 1 method))
-    (if (not (gnus-request-list method))
-       (error "Unable to contact server: " (gnus-status-message method)))
+    (or (gnus-server-opened method)
+       (gnus-open-server method)
+       (error "Unable to contact server: %s" (gnus-status-message method)))
+    (or (gnus-request-list method)
+       (error "Couldn't request list: %s" (gnus-status-message method)))
     (set-buffer (get-buffer-create "*Gnus Browse Server*"))
     (gnus-add-current-to-buffer-list)
     (buffer-disable-undo (current-buffer))
@@ -4123,7 +4281,7 @@ and the second element is the address."
   (if (eq major-mode 'gnus-browse-server-mode)
       (kill-buffer (current-buffer)))
   (switch-to-buffer gnus-group-buffer)
-  (gnus-group-list-groups 5))
+  (gnus-group-list-groups nil))
 
 (defun gnus-browse-describe-briefly ()
   "Give a one line description of the group mode commands."
@@ -4156,6 +4314,7 @@ and the second element is the address."
 (defvar gnus-summary-increase-map nil)
 (defvar gnus-summary-inc-subject-map nil)
 (defvar gnus-summary-inc-author-map nil)
+(defvar gnus-summary-inc-body-map nil)
 (defvar gnus-summary-inc-id-map nil)
 (defvar gnus-summary-inc-xref-map nil)
 (defvar gnus-summary-inc-thread-map nil)
@@ -4163,10 +4322,12 @@ and the second element is the address."
 (defvar gnus-summary-lower-map nil)
 (defvar gnus-summary-low-subject-map nil)
 (defvar gnus-summary-low-author-map nil)
+(defvar gnus-summary-low-body-map nil)
 (defvar gnus-summary-low-id-map nil)
 (defvar gnus-summary-low-xref-map nil)
 (defvar gnus-summary-low-thread-map nil)
 (defvar gnus-summary-low-fol-map nil)
+(put 'gnus-summary-mode 'mode-class 'special)
 
 (if gnus-summary-mode-map
     nil
@@ -4249,7 +4410,8 @@ and the second element is the address."
   (define-key gnus-summary-mode-map "q" 'gnus-summary-exit)
   (define-key gnus-summary-mode-map "Q" 'gnus-summary-exit-no-update)
   (define-key gnus-summary-mode-map "\C-c\C-i" 'gnus-info-find-node)
-  (define-key gnus-summary-mode-map [mouse-2] 'gnus-mouse-pick-article)
+  (define-key gnus-summary-mode-map
+    (if gnus-xemacs [button2] [mouse-2]) 'gnus-mouse-pick-article)
   (define-key gnus-summary-mode-map "m" 'gnus-summary-mail-other-window)
   (define-key gnus-summary-mode-map "a" 'gnus-summary-post-news)
   (define-key gnus-summary-mode-map "x" 'gnus-summary-delete-marked-as-read)
@@ -4259,7 +4421,7 @@ and the second element is the address."
   (define-key gnus-summary-mode-map "g" 'gnus-summary-show-article)
 ;  (define-key gnus-summary-mode-map "?" 'gnus-summary-describe-briefly)
   (define-key gnus-summary-mode-map "l" 'gnus-summary-goto-last-article)
-  (define-key gnus-summary-mode-map "\C-c\C-v\C-v" 'gnus-uu-decode-uu)
+  (define-key gnus-summary-mode-map "\C-c\C-v\C-v" 'gnus-uu-decode-uu-view)
   (define-key gnus-summary-mode-map "\C-d" 'gnus-summary-enter-digest-group)
 
 
@@ -4368,6 +4530,7 @@ and the second element is the address."
   (define-key gnus-summary-exit-map "E" 'gnus-summary-exit-no-update)
   (define-key gnus-summary-exit-map "Q" 'gnus-summary-exit)
   (define-key gnus-summary-exit-map "Z" 'gnus-summary-exit)
+  (define-key gnus-summary-exit-map "n" 'gnus-summary-catchup-and-goto-next-group)
 
 
   (define-prefix-command 'gnus-summary-article-map)
@@ -4501,6 +4664,14 @@ and the second element is the address."
   (define-key gnus-summary-inc-author-map "t" 'gnus-summary-temporarily-raise-by-author)
   (define-key gnus-summary-inc-author-map "p" 'gnus-summary-raise-by-author)
 
+  (define-prefix-command 'gnus-summary-inc-body-map)
+  (define-key gnus-summary-increase-map "b" 'gnus-summary-inc-body-map)
+  (define-key gnus-summary-increase-map "B" 'gnus-summary-temporarily-raise-by-body)
+  (define-key gnus-summary-inc-body-map "b" 'gnus-summary-temporarily-raise-by-body)
+  (define-key gnus-summary-inc-body-map "B" 'gnus-summary-raise-by-body)
+  (define-key gnus-summary-inc-body-map "t" 'gnus-summary-temporarily-raise-by-body)
+  (define-key gnus-summary-inc-body-map "p" 'gnus-summary-raise-by-body)
+
   (define-prefix-command 'gnus-summary-inc-id-map)
   (define-key gnus-summary-increase-map "i" 'gnus-summary-inc-id-map)
   (define-key gnus-summary-increase-map "I" 'gnus-summary-temporarily-raise-by-id)
@@ -4547,6 +4718,14 @@ and the second element is the address."
   (define-key gnus-summary-low-subject-map "t" 'gnus-summary-temporarily-lower-by-subject)
   (define-key gnus-summary-low-subject-map "p" 'gnus-summary-lower-by-subject)
 
+  (define-prefix-command 'gnus-summary-low-body-map)
+  (define-key gnus-summary-lower-map "b" 'gnus-summary-low-body-map)
+  (define-key gnus-summary-lower-map "B" 'gnus-summary-temporarily-lower-by-body)
+  (define-key gnus-summary-low-body-map "b" 'gnus-summary-temporarily-lower-by-body)
+  (define-key gnus-summary-low-body-map "B" 'gnus-summary-lower-by-body)
+  (define-key gnus-summary-low-body-map "t" 'gnus-summary-temporarily-lower-by-body)
+  (define-key gnus-summary-low-body-map "p" 'gnus-summary-lower-by-body)
+
   (define-prefix-command 'gnus-summary-low-author-map)
   (define-key gnus-summary-lower-map "a" 'gnus-summary-low-author-map)
   (define-key gnus-summary-lower-map "A" 'gnus-summary-temporarily-lower-by-author)
@@ -4733,14 +4912,16 @@ The following commands are available:
   ;; Update summary line after change.
   (or (not gnus-summary-default-score)
       gnus-summary-inhibit-highlight
-      (let ((gnus-summary-inhibit-highlight t))
+      (let ((gnus-summary-inhibit-highlight t)
+           (article (gnus-summary-article-number)))
        (progn
          (or dont-update
              (if (and gnus-summary-mark-below
                       (< (gnus-summary-article-score)
                          gnus-summary-mark-below))
-                 (and (not (memq (gnus-summary-article-number)
-                                 gnus-newsgroup-marked))
+                 (and (not (memq article gnus-newsgroup-marked))
+                      (not (memq article gnus-newsgroup-dormant))
+                      (memq article gnus-newsgroup-unreads)
                       (gnus-summary-mark-article nil gnus-low-score-mark))
                (and (eq (gnus-summary-article-mark) gnus-low-score-mark)
                     (gnus-summary-mark-article nil gnus-unread-mark))))
@@ -4756,6 +4937,12 @@ The following commands are available:
           (gnus-summary-update-line)
           (forward-line 1)))))
 
+(defun gnus-summary-number-of-articles-in-thread (thread)
+  ;; Sum up all elements (and sub-elements) in a list.
+  (if (listp thread) 
+      (apply '+ (mapcar 'gnus-summary-number-of-articles-in-thread thread))
+    1))
+
 (defun gnus-summary-read-group (group &optional show-all no-article kill-buffer)
   "Start reading news in newsgroup GROUP.
 If SHOW-ALL is non-nil, already read articles are also listed.
@@ -4764,6 +4951,7 @@ If NO-ARTICLE is non-nil, no article is selected initially."
   (gnus-summary-setup-buffer group)
   (if (gnus-select-newsgroup group show-all)
       (progn
+       (gnus-set-global-variables)
        ;; You can change the subjects in this hook.
        (run-hooks 'gnus-select-group-hook)
        ;; Do Score Processing.
@@ -4771,17 +4959,22 @@ If NO-ARTICLE is non-nil, no article is selected initially."
        ;; Update the format specifiers.
        (gnus-update-format-specifications)
        (gnus-summary-prepare)
-       (if (and (zerop (buffer-size))
-                gnus-newsgroup-dormant)
-           (gnus-summary-show-all-dormant))
-       (gnus-set-global-variables)
+       (if (zerop (buffer-size))
+           (cond (gnus-newsgroup-dormant
+                  (gnus-summary-show-all-dormant))
+                 ((and gnus-newsgroup-scored show-all)
+                  (gnus-summary-show-all-expunged))))
        ;; Function `gnus-apply-kill-file' must be called in this hook.
        (run-hooks 'gnus-apply-kill-hook)
        (if (zerop (buffer-size))
            (progn
              ;; This newsgroup is empty.
              (gnus-summary-catchup-and-exit nil t) ;Without confirmations.
-             (message "No unread news"))
+             (message "No unread news")
+             (and kill-buffer
+                  (get-buffer kill-buffer)
+                  (buffer-name (get-buffer kill-buffer))
+                  (kill-buffer kill-buffer)))
          (save-excursion
            (if kill-buffer
                (let ((gnus-summary-buffer kill-buffer))
@@ -4845,9 +5038,13 @@ If NO-ARTICLE is non-nil, no article is selected initially."
        (setq subject (header-subject (car (car threads)))
              whole-subject subject)
        (and gnus-summary-gather-subject-limit
-            (> (length subject) gnus-summary-gather-subject-limit)
-            (setq subject
-                  (substring subject 0 gnus-summary-gather-subject-limit)))
+            (or (and (numberp gnus-summary-gather-subject-limit)
+                     (> (length subject) gnus-summary-gather-subject-limit)
+                     (setq subject
+                           (substring subject 0 
+                                      gnus-summary-gather-subject-limit)))
+                (and (eq 'fuzzy gnus-summary-gather-subject-limit)
+                     (setq subject (gnus-simplify-subject-fuzzy subject)))))
        (if (setq hthread 
                  (gnus-gethash 
                   (setq unre-subject (gnus-simplify-subject-re subject))
@@ -5198,7 +5395,13 @@ or a straight list of headers."
               (gnus-summary-prepare-threads (list (car (cdr thread))) 0)
               (setq thread (cdr (cdr thread)))
               (while thread
-                (gnus-summary-prepare-threads (list (car thread)) 0 nil t)
+                (gnus-summary-prepare-threads 
+                 (list (car thread)) 0 nil
+                 (not (and (eq gnus-summary-gather-subject-limit 'fuzzy)
+                           (not (string=  
+                                 (gnus-simplify-subject-re 
+                                  (header-subject (car (car thread))))
+                                 (gnus-simplify-subject-re header))))))
                 (setq thread (cdr thread))))
              (t
               ;; We do not make a root for the gathered
@@ -5220,7 +5423,8 @@ or a straight list of headers."
               (t gnus-ancient-mark))
         (memq number gnus-newsgroup-replied)
         (memq number gnus-newsgroup-expirable)
-        (if no-subject gnus-summary-same-subject
+        (if no-subject 
+            gnus-summary-same-subject
           (if (or (zerop level)
                   (and gnus-thread-ignore-subject
                        (not (string= 
@@ -5259,19 +5463,6 @@ If READ-ALL is non-nil, all articles in the group are selected."
     (setq gnus-newsgroup-unselected nil)
     (setq gnus-newsgroup-unreads (gnus-list-of-unread-articles group))
 
-    (and info
-        (let (marked)
-          (gnus-adjust-marked-articles info)
-          (setq gnus-newsgroup-marked 
-                (cdr (assq 'tick (setq marked (nth 3 info)))))
-          (setq gnus-newsgroup-replied (cdr (assq 'reply marked)))
-          (setq gnus-newsgroup-expirable (cdr (assq 'expire marked)))
-          (setq gnus-newsgroup-killed (cdr (assq 'killed marked)))
-          (setq gnus-newsgroup-bookmarks (cdr (assq 'bookmark marked)))
-          (setq gnus-newsgroup-dormant (cdr (assq 'dormant marked)))
-          (setq gnus-newsgroup-scored (cdr (assq 'score marked)))
-          (setq gnus-newsgroup-processable nil)))
-
     (if (not (setq articles (gnus-articles-to-read group read-all)))
        nil
       ;; Init the dependencies hash table.
@@ -5302,6 +5493,19 @@ If READ-ALL is non-nil, all articles in the group are selected."
             gnus-newsgroup-unreads
             (mapcar (lambda (headers) (header-number headers))
                     gnus-newsgroup-headers)))
+      ;; Adjust and set lists of article marks.
+      (and info
+          (let (marked)
+            (gnus-adjust-marked-articles info)
+            (setq gnus-newsgroup-marked 
+                  (cdr (assq 'tick (setq marked (nth 3 info)))))
+            (setq gnus-newsgroup-replied (cdr (assq 'reply marked)))
+            (setq gnus-newsgroup-expirable (cdr (assq 'expire marked)))
+            (setq gnus-newsgroup-killed (cdr (assq 'killed marked)))
+            (setq gnus-newsgroup-bookmarks (cdr (assq 'bookmark marked)))
+            (setq gnus-newsgroup-dormant (cdr (assq 'dormant marked)))
+            (setq gnus-newsgroup-scored (cdr (assq 'score marked)))
+            (setq gnus-newsgroup-processable nil)))
       ;; Check whether auto-expire is to be done in this group.
       (setq gnus-newsgroup-auto-expire
            (or (and (stringp gnus-auto-expirable-newsgroups)
@@ -5377,8 +5581,8 @@ If READ-ALL is non-nil, all articles in the group are selected."
            (setq articles (nthcdr (- number select) articles))))
       (setq gnus-newsgroup-unselected
            (gnus-sorted-intersection
-            gnus-newsgroup-unselected 
-            (gnus-sorted-complement articles total-articles)))
+            gnus-newsgroup-unreads
+            (gnus-sorted-complement gnus-newsgroup-unreads articles)))
       articles)))
 
 (defun gnus-killed-articles (killed articles)
@@ -5394,8 +5598,7 @@ If READ-ALL is non-nil, all articles in the group are selected."
   (let ((marked-lists (nth 3 info))
        (active (or active (gnus-gethash (car info) gnus-active-hashtb)))
        marked m prev)
-    ;; There are four types of marked articles - ticked, replied,
-    ;; expirable and dormant.  
+    ;; There are many types of marked articles.
     (while marked-lists
       (setq m (cdr (setq prev (car marked-lists))))
       (cond ((or (eq 'tick (car prev)) (eq 'dormant (car prev)))
@@ -5434,8 +5637,8 @@ If READ-ALL is non-nil, all articles in the group are selected."
             (if (and m (< (or (and (numberp (car m)) (car m))
                               (car (car m)))
                           (car active))) 
-                (setcar (and (numberp (car m)) m (car m)) (car active))))
-           ((or (eq 'reply (car marked)) (eq 'expire (car marked)))
+                (setcar (if (numberp (car m)) m (car m)) (car active))))
+           ((or (eq 'reply (car prev)) (eq 'expire (car prev)))
             ;; The replied and expirable articles have to be articles
             ;; that are active. 
             (while m
@@ -5460,8 +5663,9 @@ If READ-ALL is non-nil, all articles in the group are selected."
     ;; Finally, if there are no marked lists at all left, and if there
     ;; are no elements after the lists in the info list, we just chop
     ;; the info list off before the marked lists.
-    (if (and (null marked-lists) (not (nthcdr 4 info)))
-       (setcdr (nthcdr 2 info) nil)))
+    (and (null marked-lists) 
+        (not (nthcdr 4 info))
+        (setcdr (nthcdr 2 info) nil)))
   info)
 
 (defun gnus-set-marked-articles 
@@ -5851,56 +6055,59 @@ list of headers that match SEQUENCE (see `nntp-retrieve-headers')."
       (setq eol (point)))
     (forward-char)
     ;; overview: [num subject from date id refs chars lines misc]
-    (setq header
-         (vector 
-          number                       ; number
-          (gnus-nov-field)             ; subject
-          (gnus-nov-field)             ; from
-          (gnus-nov-field)             ; date
-          (setq id (gnus-nov-field))   ; id
-          (progn
-            (save-excursion
-              (let ((beg (point)))
-                (search-forward "\t" eol)
-                (if (search-backward ">" beg t)
-                    (setq ref 
-                          (downcase 
-                           (buffer-substring 
-                            (1+ (point))
-                            (progn
-                              (search-backward "<" beg t)
-                              (point)))))
-                  (setq ref nil))))
-            (gnus-nov-field))          ; refs
-          (read (current-buffer))      ; chars
-          (read (current-buffer))      ; lines
-          (if (/= (following-char) ?\t)
-              nil
-            (forward-char 1)
-            (gnus-nov-field))          ; misc
-          ))
+    (save-restriction
+      (narrow-to-region (point) eol)
+      (condition-case nil
+         (setq header
+               (vector 
+                number                 ; number
+                (gnus-nov-field)       ; subject
+                (gnus-nov-field)       ; from
+                (gnus-nov-field)       ; date
+                (setq id (or (gnus-nov-field)
+                             (concat "none+"
+                                    (int-to-string 
+                                     (setq none (1+ none)))))) ; id
+                (progn
+                  (save-excursion
+                    (let ((beg (point)))
+                      (search-forward "\t" eol)
+                      (if (search-backward ">" beg t)
+                          (setq ref 
+                                (downcase 
+                                 (buffer-substring 
+                                  (1+ (point))
+                                  (progn
+                                    (search-backward "<" beg t)
+                                    (point)))))
+                        (setq ref nil))))
+                  (gnus-nov-field))    ; refs
+                (read (current-buffer)) ; chars
+                (read (current-buffer)) ; lines
+                (if (/= (following-char) ?\t)
+                    nil
+                  (forward-char 1)
+                  (gnus-nov-field))    ; misc
+                ))
+       (error (progn 
+                (setq header nil)
+                (goto-char eol)))))
     ;; We build the thread tree.
-    (if (boundp 
-        (setq dep 
-              (intern 
-               (downcase 
-                (or id (concat "none+"
-                               (int-to-string 
-                                (setq none (1+ none))))))
-               dependencies)))
-       (if (car (symbol-value dep))
-           ;; An article with this Message-ID has already been seen,
-           ;; so we ignore this one, except we add any additional
-           ;; Xrefs (in case the two articles came from different
-           ;; servers.
-           (progn
-             (header-set-xref 
-              (car (symbol-value dep))
-              (concat (or (header-xref (car (symbol-value dep))) "")
-                      (or (header-xref header) "")))
-             (setq header nil))
-         (setcar (symbol-value dep) header))
-      (set dep (list header)))
+    (and header
+        (if (boundp (setq dep (intern (downcase id) dependencies)))
+            (if (car (symbol-value dep))
+                ;; An article with this Message-ID has already been seen,
+                ;; so we ignore this one, except we add any additional
+                ;; Xrefs (in case the two articles came from different
+                ;; servers.
+                (progn
+                  (header-set-xref 
+                   (car (symbol-value dep))
+                   (concat (or (header-xref (car (symbol-value dep))) "")
+                           (or (header-xref header) "")))
+                  (setq header nil))
+              (setcar (symbol-value dep) header))
+          (set dep (list header))))
     (if header
        (progn
          (if (boundp (setq dep (intern (or ref "none") 
@@ -6026,12 +6233,13 @@ If optional argument BACKWARD is non-nil, search backward instead."
           (if use-level (gnus-group-group-level) nil))
          (gnus-group-group-name)))))
 
-(defun gnus-summary-best-group ()
-  "Find the name of the best unread group."
+(defun gnus-summary-best-group (&optional exclude-group)
+  "Find the name of the best unread group.
+If EXCLUDE-GROUP, do not go to this group."
   (save-excursion
     (set-buffer gnus-group-buffer)
     (save-excursion
-      (gnus-group-best-unread-group))))
+      (gnus-group-best-unread-group exclude-group))))
 
 (defun gnus-summary-search-subject (&optional backward unread subject)
   "Search for article forward.
@@ -6086,30 +6294,31 @@ the same subject will be searched for."
   "The article number of the article on the current line.
 If there isn's an article number here, then we return the current
 article number."
-  (let ((number (get-text-property (save-excursion (beginning-of-line) (point))
-                                  'gnus-number)))
+  (let* ((p (point))
+        (number (get-text-property 
+                 (progn (beginning-of-line) (prog1 (point) (goto-char p)))
+                 'gnus-number)))
     (if number-or-nil number (or number gnus-current-article))))
 
 (defun gnus-summary-thread-level ()
   "The thread level of the article on the current line."
-  (or (get-text-property (save-excursion (beginning-of-line) (point))
-                        'gnus-thread)
+  (or (get-text-property 
+       (save-excursion (beginning-of-line) (point)) 'gnus-thread)
       0))
 
 (defun gnus-summary-pseudo-article ()
   "The thread level of the article on the current line."
-  (get-text-property (save-excursion (beginning-of-line) (point)) 
-                    'gnus-pseudo))
+  (get-text-property 
+   (save-excursion (beginning-of-line) (point)) 'gnus-pseudo))
 
 (defun gnus-summary-article-mark ()
   "The mark on the current line."
-  (get-text-property (save-excursion (beginning-of-line) (point))
-                    'gnus-mark))
+  (get-text-property (save-excursion (beginning-of-line) (point)) 'gnus-mark))
 
 (defun gnus-summary-subject-string ()
   "Return current subject string or nil if nothing."
-  (get-text-property (save-excursion (beginning-of-line) (point))
-                    'gnus-subject))
+  (get-text-property 
+   (save-excursion (beginning-of-line) (point)) 'gnus-subject))
 
 (defalias 'gnus-summary-score 'gnus-summary-article-score)
 (make-obsolete 'gnus-summary-score 'gnus-summary-article-score)
@@ -6139,6 +6348,23 @@ displayed, no centering will be performed."
       (get-buffer-window (current-buffer)) 
       (save-excursion (forward-line (- top)) (point))))))
 
+;; Function written by Stainless Steel Rat <ratinox@ccs.neu.edu>.
+(defun gnus-short-group-name (newsgroup)
+  "Convert a long group name to an initialized form.
+The last part of the name is left intact: \"rec.arts.anime\" becomes
+\"r.a.anime\"."
+  (let ((name ""))
+    (while newsgroup
+      (if (string-match "\\." newsgroup)
+         (progn
+           (setq name (concat name (substring newsgroup 0 1))
+                 newsgroup (substring newsgroup (match-end 0))
+                 name (concat name ".")))
+         (setq name (concat name newsgroup)
+               newsgroup nil)))
+    name))
+
+
 (defun gnus-summary-jump-to-group (newsgroup)
   "Move point to NEWSGROUP in group mode buffer."
   ;; Keep update point of group mode buffer if visible.
@@ -6165,13 +6391,16 @@ displayed, no centering will be performed."
         unread first nlast unread)
     ;; If none are read, then all are unread. 
     (if (not read)
-         (setq first (car active))
+       (setq first (car active))
       ;; If the range of read articles is a single range, then the
       ;; first unread article is the article after the last read
       ;; article. Sounds logical, doesn't it?
       (if (not (listp (cdr read)))
          (setq first (1+ (cdr read)))
        ;; `read' is a list of ranges.
+       (if (/= (setq nlast (or (and (numberp (car read)) (car read)) 
+                               (car (car read)))) 1)
+           (setq first 1))
        (while read
          (if first 
              (while (< first nlast)
@@ -6216,9 +6445,9 @@ With arg, turn line truncation on iff arg is positive."
          (> (prefix-numeric-value arg) 0)))
   (redraw-display))
 
-(defun gnus-summary-reselect-current-group (show-all)
+(defun gnus-summary-reselect-current-group (all)
   "Once exit and then reselect the current newsgroup.
-Prefix argument SHOW-ALL means to select all articles."
+The prefix argument ALL means to select all articles."
   (interactive "P")
   (gnus-set-global-variables)
   (let ((current-subject (gnus-summary-article-number)))
@@ -6227,7 +6456,7 @@ Prefix argument SHOW-ALL means to select all articles."
     ;; current point was moved to the next unread newsgroup by
     ;; exiting.
     (gnus-summary-jump-to-group gnus-newsgroup-name)
-    (gnus-group-read-group show-all t)
+    (gnus-group-read-group all t)
     (gnus-summary-goto-subject current-subject)))
 
 (defun gnus-summary-rescan-group (all)
@@ -6402,8 +6631,9 @@ If BACKWARD, go to previous group instead."
        (unwind-protect
            (gnus-summary-read-group group nil no-article buf)
          (and (string= gnus-newsgroup-name ingroup)
+              (bufferp sumbuf) (buffer-name sumbuf)
               (progn
-                (set-buffer sumbuf)
+                (set-buffer (setq gnus-summary-buffer sumbuf))
                 (gnus-summary-exit-no-update t))))))))
 
 (defun gnus-summary-prev-group (no-article)
@@ -6499,7 +6729,7 @@ If optional argument UNREAD is non-nil, only unread article is selected."
   (if (null article)
       nil
     (gnus-article-prepare article all-header)
-    (if (= (gnus-summary-article-mark) ?Z) 
+    (if (eq (gnus-summary-article-mark) ?Z)
        (progn
          (forward-line 1)
          (gnus-summary-position-cursor)))
@@ -6512,7 +6742,7 @@ If optional argument UNREAD is non-nil, only unread article is selected."
     (gnus-summary-update-line)
     t))
 
-(defun gnus-summary-select-article (&optional all-headers force pseudo)
+(defun gnus-summary-select-article (&optional all-headers force pseudo article)
   "Select the current article.
 If ALL-HEADERS is non-nil, show all header fields.  If FORCE is
 non-nil, the article will be re-fetched even if it already present in
@@ -6520,7 +6750,7 @@ the article buffer.  If PSEUDO is non-nil, pseudo-articles will also
 be displayed."
   (and (not pseudo) (gnus-summary-pseudo-article)
        (error "This is a pseudo-article."))
-  (let ((article (gnus-summary-article-number))
+  (let ((article (or article (gnus-summary-article-number)))
        (all-headers (not (not all-headers)))) ;Must be T or NIL.
     (if (or (null gnus-current-article)
            (null gnus-article-current)
@@ -6574,7 +6804,8 @@ If BACKWARD, the previous article is selected instead of the next."
       (gnus-summary-jump-to-group gnus-newsgroup-name)
       (let ((cmd (aref (this-command-keys) 0))
            (group 
-            (if (eq gnus-keep-same-level 'best) (gnus-summary-best-group)
+            (if (eq gnus-keep-same-level 'best) 
+                (gnus-summary-best-group gnus-newsgroup-name)
               (gnus-summary-search-group backward gnus-keep-same-level))))
        ;; Keep just the event type of CMD.
        (and (listp cmd) (setq cmd (car cmd)))
@@ -7006,7 +7237,8 @@ If ARG is a negative number, hide the unwanted header lines."
        (if (text-property-any 1 (point-max) 'invisible t)
            (remove-text-properties 1 (point-max) '(invisible t))
          (let ((gnus-have-all-headers nil))
-           (run-hooks 'gnus-article-display-hook)))))))
+           (run-hooks 'gnus-article-display-hook))))
+      (set-window-point (get-buffer-window (current-buffer)) 1))))
 
 (defun gnus-summary-show-all-headers ()
   "Make all header lines visible."
@@ -7071,12 +7303,20 @@ and `request-accept' functions. (Ie. mail newsgroups at present.)"
     (if (and (not to-newsgroup) (not select-method))
        (setq to-newsgroup
              (completing-read 
-              (format "Where do you want to move %s? "
+              (format "Where do you want to move %s? %s"
                       (if (> (length articles) 1)
                           (format "these %d articles" (length articles))
-                        "this article"))
-              gnus-active-hashtb nil t 
+                        "this article")
+                      (if gnus-current-move-group
+                          (format "(%s default) " gnus-current-move-group)
+                        ""))
+              gnus-active-hashtb nil nil
               (gnus-group-real-prefix gnus-newsgroup-name))))
+    (if (string= to-newsgroup "")
+       (setq to-newsgroup (or gnus-current-move-group "")))
+    (or (gnus-gethash to-newsgroup gnus-active-hashtb)
+       (error "No such group: %s" to-newsgroup))
+    (setq gnus-current-move-group to-newsgroup)
     (or (gnus-check-backend-function 'request-accept-article 
                                     (or select-method to-newsgroup))
        (error "%s does not support article moving" to-newsgroup))
@@ -7126,7 +7366,11 @@ and `request-accept' functions. (Ie. mail newsgroups at present.)"
                (if (memq article (symbol-value (cdr (car marks))))
                    (gnus-add-marked-articles 
                     (car info) (car (car marks)) (list to-article) info))
-               (setq marks (cdr marks)))))
+               (setq marks (cdr marks))))
+           (setq gnus-newsgroup-marked (delq article gnus-newsgroup-marked))
+           (setq gnus-newsgroup-unreads (delq article gnus-newsgroup-unreads))
+           (setq gnus-newsgroup-dormant
+                 (delq article gnus-newsgroup-dormant)))
        (message "Couldn't move article %s" (car articles)))
       (gnus-summary-remove-process-mark (car articles))
       (setq articles (cdr articles)))))
@@ -7174,12 +7418,20 @@ functions. (Ie. mail newsgroups at present.)"
     (if (and (not to-newsgroup) (not select-method))
        (setq to-newsgroup
              (completing-read 
-              (format "Where do you want to copy %s? "
+              (format "Where do you want to copy %s? %s"
                       (if (> (length articles) 1)
                           (format "these %d articles" (length articles))
-                        "this article"))
-              gnus-active-hashtb nil t 
+                        "this article")
+                      (if gnus-current-move-group
+                          (format "(%s default) " gnus-current-move-group)
+                        ""))
+              gnus-active-hashtb nil nil
               (gnus-group-real-prefix gnus-newsgroup-name))))
+    (if (string= to-newsgroup "")
+       (setq to-newsgroup (or gnus-current-move-group "")))
+    (or (gnus-gethash to-newsgroup gnus-active-hashtb)
+       (error "No such group: %s" to-newsgroup))
+    (setq gnus-current-move-group to-newsgroup)
     (or (gnus-check-backend-function 'request-accept-article 
                                     (or select-method to-newsgroup))
        (error "%s does not support article copying" to-newsgroup))
@@ -7326,7 +7578,8 @@ This will have permanent effect only in mail groups."
   (interactive "p")
   ;; Skip dummy header line.
   (save-excursion
-    (if (= (gnus-summary-article-mark) ?Z) (forward-line 1))
+    (if (eq (gnus-summary-article-mark) ?Z)
+       (forward-line 1))
     (let ((buffer-read-only nil))
       ;; Set score.
       (gnus-summary-update-mark
@@ -7557,7 +7810,8 @@ the actual number of articles marked is returned."
   (let ((buffer-read-only nil))
     (if (gnus-summary-goto-subject article)
        (progn
-         (if (= (gnus-summary-article-mark) ?Z) (forward-line 1))
+         (and (eq (gnus-summary-article-mark) ?Z)
+              (forward-line 1))
          (gnus-summary-update-mark gnus-process-mark 'replied)
          t))))
 
@@ -7567,13 +7821,14 @@ the actual number of articles marked is returned."
   (let ((buffer-read-only nil))
     (if (gnus-summary-goto-subject article)
        (progn
-         (and (= (gnus-summary-article-mark) ?Z) (forward-line 1))
+         (and (eq (gnus-summary-article-mark) ?Z)
+              (forward-line 1))
          (gnus-summary-update-mark ?  'replied)
          (if (memq article gnus-newsgroup-replied) 
              (gnus-summary-update-mark gnus-replied-mark 'replied))
          t))))
 
-(defun gnus-summary-mark-forward (n &optional mark)
+(defun gnus-summary-mark-forward (n &optional mark no-expire)
   "Mark N articles as read forwards.
 If N is negative, mark backwards instead.
 Mark with MARK. If MARK is ? , ?! or ??, articles will be
@@ -7586,7 +7841,7 @@ returned."
        (n (abs n))
        (mark (or mark gnus-dread-mark)))
   (while (and (> n 0)
-             (gnus-summary-mark-article nil mark)
+             (gnus-summary-mark-article nil mark no-expire)
              (zerop (gnus-summary-next-subject 
                      (if backward -1 1) gnus-summary-goto-unread)))
     (setq n (1- n)))
@@ -7594,7 +7849,7 @@ returned."
   (gnus-set-mode-line 'summary)
   n))
 
-(defun gnus-summary-mark-article (&optional article mark)
+(defun gnus-summary-mark-article (&optional article mark no-expire)
   "Mark ARTICLE with MARK.
 MARK can be any character.
 Five MARK strings are reserved: ?  (unread), 
@@ -7603,13 +7858,14 @@ If MARK is nil, then the default character ?D is used.
 If ARTICLE is nil, then the article on the current line will be
 marked." 
   ;; If no mark is given, then we check auto-expiring.
-  (and (or (not mark)
+  (and (not no-expire)
+       gnus-newsgroup-auto-expire 
+       (or (not mark)
           (and (numberp mark) (or (= mark gnus-killed-mark)
                                   (= mark gnus-dread-mark)
                                   (= mark gnus-catchup-mark)
                                   (= mark gnus-low-score-mark)
                                   (= mark gnus-read-mark))))
-       gnus-newsgroup-auto-expire 
        (setq mark gnus-expirable-mark))
   (let* ((buffer-read-only nil)
         (mark (or (and (stringp mark) (aref mark 0)) mark gnus-dread-mark))
@@ -7623,7 +7879,8 @@ marked."
        (progn
          (gnus-summary-show-thread)
          (beginning-of-line)
-         (if (= (gnus-summary-article-mark) ?Z) (forward-line 1))
+         (and (eq (gnus-summary-article-mark) ?Z)
+              (forward-line 1))
          ;; Fix the mark.
          (gnus-summary-update-mark mark 'unread)
          t))))
@@ -7715,20 +7972,19 @@ If N is negative, mark backwards instead.
 The difference between N and the actual number of articles marked is
 returned."
   (interactive "p")
-  (gnus-summary-mark-forward n))
+  (gnus-summary-mark-forward n gnus-dread-mark t))
 
 (defun gnus-summary-mark-as-read-backward (n)
   "Mark the N articles as read backwards.
 The difference between N and the actual number of articles marked is
 returned."
   (interactive "p")
-  (gnus-summary-mark-forward (- n)))
+  (gnus-summary-mark-forward (- n) gnus-dread-mark t))
 
 (defun gnus-summary-mark-as-read (&optional article mark)
   "Mark current article as read.
 ARTICLE specifies the article to be marked as read.
-MARK specifies a string to be inserted at the beginning of the line.
-Any kind of string (length 1) except for a space and `-' is ok."
+MARK specifies a string to be inserted at the beginning of the line."
   (gnus-summary-mark-article article mark))
 
 (defun gnus-summary-clear-mark-forward (n)
@@ -7922,8 +8178,9 @@ The number of articles marked as read is returned."
                (while (and (gnus-summary-mark-as-read nil gnus-catchup-mark)
                            (if to-here (< (point) to-here) t)
                            (gnus-summary-search-subject nil (not all)))))
-           (- unreads (length gnus-newsgroup-unreads))))
-    (setq gnus-newsgroup-unreads gnus-newsgroup-marked)
+           (- unreads (length gnus-newsgroup-unreads))
+           (or to-here
+               (setq gnus-newsgroup-unreads gnus-newsgroup-marked))))
     (gnus-summary-position-cursor)))
 
 (defun gnus-summary-catchup-to-here (&optional all)
@@ -7954,6 +8211,15 @@ If prefix argument ALL is non-nil, all articles are marked as read."
   (interactive)
   (gnus-summary-catchup-and-exit t quietly))
 
+;; Suggested by "Arne Eofsson" <arne@hodgkin.mbi.ucla.edu>.
+(defun gnus-summary-catchup-and-goto-next-group (all)
+  "Mark all articles in this group as read and select the next group.
+If given a prefix, mark all articles, unread as well as ticked, as
+read." 
+  (interactive "P")
+  (gnus-summary-catchup all)
+  (gnus-summary-next-group))
+
 ;; Thread-based commands.
 
 (defun gnus-summary-toggle-threads (arg)
@@ -8194,16 +8460,20 @@ Argument REVERSE means reverse order."
    (cons 'gnus-summary-article-score 'gnus-thread-sort-by-score)
    (not reverse)))
 
+(defvar gnus-summary-already-sorted nil)
 (defun gnus-summary-sort (predicate reverse)
   ;; Sort summary buffer by PREDICATE.  REVERSE means reverse order. 
-  (let (buffer-read-only)
-    (if (not gnus-show-threads)
-       (progn
-         (goto-char (point-min))
-         (sort-subr reverse 'forward-line 'end-of-line (car predicate)))
-      (let ((gnus-thread-sort-functions (list (cdr predicate))))
-       (gnus-summary-prepare)))))
-
+  (if gnus-summary-already-sorted
+      ()
+    (let (buffer-read-only)
+      (if (not gnus-show-threads)
+         (progn
+           (goto-char (point-min))
+           (sort-subr reverse 'forward-line 'end-of-line (car predicate)))
+       (let ((gnus-thread-sort-functions (list (cdr predicate)))
+             (gnus-summary-already-sorted nil))
+         (gnus-summary-prepare))))))
+  
 (defun gnus-sortable-date (date)
   "Make sortable string by string-lessp from DATE.
 Timezone package is used."
@@ -8249,9 +8519,21 @@ The variable `gnus-default-article-saver' specifies the saver function."
                                  gnus-newsgroup-headers-hashtb-by-number)))
        (if (vectorp header)
            (progn
-             (gnus-summary-display-article (car articles) t)
-             (if (not gnus-save-all-headers)
+             (gnus-summary-select-article t nil nil (car articles))
+             (or gnus-save-all-headers
                  (gnus-article-hide-headers t))
+             ;; Remove any X-Gnus lines.
+             (save-excursion
+               (save-restriction
+                 (set-buffer gnus-article-buffer)
+                 (let ((buffer-read-only nil))
+                   (goto-char (point-min))
+                   (narrow-to-region (point) (or (search-forward "\n\n" nil t)
+                                                 (point-max)))
+                   (while (re-search-forward "^X-Gnus" nil t)
+                     (delete-region (progn (beginning-of-line) (point))
+                                    (progn (forward-line 1) (point))))
+                   (widen))))
              (if gnus-default-article-saver
                  (funcall gnus-default-article-saver)
                (error "No default saver is defined.")))
@@ -8328,7 +8610,7 @@ is initialized from the SAVEDIR environment variable."
     (or filename
        (setq filename
              (read-file-name
-              (concat "Save article in rmail file: (default "
+              (concat "Save in rmail file: (default "
                       (file-name-nondirectory default-name) ") ")
               (file-name-directory default-name)
               default-name)))
@@ -8354,7 +8636,7 @@ is initialized from the SAVEDIR environment variable."
     (or filename
        (setq filename
              (read-file-name
-              (concat "Save article in Unix mail file: (default "
+              (concat "Save in Unix mail file: (default "
                       (file-name-nondirectory default-name) ") ")
               (file-name-directory default-name)
               default-name)))
@@ -8386,7 +8668,7 @@ is initialized from the SAVEDIR environment variable."
     (or filename
        (setq filename
              (read-file-name
-              (concat "Save article in file: (default "
+              (concat "Save in file: (default "
                       (file-name-nondirectory default-name) ") ")
               (file-name-directory default-name)
               default-name)))
@@ -8422,6 +8704,8 @@ is initialized from the SAVEDIR environment variable."
        b)
     (or (gnus-summary-goto-subject article)
        (error (format "No such article: %d" article)))
+    (or gnus-newsgroup-headers-hashtb-by-number
+       (gnus-make-headers-hashtable-by-number))
     (gnus-summary-position-cursor)
     (if gnus-view-pseudos
        (while pslist
@@ -8509,9 +8793,16 @@ DATE is the expire date."
         (if (y-or-n-p "Expire kill? ")
             (current-time-string)
           nil)))
-  (and prompt (setq match (read-string "Match: " match)))
-  (let ((score (gnus-score-default score)))
-    (gnus-summary-score-effect header match type score)
+  (let ((score (gnus-score-default score))
+       (header (downcase header)))
+    (and prompt (setq match (read-string 
+                            (format "Match %s on %s, %s: " 
+                                    (if date "temp" "permanent") 
+                                    header
+                                    (if (< score 0) "lower" "raise"))
+                            match)))
+    (and (>= (nth 1 (assoc header gnus-header-index)) 0)
+        (gnus-summary-score-effect header match type score))
     (and (= score gnus-score-interactive-default-score)
         (setq score nil))
     (let ((new (cond (type
@@ -8590,6 +8881,13 @@ See `gnus-score-expiry-days'."
    "from" (gnus-summary-header "from") nil (- (gnus-score-default level)) 
    (current-time-string) t))
 
+(defun gnus-summary-temporarily-lower-by-body (level)
+  "Temporarily lower score by LEVEL for a match on the body of the article.
+See `gnus-score-expiry-days'."
+  (interactive "P")
+  (gnus-summary-score-entry
+   "body" "" nil (- (gnus-score-default level)) (current-time-string) t))
+
 (defun gnus-summary-temporarily-lower-by-id (level)
   "Temporarily lower score by LEVEL for current message-id.
 See `gnus-score-expiry-days'."
@@ -8629,6 +8927,12 @@ See `gnus-score-expiry-days'."
    "from" (gnus-summary-header "from") nil 
    (- (gnus-score-default level)) nil t))
 
+(defun gnus-summary-lower-by-body (level)
+  "Lower score by LEVEL for a match on the body of the article."
+  (interactive "P")
+  (gnus-summary-score-entry
+   "body" "" nil (- (gnus-score-default level)) nil t))
+
 (defun gnus-summary-lower-by-id (level)
   "Lower score by LEVEL for current message-id."
   (interactive "P")
@@ -8662,6 +8966,12 @@ See `gnus-score-expiry-days'."
   (gnus-summary-score-entry
    "from" (gnus-summary-header "from") nil level (current-time-string) t))
 
+(defun gnus-summary-temporarily-raise-by-body (level)
+  "Temporarily raise score by LEVEL for a match on the body of the article.
+See `gnus-score-expiry-days'."
+  (interactive "P")
+  (gnus-summary-score-entry "body" "" nil level (current-time-string) t))
+
 (defun gnus-summary-temporarily-raise-by-id (level)
   "Temporarily raise score by LEVEL for current message-id.
 See `gnus-score-expiry-days'."
@@ -8697,6 +9007,11 @@ See `gnus-score-expiry-days'."
   (gnus-summary-score-entry
    "from" (gnus-summary-header "from") nil level nil t))
 
+(defun gnus-summary-raise-by-body (level)
+  "Raise score by LEVEL for a match on the body of the article."
+  (interactive "P")
+  (gnus-summary-score-entry "body" "" nil level nil t))
+
 (defun gnus-summary-raise-by-id (level)
   "Raise score by LEVEL for current message-id."
   (interactive "P")
@@ -8740,6 +9055,8 @@ See `gnus-score-expiry-days'."
 ;;; Gnus article mode
 ;;;
 
+(put 'gnus-article-mode 'mode-class 'special)
+
 (if gnus-article-mode-map
     nil
   (setq gnus-article-mode-map (make-keymap))
@@ -9424,13 +9741,17 @@ ROT47 will be performed for Japanese text in any case."
        (if (or (not (boundp 'caesar-translate-table))
                (not caesar-translate-table)
                (/= (aref caesar-translate-table ?a) (+ ?a n)))
-           (let ((i 0) (lower "abcdefghijklmnopqrstuvwxyz") upper)
+           (let ((i 0) 
+                 (lower "abcdefghijklmnopqrstuvwxyz")
+                 upper)
              (message "Building caesar-translate-table...")
              (setq caesar-translate-table (make-vector 256 0))
              (while (< i 256)
                (aset caesar-translate-table i i)
                (setq i (1+ i)))
-             (setq lower (concat lower lower) upper (upcase lower) i 0)
+             (setq lower (concat lower lower)
+                   upper (upcase lower)
+                   i 0)
              (while (< i 26)
                (aset caesar-translate-table (+ ?a i) (aref lower (+ i n)))
                (aset caesar-translate-table (+ ?A i) (aref upper (+ i n)))
@@ -9538,7 +9859,7 @@ If NEWSGROUP is nil, return the global kill file name instead."
         ;; The global KILL file is placed at top of the directory.
         (expand-file-name gnus-kill-file-name
                           (or gnus-kill-files-directory "~/News")))
-       (gnus-use-long-file-name
+       ((gnus-use-long-file-name 'not-kill)
         ;; Append ".KILL" to newsgroup name.
         (expand-file-name (concat newsgroup "." gnus-kill-file-name)
                           (or gnus-kill-files-directory "~/News")))
@@ -9642,42 +9963,47 @@ If NEWSGROUP is nil, return the global kill file name instead."
   
 (defun gnus-score-load-file (file)
   ;; Load score file FILE.  Returns a list a retrieved score-alists.
-  (let* ((file (expand-file-name (or (and (string-match "^/" file) file)
-                                    (concat gnus-kill-files-directory file))))
+  (setq gnus-kill-files-directory (or gnus-kill-files-directory "~/News/"))
+  (let* ((file (expand-file-name 
+               (or (and (string-match
+                         (concat "^" (expand-file-name
+                                      gnus-kill-files-directory)) 
+                         (expand-file-name file))
+                        file)
+                   (concat gnus-kill-files-directory file))))
         (cached (assoc file gnus-score-cache))
         (global (member file gnus-internal-global-score-files))
         lists alist)
     (if cached
        ;; The score file was already loaded.
-       (setq gnus-score-alist (cdr cached))
+       (setq alist (cdr cached))
       ;; We load the score file.
       (setq gnus-score-alist nil)
       (setq alist (gnus-score-load-score-alist file))
       ;; We add '(touched) to the alist to signify that it hasn't been
       ;; touched (yet). 
-      (if (not (assq 'touched gnus-score-alist))
-         (setq gnus-score-alist 
-               (cons (list 'touched nil) gnus-score-alist)))
+      (or (assq 'touched alist) (setq alist (cons (list 'touched nil) alist)))
       ;; If it is a global score file, we make it read-only.
       (and global
-          (not (assq 'read-only gnus-score-alist))
-          (setq gnus-score-alist 
-                (cons (list 'read-only t) gnus-score-alist)))
+          (not (assq 'read-only alist))
+          (setq alist (cons (list 'read-only t) alist)))
       ;; Update cache.
       (setq gnus-score-cache
-           (cons (cons file gnus-score-alist) gnus-score-cache)))
+           (cons (cons file alist) gnus-score-cache)))
     ;; If there are actual scores in the alist, we add it to the
     ;; return value of this function.
-    (if (memq t (mapcar (lambda (e) (stringp (car e))) gnus-score-alist))
-       (setq lists (list gnus-score-alist)))
+    (if (memq t (mapcar (lambda (e) (stringp (car e))) alist))
+       (setq lists (list alist)))
     ;; Treat the other possible atoms in the score alist.
-    (let ((mark (car (gnus-score-get 'mark gnus-score-alist)))
-         (expunge (car (gnus-score-get 'expunge gnus-score-alist)))
+    (let ((mark (car (gnus-score-get 'mark alist)))
+         (expunge (car (gnus-score-get 'expunge alist)))
          (mark-and-expunge 
-          (car (gnus-score-get 'mark-and-expunge gnus-score-alist)))
-         (read-only (gnus-score-get 'read-only gnus-score-alist))
-         (files (gnus-score-get 'files gnus-score-alist))
-         (eval (gnus-score-get 'eval gnus-score-alist)))
+          (car (gnus-score-get 'mark-and-expunge alist)))
+         (read-only (gnus-score-get 'read-only alist))
+         (files (gnus-score-get 'files alist))
+         (exclude-files (gnus-score-get 'exclude-files alist))
+          (orphan (gnus-score-get 'orphan alist))
+         (eval (gnus-score-get 'eval alist)))
       ;; We do not respect eval and files atoms from global score
       ;; files. 
       (and files (not global)
@@ -9686,8 +10012,11 @@ If NEWSGROUP is nil, return the global kill file name instead."
                                        (gnus-score-load-file file)) 
                                      files))))
       (and eval (not global) (eval eval))
-      (setq gnus-summary-mark-below (or mark mark-and-expunge))
-      (setq gnus-summary-expunge-below (or expunge mark-and-expunge)))
+      (if orphan (setq gnus-orphan-score (car orphan)))
+      (setq gnus-summary-mark-below 
+           (or mark mark-and-expunge gnus-summary-mark-below))
+      (setq gnus-summary-expunge-below 
+           (or expunge mark-and-expunge gnus-summary-expunge-below)))
     (setq gnus-current-score-file file)
     (setq gnus-score-alist alist)
     lists))
@@ -9700,13 +10029,13 @@ If NEWSGROUP is nil, return the global kill file name instead."
       (setq gnus-score-alist nil)
       (gnus-score-load-score-alist file)
       (or gnus-score-alist
-         (setq gnus-score-alist (copy-alist '((touched nil)))))
+         (setq gnus-score-alist (copy-alist '((touched nil)))))
       (setq gnus-score-cache
            (cons (cons file gnus-score-alist) gnus-score-cache)))))
 
 (defun gnus-score-remove-from-cache (file)
-  (setq gnus-score-cache (delq (assoc file gnus-score-cache)
-                              gnus-score-cache)))
+  (setq gnus-score-cache 
+       (delq (assoc file gnus-score-cache) gnus-score-cache)))
 
 (defun gnus-score-load-score-alist (file)
   (let (alist)
@@ -9718,18 +10047,50 @@ If NEWSGROUP is nil, return the global kill file name instead."
            (erase-buffer)
            (insert-file-contents file)
            (goto-char (point-min))
-           (setq alist
-                 (condition-case ()
-                     (read (current-buffer))
-                   (error 
-                    (progn
-                      (message "Problem with score file %s" file)
-                      (ding) 
-                      nil)))))
+           ;; Only do the loading if the score file isn't empty.
+           (if (save-excursion (re-search-forward "[()0-9a-zA-Z]" nil t))
+               (setq alist
+                     (condition-case ()
+                         (read (current-buffer))
+                       (error 
+                        (progn
+                          (message "Problem with score file %s" file)
+                          (ding) 
+                          (sit-for 2)
+                          nil))))))
          (if (eq (car alist) 'setq)
-             (setq gnus-score-alist
-                   (gnus-score-transform-old-to-new alist))
-           (setq gnus-score-alist alist))))))
+             (setq gnus-score-alist (gnus-score-transform-old-to-new alist))
+           (setq gnus-score-alist alist))
+         (setq gnus-score-alist
+               (gnus-score-check-syntax gnus-score-alist)))
+      (setq gnus-score-alist nil))))
+
+(defun gnus-score-check-syntax (alist)
+  (cond 
+   ((null alist)
+    nil)
+   ((not (consp alist))
+    (message "Score file is not a list: %s" alist)
+    (ding)
+    nil)
+   (t
+    (let ((a alist)
+         err)
+      (while (and a (not err))
+       (cond ((not (listp (car a)))
+              (message "Illegal score element: %s" (car a))
+              (setq err t))
+             ((and (stringp (car (car a)))
+                   (not (listp (nth 1 (car a)))))
+              (message "Illegal header match: %s" (nth 1 (car a)))
+              (setq err t))
+             (t
+              (setq a (cdr a)))))
+      (if err
+         (progn
+           (ding)
+           nil)
+       alist)))))    
 
 (defun gnus-score-transform-old-to-new (alist)
   (let* ((alist (nth 2 alist))
@@ -9748,17 +10109,16 @@ If NEWSGROUP is nil, return the global kill file name instead."
                                 (gnus-day-number (nth 3 (car scor))))
                            (if (nth 1 (car scor)) 'r 's)))
              (setq scor (cdr scor))))
-       (setq out (cons (list (car entry) (cdr entry)) out)))
+       (setq out (cons (if (not (listp (cdr entry))) 
+                           (list (car entry) (cdr entry))
+                         entry)
+                       out)))
       (setq alist (cdr alist)))
     (cons (list 'touched t) (nreverse out))))
   
 (defun gnus-score-save ()
   ;; Save all SCORE information.
-  (let (cache)
-    (save-excursion
-      (set-buffer gnus-summary-buffer)
-      (setq cache gnus-score-cache
-           gnus-score-cache nil))
+  (let ((cache gnus-score-cache))
     (save-excursion
       (setq gnus-score-alist nil)
       (set-buffer (get-buffer-create "*Score*"))
@@ -9777,20 +10137,31 @@ If NEWSGROUP is nil, return the global kill file name instead."
            (erase-buffer)
            (let (emacs-lisp-mode-hook)
              (pp score (current-buffer)))
-           (make-directory (file-name-directory file) t)
+           (gnus-make-directory (file-name-directory file))
            (write-region (point-min) (point-max) file nil 'silent))))
       (kill-buffer (current-buffer)))))
   
 (defun gnus-score-headers ()
   ;; Score `gnus-newsgroup-headers'.
-  (let ((score-files (and (symbolp gnus-score-find-score-files-function)
-                         (fboundp gnus-score-find-score-files-function)
-                         (funcall gnus-score-find-score-files-function
-                                  gnus-newsgroup-name)))
-       scores)
+  (let ((func gnus-score-find-score-files-function)
+       score-files scores)
+    (and func (not (listp func))
+        (setq func (list func)))
+    ;; Go through all the functions for finding score files (or actual
+    ;; scores) and add them to a list.
+    (while func
+      (and (symbolp (car func))
+          (fboundp (car func))
+          (setq score-files 
+                (nconc score-files (funcall (car func) gnus-newsgroup-name))))
+      (setq func (cdr func)))
+    ;; PLM: probably this is not the best place to clear orphan-score
+    (setq gnus-orphan-score nil)
     ;; Load the SCORE files.
     (while score-files
-      (setq scores (nconc (gnus-score-load-file (car score-files)) scores))
+      (if (stringp (car score-files))
+         (setq scores (nconc (gnus-score-load-file (car score-files)) scores))
+       (setq scores (nconc (car score-files) scores)))
       (setq score-files (cdr score-files)))
     (if (not (and gnus-summary-default-score
                  scores
@@ -9814,10 +10185,16 @@ If NEWSGROUP is nil, return the global kill file name instead."
              (setq gnus-scores-articles       ;Total of 2 * N cons-cells used.
                    (cons (cons header (or gnus-summary-default-score 0))
                          gnus-scores-articles))))
-  
+
        (save-excursion
          (set-buffer (get-buffer-create "*Headers*"))
          (buffer-disable-undo (current-buffer))
+          ;; score orphans
+          (if gnus-orphan-score 
+              (progn
+                (setq gnus-score-index 
+                      (nth 1 (assoc "references" gnus-header-index)))
+                (gnus-score-orphans gnus-orphan-score)))
          ;; Run each header through the score process.
          (while entries
            (setq entry (car entries)
@@ -9844,11 +10221,255 @@ If NEWSGROUP is nil, return the global kill file name instead."
 
        (message "Scoring...done")))))
 
-;;(defun gnus-score-integer (scores header now expire)
-;;  )
 
-;;(defun gnus-score-date (scores header now expire)
-;;  )
+(defun gnus-get-new-thread-ids (articles)
+  (let ((index (nth 1 (assoc "message-id" gnus-header-index)))
+        (refind gnus-score-index)
+        id-list art this tref)
+    (while articles
+      (setq art (car articles)
+            this (aref (car art) index)
+            tref (aref (car art) refind)
+            articles (cdr articles))
+      (if (string-equal tref "")        ;no references line
+          (setq id-list (cons this id-list))))
+    id-list))
+
+;; Orphan functions written by plm@atcmp.nl (Peter Mutsaers).
+(defun gnus-score-orphans (score)
+  (let ((new-thread-ids (gnus-get-new-thread-ids gnus-scores-articles))
+        (index (nth 1 (assoc "references" gnus-header-index)))
+        alike articles art arts this last this-id)
+    
+    (setq gnus-scores-articles (sort gnus-scores-articles 'gnus-score-string<)
+         articles gnus-scores-articles)
+
+    ;;more or less the same as in gnus-score-string
+    (erase-buffer)
+    (while articles
+      (setq art (car articles)
+            this (aref (car art) gnus-score-index)
+            articles (cdr articles))
+      ;;completely skip if this is empty (not a child, so not an orphan)
+      (if (not (string= this ""))
+          (if (equal last this)
+              ;; O(N*H) cons-cells used here, where H is the number of
+              ;; headers.
+              (setq alike (cons art alike))
+            (if last
+                (progn
+                  ;; Insert the line, with a text property on the
+                  ;; terminating newline refering to the articles with
+                  ;; this line.
+                  (insert last ?\n)
+                  (put-text-property (1- (point)) (point) 'articles alike)))
+            (setq alike (list art)
+                  last this))))
+    (and last                           ; Bwadr, duplicate code.
+         (progn
+           (insert last ?\n)                    
+           (put-text-property (1- (point)) (point) 'articles alike)))
+
+    ;; PLM: now delete those lines that contain an entry from new-thread-ids
+    (while new-thread-ids
+      (setq this-id (car new-thread-ids)
+            new-thread-ids (cdr new-thread-ids))
+      (goto-char (point-min))
+      (while (search-forward this-id nil t)
+        ;; found a match. remove this line
+        (progn
+          (beginning-of-line)
+          (kill-line 1))))
+
+    ;; now for each line: update its articles with score by moving to
+    ;; every end-of-line in the buffer and read the articles property
+    (goto-char (point-min))
+    (while (eq 0 (progn
+                   (end-of-line)
+                   (setq arts (get-text-property (point) 'articles))
+                   (while arts
+                     (setq art (car arts)
+                           arts (cdr arts))
+                     (setcdr art (+ score (cdr art))))
+                   (forward-line))))))
+             
+
+(defun gnus-score-integer (scores header now expire)
+  (let ((gnus-score-index (nth 1 (assoc header gnus-header-index)))
+       alike last this art entries alist articles)
+
+    ;; Find matches.
+    (while scores
+      (setq alist (car scores)
+           scores (cdr scores)
+           entries (assoc header alist))
+      (while (cdr entries)             ;First entry is the header index.
+       (let* ((rest (cdr entries))             
+              (kill (car rest))
+              (match (nth 0 kill))
+              (type (or (nth 3 kill) '>))
+              (score (or (nth 1 kill) gnus-score-interactive-default-score))
+              (date (nth 2 kill))
+              (found nil)
+              (match-func (if (or (eq type '>) (eq type '<) (eq type '<=)
+                                  (eq type '>=) (eq type '=))
+                              type
+                            (error "Illegal match type: %s" type)))
+              (articles gnus-scores-articles)
+              arts art)
+         ;; Instead of doing all the clever stuff that
+         ;; `gnus-score-string' does to minimize searches and stuff,
+         ;; I will assume that people generally will put so few
+         ;; matches on numbers that any cleverness will take more
+         ;; time than one would gain.
+         (while articles
+           (and (funcall match-func match 
+                         (or (aref (car (car articles)) gnus-score-index) 0))
+                (progn
+                  (setq found t)
+                  (setcdr (car articles) (+ score (cdr (car articles))))))
+           (setq articles (cdr articles)))
+         ;; Update expire date
+         (cond ((null date))           ;Permanent entry.
+               (found                  ;Match, update date.
+                (gnus-score-set 'touched '(t) alist)
+                (setcar (nthcdr 2 kill) now))
+               ((< date expire) ;Old entry, remove.
+                (gnus-score-set 'touched '(t) alist)
+                (setcdr entries (cdr rest))
+                (setq rest entries)))
+         (setq entries rest))))))
+
+(defun gnus-score-date (scores header now expire)
+  (let ((gnus-score-index (nth 1 (assoc header gnus-header-index)))
+       alike last this art entries alist articles)
+
+    ;; Find matches.
+    (while scores
+      (setq alist (car scores)
+           scores (cdr scores)
+           entries (assoc header alist))
+      (while (cdr entries)             ;First entry is the header index.
+       (let* ((rest (cdr entries))             
+              (kill (car rest))
+              (match (timezone-make-date-sortable (nth 0 kill)))
+              (type (or (nth 3 kill) 'before))
+              (score (or (nth 1 kill) gnus-score-interactive-default-score))
+              (date (nth 2 kill))
+              (found nil)
+              (match-func 
+               (cond ((eq type 'after) 'string<)
+                     ((eq type 'before) 'gnus-string>)
+                     ((eq type 'at) 'string=)
+                     (t (error "Illegal match type: %s" type))))
+              (articles gnus-scores-articles)
+              arts art l)
+         ;; Instead of doing all the clever stuff that
+         ;; `gnus-score-string' does to minimize searches and stuff,
+         ;; I will assume that people generally will put so few
+         ;; matches on numbers that any cleverness will take more
+         ;; time than one would gain.
+         (while articles
+           (and
+            (setq l (aref (car (car articles)) gnus-score-index))
+            (funcall match-func match (timezone-make-date-sortable l))
+            (progn
+              (setq found t)
+              (setcdr (car articles) (+ score (cdr (car articles))))))
+           (setq articles (cdr articles)))
+         ;; Update expire date
+         (cond ((null date))           ;Permanent entry.
+               (found                  ;Match, update date.
+                (gnus-score-set 'touched '(t) alist)
+                (setcar (nthcdr 2 kill) now))
+               ((< date expire) ;Old entry, remove.
+                (gnus-score-set 'touched '(t) alist)
+                (setcdr entries (cdr rest))
+                (setq rest entries)))
+         (setq entries rest))))))
+
+(defun gnus-score-body (scores header now expire)
+  (save-excursion
+    (set-buffer nntp-server-buffer)
+    (save-restriction
+      (let* ((buffer-read-only nil)
+            (articles gnus-scores-articles)
+            (all-scores scores)
+            (request-func (cond ((string= "head" (downcase header))
+                                 'gnus-request-head)
+                                ((string= "body" (downcase header))
+                                 'gnus-request-body)
+                                (t 'gnus-request-article)))
+            alike last this art entries alist ofunc article)
+       ;; Not all backends support partial fetching.  In that case,
+       ;; we just fetch the entire article.
+       (or (gnus-check-backend-function request-func gnus-newsgroup-name)
+           (progn
+             (setq ofunc request-func)
+             (setq request-func 'gnus-request-article)))
+       (while articles
+         (setq article (header-number (car (car articles))))
+         (message "Scoring on article %s..." article)
+         (if (not (funcall request-func article gnus-newsgroup-name))
+             ()
+           (widen)
+           (goto-char (point-min))
+           ;; If just parts of the article is to be searched, but the
+           ;; backend didn't support partial fetching, we just narrow
+           ;; to the relevant parts.
+           (if ofunc
+               (if (eq ofunc 'gnus-request-head)
+                   (narrow-to-region
+                    (point)
+                    (or (search-forward "\n\n" nil t) (point-max)))
+                 (narrow-to-region
+                  (or (search-forward "\n\n" nil t) (point))
+                  (point-max))))
+           (setq scores all-scores)
+           ;; Find matches.
+           (while scores
+             (setq alist (car scores)
+                   scores (cdr scores)
+                   entries (assoc header alist))
+             (while (cdr entries)      ;First entry is the header index.
+               (let* ((rest (cdr entries))             
+                      (kill (car rest))
+                      (match (nth 0 kill))
+                      (type (or (nth 3 kill) 's))
+                      (score (or (nth 1 kill) 
+                                 gnus-score-interactive-default-score))
+                      (date (nth 2 kill))
+                      (found nil)
+                      (case-fold-search 
+                       (not (or (eq type 'R) (eq type 'S)
+                                (eq type 'Regexp) (eq type 'String))))
+                      (search-func 
+                       (cond ((or (eq type 'r) (eq type 'R)
+                                  (eq type 'regexp) (eq type 'Regexp))
+                              're-search-forward)
+                             ((or (eq type 's) (eq type 'S)
+                                  (eq type 'string) (eq type 'String))
+                              'search-forward)
+                             (t
+                              (error "Illegal match type: %s" type))))
+                      arts art)
+                 (goto-char (point-min))
+                 (if (funcall search-func match nil t)
+                     ;; Found a match, update scores.
+                     (progn
+                       (setcdr (car articles) (+ score (cdr (car articles))))
+                       (setq found t)))
+                 ;; Update expire date
+                 (cond ((null date))   ;Permanent entry.
+                       (found          ;Match, update date.
+                        (gnus-score-set 'touched '(t) alist)
+                        (setcar (nthcdr 2 kill) now))
+                       ((< date expire) ;Old entry, remove.
+                        (gnus-score-set 'touched '(t) alist)
+                        (setcdr entries (cdr rest))
+                        (setq rest entries)))
+                 (setq entries rest)))))
+         (setq articles (cdr articles)))))))
 
 (defun gnus-score-string (scores header now expire)
   ;; Score ARTICLES according to HEADER in SCORES.
@@ -9905,13 +10526,20 @@ If NEWSGROUP is nil, return the global kill file name instead."
               (score (or (nth 1 kill) gnus-score-interactive-default-score))
               (date (nth 2 kill))
               (found nil)
-              (case-fold-search t)
+              (case-fold-search 
+               (not (or (eq type 'R) (eq type 'S)
+                        (eq type 'Regexp) (eq type 'String))))
+              (search-func (cond ((or (eq type 'r) (eq type 'R)
+                                      (eq type 'regexp) (eq type 'Regexp))
+                                  're-search-forward)
+                                 ((or (eq type 's) (eq type 'S)
+                                      (eq type 'string) (eq type 'String))
+                                  'search-forward)
+                                 (t
+                                  (error "Illegal match type: %s" type))))
               arts art)
          (goto-char (point-min))
-         (while (cond ((eq type 'r)
-                       (re-search-forward match nil t))
-                      ((eq type 's)
-                       (search-forward match nil t)))
+         (while (funcall search-func match nil t)
            (end-of-line 1)
            (setq found t
                  arts (get-text-property (point) 'articles))
@@ -9951,7 +10579,10 @@ If NEWSGROUP is nil, return the global kill file name instead."
     ("references" 5 gnus-score-string) 
     ("chars" 6 gnus-score-integer) 
     ("lines" 7 gnus-score-integer) 
-    ("xref" 8 gnus-score-string)))
+    ("xref" 8 gnus-score-string)
+    ("head" -1 gnus-score-body)
+    ("body" -1 gnus-score-body)
+    ("all" -1 gnus-score-body)))
 
 (defun gnus-score-file-name (newsgroup)
   "Return the name of a score file for NEWSGROUP."
@@ -9960,7 +10591,7 @@ If NEWSGROUP is nil, return the global kill file name instead."
          ;; The global score file is placed at top of the directory.
          (expand-file-name gnus-score-file-suffix
                            (or gnus-kill-files-directory "~/News")))
-        (gnus-use-long-file-name
+        ((gnus-use-long-file-name 'not-score)
          ;; Append ".SCORE" to newsgroup name.
          (expand-file-name (concat newsgroup "." gnus-score-file-suffix)
                            (or gnus-kill-files-directory "~/News")))
@@ -9980,7 +10611,7 @@ If NEWSGROUP is nil, return the global kill file name instead."
         (or gnus-kill-files-directory "~/News/")))
   (if (not (file-readable-p gnus-kill-files-directory))
       (setq gnus-score-file-list nil)
-    (if gnus-use-long-file-name
+    (if (gnus-use-long-file-name 'not-score)
        (if (or (not gnus-score-file-list)
                (gnus-file-newer-than gnus-kill-files-directory
                                      (car gnus-score-file-list)))
@@ -10079,7 +10710,7 @@ GROUP using BNews sys file syntax."
       ;; file, and not end up in some global score file.
       (let ((localscore
             (expand-file-name
-             (if gnus-use-long-file-name
+             (if (gnus-use-long-file-name 'not-score)
                  (concat gnus-kill-files-directory group "." 
                          gnus-score-file-suffix)
                (concat gnus-kill-files-directory
@@ -10167,15 +10798,14 @@ This mode is an extended emacs-lisp mode.
   (gnus-set-global-variables)
   ;; Save window configuration.
   (setq gnus-winconf-post-news (current-window-configuration))
-  ;; Fix by Sudish Joseph <joseph@cis.ohio-state.edu>.
-  (or gnus-newsgroup-name (setq gnus-newsgroup-name (gnus-group-group-name)))
-  (unwind-protect
-      (gnus-post-news 'post)
-    (or (and (eq (current-buffer) (get-buffer gnus-post-news-buffer))
-            (not (zerop (buffer-size))))
-       ;; Restore last window configuration.
-       (and gnus-winconf-post-news
-            (set-window-configuration gnus-winconf-post-news))))
+  (let ((gnus-newsgroup-name nil))
+    (unwind-protect
+       (gnus-post-news 'post)
+      (or (and (eq (current-buffer) (get-buffer gnus-post-news-buffer))
+              (not (zerop (buffer-size))))
+         ;; Restore last window configuration.
+         (and gnus-winconf-post-news
+              (set-window-configuration gnus-winconf-post-news)))))
   ;; We don't want to return to summary buffer nor article buffer later.
   (setq gnus-winconf-post-news nil)
   (if (get-buffer gnus-summary-buffer)
@@ -10229,7 +10859,8 @@ If prefix argument YANK is non-nil, original article is yanked automatically."
            (and gnus-winconf-post-news
                 (set-window-configuration gnus-winconf-post-news))))
       ;; We don't want to return to article buffer later.
-      (bury-buffer gnus-article-buffer))))
+      (bury-buffer gnus-article-buffer)))
+  (gnus-article-hide-headers-if-wanted))
 
 (defun gnus-summary-followup-with-original ()
   "Compose a followup to an article and include the original article."
@@ -10253,8 +10884,8 @@ If prefix argument YANK is non-nil, original article is yanked automatically."
   (interactive)
   (gnus-set-global-variables)
   (gnus-summary-select-article t)
-  (gnus-eval-in-buffer-window gnus-article-buffer
-                             (gnus-cancel-news)))
+  (gnus-eval-in-buffer-window gnus-article-buffer (gnus-cancel-news))
+  (gnus-article-hide-headers-if-wanted))
 
 (defun gnus-summary-supersede-article ()
   "Compose an article that will supersede a previous article.
@@ -10324,75 +10955,80 @@ Type \\[describe-mode] in the buffer to get a list of commands."
               (setq subject (read-string "Subject: "))))
        (setq mail-reply-buffer article-buffer)
 
-       (setq real-group (gnus-group-real-name group))
-       (setq gnus-post-news-buffer 
-             (gnus-request-post-buffer 
-              post real-group subject header article-buffer
-              (nth 2 (gnus-gethash group gnus-newsrc-hashtb))
-              (if (and (boundp 'gnus-followup-to-function)
-                       gnus-followup-to-function)
-                  (setq follow-to
-                        (save-excursion
-                          (set-buffer article-buffer)
-                          (funcall gnus-followup-to-function group))))
-              (eq gnus-use-followup-to t)))
-       (if post
-           (progn
-             (gnus-configure-windows '(1 0 0))
-             (switch-to-buffer gnus-post-news-buffer))
-         (gnus-configure-windows '(0 1 0))
-         (if (not yank)
+       (let ((gnus-newsgroup-name (or group gnus-newsgroup-name "")))
+         (setq real-group (and group (gnus-group-real-name group)))
+         (setq gnus-post-news-buffer 
+               (gnus-request-post-buffer 
+                post real-group subject header article-buffer
+                (nth 2 (and group (gnus-gethash group gnus-newsrc-hashtb)))
+                (if (and (boundp 'gnus-followup-to-function)
+                         gnus-followup-to-function)
+                    (setq follow-to
+                          (save-excursion
+                            (set-buffer article-buffer)
+                            (funcall gnus-followup-to-function group))))
+                (eq gnus-use-followup-to t)))
+         (if post
              (progn
-               (switch-to-buffer article-buffer)
-               (pop-to-buffer gnus-post-news-buffer))
-           (switch-to-buffer gnus-post-news-buffer)))
-       (gnus-overload-functions)
-       (make-local-variable 'gnus-article-reply)
-       (make-local-variable 'gnus-article-check-size)
-       (setq gnus-article-reply sumart)
-       ;; Handle `gnus-auto-mail-to-author'.
-       ;; Suggested by Daniel Quinlan <quinlan@best.com>.
-       (let ((to (if (eq gnus-auto-mail-to-author 'ask)
-                     (and (y-or-n-p "Also send mail to author? ") from)
-                   (and gnus-auto-mail-to-author from))))
-         (if to
+               (gnus-configure-windows '(1 0 0))
+               (switch-to-buffer gnus-post-news-buffer))
+           (gnus-configure-windows '(0 1 0))
+           (if (not yank)
+               (progn
+                 (switch-to-buffer article-buffer)
+                 (pop-to-buffer gnus-post-news-buffer))
+             (switch-to-buffer gnus-post-news-buffer)))
+         (gnus-overload-functions)
+         (make-local-variable 'gnus-article-reply)
+         (make-local-variable 'gnus-article-check-size)
+         (setq gnus-article-reply sumart)
+         ;; Handle `gnus-auto-mail-to-author'.
+         ;; Suggested by Daniel Quinlan <quinlan@best.com>.
+         (let ((to (if (eq gnus-auto-mail-to-author 'ask)
+                       (and (y-or-n-p "Also send mail to author? ") from)
+                     (and gnus-auto-mail-to-author from))))
+           (if to
+               (progn
+                 (if (mail-fetch-field "To")
+                     (progn
+                       (beginning-of-line)
+                       (insert "Cc: " to "\n"))
+                   (mail-position-on-field "To")
+                   (insert to)))))
+         ;; Handle author copy using BCC field.
+         (if (and gnus-mail-self-blind
+                  (not (mail-fetch-field "bcc")))
              (progn
-               (if (mail-fetch-field "To")
-                   (progn
-                     (beginning-of-line)
-                     (insert "Cc: " to "\n"))
-                 (mail-position-on-field "To")
-                 (insert to)))))
-       ;; Handle author copy using BCC field.
-       (if (and gnus-mail-self-blind
-                (not (mail-fetch-field "bcc")))
-           (progn
-             (mail-position-on-field "Bcc")
-             (insert (if (stringp gnus-mail-self-blind)
-                         gnus-mail-self-blind
-                       (user-login-name)))))
-       ;; Handle author copy using FCC field.
-       (if gnus-author-copy
-           (progn
-             (mail-position-on-field "Fcc")
-             (insert gnus-author-copy)))
-       (goto-char (point-min))
-       (if post 
-           (cond ((not group)
-                  (re-search-forward "^Newsgroup:" nil t)
-                  (end-of-line))
-                 ((not subject)
-                  (re-search-forward "^Subject:" nil t)
-                  (end-of-line))
-                 (t
-                  (search-forward (concat "\n" mail-header-separator "\n"))))
-         (search-forward (concat "\n" mail-header-separator "\n"))
-         (if yank 
-             (save-excursion
-               (run-hooks 'news-reply-header-hook)
-               (mail-yank-original nil)))
-         (if gnus-post-prepare-function
-             (funcall gnus-post-prepare-function group)))))
+               (mail-position-on-field "Bcc")
+               (insert (if (stringp gnus-mail-self-blind)
+                           gnus-mail-self-blind
+                         (user-login-name)))))
+         ;; Handle author copy using FCC field.
+         (if gnus-author-copy
+             (progn
+               (mail-position-on-field "Fcc")
+               (insert gnus-author-copy)))
+         (goto-char (point-min))
+         (if post 
+             (cond ((not group)
+                    (re-search-forward "^Newsgroup:" nil t)
+                    (end-of-line))
+                   ((not subject)
+                    (re-search-forward "^Subject:" nil t)
+                    (end-of-line))
+                   (t
+                    (re-search-forward 
+                     (concat "^" (regexp-quote mail-header-separator) "$"))
+                    (forward-line 1)))
+           (re-search-forward 
+            (concat "^" (regexp-quote mail-header-separator) "$"))
+           (forward-line 1)
+           (if yank 
+               (save-excursion
+                 (run-hooks 'news-reply-header-hook)
+                 (mail-yank-original nil)))
+           (if gnus-post-prepare-function
+               (funcall gnus-post-prepare-function group))))))
   (setq gnus-article-check-size (cons (buffer-size) (gnus-article-checksum)))
   (message "")
   t)
@@ -10423,8 +11059,8 @@ will attempt to use the foreign server to post the article."
           (point-min)
           (progn
             (goto-char (point-min))
-            (search-forward (concat "\n" mail-header-separator "\n"))
-            (point)))
+            (re-search-forward 
+             (concat "^" (regexp-quote mail-header-separator) "$"))))
 
          ;; Correct newsgroups field: change sequence of spaces to comma and 
          ;; eliminate spaces around commas.  Eliminate imbedded line breaks.
@@ -10494,7 +11130,9 @@ will attempt to use the foreign server to post the article."
                          (progn
                            ;; Insert "courtesy" mail message.
                            (goto-char 1)
-                           (re-search-forward mail-header-separator)
+                           (re-search-forward
+                            (concat "^" (regexp-quote
+                                         mail-header-separator) "$"))
                            (forward-line 1)
                            (insert gnus-mail-courtesy-message)
                            (funcall gnus-mail-send-method)
@@ -10507,7 +11145,9 @@ will attempt to use the foreign server to post the article."
                      
                      (goto-char 1)
                      (narrow-to-region
-                      1 (re-search-forward mail-header-separator))
+                      1 (re-search-forward 
+                         (concat "^" (regexp-quote 
+                                      mail-header-separator) "$")))
                      (goto-char 1)
                      (delete-matching-lines "BCC:.*")))
                (ding)
@@ -10552,7 +11192,10 @@ will attempt to use the foreign server to post the article."
    (save-excursion
      (save-restriction
        (goto-char (point-min))
-       (narrow-to-region (point) (search-forward mail-header-separator))
+       (narrow-to-region 
+       (point) 
+       (re-search-forward 
+        (concat "^" (regexp-quote mail-header-separator) "$")))
        (if (string-match "^cmsg " (mail-fetch-field "subject"))
           (gnus-y-or-n-p
            "The control code \"cmsg \" is in the subject. Really post? ")
@@ -10568,7 +11211,10 @@ will attempt to use the foreign server to post the article."
      (save-excursion
        (save-restriction
         (goto-char (point-min))
-        (narrow-to-region (point) (search-forward mail-header-separator))
+        (narrow-to-region
+         (point) 
+         (re-search-forward 
+          (concat "^" (regexp-quote mail-header-separator) "$")))
         (goto-char (point-min))
         (while (and (not found) (re-search-forward "^[^ \t:]+: " nil t))
           (save-excursion
@@ -10586,16 +11232,23 @@ will attempt to use the foreign server to post the article."
    (save-excursion
      (save-restriction
        (goto-char (point-min))
-       (narrow-to-region (point) (search-forward mail-header-separator))
+       (narrow-to-region
+       (point) 
+       (re-search-forward 
+        (concat "^" (regexp-quote mail-header-separator) "$")))
        (if (re-search-backward "^Sendsys:\\|^Version:" nil t)
           (gnus-yes-or-no-p
            (format "The article contains a %s command. Really post? "
                    (buffer-substring (match-beginning 0) (match-end 0))))
         t)))
+   ;; Check the From header.
    (save-excursion
      (save-restriction
        (goto-char (point-min))
-       (narrow-to-region (point) (search-forward mail-header-separator))
+       (narrow-to-region
+       (point)
+       (re-search-forward
+        (concat "^" (regexp-quote mail-header-separator) "$")))
        (let* ((case-fold-search t)
              (from (mail-fetch-field "from")))
         (if (and from
@@ -10605,6 +11258,24 @@ will attempt to use the foreign server to post the article."
              (format "The domain looks strange: \"%s\". Really post? "
                      from))
           t))))
+   ;; Check for long lines.
+   (save-excursion
+     (save-restriction
+       (goto-char (point-min))
+       (narrow-to-region
+       (point)
+       (re-search-forward
+        (concat "^" (regexp-quote mail-header-separator) "$")))
+       (while 
+          (and
+           (progn
+             (end-of-line)
+             (< (current-column) 80))
+           (zerop (forward-line 1))))
+       (or (eobp)
+          (gnus-yes-or-no-p
+           (format
+            "You have lines longer than 79 characters.  Really post? ")))))
    ;; Use the (size . checksum) variable to see whether the
    ;; article is empty or has only quoted text.
    (if (and (= (buffer-size) (car gnus-article-check-size))
@@ -10684,9 +11355,9 @@ will attempt to use the foreign server to post the article."
       (narrow-to-region 
        (point-min) 
        (save-excursion
-        (search-forward (concat "\n" mail-header-separator "\n")) 
-        (forward-line -1) 
-        (point)))
+        (re-search-forward 
+         (concat "^" (regexp-quote mail-header-separator) "$"))
+        (match-beginning 0)))
       (gnus-inews-insert-headers)
       (run-hooks gnus-inews-article-header-hook)
       (widen))
@@ -10697,7 +11368,8 @@ will attempt to use the foreign server to post the article."
       (insert-buffer-substring artbuf)
       ;; Remove the header separator.
       (goto-char (point-min))
-      (search-forward (concat "\n" mail-header-separator "\n"))
+      (re-search-forward
+       (concat "^" (regexp-quote mail-header-separator) "$"))
       (replace-match "\n\n")
       ;; This hook may insert a signature.
       (run-hooks 'gnus-prepare-article-hook)
@@ -10767,6 +11439,7 @@ Headers in `gnus-required-headers' will be generated."
              (if (setq subject (mail-fetch-field "subject"))
                  (progn
                    (and gnus-summary-gather-subject-limit
+                        (numberp gnus-summary-gather-subject-limit)
                         (> (length subject) gnus-summary-gather-subject-limit)
                         (setq subject
                               (substring subject 0
@@ -10912,17 +11585,18 @@ a program specified by the rest of the value."
 (defun gnus-inews-user-name ()
   "Return user's network address as \"NAME@DOMAIN (FULL-NAME)\"."
   (let ((full-name (gnus-inews-full-name)))
-    (concat (if (or gnus-user-login-name gnus-use-generic-from
-                   gnus-local-domain (getenv "DOMAINNAME"))
-               (concat (gnus-inews-login-name) "@"
-                       (gnus-inews-domain-name gnus-use-generic-from))
-             user-mail-address)
-           ;; User's full name.
-           (cond ((string-equal full-name "") "")
-                 ((string-equal full-name "&") ;Unix hack.
-                  (concat " (" (user-login-name) ")"))
-                 (t
-                  (concat " (" full-name ")"))))))
+    (or gnus-user-from-line
+       (concat (if (or gnus-user-login-name gnus-use-generic-from
+                       gnus-local-domain (getenv "DOMAINNAME"))
+                   (concat (gnus-inews-login-name) "@"
+                           (gnus-inews-domain-name gnus-use-generic-from))
+                 user-mail-address)
+               ;; User's full name.
+               (cond ((string-equal full-name "") "")
+                     ((string-equal full-name "&") ;Unix hack.
+                      (concat " (" (user-login-name) ")"))
+                     (t
+                      (concat " (" full-name ")")))))))
 
 (defun gnus-inews-login-name ()
   "Return login name."
@@ -11048,11 +11722,13 @@ Customize the variable gnus-mail-reply-method to use another mailer."
   (interactive "P")
   ;; Bug fix by jbw@bigbird.bu.edu (Joe Wells)
   ;; Stripping headers should be specified with mail-yank-ignored-headers.
+  (gnus-set-global-variables)
   (gnus-summary-select-article t)
   (setq gnus-winconf-post-news (current-window-configuration))
   (let ((gnus-newsgroup-name gnus-newsgroup-name))
     (bury-buffer gnus-article-buffer)
-    (funcall gnus-mail-reply-method yank)))
+    (funcall gnus-mail-reply-method yank))
+  (gnus-article-hide-headers-if-wanted))
 
 (defun gnus-summary-reply-with-original ()
   "Reply mail to news author with original article.
@@ -11068,7 +11744,8 @@ Customize the variable gnus-mail-forward-method to use another mailer."
   (setq gnus-winconf-post-news (current-window-configuration))
   (set-buffer gnus-article-buffer)
   (let ((gnus-newsgroup-name gnus-newsgroup-name))
-    (funcall gnus-mail-forward-method)))
+    (funcall gnus-mail-forward-method))
+  (gnus-article-hide-headers-if-wanted))
 
 (defun gnus-summary-mail-other-window ()
   "Compose mail in other window.
@@ -11132,8 +11809,16 @@ mailer."
          (widen))
        (setq news-reply-yank-from from)
        (setq news-reply-yank-message-id message-id)
-       (mail-setup (or to-address follow-to reply-to from sender "") 
+       (mail-setup (or to-address 
+                       (if (and follow-to (not (stringp follow-to))) ""
+                         (or follow-to reply-to from sender "")))
                    subject message-of nil gnus-article-buffer nil)
+       (if (and follow-to (listp follow-to))
+           (progn
+             (goto-char (point-min))
+             (while follow-to
+               (insert (car (car follow-to)) ": " (cdr (car follow-to)) "\n")
+               (setq follow-to (cdr follow-to)))))
        ;; Fold long references line to follow RFC1036.
        (mail-position-on-field "References")
        (let ((begin (- (point) (length "References: ")))
@@ -11146,7 +11831,9 @@ mailer."
          ;; without inserting extra newline.
          (fill-region-as-paragraph begin (1+ (point))))
        (goto-char (point-min))
-       (search-forward (concat "\n" mail-header-separator "\n"))
+       (re-search-forward
+        (concat "^" (regexp-quote mail-header-separator) "$"))
+       (forward-line 1)
        (if yank
            (let ((last (point)))
              (run-hooks 'news-reply-header-hook)
@@ -11190,8 +11877,13 @@ mailer."
   ;; This is almost a carbon copy of rmail-forward in rmail.el.
   (let ((forward-buffer (current-buffer))
        (subject
-        (concat "[" gnus-newsgroup-name "] "
-                (or (gnus-fetch-field "Subject") "")))
+        (concat "[" (if (memq 'mail (assoc (symbol-name 
+                                            (car (gnus-find-method-for-group 
+                                                  gnus-newsgroup-name)))
+                                           gnus-valid-select-methods))
+                        (gnus-fetch-field "From")
+                      gnus-newsgroup-name)
+                "] " (or (gnus-fetch-field "Subject") "")))
        beg)
     ;; If only one window, use it for the mail buffer.
     ;; Otherwise, use another window for the mail buffer
@@ -11220,8 +11912,8 @@ mailer."
 
 (defun gnus-mail-other-window-using-mail ()
   "Compose mail other window using mail."
-  (mail-other-window nil nil nil nil nil nil (get-buffer gnus-article-buffer))
-  (use-local-map (copy-keymap emacs-lisp-mode-map))
+  (mail-other-window nil nil nil nil nil (get-buffer gnus-article-buffer))
+  (use-local-map (copy-keymap (current-local-map)))
   (local-set-key "\C-c\C-c" 'gnus-mail-send-and-exit))
 
 \f
@@ -11412,6 +12104,11 @@ mailer."
 ;; to update any tables (nov buffers, etc) that it maintains after
 ;; replacing the article.
 ;;
+;; `choke-request-create-group GROUP &optional SERVER'
+;; Create GROUP on SERVER.  This might be a new, empty group, or it
+;; might be a group that already exists, but hasn't been registered
+;; yet. 
+;;
 ;; All these functions must return nil if they couldn't service the
 ;; request. If the optional arguments are not supplied, some "current"
 ;; or "default" values should be used. In short, one should emulate an
@@ -11474,10 +12171,14 @@ If CONFIRM is non-nil, the user will be asked for an NNTP server."
        ;; gnus-open-server-hook might have opened it
        (gnus-server-opened gnus-select-method)  
        (gnus-open-server gnus-select-method)
-       (error "%s" (gnus-nntp-message 
-                   (format "Cannot open NNTP server on %s" 
-                           where))))
-      gnus-select-method)))
+       (gnus-y-or-n-p
+       (format
+        "%s server on %s can't be opened. Continue? "
+        (car gnus-select-method) (nth 1 gnus-select-method)))
+       (progn
+        (message "Couldn't open server on %s" (nth 1 gnus-select-method))
+        (ding)
+        nil)))))
 
 (defun gnus-check-news-server (method)
   "If the news server is down, start it up again."
@@ -11541,6 +12242,13 @@ is returned insted of the status string."
     (funcall (gnus-get-function method 'request-group) 
             (gnus-group-real-name group) (nth 1 method) dont-check)))
 
+(defun gnus-list-active-group (group)
+  (let ((method (gnus-find-method-for-group group))
+       (func 'list-active-group))
+    (and (gnus-check-backend-function func group)
+        (funcall (gnus-get-function method func) 
+                 (gnus-group-real-name group) (nth 1 method)))))
+
 (defun gnus-request-group-description (group)
   (let ((method (gnus-find-method-for-group group))
        (func 'request-group-description))
@@ -11558,7 +12266,7 @@ is returned insted of the status string."
     (funcall (gnus-get-function method 'retrieve-headers) 
             articles (gnus-group-real-name group) (nth 1 method))))
 
-(defun gnus-request-article (article group buffer)
+(defun gnus-request-article (article group &optional buffer)
   (let ((method (gnus-find-method-for-group group)))
     (funcall (gnus-get-function method 'request-article) 
             article (gnus-group-real-name group) (nth 1 method) buffer)))
@@ -11571,7 +12279,8 @@ is returned insted of the status string."
 ;; Fix by Sudish Joseph <joseph@cis.ohio-state.edu>.
 (defun gnus-request-post-buffer (post group subject header artbuf
                                      info follow-to respect-poster)
-   (let* ((info (or info (nth 2 (gnus-gethash group gnus-newsrc-hashtb))))
+   (let* ((info (or info (and group (nth 2 (gnus-gethash 
+                                           group gnus-newsrc-hashtb)))))
          (method
           (if (and gnus-post-method
                    ;; Fix by Sudish Joseph <joseph@cis.ohio-state.edu>.
@@ -11620,6 +12329,12 @@ is returned insted of the status string."
     (funcall (intern (format "%s-request-replace-article" func))
             article (gnus-group-real-name group) buffer)))
 
+(defun gnus-request-create-group (group)
+  (let ((method (gnus-find-method-for-group group)))
+    (funcall (gnus-get-function method 'request-create-group) 
+            (gnus-group-real-name group) (nth 1 method))))
+
+
 (defun gnus-find-method-for-group (group)
   (or gnus-override-method
       (and (not group)
@@ -11690,7 +12405,9 @@ If LEVEL is non-nil, the news will be set up at level LEVEL."
     ;; If `gnus-read-active-file' is nil, then we just create an empty
     ;; hash table. The partial filling out of the hash table will be
     ;; done in `gnus-get-unread-articles'.
-    (if (and gnus-read-active-file (not level))
+    (if (and gnus-read-active-file 
+            (not level)
+            (gnus-server-opened gnus-select-method))
        (gnus-read-active-file)
       (setq gnus-active-hashtb (make-vector 4095 0)))
 
@@ -11699,10 +12416,12 @@ If LEVEL is non-nil, the news will be set up at level LEVEL."
     ;; Find the number of unread articles in each non-dead group.
     (gnus-get-unread-articles (or level 6))
     ;; Find new newsgroups and treat them.
-    (if (and init gnus-check-new-newsgroups gnus-read-active-file (not level))
+    (if (and init gnus-check-new-newsgroups gnus-read-active-file (not level)
+            (gnus-server-opened gnus-select-method))
        (gnus-find-new-newsgroups))
     (if (and init gnus-check-bogus-newsgroups 
-            gnus-read-active-file (not level))
+            gnus-read-active-file (not level)
+            (gnus-server-opened gnus-select-method))
        (gnus-check-bogus-newsgroups))))
 
 (defun gnus-find-new-newsgroups ()
@@ -11727,21 +12446,20 @@ The `-n' option line from .newsrc is respected."
             (if (or (gnus-gethash group gnus-killed-hashtb)
                     (gnus-gethash group gnus-newsrc-hashtb))
                 ()
-              (if (and gnus-newsrc-options-n-yes
-                       (string-match gnus-newsrc-options-n-yes group))
-                  (progn
-                    (setq groups (1+ groups))
-                    (gnus-sethash group group gnus-killed-hashtb)
-                    (funcall gnus-subscribe-options-newsgroup-method group))
-                (if (or (null gnus-newsrc-options-n-no)
-                        (not (string-match gnus-newsrc-options-n-no group)))
-                    ;; Add this group.
-                    (progn
-                      (setq groups (1+ groups))
-                      (gnus-sethash group group gnus-killed-hashtb)
-                      (if gnus-subscribe-hierarchical-interactive
-                          (setq new-newsgroups (cons group new-newsgroups))
-                        (funcall gnus-subscribe-newsgroup-method group)))))))
+              (let ((do-sub (gnus-matches-options-n group)))
+                (cond ((eq do-sub 'subscribe)
+                       (setq groups (1+ groups))
+                       (gnus-sethash group group gnus-killed-hashtb)
+                       (funcall 
+                        gnus-subscribe-options-newsgroup-method group))
+                      ((eq do-sub 'ignore)
+                       nil)
+                      (t
+                       (setq groups (1+ groups))
+                       (gnus-sethash group group gnus-killed-hashtb)
+                       (if gnus-subscribe-hierarchical-interactive
+                           (setq new-newsgroups (cons group new-newsgroups))
+                         (funcall gnus-subscribe-newsgroup-method group)))))))
           gnus-active-hashtb)
          (if new-newsgroups 
              (gnus-subscribe-hierarchical-interactive new-newsgroups))
@@ -11750,6 +12468,30 @@ The `-n' option line from .newsrc is respected."
              (message "%d new newsgroup%s arrived." 
                       groups (if (> groups 1) "s have" " has")))))))
 
+(defun gnus-matches-options-n (group)
+  ;; Returns `subscribe' if the group is to be uncoditionally
+  ;; subscribed, `ignore' if it is to be ignored, and nil if there is
+  ;; no match for the group.
+
+  ;; First we check the two user variables.
+  (cond
+   ((and gnus-options-not-subscribe
+        (string-match gnus-options-not-subscribe group))
+    'subscribe)
+   ((and gnus-options-subscribe
+        (string-match gnus-options-subscribe group))
+    'ignore)
+   ;; Then we go through the list that was retrieved from the .newsrc
+   ;; file.  This list has elements on the form 
+   ;; `(REGEXP . {ignore,subscribe})'. The first match found (the list
+   ;; is in the reverse order of the options line) is returned.
+   (t
+    (let ((regs gnus-newsrc-options-n))
+      (while (and regs
+                 (not (string-match (car (car gnus-newsrc-options-n)) group)))
+       (setq regs (cdr regs)))
+      (and regs (cdr (car regs)))))))
+
 (defun gnus-ask-server-for-new-groups ()
   (let* ((date (or gnus-newsrc-last-checked-date (current-time-string)))
         (methods (cons gnus-select-method 
@@ -11783,19 +12525,20 @@ The `-n' option line from .newsrc is respected."
           ;; The group is already known.
           ()
         (gnus-sethash group (symbol-value group-sym) gnus-active-hashtb)
-        (if (and gnus-newsrc-options-n-yes
-                 (string-match gnus-newsrc-options-n-yes group))
-            (progn
-              (setq groups (1+ groups))
-              (funcall gnus-subscribe-options-newsgroup-method group))
-          (if (or (null gnus-newsrc-options-n-no)
-                  (not (string-match gnus-newsrc-options-n-no group)))
-              ;; Add this group.
-              (progn
-                (setq groups (1+ groups))
-                (if gnus-subscribe-hierarchical-interactive
-                    (setq new-newsgroups (cons group new-newsgroups))
-                  (funcall gnus-subscribe-newsgroup-method group)))))))
+        (let ((do-sub (gnus-matches-options-n group)))
+          (cond ((eq do-sub 'subscribe)
+                 (setq groups (1+ groups))
+                 (gnus-sethash group group gnus-killed-hashtb)
+                 (funcall 
+                  gnus-subscribe-options-newsgroup-method group))
+                ((eq do-sub 'ignore)
+                 nil)
+                (t
+                 (setq groups (1+ groups))
+                 (gnus-sethash group group gnus-killed-hashtb)
+                 (if gnus-subscribe-hierarchical-interactive
+                     (setq new-newsgroups (cons group new-newsgroups))
+                   (funcall gnus-subscribe-newsgroup-method group)))))))
      hashtb)
     (if new-newsgroups 
        (gnus-subscribe-hierarchical-interactive new-newsgroups))
@@ -11822,12 +12565,15 @@ The `-n' option line from .newsrc is respected."
        (mapatoms
         (lambda (sym)
           (setq group (symbol-name sym))
-          (if (and gnus-newsrc-options-n-yes
-                   (string-match gnus-newsrc-options-n-yes group))
-              (funcall gnus-subscribe-options-newsgroup-method group)
-            (and (or (null gnus-newsrc-options-n-no)
-                     (not (string-match gnus-newsrc-options-n-no group)))
-                 (setq gnus-killed-list (cons group gnus-killed-list)))))
+          (let ((do-sub (gnus-matches-options-n group)))
+            (cond ((eq do-sub 'subscribe)
+                   (gnus-sethash group group gnus-killed-hashtb)
+                   (funcall 
+                    gnus-subscribe-options-newsgroup-method group))
+                  ((eq do-sub 'ignore)
+                   nil)
+                  (t
+                   (setq gnus-killed-list (cons group gnus-killed-list))))))
         gnus-active-hashtb)
        (while groups
          (if (gnus-gethash (car groups) gnus-active-hashtb)
@@ -11899,9 +12645,10 @@ The `-n' option line from .newsrc is respected."
     ;; go, and change the subscription level. If it is to be killed,
     ;; we enter it into the killed or zombie list.
     (cond ((>= level 8)
-          (if (= level 8)
-              (setq gnus-zombie-list (cons group gnus-zombie-list))
-            (setq gnus-killed-list (cons group gnus-killed-list))))
+          (and (string= group (gnus-group-real-name group))
+               (if (= level 8)
+                   (setq gnus-zombie-list (cons group gnus-zombie-list))
+                 (setq gnus-killed-list (cons group gnus-killed-list)))))
          (t
           ;; If the list is to be entered into the newsrc assoc, and
           ;; it was killed, we have to create an entry in the newsrc
@@ -12034,8 +12781,9 @@ newsgroup."
     ;; other groups. 
     ;; !!! If one virtual group contains another virtual group, even
     ;; doing it this way might cause problems.
-    (while virtuals
-      (gnus-activate-newsgroup (car (car virtuals)))
+   (while virtuals
+      (and (setq active (gnus-activate-newsgroup (car (car virtuals))))
+          (gnus-get-unread-articles-in-group (car virtuals) active))
       (setq virtuals (cdr virtuals)))
 
     (message "Checking new news... done")))
@@ -12083,7 +12831,8 @@ newsgroup."
          ((not (listp (cdr range)))
           ;; Fix a single (num . num) range according to the
           ;; active hash table.
-          (and (< (cdr range) (car active)) (setcdr range (car active)))
+          ;; Fix by Carsten Bormann <cabo@Informatik.Uni-Bremen.DE>.
+          (and (< (cdr range) (car active)) (setcdr range (1- (car active))))
           (and (> (cdr range) (cdr active)) (setcdr range (cdr active)))
           ;; Compute number of unread articles.
           (setq num (max 0 (- (cdr active) 
@@ -12110,7 +12859,7 @@ newsgroup."
           ;; Adjust the first element to be the same as the lower limit. 
           (if (and (not (atom (car range))) 
                    (< (cdr (car range)) (car active)))
-              (setcdr (car range) (car active)))
+              (setcdr (car range) (1- (car active))))
           ;; Then we want to peel off any elements that are higher
           ;; than the upper active limit.  
           (let ((srange range))
@@ -12218,7 +12967,7 @@ Returns whether the updating was successful."
          (setq read (cons (cons prev (cdr active)) read)))
       ;; Enter this list into the group info.
       (setcar (cdr (cdr info)) 
-             (if (> (length read) 1) (nreverse read) (car read)))
+             (if (> (length read) 1) (nreverse read) read))
       ;; Enter the list of ticked articles.
       (gnus-set-marked-articles 
        info ticked
@@ -12251,6 +13000,7 @@ Returns whether the updating was successful."
                               (concat " from " where) "")
                           (car (car methods)))))
        (message mesg)
+       (gnus-check-news-server (car methods))
        (if (gnus-request-list (car methods)) ; Get active 
            (save-excursion
              (set-buffer nntp-server-buffer)
@@ -12438,6 +13188,7 @@ If FORCE is non-nil, the .newsrc file is read."
        (while marked
          (or (eq 'score (car (car marked)))
              (eq 'bookmark (car (car marked)))
+             (eq 'killed (car (car marked)))
              (setcdr (car marked) (gnus-uncompress-range (cdr (car marked)))))
          (setq marked (cdr marked))))
       (setq newsrc (cdr newsrc)))))
@@ -12452,6 +13203,7 @@ If FORCE is non-nil, the .newsrc file is read."
        (while marked
          (or (eq 'score (car (car marked)))
              (eq 'bookmark (car (car marked)))
+             (eq 'killed (car (car marked)))
              (setcdr (car marked) 
                      (gnus-compress-sequence (sort (cdr (car marked)) '<) t)))
          (setq marked (cdr marked))))
@@ -12471,8 +13223,7 @@ If FORCE is non-nil, the .newsrc file is read."
   ;; gnus-killed-list) because the quick startup file may contain bogus
   ;; values.
   (setq gnus-newsrc-options nil)
-  (setq gnus-newsrc-options-n-yes nil)
-  (setq gnus-newsrc-options-n-no nil)
+  (setq gnus-newsrc-options-n nil)
   (gnus-parse-options-lines)
   (gnus-parse-newsrc-body))
 
@@ -12522,15 +13273,8 @@ If FORCE is non-nil, the .newsrc file is read."
                               (substring gnus-newsrc-options
                                          (match-beginning 1)
                                          (match-end 1))))))
-    (let ((yes-and-no (and result (gnus-parse-n-options result))))
-      (and (or gnus-options-subscribe (car yes-and-no))
-          (setq gnus-newsrc-options-n-yes 
-                (concat (or gnus-options-subscribe "") 
-                        (or (car yes-and-no) ""))))
-      (and (or gnus-options-not-subscribe (cdr yes-and-no))
-          (setq gnus-newsrc-options-n-no 
-                (concat (or gnus-options-not-subscribe "") 
-                        (or (cdr yes-and-no) "")))))
+
+    (and result (gnus-parse-n-options result))
     nil))
 
 (defun gnus-parse-newsrc-body ()
@@ -12637,10 +13381,7 @@ If FORCE is non-nil, the .newsrc file is read."
 
 (defun gnus-parse-n-options (options)
   "Parse -n NEWSGROUPS options and return a cons of YES and NO regexps."
-  (let ((yes nil)
-       (no nil)
-       (yes-or-no nil)                 ;`!' or not.
-       (newsgroup nil))
+  (let (yes no yes-or-no out newsgroup)
     ;; Parse each newsgroup description such as "comp.all".  Commas
     ;; and white spaces can be a newsgroup separator.
     (while
@@ -12660,32 +13401,10 @@ If FORCE is non-nil, the .newsrc file is read."
                      ".+"
                      (substring newsgroup (match-beginning 2)))))
       ;; It is yes or no.
-      (cond ((string-equal yes-or-no "!")
-            (setq no (cons newsgroup no)))
-           ((string-equal newsgroup ".+")) ;Ignore `all'.
-           (t
-            (setq yes (cons newsgroup yes)))))
-    ;; Make a cons of regexps from parsing result.
-    ;; We have to append \(\.\|$\) to prevent matching substring of
-    ;; newsgroup.  For example, "jp.net" should not match with
-    ;; "jp.network".
-    ;; Fixes for large regexp problems are from yonezu@nak.math.keio.ac.jp.
-    (cons (if yes
-             (concat "^\\("
-                     (apply (function concat)
-                            (mapcar
-                             (lambda (newsgroup)
-                               (concat newsgroup "\\|"))
-                             (cdr yes)))
-                     (car yes) "\\)\\(\\.\\|$\\)"))
-         (if no
-             (concat "^\\("
-                     (apply (function concat)
-                            (mapcar
-                             (lambda (newsgroup)
-                               (concat newsgroup "\\|"))
-                             (cdr no)))
-                     (car no) "\\)\\(\\.\\|$\\)")))))
+      (setq out (cons (cons (if (string= yes-or-no "!") 'ignore 'subscribe)
+                           (concat "^" newsgroup "$")) 
+                     out)))
+    (setq gnus-newsrc-options-n out)))
 
 (defun gnus-save-newsrc-file ()
   "Save .newsrc file."
index 9422f3a..7a63080 100644 (file)
@@ -26,6 +26,8 @@
 ;; For an overview of what the interface functions do, please see the
 ;; Gnus sources.  
 
+;; Various enhancements by byer@mv.us.adobe.com (Scott Byer).
+
 ;;; Code:
 
 (require 'nnheader)
 (defconst nnfolder-version "nnfolder 0.1"
   "nnfolder version.")
 
+(defconst nnfolder-article-marker "X-Gnus-Article-Number: "
+  "String used to demarcate what the article number for a message is.")
+
 (defvar nnfolder-current-group nil)
 (defvar nnfolder-current-buffer nil)
 (defvar nnfolder-status-string "")
 (defvar nnfolder-group-alist nil)
 (defvar nnfolder-buffer-alist nil)
 
+(defmacro nnfolder-article-string (article)
+  (` (concat "\n" nnfolder-article-marker (int-to-string (, article)) "")))
+
 ;;; Interface functions
 
 (defun nnfolder-retrieve-headers (sequence &optional newsgroup server)
@@ -63,6 +71,7 @@
     (erase-buffer)
     (let ((file nil)
          (number (length sequence))
+         (delim-string (concat "^" rmail-unix-mail-delimiter))
          beg article art-string start stop)
       (nnfolder-possibly-change-group newsgroup)
       (while sequence
        (setq art-string (nnfolder-article-string article))
        (set-buffer nnfolder-current-buffer)
        (if (or (search-forward art-string nil t)
-               (progn (goto-char 1)
-                      (search-forward art-string nil t)))
+               ;; Don't search the whole file twice!  Also, articles
+               ;; probably have some locality by number, so searching
+               ;; backwards will be faster.  Especially if we're at the
+               ;; beginning of the buffer :-). -SLB
+               (search-backward art-string nil t))
            (progn
-             (setq start 
-                   (save-excursion
-                     (re-search-backward 
-                      (concat "^" rmail-unix-mail-delimiter) nil t)
-                     (point)))
+             (setq start (or (re-search-backward delim-string nil t)
+                             (point)))
              (search-forward "\n\n" nil t)
              (setq stop (1- (point)))
              (set-buffer nntp-server-buffer)
@@ -89,6 +98,7 @@
        (setq sequence (cdr sequence)))
 
       ;; Fold continuation lines.
+      (set-buffer nntp-server-buffer)
       (goto-char 1)
       (while (re-search-forward "\\(\r?\n[ \t]+\\)+" nil t)
        (replace-match " " t t))
 (defun nnfolder-close-server (&optional server)
   t)
 
+(defun nnfolder-request-close ()
+  (let ((alist nnfolder-buffer-alist))
+    (while alist
+      (nnfolder-close-group (car (car alist)))
+      (setq alist (cdr alist))))
+  (setq nnfolder-buffer-alist nil
+       nnfolder-group-alist nil))
+
 (defun nnfolder-server-opened (&optional server)
   (and nntp-server-buffer
        (buffer-name nntp-server-buffer)))
               t
             (nnfolder-get-new-mail)
             (let ((active (assoc group nnfolder-group-alist)))
+              ;; I've been getting stray 211 lines in my nnfolder active
+              ;; file.  So, let's make sure that doesn't happen. -SLB
+              (set-buffer nntp-server-buffer)
               (insert (format "211 %d %d %d %s\n" 
                               (1+ (- (cdr (car (cdr active)))
                                      (car (car (cdr active)))))
             t)))))
 
 (defun nnfolder-close-group (group &optional server)
+  (nnfolder-possibly-change-group group)
+  (save-excursion
+    (set-buffer nnfolder-current-buffer)
+    (or (buffer-modified-p)
+       (kill-buffer (current-buffer))))
+  (setq nnfolder-buffer-alist (delq (assoc group nnfolder-buffer-alist)
+                                   nnfolder-buffer-alist))
+  (setq nnfolder-current-group nil
+       nnfolder-current-buffer nil)
   t)
 
 (defun nnfolder-request-list (&optional server)
   (if server (nnfolder-get-new-mail))
-  (or (nnmail-find-file nnfolder-active-file)
+  (or nnfolder-group-alist
+      (nnmail-find-file nnfolder-active-file)
       (progn
        (setq nnfolder-group-alist (nnmail-get-active))
        (nnmail-save-active nnfolder-group-alist nnfolder-active-file)
        (goto-char (point-min))
        (while (not (search-forward
                     (nnfolder-article-string (car active)) nil t))
-         (setcar (car active) (1+ (car active)))
+         (setcar active (1+ (car active)))
          (goto-char (point-min))))
       (nnmail-save-active nnfolder-group-alist nnfolder-active-file)
       rest)))
                                            nnfolder-buffer-alist))))))
   (setq nnfolder-current-group group))
 
-(defun nnfolder-article-string (article)
-  (concat "\nX-Gnus-Article-Number: " (int-to-string article) " "))
-
 (defun nnfolder-save-mail (&optional group)
   "Called narrowed to an article."
   (let* ((nnmail-split-methods 
          (goto-char (point-max))
          (insert-buffer-substring obuf beg end)))
       (goto-char (point-min))
-      (search-forward "\nX-Gnus-Article-Number: ")
+      (search-forward (concat "\n" nnfolder-article-marker))
       (delete-region (progn (beginning-of-line) (point))
                     (progn (forward-line 1) (point))))))
 
     (if (search-forward "\n\n" nil t)
        (progn
          (forward-char -1)
-         (insert (format "X-Gnus-Article-Number: %d   %s\n" 
+         (insert (format (concat nnfolder-article-marker "%d   %s\n")
                          (cdr group-art) (current-time-string)))))))
 
 (defun nnfolder-active-number (group)
+  (if (not nnfolder-group-alist)
+      (save-excursion
+       (nnfolder-request-list)
+       (setq nnfolder-group-alist (nnmail-get-active))))
   (let ((active (car (cdr (assoc group nnfolder-group-alist)))))
     (setcdr active (1+ (cdr active)))
     (cdr active)))
 
+
+;; This method has a problem if you've accidentally let the active list get
+;; out of sync with the files.  This could happen, say, if you've
+;; accidentally gotten new mail with something other than (ding) (but why
+;; would _that_ ever happen? :-).  In that case, we will be in the middle of
+;; processing the file, ready to add new X-Gnus article number markers, and
+;; we'll run accross a message with no ID yet - the active list _may_not_ be
+;; ready for us yet.
+
+;; To handle this, I'm modifying this routine to maintain the maximum ID seen
+;; so far, and when we hit a message with no ID, we will _manually_ scan the
+;; rest of the message looking for any more, possibly higher IDs.  We'll
+;; assume the maximum that we find is the highest active.  Note that this
+;; shouldn't cost us much extra time at all, but will be a lot less
+;; vulnerable to glitches between the mbox and the active file.
+
 (defun nnfolder-read-folder (file)
-  (nnfolder-request-list)
-  (setq nnfolder-group-alist (nnmail-get-active))
   (save-excursion
-    (set-buffer
-     (setq nnfolder-current-buffer 
-          (find-file-noselect file)))
+    (if (not nnfolder-group-alist)
+       (progn
+         (nnfolder-request-list)
+         (setq nnfolder-group-alist (nnmail-get-active))))
+    ;; We should be paranoid here and make sure the group is in the alist,
+    ;; and add it if it isn't.
+    ;;(if (not (assoc nnfoler-current-group nnfolder-group-alist)
+    (set-buffer (setq nnfolder-current-buffer (find-file-noselect file)))
     (buffer-disable-undo (current-buffer))
     (let ((delim (concat "^" rmail-unix-mail-delimiter))
-         start end)
+         (marker (concat "\n" nnfolder-article-marker))
+         (number "[0-9]+")
+         (active (car (cdr (assoc nnfolder-current-group 
+                                  nnfolder-group-alist))))
+         activenumber start end)
       (goto-char (point-min))
-      (while (re-search-forward delim nil t)
-       (setq start (match-beginning 0))
-       (if (not (search-forward "\nX-Gnus-Article-Number: " 
-                                (save-excursion 
-                                  (setq end
-                                        (or
-                                         (and
-                                          (re-search-forward delim nil t)
-                                          (match-beginning 0))
-                                         (point-max))))
-                                t))
-           (save-excursion
-             (save-restriction
-               (narrow-to-region start end)
-               (nnmail-insert-lines)
-               (nnfolder-insert-newsgroup-line 
-                (cons nil (nnfolder-active-number nnfolder-current-group))))))
-       (goto-char end)))
-    (nnmail-save-active nnfolder-group-alist nnfolder-active-file)
-    (current-buffer)))
+      ;;
+      ;; Anytime the active number is 1 or 0, it is supect.  In that case,
+      ;; search the file manually to find the active number.
+      (setq activenumber (cdr active))
+      (if (< activenumber 2)
+         (progn
+           (while (and (search-forward marker nil t)
+                       (re-search-forward number nil t))
+             (setq activenumber (max activenumber
+                                     (string-to-number (buffer-substring
+                                                        (match-beginning 0)
+                                                        (match-end 0))))))
+           (goto-char (point-min))))
+
+      ;; Keep track of the active number on our own, and insert it back into
+      ;; the active list when we're done. Also, prime the pump to cut down on
+      ;; the number of searches we do.
+      (setq end (or (and (re-search-forward delim nil t)
+                        (match-beginning 0))
+                   (point-max)))
+      (while (not (= end (point-max)))
+       (setq start end)
+       (goto-char end)
+       (end-of-line)
+       (setq end (or (and (re-search-forward delim nil t)
+                          (match-beginning 0))
+                     (point-max)))
+       (goto-char start)
+       (if (not (search-forward marker end t))
+           (progn
+             (narrow-to-region start end)
+             (nnmail-insert-lines)
+             (setq activenumber (1+ activenumber))
+             (nnfolder-insert-newsgroup-line (cons nil activenumber))
+             (widen))))
+
+      ;; Make absolutely sure that the active list reflects reality!
+      (setcdr active activenumber)
+      (nnmail-save-active nnfolder-group-alist nnfolder-active-file)
+      (current-buffer))))
 
 (defun nnfolder-get-new-mail ()
   (let (incoming)
              (setq nnfolder-buffer-alist 
                    (delq (car bufs) nnfolder-buffer-alist))
            (set-buffer (nth 1 (car bufs)))
-           (save-buffer))
+           (and (buffer-modified-p)
+                (save-buffer)))
          (setq bufs (cdr bufs)))))
     ;; (if incoming (delete-file incoming))
     ))
index 34e4175..465ea74 100644 (file)
@@ -151,7 +151,8 @@ messages will be shown to indicate the current status.")
            (goto-char (point-min))
            (narrow-to-region (point-min)
                              (progn (search-forward "\n\n") (point)))
-           (set-text-properties (point-min) (point-max) nil)
+           (let ((buffer-read-only nil))
+             (set-text-properties (point-min) (point-max) nil))
            (setq from (header-from header))
            (setq date (header-date header))
            (and from
@@ -172,11 +173,19 @@ messages will be shown to indicate the current status.")
            (widen))
          (setq news-reply-yank-from from)
          (setq news-reply-yank-message-id message-id)
-         (mail-setup (or follow-to method-address 
-                         (concat (or sender reply-to from "")
-                                 (if to (concat ", " to) "")
-                                 (if cc (concat ", " cc) "")))
+         (mail-setup (if (and follow-to (listp follow-to)) ""
+                       (or method-address 
+                           (concat (or sender reply-to from "")
+                                   (if to (concat ", " to) "")
+                                   (if cc (concat ", " cc) ""))))
                      subject message-of nil article-buffer nil)
+         (if (and follow-to (listp follow-to))
+             (progn
+               (goto-char (point-min))
+               (while follow-to
+                 (insert 
+                  (car (car follow-to)) ": " (cdr (car follow-to)) "\n")
+                 (setq follow-to (cdr follow-to)))))
          ;; Fold long references line to follow RFC1036.
          (mail-position-on-field "References")
          (let ((begin (- (point) (length "References: ")))
@@ -327,7 +336,8 @@ nn*-request-list should have been called before calling this function."
                                                   (match-end 2)))))
                    group-assoc))))
     ;; In addition, add all groups mentioned in `nnmail-split-methods'.
-    (let ((methods nnmail-split-methods))
+    (let ((methods (and (not (symbolp nnmail-split-methods))
+                       nnmail-split-methods)))
       (while methods
        (if (not (assoc (car (car methods)) group-assoc))
            (setq group-assoc
@@ -404,29 +414,36 @@ FUNC will be called with the group name to determine the article number."
       (goto-char (point-min))
       (while (re-search-forward "\\(\r?\n[ \t]+\\)+" nil t)
        (replace-match " " t t))
-      ;; Go throught the split methods to find a match.
-      (while (and methods (or nnmail-crosspost (not group-art)))
-       (goto-char (point-max))
-       (if (or (cdr methods)
-               (not (equal "" (nth 1 (car methods)))))
-           (if (and (condition-case () 
-                        (if (stringp (nth 1 (car methods)))
-                            (re-search-backward
-                             (car (cdr (car methods))) nil t)
-                          ;; Suggested by Brian Edmonds <edmonds@cs.ubc.ca>.
-                          (funcall (nth 1 (car methods)) (car (car methods))))
-                      (error nil))
-                    ;; Don't enter the article into the same group twice.
-                    (not (assoc (car (car methods)) group-art)))
-               (setq group-art
-                     (cons (cons (car (car methods))
-                                 (funcall func (car (car methods)))) 
-                           group-art)))
-         (or group-art
-             (setq group-art 
-                   (list (cons (car (car methods)) 
-                               (funcall func (car (car methods))))))))
-       (setq methods (cdr methods)))
+      (if (and (symbolp nnmail-split-methods)
+              (fboundp nnmail-split-methods))
+         (setq group-art
+               (mapcar
+                (lambda (group) (cons group (funcall func group)))
+                (funcall nnmail-split-methods)))
+       ;; Go throught the split methods to find a match.
+       (while (and methods (or nnmail-crosspost (not group-art)))
+         (goto-char (point-max))
+         (if (or (cdr methods)
+                 (not (equal "" (nth 1 (car methods)))))
+             (if (and (condition-case () 
+                          (if (stringp (nth 1 (car methods)))
+                              (re-search-backward
+                               (car (cdr (car methods))) nil t)
+                            ;; Suggested by Brian Edmonds <edmonds@cs.ubc.ca>.
+                            (funcall (nth 1 (car methods)) 
+                                     (car (car methods))))
+                        (error nil))
+                      ;; Don't enter the article into the same group twice.
+                      (not (assoc (car (car methods)) group-art)))
+                 (setq group-art
+                       (cons (cons (car (car methods))
+                                   (funcall func (car (car methods)))) 
+                             group-art)))
+           (or group-art
+               (setq group-art 
+                     (list (cons (car (car methods)) 
+                                 (funcall func (car (car methods))))))))
+         (setq methods (cdr methods))))
       (kill-buffer (current-buffer))
       group-art)))
 
index 895268e..59f4dc5 100644 (file)
 
 (defun nnmh-request-group (group &optional server dont-check)
   (and nnmh-get-new-mail (or dont-check (nnmh-get-new-mail)))
-  (let ((pathname (nnmail-article-pathname group nnmh-directory))
+  (let ((pathname (nnmh-article-pathname group nnmh-directory))
        dir)
     (if (file-directory-p pathname)
        (progn
   (nnmh-possibly-change-directory group)
   (save-excursion
     (set-buffer buffer)
+    (nnmh-possibly-create-directory group)
     (condition-case ()
        (progn
          (write-region (point-min) (point-max)
 
 (defun nnmh-possibly-change-directory (newsgroup)
   (if newsgroup
-      (let ((pathname (nnmail-article-pathname newsgroup nnmh-directory)))
+      (let ((pathname (nnmh-article-pathname newsgroup nnmh-directory)))
        (if (file-directory-p pathname)
            (setq nnmh-current-directory pathname)
          (error "No such newsgroup: %s" newsgroup)))))
 
-(defun nnmh-create-directories ()
-  (let ((methods nnmail-split-methods)
-       dir dirs)
-    (while methods
-      (setq dir (nnmail-article-pathname (car (car methods)) nnmh-directory))
-      (while (not (file-directory-p dir))
-       (setq dirs (cons dir dirs))
-       (setq dir (file-name-directory (directory-file-name dir))))
-      (while dirs
-       (if (make-directory (directory-file-name (car dirs)))
-           (error "Could not create directory %s" (car dirs)))
-       (message "Creating mail directory %s" (car dirs))
-       (setq dirs (cdr dirs)))
-      (setq methods (cdr methods)))))
-
+(defun nnmh-possibly-create-directory (group)
+  (let (dir dirs)
+    (setq dir (nnmail-article-pathname group nnmh-directory))
+    (while (not (file-directory-p dir))
+      (setq dirs (cons dir dirs))
+      (setq dir (file-name-directory (directory-file-name dir))))
+    (while dirs
+      (if (make-directory (directory-file-name (car dirs)))
+         (error "Could not create directory %s" (car dirs)))
+      (and gnus-verbose-backends 
+          (message "Creating mail directory %s" (car dirs)))
+      (setq dirs (cdr dirs)))))
+            
 (defun nnmh-save-mail ()
   "Called narrowed to an article."
   (let ((group-art (nreverse (nnmail-article-group 'nnmh-active-number)))
     (let ((ga group-art)
          first)
       (while ga
-       (let ((file (concat (nnmail-article-pathname 
+       (nnmh-possibly-create-directory (car (car ga)))
+       (let ((file (concat (nnmh-article-pathname 
                             (car (car ga)) nnmh-directory) 
                            (int-to-string (cdr (car ga))))))
          (if first
     (setcdr active (1+ (cdr active)))
     (let (file)
       (while (file-exists-p
-             (setq file (concat (nnmail-article-pathname 
+             (setq file (concat (nnmh-article-pathname 
                                  group nnmh-directory)
                                 (int-to-string (cdr active)))))
        (setcdr active (1+ (cdr active)))))
     (cdr active)))
 
+(defun nnmh-article-pathname (group mail-dir)
+  "Make pathname for GROUP."
+  (let ((mail-dir (file-name-as-directory (expand-file-name mail-dir))))
+    (if (file-directory-p (concat mail-dir group))
+       (concat mail-dir group "/")
+      (concat mail-dir (nnmail-replace-chars-in-string group ?. ?/) "/"))))
+
 (defun nnmh-get-new-mail ()
   "Read new incoming mail."
   (let (incoming)
             (> (nth 7 (file-attributes nnmail-spool-file)) 0))
        (progn
          (message "nnmh: Reading incoming mail...")
-         (nnmh-create-directories)
          (setq incoming 
                (nnmail-move-inbox nnmail-spool-file
                                   (concat nnmh-directory "Incoming")))
index f591ff3..fbf9ca4 100644 (file)
@@ -176,6 +176,27 @@ all. This may very well take some time.")
 (defun nnml-close-group (group &optional server)
   t)
 
+(defun nnml-request-create-group (group &optional server) 
+  (nnml-request-list)
+  (setq nnml-group-alist (nnmail-get-active))
+  (or (assoc group nnml-group-alist)
+      (let (active)
+       (setq nnml-group-alist (cons (list group (setq active (cons 0 0)))
+                                    nnml-group-alist))
+       (nnml-possibly-create-directory group)
+       (nnml-possibly-change-directory group)
+       (let ((articles (mapcar
+                        (lambda (file)
+                          (int-to-string file))
+                        (directory-files 
+                         nnml-current-directory nil "^[0-9]+$"))))
+         (and articles
+              (progn
+                (setcar active (apply 'min articles))
+                (setcdr active (apply 'max articles)))))
+       (nnmail-save-active nnml-group-alist nnml-active-file)))
+  t)
+
 (defun nnml-request-list (&optional server)
   (if server (nnml-get-new-mail))
   (save-excursion
@@ -204,13 +225,14 @@ all. This may very well take some time.")
            (lambda (name)
              (string-to-int name)))
           (directory-files nnml-current-directory nil "^[0-9]+$" t)))
-        (max-article (max active-articles))
+        (max-article (and active-articles (apply 'max active-articles)))
         article rest mod-time)
     (while articles
       (setq article (concat nnml-current-directory (int-to-string
                                                      (car articles))))
       (if (setq mod-time (nth 5 (file-attributes article)))
          (if (and (or (not nnmail-keep-last-article)
+                      (not max-article)
                       (not (= (car articles) max-article)))
                   (or force
                       (> (nnmail-days-between
@@ -227,7 +249,8 @@ all. This may very well take some time.")
            (setq rest (cons (car articles) rest))))
       (setq articles (cdr articles)))
     (let ((active (nth 1 (assoc newsgroup nnml-group-alist))))
-      (setcar active (min active-articles))
+      (setcar active (or (and active-articles (apply 'min active-articles))
+                        0))
       (nnmail-save-active nnml-group-alist nnml-active-file))
     (nnml-save-nov)
     rest))
@@ -279,6 +302,7 @@ all. This may very well take some time.")
   (nnml-possibly-change-directory group)
   (save-excursion
     (set-buffer buffer)
+    (nnml-possibly-create-directory group)
     (if (not (condition-case ()
                 (progn
                   (write-region (point-min) (point-max)
@@ -332,29 +356,26 @@ all. This may very well take some time.")
            (if (not (eobp)) (delete-region (point) (point-max)))
            t)))))
 
-(defun nnml-possibly-change-directory (newsgroup)
+(defun nnml-possibly-change-directory (newsgroup &optional force)
   (if newsgroup
       (let ((pathname (nnmail-article-pathname newsgroup nnml-directory)))
-       (and (file-directory-p pathname)
+       (and (or force (file-directory-p pathname))
             (setq nnml-current-directory pathname)))
     t))
-            
-(defun nnml-create-directories ()
-  (let ((methods nnmail-split-methods)
-       dir dirs)
-    (while methods
-      (setq dir (nnmail-article-pathname (car (car methods)) nnml-directory))
-      (while (not (file-directory-p dir))
-       (setq dirs (cons dir dirs))
-       (setq dir (file-name-directory (directory-file-name dir))))
-      (while dirs
-       (if (make-directory (directory-file-name (car dirs)))
-           (error "Could not create directory %s" (car dirs)))
-       (and gnus-verbose-backends 
-            (message "Creating mail directory %s" (car dirs)))
-       (setq dirs (cdr dirs)))
-      (setq methods (cdr methods)))))
 
+(defun nnml-possibly-create-directory (group)
+  (let (dir dirs)
+    (setq dir (nnmail-article-pathname group nnml-directory))
+    (while (not (file-directory-p dir))
+      (setq dirs (cons dir dirs))
+      (setq dir (file-name-directory (directory-file-name dir))))
+    (while dirs
+      (if (make-directory (directory-file-name (car dirs)))
+         (error "Could not create directory %s" (car dirs)))
+      (and gnus-verbose-backends 
+          (message "Creating mail directory %s" (car dirs)))
+      (setq dirs (cdr dirs)))))
+            
 (defun nnml-save-mail ()
   "Called narrowed to an article."
   (let ((group-art (nreverse (nnmail-article-group 'nnml-active-number)))
@@ -369,6 +390,7 @@ all. This may very well take some time.")
     (let ((ga group-art)
          first)
       (while ga
+       (nnml-possibly-create-directory (car (car ga)))
        (let ((file (concat (nnmail-article-pathname 
                             (car (car ga)) nnml-directory)
                            (int-to-string (cdr (car ga))))))
@@ -394,6 +416,10 @@ all. This may very well take some time.")
 (defun nnml-active-number (group)
   "Compute the next article number in GROUP."
   (let ((active (car (cdr (assoc group nnml-group-alist)))))
+    (or active
+       (progn
+         (setq active (cons 1 0))
+         (setq nnml-group-alist (cons (list group active) nnml-group-alist))))
     (setcdr active (1+ (cdr active)))
     (let (file)
       (while (file-exists-p
@@ -406,7 +432,6 @@ all. This may very well take some time.")
 (defun nnml-get-new-mail ()
   "Read new incoming mail."
   (let (incoming)
-    (nnml-create-directories)
     (if (and nnml-get-new-mail nnmail-spool-file
             (file-exists-p nnmail-spool-file)
             (> (nth 7 (file-attributes nnmail-spool-file)) 0))
@@ -493,7 +518,11 @@ all. This may very well take some time.")
        (format "\t%s\t%s\t%s\t%s\t%s\t%d\t%s\t%s\t\n"
                (or subject "(none)")
                (or from "(nobody)") (or date "")
-               (or id "") (or references "")
+               (or id (concat "nnml-dummy-id-" 
+                              (mapconcat 
+                               (lambda (time) (int-to-string time))
+                               (current-time) "-")))
+               (or references "")
                (or chars 0) (or lines "0") (or xref ""))))))
 
 (defun nnml-open-nov (group)
@@ -575,11 +604,13 @@ all. This may very well take some time.")
                                                (setq chars (- (point-max) 
                                                               (point)))
                                                (point)))
-           (setq nov-line (nnml-make-nov-line chars))
-           (save-excursion
-             (set-buffer nov-buffer)
-             (goto-char (point-max))
-             (insert (int-to-string (car files)) nov-line))
+           (if (not (= 0 chars)) ; none of them empty files...
+               (progn
+                 (setq nov-line (nnml-make-nov-line chars))
+                 (save-excursion
+                   (set-buffer nov-buffer)
+                   (goto-char (point-max))
+                   (insert (int-to-string (car files)) nov-line))))
            (widen)
            (setq files (cdr files)))
          (save-excursion
index 16f2ae8..ea2969b 100644 (file)
@@ -252,19 +252,20 @@ If the stream is opened, return T, otherwise return NIL."
        (while (and (not (looking-at 
                          "\\([^ ]+\\) +\\([0-9]+\\)[0-9][0-9][0-9] "))
                    (zerop (forward-line -1))))
-       (let ((seconds (nnspool-date-to-seconds date))
+       (let ((seconds (nnspool-seconds-since-epoch))
              groups)
-         ;; Go through lines and add groups that are recent to a list.
-         (while (and (looking-at "\\([^ ]+\\) +\\([0-9]+\\)[0-9][0-9][0-9] ")
-                     ;; We ignore the last three digits in the number
-                     ;; of seconds. This is quite naughty, but large
-                     ;; numbers are so tiresome to deal with. Perhaps
-                     ;; one could switch to floats instead? 
-                     (> (save-restriction 
-                          (goto-char (match-beginning 2))
-                          (narrow-to-region (point) (match-end 2))
-                          (read (current-buffer)))
-                        seconds)
+         ;; Go through lines and add the latest groups to a list.
+         (while (and (looking-at "\\([^ ]+\\) +[0-9]+ ")
+                     (progn
+                       ;; We insert a .0 to make the list reader
+                       ;; interpret the number as a float. It is far
+                       ;; too big to be stored in a lisp integer. 
+                       (goto-char (1- (match-end 0)))
+                       (insert ".0")
+                       (> (progn
+                            (goto-char (match-end 1))
+                            (read (current-buffer)))
+                          seconds))
                      (setq groups (cons (buffer-substring
                                          (match-beginning 1) (match-end 1))
                                         groups))
@@ -416,20 +417,10 @@ If the stream is opened, return T, otherwise return NIL."
         (setcar num (/ (car num) 10))
         (nnspool-number-base-10 num (1- pos))))))))
 
-(defun nnspool-days-between (date1 date2)
-  ;; Return the number of days between date1 and date2.
-  (let ((d1 (mapcar (lambda (s) (and s (string-to-int s)))
-                   (timezone-parse-date date1)))
-       (d2 (mapcar (lambda (s) (and s (string-to-int s)))
-                   (timezone-parse-date date2))))
-    (- (timezone-absolute-from-gregorian 
-       (nth 1 d1) (nth 2 d1) (car d1))
-       (timezone-absolute-from-gregorian 
-       (nth 1 d2) (nth 2 d2) (car d2)))))
-
-(defun nnspool-date-to-seconds (string)
-  (let ((days (nnspool-days-between string "Jan 1 00:00:00 1970")))
-    (* days 86)))
+(defun nnspool-seconds-since-epoch ()
+  (let ((time (current-time)))
+    (+ (* 1.0 (car time) 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2)
+       (nth 1 time))))
 
 (provide 'nnspool)
 
index 81de65f..e942036 100644 (file)
@@ -64,7 +64,7 @@ on servers that use strict access control.")
 If the number of the articles is greater than the value, verbose
 messages will be shown to indicate the current status.")
 
-(defvar nntp-buggy-select (memq system-type '(usg-unix-v fujitsu-uts))
+(defvar nntp-buggy-select (memq system-type '(fujitsu-uts))
   "t if your select routine is buggy.
 If the select routine signals error or fall into infinite loop while
 waiting for the server response, the variable must be set to t.  In
@@ -116,6 +116,7 @@ instead call function `nntp-status-message' to get status message.")
 (defvar nntp-current-server "")
 (defvar nntp-server-alist nil)
 (defvar nntp-server-xover t)
+(defvar nntp-server-list-active-group t)
 (defvar nntp-current-group "")
 
 ;;; Interface funtions.
@@ -198,9 +199,12 @@ If SERVICE, this this as the port number."
     (message "nntp: Connecting to server on %s..." server)
     (cond ((and server (nntp-open-server-internal server service))
           (setq nntp-current-server server)
-          (condition-case nil
-              (setq status (nntp-wait-for-response "^[23].*\r$"))
-            (error (nntp-close-server-internal server)))
+          (setq status
+                (condition-case nil
+                    (nntp-wait-for-response "^[23].*\r$")
+                  (error nil)
+                  (quit nil)))
+          (or status (nntp-close-server-internal server))
           (and nntp-server-process
                (progn
                  (set-process-sentinel 
@@ -231,6 +235,14 @@ If SERVICE, this this as the port number."
 
 (fset 'nntp-request-quit (symbol-function 'nntp-close-server))
 
+(defun nntp-request-close ()
+  "Close all server connections."
+  (while nntp-server-alist
+    (delete-process (car (cdr (car nntp-server-alist))))
+    (setq nntp-server-alist (cdr nntp-server-alist)))
+  (setq nntp-current-server "")
+  (setq nntp-server-process nil))
+
 (defun nntp-server-opened (&optional server)
   "Say whether a connection to SERVER has been opened."
   (if (or server nntp-current-server)
@@ -255,7 +267,8 @@ If SERVICE, this this as the port number."
   (unwind-protect
       (progn
        (if buffer (set-process-buffer nntp-server-process buffer))
-       (let ((nntp-server-buffer (or buffer nntp-server-buffer)))
+       (let ((nntp-server-buffer (or buffer nntp-server-buffer))
+             (id (or (and (numberp id) (int-to-string id)) id)))
          ;; If NEmacs, end of message may look like: "\256\215" (".^M")
          (prog1
              (nntp-send-command "^\\.\r$" "ARTICLE" id)
@@ -267,25 +280,67 @@ If SERVICE, this this as the port number."
   (nntp-possibly-change-server newsgroup server)
   (prog1
       ;; If NEmacs, end of message may look like: "\256\215" (".^M")
-      (nntp-send-command "^\\.\r$" "BODY" id)
+      (nntp-send-command
+       "^\\.\r$" "BODY" (or (and (numberp id) (int-to-string id)) id))
     (nntp-decode-text)))
 
 (defun nntp-request-head (id &optional newsgroup server)
   "Request head of article ID (message-id or number)."
   (nntp-possibly-change-server newsgroup server)
   (prog1
-      (nntp-send-command "^\\.\r$" "HEAD" id)
+      (nntp-send-command 
+       "^\\.\r$" "HEAD" (or (and (numberp id) (int-to-string id)) id))
     (nntp-decode-text)))
 
 (defun nntp-request-stat (id &optional newsgroup server)
   "Request STAT of article ID (message-id or number)."
   (nntp-possibly-change-server newsgroup server)
-  (nntp-send-command "^[23].*\r$" "STAT" id))
+  (nntp-send-command 
+   "^[23].*\r$" "STAT" (or (and (numberp id) (int-to-string id)) id)))
 
 (defun nntp-request-group (group &optional server dont-check)
   "Select GROUP."
-  (if (nntp-possibly-change-server nil server)
-      (nntp-send-command "^.*\r$" "GROUP" group)))
+  (if (not (nntp-possibly-change-server nil server))
+      ()
+    (if dont-check
+       (nntp-send-command "^.*\r$" "GROUP" group)
+      (if nntp-server-list-active-group
+         (save-excursion
+           (nntp-list-active-group group server)
+           (set-buffer nntp-server-buffer)
+           (goto-char (point-min))
+           ;; We look at the output from `nntp-list-active-group' to
+           ;; see whether the server supports this command.  If it
+           ;; does, we transform the output.  
+           (cond ((looking-at "2[0-9]+")
+                  (forward-line 1)
+                  (if (looking-at "[^ ] +\\([0-9]\\) +\\([0-9]\\)")
+                      (let ((end (progn (goto-char (match-beginning 1))
+                                        (read (current-buffer))))
+                            (beg (read (current-buffer))))
+                        (and (> beg end)
+                             (setq end 0
+                                   beg 0))
+                        (erase-buffer)
+                        (insert (format "211 %s %d %d %d\n"
+                                        group (max (- (1+ end) beg) 0)
+                                        beg end)))))
+                 ;; The server does not support the command.
+                 ((looking-at "5[0-9]+")
+                  (setq nntp-server-list-active-group nil)
+                  (setcar (nthcdr 
+                           3 (assoc nntp-current-server nntp-server-alist))
+                          nntp-server-xover)
+                  (nntp-send-command "^.*\r$" "GROUP" group))
+                 ;; The server supports it, but the group doesn't
+                 ;; exist. 
+                 ((looking-at "4[0-9]+")
+                  (erase-buffer)
+                  nil)))
+       (nntp-send-command "^.*\r$" "GROUP" group)))))
+
+(defun nntp-list-active-group (group &optional server)
+  (nntp-send-command "^.*\r$" "LIST ACTIVE" group))
 
 (defun nntp-request-group-description (group &optional server)
   "Get description of GROUP."
@@ -411,7 +466,16 @@ post to this group instead.  If RESPECT-POSTER, heed the special
              (widen))
            (setq news-reply-yank-from from)
            (setq news-reply-yank-message-id message-id)
-           (news-setup to subject message-of newsgroups article-buffer)
+           (news-setup to subject message-of 
+                       (if (stringp newsgroups) newsgroups "") 
+                       article-buffer)
+           (if (and newsgroups (listp newsgroups))
+               (progn
+                 (goto-char (point-min))
+                 (while newsgroups
+                   (insert (car (car newsgroups)) ": " 
+                           (cdr (car newsgroups)) "\n")
+                   (setq newsgroups (cdr newsgroups)))))
            ;; Fold long references line to follow RFC1036.
            (mail-position-on-field "References")
            (let ((begin (- (point) (length "References: ")))
@@ -688,8 +752,9 @@ It will prompt for a password."
            ;; Suggested by Hallvard B Furuseth <h.b.furuseth@usit.uio.no>.
            (process-kill-without-query proc)
            (setq nntp-server-xover t)
+           (setq nntp-server-list-active-group t)
            (setq nntp-server-name server)
-           (setq nntp-server-alist (cons (list server nntp-server-process t)
+           (setq nntp-server-alist (cons (list server nntp-server-process t t)
                                          nntp-server-alist))
            ;; It is possible to change kanji-fileio-code in this hook.
            (run-hooks 'nntp-server-hook)
@@ -705,14 +770,6 @@ It will prompt for a password."
                                nntp-server-alist))
   (setq nntp-current-server ""))
 
-(defun nntp-request-close ()
-  "Close all server connections."
-  (while nntp-server-alist
-    (delete-process (car (cdr (car nntp-server-alist))))
-    (setq nntp-server-alist (cdr nntp-server-alist)))
-  (setq nntp-current-server "")
-  (setq nntp-server-process nil))
-
 (defun nntp-accept-response ()
   "Read response of server.
 It is well-known that the communication speed will be much improved by
@@ -723,7 +780,7 @@ defining this function as macro."
   ;; This is a copy of `nntp-default-sentinel'.
   (if (or (not nntp-server-process)
          (not (memq (process-status nntp-server-process) '(open run))))
-      (error (format "nntp: Process connection closed"))
+      (error "nntp: Process connection closed; %s" (nntp-status-message))
     (if nntp-buggy-select
        (progn
          ;; We cannot use `accept-process-output'.
@@ -764,6 +821,7 @@ defining this function as macro."
                (setq nntp-server-name server)
                (setq nntp-server-process (nth 1 info))
                (setq nntp-server-xover (nth 2 info))
+               (setq nntp-server-list-active-group (nth 3 info))
                (setq changed-server t)
                (setq result t))))
       (setq result t))
index 71e80c0..9e900eb 100644 (file)
@@ -351,7 +351,8 @@ If the stream is opened, return T, otherwise return NIL."
                      (nconc (mapcar 
                              (lambda (art) 
                                (if (numberp art)
-                                   (+ (- art active) offset)
+                                   (if (< art active)
+                                       0 (+ (- art active) offset))
                                  (cons (+ (- (car art) active) offset)
                                        (cdr art))))
                              (cdr (assq (car m) marked)))
diff --git a/readme b/readme
index d4a7902..d73b6f9 100644 (file)
--- a/readme
+++ b/readme
@@ -1,3 +1,56 @@
-Ting som ligger under denne katalogen burde du ikke se sÃ¥ veldig mye
-pÃ¥. Den nyeste (pre-)releasen av (ding) Gnus finner du under
-«~larsi/pub/dgnus».
+This package contains a pre-release of (ding) Gnus, version 0.x. The
+lisp directory contains the source lisp files, and the texi directory
+contains an early draft of the Gnus info pages.
+
+Gnus is meant to be totally compatible with GNUS. But, alas, it
+probably isn't, which is one of the reasons for this pre-release. 
+
+To use (ding) Gnus you first have to unpack the files, which you've
+obviously done, because you are reading this. 
+
+You should definitely byte-compile the source files. To do that, you
+can simply say "make" in this directory.
+
+Then you have to tell Emacs where Gnus is. You might put something
+like
+
+   (setq load-path (cons (expand-file-name "~/dgnus/lisp") load-path))
+
+in your .emacs file, or wherever you keep such things.
+
+Note that (ding) Gnus and GNUS can not coexist in a single Emacs. They
+both use the same function and variable names. If you have been
+running GNUS in your Emacs, you should probably exit that Emacs and
+start a new one to fire up Gnus.
+
+(ding) Gnus does absolutely not work with Emacs 18, and only shakily
+with Emacs 19s before 19.26. So you definitely need a newish Emacs. 
+
+Then you do a `M-x gnus', and everything should... uhm... it should
+work, but it might not. Set `debug-on-error' to t, and mail me the
+backtraces, or, better yet, find out why Gnus does something wrong,
+fix it, and send me the diffs. :-)
+
+There are three main things I want your help and input on:
+
+1) Startup. Does eveything go smoothly, and why not?
+
+2) Any errors while you read news normally?
+
+3) Any errors if you do anything abnormal?
+
+4) Features you do not like, or do like, but would like to tweak a
+   bit, and features you would like to see.
+
+You do not have to send me typo corrections for the info pages. They
+are a very rough first draft - I haven't even read through it,
+although they should document all of Gnus, I think.
+
+I think I have implemented most of the deep-going changes that I'm
+going to. Things that will probably come in the future, but I haven't
+gotten around to yet is asynchronous posting/pre-fetch of headers and
+articles, better digest handling, a hierarchal Newsgroup buffer, and a
+few other things that I can't think of at the moment.
+
+Send any comments and all your bug fixes/complaints to
+`larsi@ifi.uio.no'. 
index 9e888e5..2983c97 100644 (file)
@@ -1,7 +1,7 @@
 \input texinfo                  @c -*-texinfo-*-
 @comment %**start of header (This is for running Texinfo on a region.)
 @setfilename gnus
-@settitle (ding) Gnus 0.41 Manual
+@settitle Gnus 0.50 Manual
 @synindex fn cp
 @synindex vr cp
 @synindex pg cp
@@ -18,7 +18,7 @@
 
 @ifinfo
 
-This file documents (ding) Gnus, the GNU Emacs newsreader.
+This file documents Gnus, the GNU Emacs newsreader.
 
 Copyright (C) 1989, 1990, 1993, 1995 Free Software Foundation, Inc.
 
@@ -43,7 +43,7 @@ into another language, under the above conditions for modified versions.
 @end ifinfo
 
 @titlepage
-@title (ding) Gnus Manual
+@title Gnus Manual
 
 @author by Lars Magne Ingebrigtsen
 @page
@@ -69,10 +69,10 @@ Cover art by Etienne Suvasa.
 @node Top
 @top The Gnus Newsreader
 
-You can read news (and mail) from within Emacs by using (ding) Gnus.
-The news can be gotten by any nefarious means you can think of - NNTP,
-local spool or your mbox file.  All at the same time, if you want to
-push your luck.
+You can read news (and mail) from within Emacs by using Gnus.  The news
+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.
 
 @menu
 * History::                 How Gnus got where it is today.
@@ -96,12 +96,8 @@ misleading information is sure to exist.
 @chapter History
 
 @cindex history
-@sc{GNUS} was written by Masanobu UMEDA.  When autumn crept up in '94,
-Lars Magne Ingebrigtsen grew bored and decided to write (ding) Gnus.
-
-(ding) Gnus is based on @sc{GNUS 4.1} and includes excellent functions
-from Per Abrahamsen, lots of fixes by Sudish Joseph, as well as bits and
-pieces from the XGnus distribution by Felix Lee and JWZ.
+@sc{gnus} was written by Masanobu UMEDA.  When autumn crept up in '94,
+Lars Magne Ingebrigtsen grew bored and decided to rewrite Gnus.
 
 The recommended pronounciation of the name this program is "ding
 guh-noose", with "ding" being half-sung in a loud, high-pitched voice,
@@ -122,7 +118,9 @@ abbreviation should probably be pronounced "news" as UMEDA intended,
 which makes it a more appropriate name, don't you think?)
 
 @menu
-* Compatibility::       Just how compatible is (ding) Gnus with @sc{GNUS}?
+* Compatibility::       Just how compatible is (ding) Gnus with @sc{gnus}?
+* Contributors::        Oodles of people.  
+* Gnus & hilit::        Old hilit19 code will not work with (ding) Gnus.
 * New Features::        A short description of all the new stuff in Gnus.
 * Newest Features::     Features so new that they haven't been written yet.
 @end menu
@@ -131,15 +129,15 @@ which makes it a more appropriate name, don't you think?)
 @section Compatibility
 
 @cindex compatability
-(ding) Gnus was designed to be fully compatible with @sc{GNUS}.  Almost
-all key binding have been kept.  More key binding have been added, of
-course, but only in one or two obscure cases have key bindings been
+(ding) Gnus was designed to be fully compatible with @sc{gnus}.  Almost
+all key bindings have been kept.  More key bindings have been added, of
+course, but only in one or two obscure cases have old bindings been
 changed.
 
 Our motto is:
 @quotation
 @cartouche
-In a cloud bones of steel.
+@center In a cloud bones of steel.
 @end cartouche
 @end quotation
 
@@ -150,13 +148,13 @@ The @code{gnus-uu} package has changed drastically. @xref{Decoding
 Articles}. 
 
 One major compatability question if the presence of several summary
-buffers.  The variables that are relevant while reading a group are
+buffers.  All variables that are relevant while reading a group are
 buffer-local to the summary buffer they belong in.  Although most
 important variables have their values copied into their global
 counterparts whenever a command is executed in the summary buffer, this
-might lead to incorrect values being used if one is not careful.
+change might lead to incorrect values being used unless you are careful.
 
-All code that relies on knowledge of @sc{GNUS} internals will probably
+All code that relies on knowledge of @sc{gnus} internals will probably
 fail.  To take two examples: Sorting @code{gnus-newsrc-assoc} (or
 changing it in any way, as a matter of fact) is strictly verboten.  Gnus
 maintains a hash table that points to the entries in this assoc (which
@@ -166,16 +164,16 @@ peculiar results.
 @cindex hilit19
 @cindex highlighting
 Old hilit19 code does not work at all.  In fact, you should probably
-remove all hihit code from all the Gnus hooks
+remove all hihit code from all Gnus hooks
 (@code{gnus-group-prepare-hook}, @code{gnus-summary-prepare-hook} and
 @code{gnus-summary-article-hook}).  (Well, at the very least the first
-two.)  Gnus provides various integrated functions for highlighting,
-which are both faster and more accurated. 
+two.)  Gnus provides various integrated functions for highlighting.
+These are faster and more accurate.
 
 Packages like @code{expire-kill} will no longer work.  As a matter of
-fact, you should probably remove all old @sc{GNUS} packages (and other
+fact, you should probably remove all old @sc{gnus} packages (and other
 code) when you start using (ding) Gnus.  More likely than not, (ding)
-Gnus already do what you have written code to make @sc{GNUS} do.
+Gnus already does what you have written code to make @sc{gnus} do.
 (Snicker.)
 
 Even though old methods of doing things are still supported, only the
@@ -183,20 +181,114 @@ new methods are documented in this manual.  If you detect a new method of
 doing something while reading this manual, that does not mean you have
 to stop doing it the old way.
 
-(ding) Gnus understands all @sc{GNUS} startup files.
+(ding) Gnus understands all @sc{gnus} startup files.
 
 @kindex M-x gnus-bug
 Overall, a casual user who hasn't written much code that depends on
-@sc{GNUS} internals should suffer no problems.  If problems occur,
+@sc{gnus} internals should suffer no problems.  If problems occur,
 please let me know (@kbd{M-x gnus-bug}).
 
+Problems specific to GNU XEmacs can be reported to popineau@@ese-metz.fr
+(Fabrice Popineau).  I will just forward any such questions to him,
+anyway, so you might have to wait longer if you mail XEmacs questions to
+me.
+
+@node Contributors
+@section Contributors
+@cindex contributors
+
+The new Gnus version couldn't have been done without the help of all the
+people on the (ding) mailing list.  Every day for months I have gotten
+tens of nice bug reports from them, filling me with joy, every single
+one of them.  Smooches.
+
+I would like to take this opportunity to thank the Academy for...  oops,
+wrong show.
+
+@itemize @bullet
+@item
+Of course, GNUS was written by Masanobu UMEDA.
+@item 
+Many excellent functions, especially dealing with scoring and
+highlighting (as well as the soon-to-come @sc{soup} support) was written
+by Per Abrahamsen.
+@item
+Innumarable bug fixes were written by Sudish Joseph.
+@item
+I stole some pieces from the XGnus distribution by Felix Lee and JWZ.
+@item 
+nnfolder has been much enhanced by Scott Byer.
+@item
+The orphan scoring was written by Peter Mutsaers.
+@item 
+GNU XEmacs support has been added by Fabrice Popineau. 
+@item 
+Various bits and pieces, especially dealing with .newsrc files, was
+suggested and added by Hallvard B Furuseth.
+@item 
+Stainless Steel Rat, Brian Edmonds, Jack Vinson, Daniel Quinlan, Ilja
+Weis, Andrew Eskilsson have all contributed code and suggestions. 
+@end itemize
+
+
+@node Gnus & hilit
+@section Gnus & hilit
+@cindex hilit
+
+@c Written by Sudish Joseph
+
+@code{gnus-visual} can be used to highlight the summary buffer.  It
+offers far more flexibility than hilit (since it has access to more
+data; eg. the article score) in deciding how to highlight a given
+article.  Also, hilit gets confused by the way Gnus manipulates the
+summary buffer, leading to errors.  Such errors may be detected by
+looking for any hilit-specific functions in the @code{*Backtrace*}
+buffer.  If such a reference exists, you should be using the code below.
+
+On the other hand, @code{gnus-visual} makes no attempt to highlight the
+article buffer, while hilit does a very good job of this.  There is a
+compatibility problem here though, since hilit uses
+@code{gnus-article-prepare-hook} to do its magic.  This hook is executed
+@emph{before} headers are hidden, via
+@code{gnus-article-hide-headers-if-wanted} which is run from
+@code{gnus-article-display-hook}.  The act of hiding the headers undoes
+all of the hilighting already done.  A similar effect occured in vanilla
+@sc{gnus 4.1} if @code{gnus-show-mime} was set to @code{t}, since
+@sc{mime} processing, too, is done after this hook is executed.
+
+The solution here is to use @code{gnus-article-display-hook} for
+highlighting (and for all purposes where you used
+@code{gnus-article-prepare-hook} earlier).  This hook is run after
+@sc{mime} processing, and is the last thing done before actually
+displaying the article.  Add the code below to your @file{.gnus} file to
+get the right functions onto the right hooks.
+
+@lisp
+(add-hook 'gnus-startup-hook
+          '(lambda ()
+             ;; gnus-visual is far better for summary highlighting
+             ;; also, hilit cannot handle a (ding) summary and will
+             ;; crash on you
+             (remove-hook 'gnus-summary-prepare-hook
+                          'hilit-rehighlight-buffer-quietly)
+             (remove-hook 'gnus-summary-prepare-hook 'hilit-install-line-hooks)
+             ;; this is too early for the purpose of highlighting
+             (remove-hook 'gnus-article-prepare-hook
+                          'hilit-rehighlight-buffer-quietly)
+             ;; use this instead.  note that the final t is *essential*,
+             ;; this must be the last thing done
+             (add-hook 'gnus-article-display-hook
+                       'hilit-rehighlight-buffer-quietly t)))
+@end lisp
+
+
 @node New Features
 @section New Features
 @cindex new features
 
 The look of all buffers can be changed by setting format-like variables.
  
-Local spool and several NNTP servers can be used at once.  Virtual
+Local spool and several @sc{nntp} servers can be used at once.  Virtual
 groups and private mail groups are featured.
 
 Gnus can use various strategies for gathering threads that have lost
@@ -232,24 +324,23 @@ months.  In fact, I would put money on it not being implemented before
 however, could possibly be included much sooner, as it is a separate
 issue. 
 @item
-Native @sc{MIME} support is something that should be done.  I was hoping
-I could steal code from @code{Mew}, the @sc{MIME} mail reader for Emacs,
+Native @sc{mime} support is something that should be done.  I was hoping
+I could steal code from @code{Mew}, the @sc{mime} mail reader for Emacs,
 but I'm not quite sure what the status of that project is.  (ding) might
-support @sc{MIME} quite soon, and it might not.
+support @sc{mime} quite soon, and it might not.
 @item 
 Some form of caching would come in handy.  Not only for those with
-extremely slow @sc{NNTP} connections, but as a more general way of
-saving articles in a simple fashion.  (You'd basically just mark is as
-@dfn{cached}, Gnus would put it in some local directory, and each time
-you request that article from that group, Gnus would fetch the local
-copy instead.)  Lots of quite interesting stuff to be considered
+extremely slow @sc{nntp} connections, but as a more general way of
+saving articles in a simple fashion.  (You'd basically just mark an
+article as @dfn{cached}.  Gnus would put it in some local directory, and
+each time you request that article from that group, Gnus would fetch the
+local copy instead.)  Lots of quite interesting stuff to be considered
 (caching @sc{nov} headers or not?) before jumping into it.  It would
 require much twiddling of Gnus internals to make it work transparently.
 @item
-Gnus could detect what articles in a newsgroup you read and which
-articles you kill, and then employ some adaptive scoring scheme.  That
-could potentially be quite interesting, and it's easy enough to
-implement. 
+Gnus could detect which articles in you read and which articles you
+kill, and then employ some adaptive scoring scheme.  That could
+potentially be quite interesting, and it's easy enough to implement.
 @item
 When the user references the parent of an article, some sort of
 re-threading should be done to build a proper tree.  The same goes for
@@ -262,14 +353,16 @@ is very easy, but slow.
 @chapter Terminology
 
 @cindex terminology
-@itemize @bullet
+@table @dfn
 @item news
+@cindex news
 This is what you are supposed to use this thing for - reading news.
-News is generally fetched from a nearby NNTP server, and is generally
-publicly available to everybody.  If you post news, the entire world is
-likely to read just what you have written, and they'll all snigger
-mischievously.  Behind your back.
+News is generally fetched from a nearby @sc{nntp} server, and is
+generally publicly available to everybody.  If you post news, the entire
+world is likely to read just what you have written, and they'll all
+snigger mischievously.  Behind your back.
 @item mail
+@cindex mail
 Everything that's delivered to you personally is mail.  Some news/mail
 readers (like Gnus) blur the distinction between mail and news, but
 there is a difference.  Mail is private.  News is public.  Mailing is
@@ -290,30 +383,38 @@ default, way of getting news.
 You can also have any number of foreign groups at the same time.  These
 are groups that use different backends for getting news.
 @item header
+@cindex header
 A line from the head of an article. 
 @item headers
+@cindex headers
 A collection of such lines, or a collection of heads.  Or even a
-collection of @sc{NOV} lines.
-@item @sc{NOV}
+collection of @sc{nov} lines.
+@item @sc{nov}
+@cindex nov
 When Gnus enters a group, it asks the backend for the headers for all
 the unread articles in the group.  Most servers support the News OverView
 format, which is much smaller and much faster to read than the normal
 HEAD format. 
 @item level
+@cindex levels
 Each group is subscribed at some @dfn{level} or other (1-9).  The ones
 that have a lower level are "more" subscribed than the groups with a
 higher level.  In fact, groups on levels 1-5 are considered
 "subscribed"; 6-7 are "unsubscribed"; 8 are zombies; and 9 are killed.
 Commands for listing groups and scanning for new articles will all use
 the numeric prefix as @dfn{working level}.  
-@end itemize
+@item active file
+The news server has to keep track of what articles it carries, and what
+groups exist.  All this information in stored in the active file, which
+is rather large, as you might surmise.
+@end table
 
 @node Starting Up
 @chapter Starting Gnus
 @cindex starting up
 
 @kindex M-x gnus
-If your system administrator has set thing up properly, starting Gnus
+If your system administrator has set things up properly, starting Gnus
 and reading news is extremely easy - you just type @kbd{M-x gnus}.
 
 If things do not go smoothly at startup, you have to twiddle some
@@ -339,8 +440,8 @@ This variable should be a list where the first element says "how" and
 the second element says "where".  This method is is your native method.
 All groups that are not fetched with this method are foreign groups.
 
-For instance, if you want to get your daily dosage of news from the NNTP
-server "news.friendly.server", you'd say:
+For instance, if you want to get your daily dosage of news from the
+@sc{nntp} server @samp{news.friendly.server}, you'd say:
 
 @lisp
 (setq gnus-select-method '(nntp "news.friendly.server"))
@@ -352,12 +453,13 @@ If you want to use a local spool, say:
 (setq gnus-select-method '(nnspool ""))
 @end lisp
 
-If you can use the local spool, you probably should, as it will almost
+If you can use a local spool, you probably should, as it will almost
 certainly be much faster.
 
 If this variable is not set, Gnus will take a look at the
 @code{NNTPSERVER} environment variable.  If that isn't set either, it
-will try to use the machine that is running Emacs as an NNTP server.
+will try to use the machine that is running Emacs as an @sc{nntp}
+server.
 
 @vindex gnus-nntp-server
 If @code{gnus-nntp-server} is set, this variable will override
@@ -365,24 +467,25 @@ If @code{gnus-nntp-server} is set, this variable will override
 @code{gnus-nntp-server} to @code{nil}, which is what it is by default.
 
 @vindex gnus-secondary-servers
-You can also make Gnus prompt you interactively for the name of an NNTP
-server.  If you give a non-numerical prefix to @code{gnus} (ie. @kbd{C-u
-M-x gnus}), Gnus will let you choose between the servers in the
-@code{gnus-secondary-servers} list (if any).  You can also just type in
-the name of any server you feel like visiting.
-
-However, if you use one NNTP server regularly, and is just interested in
-a couple of groups from a different server, you would be better served
-by using the @code{gnus-group-browse-foreign-server} command from the
-group buffer.  It will let you have a look at what groups are available,
-and you can subscribe to any of the groups you want to.  This also makes
-@file{.newsrc} maintenance much tidier.  @xref{Foreign Groups}.
+You can also make Gnus prompt you interactively for the name of an
+@sc{nntp} server.  If you give a non-numerical prefix to @code{gnus}
+(ie. @kbd{C-u M-x gnus}), Gnus will let you choose between the servers
+in the @code{gnus-secondary-servers} list (if any).  You can also just
+type in the name of any server you feel like visiting.
+
+However, if you use one @sc{nntp} server regularly, and are just
+interested in a couple of groups from a different server, you would be
+better served by using the @code{gnus-group-browse-foreign-server}
+command from the group buffer.  It will let you have a look at what
+groups are available, and you can subscribe to any of the groups you
+want to.  This also makes @file{.newsrc} maintenance much tidier.
+@xref{Foreign Groups}.
 
 @vindex gnus-secondary-select-methods
 A slightly different approach to foreign groups is to set the
 @code{gnus-secondary-select-methods} variable.  The select methods
 listed in this variable are in many ways just as native as the
-@code{gnus-select-method} server.  They will also be asked for active
+@code{gnus-select-method} server.  They will also be queried for active
 files during startup (if that's required), and new newsgroups that
 appear on these servers will be subscribed (or not) just as native
 groups are.
@@ -391,8 +494,7 @@ For instance, if you use the @code{nnmbox} backend to read you mail, you
 would typically set this variable to
 
 @lisp
-(setq gnus-secondary-select-methods 
-  '((nnmbox "")))
+(setq gnus-secondary-select-methods '((nnmbox "")))
 @end lisp
 
 @node The First Time
@@ -427,12 +529,17 @@ If the default server is down, Gnus will understandably have some
 problems starting.  However, if you have some mail groups in addition to
 the news groups, you may want to start Gnus anyway.
 
-@findex gnus-no-server
-You can do that by @kbd{M-x gnus-no-server}.  This will start Gnus
-without attempting to contact the default server.  Gnus will be started
-on level two, so you shouldn't have any groups from the native server on
-level one or two, but only have mail groups and other foreign groups on
-these two levels.
+Gnus, being the trusting sort of program, will ask whether to proceed
+without a native select method if that server can't be contacted.  This
+will happen whether the server doesn't actually exist (ie. you have
+given the wrong address) or the server has just momentarily taken ill
+for some reason or other.  
+
+If Gnus says "nntp server on <your server> can't be opened. Continue?",
+you do not want to continue unless you have some foreign groups that you
+want to read.  Even if you don't, Gnus will let you continue, but you'll
+find it difficult to actually do anything in the group buffer.  But,
+hey, that's your problem.  Blllrph!
 
 @node New Groups
 @section New Groups
@@ -508,7 +615,7 @@ also save you some time at startup.  Even if this variable is
 @code{nil}, you can always subscribe to the new groups just by pressing
 @kbd{U} in the group buffer (@pxref{Group Maintenance}).
 
-Gnus normally determine whether a group is new or not by comparing the
+Gnus normally determines whether a group is new or not by comparing the
 list of groups from the active file(s) with the lists of subscribed and
 dead groups.  This isn't a particularly fast method.  If
 @code{gnus-check-new-newsgroups} is @code{ask-server}, Gnus will ask the
@@ -531,17 +638,16 @@ Use the mantra "dingnusdingnusdingnus" to achieve permanent happiness.
 @cindex startup files
 @cindex .newsrc
 
-Now, you all know about the @file{.newsrc} files.  All information about
-what groups you read is traditionally stored in this file, which has a
-rather rigid structure.
+Now, you all know about the @file{.newsrc} file.  All subscription
+information is traditionally stored in this file.
 
-Things got a bit more complicated with @sc{GNUS}.  In addition to
+Things got a bit more complicated with @sc{gnus}.  In addition to
 keeping the @file{.newsrc} file updated, it also used a file called
 @file{.newsrc.el} for storing all the information that didn't fit into
 the @file{.newsrc} file.  (Actually, it duplicated everything in the
-@file{.newsrc} file.)  @sc{GNUS} would read whichever one of these files
+@file{.newsrc} file.)  @sc{gnus} would read whichever one of these files
 that were the most recently saved, which enabled people to swap between
-@sc{GNUS} and other newsreaders.
+@sc{gnus} and other newsreaders.
 
 That was kinda silly, so (ding) Gnus went one better: In addition to the
 @file{.newsrc} and @file{.newsrc.el} files, (ding) Gnus also has a file
@@ -549,11 +655,11 @@ called @file{.newsrc.eld}.  It will read whichever of these files that
 are most recent, but it will never write a @file{.newsrc.el} file.
 
 @vindex gnus-save-newsrc-file
-You can also turn off writing @file{.newsrc} by setting
+You can also turn off writing the @file{.newsrc} file by setting
 @code{gnus-save-newsrc-file} to @code{nil}, which means you can delete
-the file and save some space, as well as some time when quitting Gnus.
-However, that will make it impossible to use other newsreaders than
-(ding) Gnus.  But hey, who would want to, right?
+the file and save some space, as well as making exit from Gnus faster.
+However, this will make it impossible to use other newsreaders than
+Gnus.  But hey, who would want to, right?
 
 @vindex gnus-save-killed-list
 If @code{gnus-save-killed-list} is @code{nil}, Gnus will not save the
@@ -561,8 +667,8 @@ list of killed groups to the startup file.  This will save both time
 (when starting and quitting) and space (on disk).  It will also means
 that Gnus has no record of what groups are new or old, so the automatic
 new groups subscription methods become meaningless.  You should always
-set @code{gnus-check-new-newsgroups} to @code{nil} if you set this
-variable to @code{nil}.
+set @code{gnus-check-new-newsgroups} to @code{nil} or @code{ask-server}
+if you set this variable to @code{nil} (@pxref{New Groups}).
 
 @vindex gnus-startup-file
 The @code{gnus-startup-file} variable says where the startup files are.
@@ -579,16 +685,15 @@ file.
 @cindex auto-save
 
 Whenever you do something that changes the Gnus data (reading articles,
-cathing up, killing/subscribing to groups,) the change is added to a
-special @dfn{dribble} buffer.  This buffer is auto-saved the normal
+cathing up, killing/subscribing groups), the change is added to a
+special @dfn{dribble buffer}.  This buffer is auto-saved the normal
 Emacs way.  If your Emacs should crash before you have saved the
 @file{.newsrc} files, all changes you have made can be recovered from
-this file. 
+this file.
 
 If Gnus detects this file at startup, it will ask the user whether to
-read it.
-
-The auto save file is deleted whenever the real startup file is saved. 
+read it. The auto save file is deleted whenever the real startup file is
+saved.
 
 @vindex gnus-use-dribble-file
 If @code{gnus-use-dribble-file} is @code{nil}, Gnus won't create and
@@ -600,14 +705,14 @@ maintain a dribble buffer.
 @cindex ignored groups
 
 When Gnus starts, or indeed whenever it tries to determine whether new
-articles have arrived, it reads the active file.  This is a large file
-that the NNTP server maintains to keep track of what groups it carries.
+articles have arrived, it reads the active file.  This is a very large
+file that lists all the active groups and articles on the @sc{nntp}
+server.
 
 @vindex gnus-ignored-newsgroups
-Before examining the active file to see what groups are available, Gnus
-deletes all lines in this file that match the regexp
-@code{gnus-ignored-newsgroups}.  This is done primarily to reject any
-groups with bogus names (eg. groups containing characters like
+Before examining the active file, Gnus deletes all lines that match the
+regexp @code{gnus-ignored-newsgroups}.  This is done primarily to reject
+any groups with bogus names (eg. groups containing characters like
 @samp{'[]"} and so on), but you can use this variable to make Gnus
 ignore hierarchies you aren't ever interested in.
 
@@ -648,6 +753,9 @@ once in a while from the group buffer (@pxref{Group Maintenance}).
 If non-@code{nil}, the startup message won't be displayed.  That way,
 your boss might not notice thay you are reading news instead of doing
 your job.
+@item gnus-no-groups-message
+@vindex gnus-no-groups-message
+Message displayed by Gnus when no groups are available.
 @end table
 
 @node The Group Buffer
@@ -688,10 +796,10 @@ Here's a couple of example group lines:
 
 Quite simple, huh?
 
-Those lines mean that there are 25 unread articles in
-@samp{news.announce.newusers} and no unread articles, but some ticked
-articles in @samp{alt.fan.andrea-dworkin} (see that little asterisk at
-the beginning of the line?)
+You can see that there are 25 unread articles in
+@samp{news.announce.newusers}.  There are no unread articles, but some
+ticked articles, in @samp{alt.fan.andrea-dworkin} (see that little
+asterisk at the beginning of the line?)
 
 @vindex gnus-group-line-format
 You can fuck that up to your heart's delight by fiddling with the
@@ -707,7 +815,7 @@ and never more that 12 characters long.
 The default value that produced those lines above is 
 @samp{"%M%S%5y: %(%g%)\n"}.
 
-There should always be a colon on the line; the cursor always move to
+There should always be a colon on the line; the cursor always moves to
 the colon after performing an operation.  Nothing else is required - not
 even the group name.  All displayed text is just window dressing, and is
 never examined by Gnus.  Gnus stores all real information it needs using
@@ -721,49 +829,49 @@ Here's a list of all available format characters:
 
 @table @samp
 @item M    
-Only marked articles
+Only marked articles.
 @item S
-Whether the group is subscribed 
+Whether the group is subscribed.
 @item L    
-Level of subscribedness 
+Level of subscribedness.
 @item N
-Number of unread articles
+Number of unread articles.
 @item I
-Number of dormant articles
+Number of dormant articles.
 @item T
-Number of ticked articles
+Number of ticked articles.
 @item R
-Number of read articles
+Number of read articles.
 @item t
-Total number of articles
+Total number of articles.
 @item y
-Number of unread, unticked, non-dormant articles
+Number of unread, unticked, non-dormant articles.
 @item i
-Number of ticked and dormant articles
+Number of ticked and dormant articles.
 @item g
-Full group name
+Full group name.
 @item G
-Group name 
+Group name.
 @item D
-Newsgroup description
+Newsgroup description.
 @item o
-Moderated
+Moderated.
 @item O
-Moderated
+Moderated.
 @item s
-Select method 
+Select method.
 @item n
-Select from where
+Select from where.
 @item z
-A string that look like @samp{<%s:%n>} if a foreign select method is
-used. 
+A string that looks like @samp{<%s:%n>} if a foreign select method is
+used.
 @item u
 User defined specifier.  The next character in the format string should
-be a letter.  @sc{GNUS} will call the function gnus-user-format-function-X,
-where X is the letter following %u.  The function will be passed the
-current headers as argument.  The function should return a string, which
-will be inserted into the buffer just like information from any other
-specifier.
+be a letter.  @sc{gnus} will call the function
+@code{gnus-user-format-function-}@samp{X}, where @samp{X} is the letter
+following @samp{%u}.  The function will be passed the current headers as
+argument.  The function should return a string, which will be inserted
+into the buffer just like information from any other specifier.
 @end table
 
 @vindex gnus-group-mode-line-format
@@ -773,9 +881,9 @@ format specifiers:
 
 @table @samp
 @item S
-Default news server
+Default news server.
 @item M
-Default select method
+Default select method.
 @end table
 
 @node Group Manouvering
@@ -789,15 +897,15 @@ expected, hopefully.
 @item n
 @kindex n (Group)
 @findex gnus-group-next-unread-group
-Go to the next group with unread articles
-(@code{gnus-group-next-unread-group}). 
+Go to the next group that has unread articles
+(@code{gnus-group-next-unread-group}).
 @item p
 @item DEL
 @kindex DEL (Group)
 @kindex p (Group)
 @findex gnus-group-prev-unread-group
-Go to the previous group group with unread articles
-(@code{gnus-group-prev-unread-group}). 
+Go to the previous group group that has unread articles
+(@code{gnus-group-prev-unread-group}).
 @item N
 @kindex N (Group)
 @findex gnus-group-next-group
@@ -818,7 +926,7 @@ Go to the previous unread group on the same level (or lower)
 (@code{gnus-group-prev-unread-group-same-level}). 
 @end table
 
-Two commands for jumping to groups:
+Three commands for jumping to groups:
 
 @table @kbd
 @item j
@@ -832,6 +940,11 @@ like living groups.
 @findex gnus-group-best-unread-group
 Jump to the unread group with the lowest level
 (@code{gnus-group-best-unread-group}). 
+@item .
+@kindex . (Group)
+@findex gnus-group-first-unread-group
+Jump to the first group with unread articles
+(@code{gnus-group-first-unread-group}).  
 @end table
 
 @node Selecting a Group
@@ -842,12 +955,11 @@ Jump to the unread group with the lowest level
 @item SPACE
 @kindex SPACE (Group)
 @findex gnus-group-read-group
-Select the current group, switch to the summary buffer and display
-the first unread article in the group
-(@code{gnus-group-read-group}).  If there are no unread articles in the
-group, or if you give a prefix to this command, Gnus will offer to
-fetch all the old articles in this group from the server.
-server. 
+Select the current group, switch to the summary buffer and display the
+first unread article (@code{gnus-group-read-group}).  If there are no
+unread articles in the group, or if you give a prefix to this command,
+Gnus will offer to fetch all the old articles in this group from the
+server.  
 @item RET
 @kindex RET (Group)
 @findex gnus-group-select-group
@@ -868,10 +980,10 @@ Mark all articles in this group, even the ticked ones, as read
 
 @vindex gnus-large-newsgroup
 The @code{gnus-large-newsgroup} variable says what Gnus should consider
-to be a big group.  If the group has more unread articles than this
-variable, Gnus will query the user before entering the group.  The user
-can then specify how many articles should be fetched from the server.
-If the user specifies a negative number (@samp{-n}), the @samp{n} oldest
+to be a big group.  If the group has more unread articles than this,
+Gnus will query the user before entering the group.  The user can then
+specify how many articles should be fetched from the server.  If the
+user specifies a negative number (@samp{-n}), the @samp{n} oldest
 articles will be fetched.  If it is positive, the @samp{n} articles that
 have arrived most recently will be fetched.
 
@@ -890,13 +1002,13 @@ group with Huge articles) you can set this variable to @code{nil} in
 @findex gnus-thread-sort-by-author
 @findex gnus-thread-sort-by-number
 @vindex gnus-thread-sort-functions
-If you are using threaded summary display, you can sort the threads by
+If you are using threaded summary display, you can sort the threads by
 setting @code{gnus-thread-sort-functions}, which is a list of functions.
 By default, sorting is done on article numbers.  Ready-made sorting
 functions include @code{gnus-thread-sort-by-number},
 @code{gnus-thread-sort-by-author}, @code{gnus-thread-sort-by-subject},
 @code{gnus-thread-sort-by-date}, @code{gnus-thread-sort-by-score},
-@code{gnus-thread-sort-by-total-score}. 
+@code{gnus-thread-sort-by-total-score}.
 
 Each function takes two threads and return non-@code{nil} if the first
 thread should be sorted before the other.  If you use more than one
@@ -923,31 +1035,41 @@ tickles you fancy.
 @cindex subscribing
 
 @table @kbd
+@item S t
 @item u
+@kindex S t (Group)
 @kindex u (Group)
 @findex gnus-group-unsubscribe-current-group
-Unsubscribe the current group, or, if it was unsubscribed already,
-subscribe it (@code{gnus-group-unsubscribe-current-group}). 
+Toggle subscription to the current group
+(@code{gnus-group-unsubscribe-current-group}).  
+@item S s
 @item U
+@kindex S s (Group)
 @kindex U (Group)
 @findex gnus-group-unsubscribe-group
-Ask the user for a group to unsubscribe, and then unsubscribe it.  If
-it was unsubscribed already, subscribe it instead
-(@code{gnus-group-unsubscribe-group}). 
+Prompt for a group to subscribe, and then subscribe it.  If it was
+subscribed already, unsubscribe it instead
+(@code{gnus-group-unsubscribe-group}).
+@item S k
 @item C-k
+@kindex S k (Group)
 @kindex C-k (Group)
 @findex gnus-group-kill-group
 Kill the current group (@code{gnus-group-kill-group}).
+@item S y
 @item C-y
+@kindex S y (Group)
 @kindex C-y (Group)
 @findex gnus-group-yank-group
 Yank the last killed group (@code{gnus-group-yank-group}).
+@item S w
 @item C-w
+@kindex S w (Group)
 @kindex C-w (Group)
 @findex gnus-group-kill-region
 Kill all groups in the region (@code{gnus-group-kill-region}). 
-@item M-z
-@kindex M-z (Group)
+@item z
+@kindex z (Group)
 @findex gnus-group-kill-all-zombies
 Kill all zombie groups (@code{gnus-group-kill-all-zombies}).
 @end table
@@ -958,16 +1080,16 @@ Kill all zombie groups (@code{gnus-group-kill-all-zombies}).
 
 All groups have a level of @dfn{subscribedness}.  For instance, if a
 group is on level 2, it is more subscribed than a group on level 5.  You
-can ask Gnus to just list groups on a given level and lower
+can ask Gnus to just list groups on a given level or lower
 (@pxref{Listing Groups}), or to just check for new articles in groups on
-a given level and lower (@pxref{Misc Group Stuff}).
+a given level or lower (@pxref{Misc Group Stuff}).
 
 @table @kbd
-@item S
-@kindex S (Group)
+@item S l
+@kindex S (Group)
 @findex gnus-group-set-current-level
 Set the level of the current group depending on the numeric
-prefix.  For instance, @kbd{3 s} will set the level of the current
+prefix.  For instance, @kbd{3 S l} will set the level of the current
 group to three (@code{gnus-group-set-current-level}).  If no numeric
 prefix is given, this command will prompt the user for a level.
 @end table
@@ -978,7 +1100,9 @@ completely dead.  Gnus treats subscribed and unsubscribed groups exactly
 the same, but zombie and killed groups have no information on what
 articles you have read, etc, stored.  This distinction between dead and
 living groups isn't done because it is nice or clever, it is done purely
-for reasons of efficiency. 
+for reasons of efficiency.  On the other hand, if you would like a finer
+granularity to the levels - you're out of luck.  The nine levels are
+hardcoded into the source.  Sorry.
 
 It is recommended that you keep all regular groups on level 3 or higher,
 and keep your mail groups (if any) on level 1 or 2.
@@ -997,23 +1121,24 @@ rest.
 
 A @dfn{foreign group} is a group that is not read by the usual (or
 default) means.  It could be, for instance, a group from a different
-NNTP server, it could be a virtual group, or it could be your own
+@sc{nntp} server, it could be a virtual group, or it could be your own
 personal mail group.
 
 A foreign group (or any group, really) is specified by a @dfn{name} and
 a @dfn{select method}.  To take the latter first, a select method is a
 list where the first element says what backend to use (eg. nntp,
-nnspool, nnml) and the second element is the "address", in some meaning of
-the word.  There may be additional elements in the select method, where
-the value may have special meaning for the backends.
+nnspool, nnml) and the second element is the @dfn{address}, in some
+meaning of the word.  There may be additional elements in the select
+method, where the value may have special meaning for the backend in
+question.
 
 The @dfn{name} of the group is the name the backend will recognize the
 group as.
 
-For instance, the group @samp{soc.motss} on the NNTP server
+For instance, the group @samp{soc.motss} on the @sc{nntp} server
 @samp{some.where.edu} will have the name @samp{soc.motss} and select
-method @samp{(nntp "some.where.edu")}.  Gnus will call this group, in
-all circumstances @samp{nntp+some.where.edu:soc.motss}, even though the
+method @code{(nntp "some.where.edu")}.  Gnus will call this group, in
+all circumstances, @samp{nntp+some.where.edu:soc.motss}, even though the
 nntp backend just knows this group as @samp{soc.motss}.
 
 Here are some commands for making and editing general foreign groups,
@@ -1023,8 +1148,9 @@ and some commands to ease the creation of some special-purpose groups:
 @item M m
 @kindex M m (Group)
 @findex gnus-group-make-group
-Make a new group.  Gnus will prompt you for a name, a method and an
-"address" (@code{gnus-group-make-group}).
+Make a new group (@code{gnus-group-make-group}).  Gnus will prompt you
+for a name, a method and possibly an @dfn{address}.  For an easier way
+to subscribe to @sc{nntp} groups, @xref{Browse Foreign Server}.
 @item M e
 @kindex M e (Group)
 @findex gnus-group-edit-group
@@ -1039,6 +1165,13 @@ Make a directory group.  You will be prompted for a directory name
 @kindex M h (Group)
 @findex gnus-group-make-help-group
 Make the (ding) Gnus help group (@code{gnus-group-make-help-group}).
+@item M a
+@kindex M a (Group)
+@findex gnus-group-make-archive-group
+@vindex gnus-group-archive-directory
+Make the (ding) Gnus archive group
+(@code{gnus-group-make-archive-group}).  The archive group will be
+fetched from @code{gnus-group-archive-directory}.
 @item M k
 @kindex M k (Group)
 @findex gnus-group-make-kiboze-group
@@ -1050,7 +1183,7 @@ strings to match on headers (@code{gnus-group-make-kiboze-group}).
 The different methods all have their peculiarities, of course.
 
 @menu
-* nntp::             Reading news from a different NNTP server.
+* nntp::             Reading news from a different @sc{nntp} server.
 * nnspool::          Reading news from the local spool.
 * nnvirtual::        Combining articles from many groups.
 * nnkiboze::         Looking through parts of the newsfeed for articles.
@@ -1064,24 +1197,24 @@ The different methods all have their peculiarities, of course.
 If the @code{gnus-activate-foreign-newsgroups} is a positive number,
 Gnus will check all foreign groups with this level or lower at startup.
 This might take quite a while, especially if you subscribe to lots of
-groups from different NNTP servers.  It is @code{nil} by default, which
-means that you won't be told whether there are new articles in these
-groups.  How many unread articles there are will be determined when, or
-if, you decide to enter them.  You can also activate any group with
-@kbd{M-g} to see how many unread articles there are.
+groups from different @sc{nntp} servers.  It is @code{nil} by default,
+which means that you won't be told whether there are new articles in
+these groups.  How many unread articles there are will be determined
+when, or if, you decide to enter them.  You can also activate any group
+with @kbd{M-g} to see how many unread articles there are.
 
 @cindex to-address
 If the select method contains an element that looks like
 @samp{(to-address .  "some@@where.com")}, that address will be used by
 the backend when doing followups and posts.  This is primarily useful in
-mail groups that represent mailing lists.  You'd then just set this
-address to whatever the list address is.
+mail groups that represent mailing lists.  You just set this address to
+whatever the list address is.
 
 This trick will actually work whether the group is foreign or not.
 Let's say there's a group on the server that is called @samp{fa.4ad-l}.
-This is a real group, but the server has gotten the articles from a
+This is a real newsgroup, but the server has gotten the articles from a
 mail-to-news gateway.  Posting directly to this group is therefore
-impossible - you have to send mail to the mailing list address instead. 
+impossible - you have to send mail to the mailing list address instead.
 
 To achieve this, go to the group in question in the group buffer and
 type @kbd{M e} to edit the group entry.  You'll then be put in a buffer
@@ -1114,14 +1247,14 @@ Let's take time out for a poem by Reznikoff:
 
 @quotation
 Te Deum
-@sp 2
+@sp 1
 Not because of victories @*
 I sing,@*
 having none,@*
 but for the common sunshine,@*
 the breeze,@*
 the largess of the spring.
-@sp 2
+@sp 1
 Not for victory@*
 but for the day's work done@*
 as well as I was able;@*
@@ -1130,14 +1263,14 @@ but at the common table.@*
 @end quotation
 
 @node nntp
-@subsection nntp
+@subsection @sc{nntp}
 @cindex nntp
 
-Subscribing to a foreign group from an NNTP server is rather easy.  You
-just specify @code{nntp} as method and the address of the NNTP server as
-the, uhm, address.
+Subscribing to a foreign group from an @sc{nntp} server is rather easy.
+You just specify @code{nntp} as method and the address of the @sc{nntp}
+server as the, uhm, address.
 
-If the NNTP server is located at a non-standard port number, setting the
+If the @sc{nntp} server is located at a non-standard port, setting the
 third element of the select method to this port number should allow you
 to connect to the right port.  You'll have to edit the group info for
 that (@pxref{Foreign Groups}).
@@ -1146,25 +1279,25 @@ The name of the foreign group can be the same as a native group.  In
 fact, you can subscribe to the same group from as many different servers
 you feel like.  There will be no name collisions.
 
-@cindex @sc{MODE READER}
+@cindex @sc{mode reader}
 @cindex authinfo
 @findex nntp-send-authinfo
 @findex nntp-send-mode-reader
 @vindex nntp-server-opened-hook
 @code{nntp-server-opened-hook} is run after a connection has been made.
-It can be used to send commands to the NNTP server after it has been
-contacted.  By default is sends the command @samp{MODE READER} to the
-server with the @code{nntp-send-mode-reader} function.  Another popular
-function for sending the @sc{authinfo} command to the server is
-@code{nntp-send-authinfo}. 
+It can be used to send commands to the @sc{nntp} server after it has
+been contacted.  By default is sends the command @samp{MODE READER} to
+the server with the @code{nntp-send-mode-reader} function.  Another
+popular function is @code{nntp-send-authinfo}, which will prompt you for
+an @sc{nntp} password and stuff.
 
 @vindex nntp-maximum-request
-If the NNTP server doesn't support @sc{NOV} headers, this backend will
-collect headers by sending a series of @code{HEAD} commands.  To speed
-things up, the backend sends lots of these commands, without waiting for
-reply, and then reads all the replies.  This is controlled by the
-@code{nntp-maximum-request} variable, and is 400 by default.  If your
-network is buggy, you should set this to 1.
+If the @sc{nntp} server doesn't support @sc{nov} headers, this backend
+will collect headers by sending a series of @code{head} commands.  To
+speed things up, the backend sends lots of these commands without
+waiting for reply, and then reads all the replies.  This is controlled
+by the @code{nntp-maximum-request} variable, and is 400 by default.  If
+your network is buggy, you should set this to 1.
 
 @vindex nntp-connection-timeout
 If you have lots of foreign nntp groups that you connect to regularly,
@@ -1197,7 +1330,7 @@ Program used to post an article.
 Where nnspool looks for the articles.  This is normally
 @file{/usr/spool/news/}.
 @item nnspool-nov-directory 
-Where nnspool will look for @sc{NOV} files.  This is normally
+Where nnspool will look for @sc{nov} files.  This is normally
 @file{/usr/spool/news/over.view/}.
 @item nnspool-lib-dir
 Where the news lib dir is (@file{/usr/lib/news/} by default).
@@ -1208,42 +1341,46 @@ Where the news lib dir is (@file{/usr/lib/news/} by default).
 @cindex nnvirtual
 @cindex virtual groups
 
-A @dfn{virtual group} is really nothing more than a collection of
-other groups. 
-
-You specify @code{nnvirtual} as the method and a regular expression that
-says which groups that you wish to have in this one as the address. 
+An @dfn{nnvirtual group} is really nothing more than a collection of
+other groups.
 
 For instance, if you are tired of reading many small group, you can
 put them all in one big group, and then grow tired of reading one
 big, unwieldy group.  The joys of computing!
 
-All marks, read status, etc., in the nnvirtual group will stick to the
-articles in the component groups.  So if you tick an article in an
-nnvirtual group, the article will also be ticked in the component group
-from whence it it.  (And vice versa - marks from the component groups
-will also be shown in the nnvirtual group.)
+You specify @code{nnvirtual} as the method.  The address should be a
+regexp to match component groups.
 
-@example
-"^alt\\.fan\\.andrea-dworkin$\\|^rec\\.dworkin.*"
-@end example
+All marks in the virtual group will stick to the articles in the
+component groups.  So if you tick an article in a virtual group, the
+article will also be ticked in the component group from whence it came.
+(And vice versa - marks from the component groups will also be shown in
+the virtual group.)
+
+Here's an example nnvirtual method that collects all Andrea Dworkin
+newsgroups into one, big, happy newsgroup:
+
+@lisp
+(nnvirtual "^alt\\.fan\\.andrea-dworkin$\\|^rec\\.dworkin.*")
+@end lisp
 
-These groups can be native or foreign; everything should work smoothly,
-but if your computer explodes, it was probably my fault.  
+The component groups can be native or foreign; everything should work
+smoothly, but if your computer explodes, it was probably my fault.
 
 Collecting the same group from several servers might actually be a good
-idea if users have set the Distribution headers to limit distribution. 
+idea if users have set the Distribution header to limit distribution.
 If you would like to read @samp{soc.motss} both from a server in Japan
 and a server in Norway, you could use the following as the group regexp:
 
 @example
-"^nntp+some.server.jp:soc.motss\\|^nntp+some.server.no:soc.motss"
+"^nntp+some.server.jp:soc.motss$\\|^nntp+some.server.no:soc.motss$"
 @end example
 
 This should work kinda smoothly - all articles from both groups should
 end up in this one, and there should be no duplicates.  Threading (and
 the rest) will still work as usual, but there might be problems with the
-sequence of articles.  Sorting on date might be an option here.
+sequence of articles.  Sorting on date might be an option here
+(@pxref{Selecting a Group}.
 
 One limitation, however - all groups that are included in a virtual
 group has to be alive (ie. subscribed or unsubscribed).  Killed or
@@ -1254,10 +1391,10 @@ zombie groups can't be component groups for nnvirtual groups.
 @cindex nnkiboze
 @cindex kibozing
 
-@dfn{kibozing} is defined by OED as "grepping through (parts of) the
+@dfn{Kibozing} is defined by OED as "grepping through (parts of) the
 news feed".  nnkiboze is a backend that will do this for you.  Oh joy!
-Now you can grind any NNTP server down to a halt with useless requests!
-Oh happiness!
+Now you can grind any @sc{nntp} server down to a halt with useless
+requests!  Oh happiness!
 
 The address field of the nnkiboze method is, as with nnvirtual, a regexp
 to match groups to be "included" in the nnkiboze group.  There most
@@ -1265,22 +1402,21 @@ similarities between nnkiboze and nnvirtual ends.
 
 In addition to this regexp detailing component groups, an nnkiboze group
 must have a score file to say what articles that are to be included in
-the group.
+the group (@pxref{Score Files}).
 
 @kindex M-x nnkiboze-generate-groups
 @findex nnkiboze-generate-groups
-After creating the nnkiboze groups you feel like having, you must run
-@kbd{M-x nnkiboze-generate-groups}.  (This can also be reached from one
-of the menus.) This will take time.  Lots of time.  Oodles ond oodles of
-time.  Gnus has to fetch the headers from all the articles on all the
-components groups and run them through the scoring process to determine
-if there are any articles in the groups that are to be part of the
-nnkiboze groups.
+You must run @kbd{M-x nnkiboze-generate-groups} after creating the
+nnkiboze groups you want to have.  This command will take time.  Lots of
+time.  Oodles and oodles of time.  Gnus has to fetch the headers from
+all the articles in all the components groups and run them through the
+scoring process to determine if there are any articles in the groups
+that are to be part of the nnkiboze groups.
 
 Please limit the number of component groups by using restrictive
-regexps.  Or your sysadm may become annoyed with you, and the NNTP site
-may throw you off and never let you back in again.  Stranger things have
-happened.
+regexps.  Otherwise your sysadm may become annoyed with you, and the
+@sc{nntp} site may throw you off and never let you back in again.
+Stranger things have happened.
 
 nnkiboze component groups do not have to be alive - they can be dead,
 and they can be foreign.  No restrictions.
@@ -1288,12 +1424,13 @@ and they can be foreign.  No restrictions.
 @vindex nnkiboze-directory
 The generation of an nnkiboze group means writing two files in
 @code{nnkiboze-directory}, which is @file{~/News/} by default.  One
-contains the @sc{NOV} header lines for all the articles in the group, and the
-other is an additional @file{.newsrc} to store information on what
-groups that have been searched through to find component articles.
+contains the @sc{nov} header lines for all the articles in the group,
+and the other is an additional @file{.newsrc} file to store information
+on what groups that have been searched through to find component
+articles.
 
 Articles that are marked as read in the nnkiboze group will have their
-@sc{NOV} lines removed from the @sc{NOV} file.
+@sc{nov} lines removed from the @sc{nov} file.
 
 @node nndir
 @subsection nndir
@@ -1309,11 +1446,11 @@ wonderful of all wonderful Emacs packages.  When I wrote nndir, I didn't
 think much about it - a backend to read directories.  Big deal.
 
 ange-ftp changes that picture dramatically.  For instance, if you enter
-@file{/ftp/amanda} as the the directory name, ange-ftp will actually
-allow you to read this directory over at amanda as a newsgroup.
-Distributed news ahoy!
+@file{"/ftp@@sina.tcamc.uh.edu:/pub/emacs/ding-list/"} as the the
+directory name, ange-ftp will actually allow you to read this directory
+over at @samp{sina} as a newsgroup.  Distributed news ahoy!
 
-nndir supports, and will use, @sc{NOV} files if they are present.
+nndir will use @sc{nov} files if they are present.
 
 @node nndoc
 @subsection nndoc
@@ -1333,7 +1470,7 @@ the basis for a group.  And that's it.
 @cindex digest groups
 
 nndigest is a bit odd.  It will use a buffer containing a valid digest
-as the basis of the group.
+as the basis for a group.
 
 These nndigest groups are rather ephemeral.  They will never store
 information on what articles you have read, and you can't really use
@@ -1381,7 +1518,13 @@ programs, if you want to.
 @vindex nnmail-spool-file
 @code{nnmail-spool-file} says where to look for new mail.  If this
 variable is @code{nil}, the mail backends will never attempt to fetch
-mail by themselves.
+mail by themselves.  It is quite likely that Gnus supports POP-mail.
+Set this variable to begin with the string @samp{"po:"}, and everything
+should go smoothly, even though I have never tested this.
+
+@vindex nnmail-prepare-incoming-hook
+@code{nnmail-prepare-incoming-hook} is run in a buffer that holds all
+the new incoming mail, and can be used for, well, anything, really.
 
 @vindex nnmail-tmp-directory
 @code{nnmail-tmp-directory} says where to move the incoming mail to
@@ -1413,7 +1556,7 @@ it among the zombie groups, I guess, all depending on your
 @code{gnus-subscribe-newsgroup-method} variable.)
 
 @vindex nnmail-split-methods
-The you should set the variable @code{nnmail-split-methods} to specify
+Then you should set the variable @code{nnmail-split-methods} to specify
 how the incoming mail is to be split into groups.
 
 @lisp
@@ -1424,10 +1567,10 @@ how the incoming mail is to be split into groups.
 @end lisp
 
 This variable is a list of lists, where the first element of each of
-these lists contain the name of the mail group (they do not have to be
-called something beginning with @samp{"mail"}, by the way), and the
-second element is a regular expression used on the header of each mail
-to determine if it belongs in this mail group.
+these lists is the name of the mail group (they do not have to be called
+something beginning with @samp{"mail"}, by the way), and the second
+element is a regular expression used on the header of each mail to
+determine if it belongs in this mail group.
 
 The second element can also be a function.  In that case, it will be
 called narrowed to the headers with the first element of the rule as the
@@ -1438,6 +1581,12 @@ The last of these groups should always be a general one, and the regular
 expression should @emph{always} be @samp{""} so that it matches any
 mails that haven't been matched by any of the other regexps.
 
+If you like to tinker with this yourself, you can set this variable to a
+function of your choice. This function will be called without any
+arguments in a buffer narrowed to the headers of an incoming mail
+message. The function should return a list of groups names that it
+thinks should carry this mail message.
+
 @vindex nnmail-crosspost
 The mail backends all support cross-posting.  If several regexps match,
 the mail will be "cross-posted" to all those groups.
@@ -1459,27 +1608,27 @@ side effects.
 When a mail backend is queried for what groups it carries, it replies
 with the contents of that variable, along with any groups it has figured
 out that it carries by other means.  None of the backends (except
-@code{nnmh}) actually go out to the disk and check what groups that
-actually exists.  (It's not trivial to distinguish between what the user
-thinks is a basis for a newsgroup and what is just a plain old file or
-directory.)
+@code{nnmh}) actually go out to the disk and check what groups actually
+exist.  (It's not trivial to distinguish between what the user thinks is
+a basis for a newsgroup and what is just a plain old file or directory.)
 
 This means that you have to tell Gnus (and the backends) what groups
 exist by hand.
 
 Let's take the @code{nnfolder} backend as an example.  (This backend
-features one file as the basis of each group.)  
+uses one file as the basis of each group.)
 
 The folders are located in @code{nnfolder-directory}, say,
 @file{~/Mail/}.  There are three folders, @file{foo}, @file{bar} and
 @file{mail.baz}.
 
 Go to the group buffer and type @kbd{M m}.  When prompted, answer
-@samp{foo} for the name, @samp{nnfolder} for the method and @samp{""}
-for the address.  Repeat twice for the two other groups, @samp{bar} and
-@code{mail.baz}.  Be sure to include all your mail groups.
+@samp{foo} for the name and @samp{nnfolder} for the method.  Repeat
+twice for the two other groups, @samp{bar} and @samp{mail.baz}.  Be sure
+to include all your mail groups.
 
-That's it.  You are now set to read your mail.
+That's it.  You are now set to read your mail.  An active file for this
+method will be created automatically.
 
 @node Expiring Old Mail Articles
 @subsubsection Expiring Old Mail Articles
@@ -1518,7 +1667,7 @@ articles you have read to disappear after a while:
 
 @lisp
 (setq gnus-auto-expirable-newsgroups 
-      "^mail.nonsense-list\\|^mail.nice-list")
+      "mail.nonsense-list\\|mail.nice-list")
 @end lisp
 
 Another way to have auto-expiry happen is to have the element
@@ -1566,7 +1715,7 @@ ever attempt to read incoming mail, which should help.
 @vindex nnmh-get-new-mail
 @vindex nnfolder-get-new-mail
 This might be too much, if, for instance, you are reading mail quite
-happily with @code{nnml} and just want to peek at some old @sc{RMAIL}
+happily with @code{nnml} and just want to peek at some old @sc{rmail}
 file you have stashed away with @code{nnbabyl}.  All backends have
 variables called backend-@code{get-new-mail}.  If you want to disable
 the @code{nnbabyl} mail reading, you just set
@@ -1609,8 +1758,8 @@ group it belongs in.
 @cindex nnml
 @cindex mail @sc{nov} spool
 
-The spool mail format (@code{nnml}) isn't compatible with any other
-known format.  It should be used with some caution.
+The @dfn{nnml} spool mail format isn't compatible with any other known
+format.  It should be used with some caution.
 
 @vindex nnml-directory
 If you use this backend, Gnus will split all incoming mail into files;
@@ -1632,7 +1781,7 @@ to trudge through a big mbox file just to read your new mail.
 
 @code{nnml} is probably the slowest backend when it comes to article
 splitting.  It has to create lots of files, and it also generates
-@sc{NOV} databases for the incoming mails.  This makes is the fastest
+@sc{nov} databases for the incoming mails.  This makes is the fastest
 backend when it comes to reading mail.
 
 @findex nnml-generate-nov-databases
@@ -1640,7 +1789,7 @@ If your @code{nnml} groups and @sc{nov} files get totally out of whack,
 you can do a complete update by typing @kbd{M-x
 nnml-generate-nov-databases}.  This command will trawl through the
 entire @code{nnml} hierarchy, looking at each and every article, so it
-might take a while to finish.
+might take a while to complete.
 
 @node nnmh
 @subsubsection nnmh
@@ -1648,7 +1797,7 @@ might take a while to finish.
 @cindex mh-e mail spool
 
 @code{nnmh} is just like @code{nnml}, except that is doesn't generate
-@sc{NOV} databases and it doesn't keep an active file.  This makes
+@sc{nov} databases and it doesn't keep an active file.  This makes
 @code{nnmh} a @emph{much} slower backend than @code{nnml}, but it also
 makes it easier to write procmail scripts for.
 
@@ -1680,20 +1829,20 @@ These commands all list various slices of the groups that are available.
 @kindex G s (Group)
 @kindex l (Group)
 @findex gnus-group-list-groups
-List all subscribed groups that have unread articles
+List all groups that have unread articles
 (@code{gnus-group-list-groups}).  If the numeric prefix is used, this
 command will list only groups of level ARG and lower.  By default, it
 only lists groups of level five or lower (ie. just subscribed groups).
 @item L
 @item G u
-@kindex G u
+@kindex G u (Group)
 @kindex L (Group)
 @findex gnus-group-list-all-groups
-List all subscribed and unsubscribed groups, whether they have unread
-articles or not (@code{gnus-group-list-all-groups}).  If the numeric
-prefix is used, this command will list only groups of level ARG and
-lower.  By default, it lists groups of level seven or lower (ie. just
-subscribed and unsubscribed groups).
+List all groups, whether they have unread articles or not
+(@code{gnus-group-list-all-groups}).  If the numeric prefix is used,
+this command will list only groups of level ARG and lower.  By default,
+it lists groups of level seven or lower (ie. just subscribed and
+unsubscribed groups).
 @item G k
 @kindex G k (Group)
 @findex gnus-group-list-killed
@@ -1721,12 +1870,12 @@ List groups that match a regexp (@code{gnus-group-list-all-matching}).
 @item b
 @kindex b (Group)
 @findex gnus-group-check-bogus-groups
-Check bogus groups and delete them
+Find bogus groups and delete them
 (@code{gnus-group-check-bogus-groups}).
 @item F
 @kindex F (Group)
 @findex gnus-find-new-newsgroups
-Find new groups (@code{gnus-find-new-newsgroups}).
+Find new groups and process them (@code{gnus-find-new-newsgroups}).
 @item C-c C-x
 @kindex C-c C-x (Group)
 @findex gnus-group-expire-articles
@@ -1794,8 +1943,10 @@ Go to the previous group (@code{gnus-group-prev-group}).
 @findex gnus-browse-unsubscribe-current-group
 Unsubscribe to the current group, or, as will be the case here,
 subscribe to it (@code{gnus-browse-unsubscribe-current-group}). 
+@item l
 @item q
 @kindex q (Browse)
+@kindex l (Browse)
 @findex gnus-browse-exit
 Exit browse mode (@code{gnus-browse-exit}).
 @item ?
@@ -1817,7 +1968,7 @@ Yes, Gnus is ex(c)iting.
 @findex gnus-group-suspend
 Suspend Gnus (@code{gnus-group-suspend}).  This doesn't really exit Gnus,
 but it kills all buffers exept the Group buffer.  I'm not sure why this
-is a gain, but then who am I to judge.
+is a gain, but then who am I to judge?
 @item q
 @kindex q (Group)
 @findex gnus-group-exit
@@ -1887,13 +2038,13 @@ Clear the dribble buffer (@code{gnus-group-clear-dribble}).
 @findex gnus-group-describe-group
 Describe the current group (@code{gnus-group-describe-group}).  If given
 a prefix, force Gnus to re-read the description from the server.
-@item C-c C-a
-@kindex C-c C-a (Group)
+@item a
+@kindex a (Group)
 @findex gnus-group-apropos
 List all groups that have names that match a regexp
 (@code{gnus-group-apropos}).
-@item C-c M-C-a 
-@kindex C-c M-C-a (Group)
+@item G d
+@kindex G d (Group)
 @findex gnus-group-description-apropos
 List all groups that have names or descriptions that match a regexp
 (@code{gnus-group-description-apropos}).
@@ -1929,9 +2080,9 @@ Go to the Gnus info node (@code{gnus-info-find-node}).
 @end table
 
 @vindex gnus-group-prepare-hook
-@code{gnus-group-prepare-hook} is called after the group list is
-created in the Group buffer.  It may be used to modify the group
-buffer in some strange, unnatural way.
+@code{gnus-group-prepare-hook} is called after the group buffer is
+generated.  It may be used to modify the buffer in some strange,
+unnatural way.
 
 @node The Summary Buffer
 @chapter The Summary Buffer
@@ -2048,11 +2199,13 @@ Date
 Message-ID
 @item r
 References
+@item t
+Number of articles in the current subthread.
 @item x
 Xref
 @item u
 User defined specifier.  The next character in the format string should
-be a letter.  @sc{GNUS} will call the function
+be a letter.  @sc{gnus} will call the function
 gnus-user-format-function-X, where X is the letter following @samp{%u}.
 The function will be passed the current header as argument.  The
 function should return a string, which will be inserted into the summary
@@ -2092,13 +2245,22 @@ Current article number
 Gnus version
 @item U
 Number of unread articles in this group
-@item u
+@item e
 Number of unselected articles in this group
 @item Z
 A string with the number of unread and unselected articles represented
 either as @samp{<%U(+%u) more>} if there are both unselected articles,
 and just as @samp{<%U more>} if there are just unread articles and no
 unselected ones.
+@item g
+Shortish group name. For instance, @samp{rec.arts.anime} will be
+shortened to @samp{r.a.anime}. 
+@item S
+Subject of the current article.
+@item u
+Used-defined spec.
+@item s
+Name of the current score file.
 @end table
 
 
@@ -2260,6 +2422,22 @@ highlighting the article in some way.  It is not run if
 @vindex gnus-visual-summary-update-hook
 This hook is called when a summary line is changed.  It is not run if
 @code{gnus-visual} is @code{nil}.
+@item gnus-summary-selected-face
+@vindex gnus-summary-selected-face
+This is the face (or @dfn{font} as some people call it) that is used to
+highlight the current article in the summary buffer.
+@item gnus-visual-summary-highlight
+@vindex gnus-visual-summary-highlight
+Summary lines are highlighted according to this variable, which is a
+list where the elements are on the format @code{FORM . FACE}.  If you
+would, for instance, like ticked articles to be italic and high-scored
+articles to be bold, you could set this variable to something like
+@lisp
+(((eq mark gnus-ticked-mark) . italic)
+ ((> score default) . bold))
+@end lisp
+As you may have guessed, if @var{FORM} returns a non-nil value,
+@var{FACE} will be applied to the line.
 @end table
 
 @node Paging the Article
@@ -2377,8 +2555,19 @@ the group "mail.stupid-list", you could do something like this:
               nil))))
 @end lisp
 
-This functions will be called from the buffer of the article that is
-being replied to.
+This function will be called narrow to the headers of the article that
+is being replied to.
+
+As you can see, this function should return a string if it has an
+opinion as to what the To header should be.  If it does not, it should
+just return nil, and the normal methods for determining the To header
+will be used.
+
+This function can also return a list.  In that case, each list element
+should be a cons, where the car should be the name of an header
+(eg. @samp{Cc}) and the cdr should be the header value
+(eg. @samp{larsi@@ifi.uio.no}).  All these headers will be inserted into
+the head of the outgoing mail. 
 
 @item gnus-mail-send-method
 @vindex gnus-mail-send-method
@@ -2585,9 +2774,27 @@ prepended with @samp{> }, so @code{(setq mail-yank-prefix "> ")} in your
 When you yank a message, you do not want to include any headers from
 this message, so @code{(setq mail-yank-ignored-headers ":")}.  
 
+@item user-mail-address
+@vindex user-mail-address
+If all of @code{gnus-user-login-name}, @code{gnus-use-generic-from} and
+@code{gnus-local-domain} are nil, Gnus will use @code{user-mail-address}
+as the address part of the From header. 
+
+@item gnus-user-from-line
+@vindex gnus-user-from-line
+Your full, complete e-mail address.  This variable overrides the other
+Gnus variables if it is non-nil. 
+
+Here are two example values of this variable:
+@samp{"Lars Magne Ingebrigtsen <larsi@@ifi.uio.no>"}
+and @samp{"larsi@@ifi.uio.no (Lars Magne Ingebrigtsen)"}. 
+The first version is recommended, but the name has to be quoted if it
+contains non-alphanumerical characters - @samp{"\"Lars M. Ingebrigtsen\"
+<larsi@@ifi.uio.no>"}.
+
 @item mail-default-headers
 @vindex mail-default-headers
-This is a string that will be inserted into the heade of all outgoing
+This is a string that will be inserted into the header of all outgoing
 mail messages and news articles.  Convenient to use to insert standard
 headers. 
 
@@ -2984,7 +3191,7 @@ more old headers - headers to articles that are marked as read.  If you
 would like to display as few summary lines as possible, but still
 connect as many loose threads as possible, you should set this variable
 to @code{some}.  In either case, fetching old headers only works if the
-select method you are using supports @sc{XOVER}.  Also remember that if
+select method you are using supports @sc{xover}.  Also remember that if
 the root of the thread has been expired by the server, there's not much
 Gnus can do about that.
 @item gnus-gather-loose-threads
@@ -3004,6 +3211,10 @@ first 20 characters of the subjects have to match.  If you set this
 variable to a real low number, you'll find that Gnus will gather
 everything in sight into one thread, which isn't very helpful.
 
+@cindex fuzzy article gathering
+If you set this variable to the special value @code{fuzzy}, Gnus will
+use a fuzzy string comparison algorithm on the subjects.
+
 @item gnus-summary-make-false-root
 @vindex gnus-summary-make-false-root
 When there is no real root of a thread, Gnus will have to fudge
@@ -3160,6 +3371,11 @@ Mark all unticked articles in the group as read and then exit
 @findex gnus-summary-catchup-all-and-exit
 Mark all articles, even the ticked ones, as read and then exit
 (@code{gnus-summary-catchup-all-and-exit}).
+@item Z n
+@kindex Z n (Summary)
+@findex gnus-summary-catchup-and-goto-next-group
+Mark all articles as read and go to the next group
+(@code{gnus-summary-catchup-and-goto-next-group}). 
 @end table
 
 @vindex gnus-exit-group-hook
@@ -3187,14 +3403,14 @@ Remember: Cross-posting is kinda ok, but posting the same article
 separately to several groups is not.
 
 One thing that may cause Gnus to not do the cross-posting thing
-correctly is if you use an NNTP server that supports @sc{xover} (which
-is very nice, because it speeds things up considerably) which does not
-include the Xref header in its @sc{NOV} lines.  This is Evil, but it's
-common.  Gnus tries to Do The Right Thing even with @sc{xover} by
-registering the Xref lines of all articles you actually read, but if you
-kill the articles, or just mark them as read without reading them, Gnus
-will not get a chance to snoop the Xref lines out of these articles, and
-will be unable to use the cross reference mechanism.
+correctly is if you use an @sc{nntp} server that supports @sc{xover}
+(which is very nice, because it speeds things up considerably) which
+does not include the Xref header in its @sc{nov} lines.  This is Evil,
+but it's common.  Gnus tries to Do The Right Thing even with @sc{xover}
+by registering the Xref lines of all articles you actually read, but if
+you kill the articles, or just mark them as read without reading them,
+Gnus will not get a chance to snoop the Xref lines out of these
+articles, and will be unable to use the cross reference mechanism.
 
 @vindex gnus-nov-is-evil
 If you want Gnus to get the Xrefs right all the time, you have to set
@@ -3342,6 +3558,13 @@ all the files in the toplevel directory
 (@samp{~/News/alt/andrea-dworkin} instead of
 @samp{~/News/alt.andrea-dworkin}.)
 
+This function also affects kill and score file names.  If this variable
+is a list, and the list contains the element @code{not-score}, long file
+names will not be used for score files, if it contains the element
+@code{not-save}, long file names will not be used for saving, and if it
+contains the element @code{not-kill}, long file names will not be used
+for kill files.
+
 @node Decoding Articles
 @section Decoding Articles
 @cindex decoding articles
@@ -3408,7 +3631,7 @@ entire newsgroup, you'd typically do @kbd{M p a}
 (@code{gnus-uu-mark-all}) and then @kbd{X U} (@code{gnus-uu-decode-uu}).
 
 All this is very much different from how @code{gnus-uu} worked with
-@sc{GNUS 4.1}, where you had explicit keystrokes for everything under
+@sc{gnus 4.1}, where you had explicit keystrokes for everything under
 the sun.  This version of @code{gnus-uu} generally assumes that you
 either mark articles in some way (@pxref{Setting Process Marks}) and
 then press @kbd{X u}.
@@ -3498,9 +3721,10 @@ Files with name matching this regular expression won't be viewed.
 
 @item gnus-uu-ignore-files-by-type
 @vindex gnus-uu-ignore-files-by-type
-Files with a MIME type matching this variable won't be viewed.  Note
-that Gnus tries to guess what type the file is based on the name.
-@code{gnus-uu} is not a MIME package, so this is slightly kludgy.
+Files with a @sc{mime} type matching this variable won't be viewed.
+Note that Gnus tries to guess what type the file is based on the name.
+@code{gnus-uu} is not a @sc{mime} package (yet), so this is slightly
+kludgy.
 
 @item gnus-uu-tmp-dir
 @vindex gnus-uu-tmp-dir
@@ -3544,9 +3768,9 @@ uuencoded files that have had traling spaces deleted.
 @item gnus-uu-view-with-metamail
 @vindex gnus-uu-view-with-metamail
 Non-@code{nil} means that @code{gnus-uu} will ignore the viewing
-commands defined by the rule variables and just fudge a MIME content
-type based on the file name.  The result will be fed to metamail for
-viewing.
+commands defined by the rule variables and just fudge a @sc{mime}
+content type based on the file name.  The result will be fed to metamail
+for viewing.
 
 @item gnus-uu-save-in-digest
 @vindex gnus-uu-save-in-digest
@@ -3647,8 +3871,8 @@ Toggle whether to display all headers in the article buffer
 @item A m
 @kindex A m (Summary)
 @findex gnus-summary-toggle-mime
-Toggle whether to run the article through MIME before displaying
-(@code{gnus-summary-toggle-mime}). 
+Toggle whether to run the article through @sc{mime} before displaying
+(@code{gnus-summary-toggle-mime}).
 @end table
 
 There's a battery of commands for washing the article buffer:
@@ -3727,8 +3951,8 @@ Commands}).
 @kindex ^ (Summary)
 If you'd like to read the parent of the current article, and it is not
 displayed in the article buffer, you might still be able to.  That is,
-if the current group is fetched by NNTP, the parent hasn't expired and
-the References in the current article are not mangled, you can just
+if the current group is fetched by @sc{nntp}, the parent hasn't expired
+and the References in the current article are not mangled, you can just
 press @kbd{^} or @kbd{A r} (@code{gnus-summary-refer-parent-article}).
 If everything goes well, you'll get the parent.  If the parent is
 already displayed in the summary buffer, point will just move to this
@@ -3736,19 +3960,20 @@ article.
 
 @findex gnus-summary-refer-article
 @kindex M-^ (Summary)
-You can also ask the NNTP server for an arbitrary article, no matter
-what group it belongs to.  @kbd{V r} (@code{gnus-summary-refer-article})
-will ask you for a message-id, which is one of those long thingies that
-look something like @samp{<38o6up$6f2@@hymir.ifi.uio.no>}.  You have to
-get it all exactly right.  No fuzzy searches, I'm afraid.
+You can also ask the @sc{nntp} server for an arbitrary article, no
+matter what group it belongs to.  @kbd{V r}
+(@code{gnus-summary-refer-article}) will ask you for a message-id, which
+is one of those long thingies that look something like
+@samp{<38o6up$6f2@@hymir.ifi.uio.no>}.  You have to get it all exactly
+right.  No fuzzy searches, I'm afraid.
 
 @vindex gnus-refer-article-method
 If the group you are reading is located on a backend that does not
 support fetching by Message-ID very well (like @code{nnspool}), you can
-set @code{gnus-refer-article-method} to an NNTP method.  It would,
-perhaps, be best if the NNTP server you consult is the same as the one
-that keeps the spool you are erading from updated, but that's not really
-necessary. 
+set @code{gnus-refer-article-method} to an @sc{nntp} method.  It would,
+perhaps, be best if the @sc{nntp} server you consult is the same as the
+one that keeps the spool you are reading from updated, but that's not
+really necessary.
 
 @node Score Files
 @section Score Files
@@ -3784,7 +4009,9 @@ silently to help keep the sizes of the score files down.
 * Score File Format::        What a score file may contain.
 * Score File Editing::       You can edit score files by hand as well.
 * Scoring Tips::             How to score effectively.
+* Reverse Scoring::          That problem child of old is not problem.
 * Global Score Files::       Earth-spanning, ear-splitting score files.
+* Kill Files::               They are still here, but they can be ignored.
 @end menu
 
 @node Summary Score Commands
@@ -3875,6 +4102,17 @@ Increase the current author temporarily
 @findex gnus-summary-raise-by-author
 Increase the current author permanently
 (@code{gnus-summary-raise-by-author}).
+@item I b t
+@kindex I b t (Summary)
+@findex gnus-summary-temporarily-raise-by-body
+Increase based on a match on the body of an article temporarily
+(@code{gnus-summary-temporarily-raise-by-body}).  This is a very slow
+operation. 
+@item I b p
+@kindex I b p (Summary)
+@findex gnus-summary-raise-by-body
+Increase based on a match on the body of an article
+(@code{gnus-summary-raise-by-body}). This is a very slow operation
 @item I i t
 @kindex I i t (Summary)
 @findex gnus-summary-temporarily-raise-by-id
@@ -3940,13 +4178,24 @@ Lower the current author temporarily
 @findex gnus-summary-lower-by-author
 Lower the current author permanently
 (@code{gnus-summary-lower-by-author}).
+@item L b t
+@kindex L b t (Summary)
+@findex gnus-summary-temporarily-lower-by-body
+Lower based on a match on the article body
+(@code{gnus-summary-temporarily-lower-by-body}).  This is a very slow
+operation. 
+@item L b p
+@kindex L b p (Summary)
+@findex gnus-summary-lower-by-body
+Lower bsed on a match on the article body
+(@code{gnus-summary-lower-by-body}).  This is a very slow operation.
 @item L i t
 @kindex L i t (Summary)
 @findex gnus-summary-temporarily-lower-by-id
 Lower the current message-id temporarily
 (@code{gnus-summary-temporarily-lower-by-id}).
-@item L a p
-@kindex L a p (Summary)
+@item L i p
+@kindex L i p (Summary)
 @findex gnus-summary-lower-by-id
 Lower the current message-id permanently
 (@code{gnus-summary-lower-by-id}).
@@ -4062,15 +4311,18 @@ Anyway, if you'd like to dig into it yourself, here's an example:
 (("from"
   ("Lars Ingebrigtsen" -10000)
   ("Per Abrahamsen")
-  ("larsi\\|lmi" -50000 nil r))
+  ("larsi\\|lmi" -50000 nil R))
  ("subject"
   ("Ding is Badd" nil 728373))
  ("xref"
   ("alt.politics" -1000 728372 s))
+ ("lines"
+  (2 -100 nil <))
  (mark 0)
  (expunge -1000)
  (mark-and-expunge -10)
- (read-only t)
+ (read-only nil)
+ (orphan -10)
  (files "/hom/larsi/News/gnu.SCORE")
  (eval (ding)))
 @end lisp
@@ -4085,12 +4337,24 @@ Six keys are supported by this alist:
 
 @table @code
 @item STRING
-If the key is a string, it is a header name to perform the scoring on.
+If the key is a string, it is the name of the header to perform the
+match on.  Scoring can only be performed on these eight headers:
+@samp{From}, @samp{Subject}, @samp{References}, @samp{Message-ID},
+@samp{Xref}, @samp{Lines}, @samp{Chars} and @samp{Date}.  In addition to
+these headers, there are three strings to tell Gnus to fetch the entire
+article and do the match on larger parts of the article: @samp{Body}
+will perform the match on the body of the article, @samp{Head} will
+perform the match on the head of the article, and @samp{All} will
+perform the match on the entire article.  Note that using any of these
+last three keys will slow down group entry @emph{considerably}.  
+
 Following this key is a random number of element score entries, where
-each score entry have one to four elements.  
+each score entry have one to four elements.
 @enumerate
 @item 
-The first element is always a string - the @dfn{match element}.
+The first element is the @dfn{match element}.  On most headers this will
+be a string, but on the Lines and Chars headers, this must be an
+integer. 
 @item 
 If the second element is present, it should be a number - the @dfn{score
 element}.  This number should be an integer in the neginf to posinf
@@ -4104,9 +4368,31 @@ this element is not present, the score entry is premanent.  The date is
 represented by the number of days since December 31, 1 CE.
 @item 
 If the fourth element is present, it should be a symbol - the @dfn{type
-element}.  Currently supported are the @code{r} (regexp) and @code{s}
-(substring) types.  If this element is not present, Gnus will assume
-that substring matching should be used.
+element}.  This element specifies what function should be performed to
+see whether this score entry matches the article.  What match types that
+can be used depends on what header you wish to perform the match on.
+@itemize @bullet
+@item From, Subject, References, Xref, Message-ID
+For most header types, there are the @code{r} and @code{R} (regexp) as
+well as @code{s} and @code{S} (substring) types.  If this element is not
+present, Gnus will assume that substring matching should be used.
+@code{R} and @code{S} differ from the other two in that the matches will
+be done in a case-sensitive manner.  All these one-letter types are
+really just abbreviations for the @code{regexp} and @code{string} types,
+which you can use instead, if you feel like.
+@item Lines, Chars
+These two headers use different match types: @code{<}, @code{>},
+@code{=}, @code{>=} and @code{<=}.
+@item Date
+For the Date header we have three match types: @code{before}, @code{at}
+and @code{after}.  I can't really imagine this ever being useful, but,
+like, it would feel kinda silly not to provide this function.  Just in
+case.  You never know.  Better safe than sorry.  Once burnt, twice shy.
+Don't judge a book by its cover.  Never not have sex on a first date.
+@item Head, Body, All
+These three match keys use the same match types as the From (etc) header
+uses. 
+@end itemize
 @end enumerate
 
 @item mark
@@ -4129,6 +4415,9 @@ ignored when handling global score files.
 @item read-only
 Read-only score files will not be updated or saved.  Global score files
 should feature this atom (@pxref{Global Score Files}).
+@item orphan
+The value of this entry should be a number.  Articles that do not have
+parents will get this number added to their scores.
 @end table
 
 @node Score File Editing
@@ -4172,8 +4461,43 @@ more than, say, 3 groups:
 @lisp
 ("xref" (" +[^ ]+:[0-9]+ +[^ ]+:[0-9]+ +[^ ]+:[0-9]+" -1000 nil r))
 @end lisp
+@item Matching on the body
+This is generally not a very good idea - it takes a very long time.
+Gnus actually has to fetch each individual article from the server.  But
+you might want to anyway, I guess.  Even though there are three match
+keys (@code{Head}, @code{Body} and @code{All}), you should choose one
+and stick with it in each score file.  If you use any two, each article
+will be fetched @emph{twice}.  If you want to match a bit on the
+@code{Head} and a bit on the @code{Body}, just use @code{All} for all
+the matches.  
+@item Marking as read
+You will probably want to mark articles that has a score below a certain
+number as read.  This is most easily achieved by putting the following
+in your @file{all.SCORE} file:
+@lisp
+((mark -100))
+@end lisp
+You may also consider doing something similar with @code{expunge}.  
 @end itemize
 
+@node Reverse Scoring
+@subsection Reverse Scoring
+@cindex reverse scoring
+
+If you want to keep just articles that have @samp{Sex with Emacs} in the
+subject header, and expunge all other articles, you could put something
+like this in your score file:
+
+@lisp
+(("subject"
+  ("Sex with Emacs" 2))
+ (mark 1)
+ (expunge 1))
+@end lisp
+
+So, you raise all articles that match @samp{Sex with Emacs} and mark the
+rest as read, and expunge them to boot.
+
 @node Global Score Files
 @subsection Global Score Files
 @cindex global score files
@@ -4245,6 +4569,47 @@ in the future.  @emph{Snicker}.  Yup, any day now, newsreaders like Blue
 Wave, xrn and 1stReader are bound to implement scoring.  Should we start
 holding our breath yet?
 
+@node Kill Files
+@subsection Kill Files
+@cindex kill files
+
+(ding) Gnus still supports those pesky old kill files.  In fact, the
+kill file netries can now be expiring, which is something I wrote before
+Per thought of doing score files, so I've left the code there.
+
+In short, kill processing is a lot slower (and I do mean @emph{a lot})
+than score processing, so it might be a good idea to rewrite your kill
+files into score files.
+
+Anyway, a kill file is a normal elisp file.  You can put any forms into
+this file, which means that you can use kill files as some sort of
+primitive hook function to be run on group entry, even though that isn't
+really a very good idea.
+
+Normal kill files look like this:
+
+@lisp
+(gnus-kill "From" "Lars Ingebrigtsen")
+(gnus-kill "Subject" "ding")
+(gnus-expunge "X")
+@end lisp
+
+This will mark every article written by me as read, and remove them from
+the summary buffer.  Very useful, you'll agree.
+
+Two functions for entering kill file editing:
+
+@table @kbd
+@item V k
+@kindex V k (Summary)
+@findex gnus-summary-edit-local-kill
+Edit this group's kill file (@code{gnus-summary-edit-local-kill}).
+@item V K
+@kindex V K (Summary)
+@findex gnus-summary-edit-global-kill
+Edit the general kill file (@code{gnus-summary-edit-local-kill}).
+@end table
+
 @node Mail Group Commands
 @section Mail Group Commands
 @cindex mail group commands
@@ -4385,22 +4750,16 @@ Expand the summary buffer window (@code{gnus-summary-expand-window}).
 @kindex V S (Summary)
 @findex gnus-summary-reselect-current-group
 Exit this group, and then enter it again
-(@code{gnus-summary-reselect-current-group}).
+(@code{gnus-summary-reselect-current-group}).  If given a prefix, select
+all articles, both read and unread.
 @item V g
 @item M-g
 @kindex V g (Summary)
 @kindex M-g (Summary)
 @findex gnus-summary-rescan-group
 Exit group, check for new articles in the group, and select the group
-(@code{gnus-summary-rescan-group}).
-@item V k
-@kindex V k (Summary)
-@findex gnus-summary-edit-local-kill
-Edit this group's kill file (@code{gnus-summary-edit-local-kill}).
-@item V K
-@kindex V K (Summary)
-@findex gnus-summary-edit-global-kill
-Edit the general kill file (@code{gnus-summary-edit-local-kill}).
+(@code{gnus-summary-rescan-group}).  If given a prefix, select all
+articles, both read and unread.
 @end table
 
 @node The Article Buffer
@@ -4412,7 +4771,7 @@ one.  All the summary buffer share the same article buffer.
 
 @menu
 * Hiding Headers::        Deciding what headers should be displayed.
-* Using Mime::            Pushing articles through MIME before reading them.
+* Using Mime::            Pushing articles through @sc{mime} before reading them.
 * Customizing Articles::  Tailoring the look of the articles.
 * Article Keymap::        Keystrokes available in the article buffer
 * Misc Article::          Other stuff.
@@ -4489,33 +4848,33 @@ are listed in this variable.
 
 @node Using Mime
 @section Using Mime
-@cindex MIME
+@cindex @sc{mime}
 
 Mime is a standard for waving your hands through the air, aimlessly,
 while people stand around yawning.
 
-MIME, however, is a standard for encoding your articles, aimlessly,
+@sc{mime}, however, is a standard for encoding your articles, aimlessly,
 while all newsreaders die of fear.
 
-MIME may specify what character set the article uses, the encoding of
-the characters, and it also makes it possible to embed pictures and
+@sc{mime} may specify what character set the article uses, the encoding
+of the characters, and it also makes it possible to embed pictures and
 other naughty stuff in innocent-looking articles.
 
 @vindex gnus-show-mime
 @vindex gnus-show-mime-method
-Gnus handles MIME by shoving the articles through
+Gnus handles @sc{mime} by shoving the articles through
 @code{gnus-show-mime-method}, which is @code{metamail-buffer} by
-default.  Set @code{gnus-show-mime} to @code{t} if you want to use MIME
-all the time; it might be best just use the toggling functions from the
-summary buffer to avoid getting nasty surprises (for instance, you enter
-the group @samp{alt.sing-a-long} and, before you know it, MIME has
-decoded the sounds file in the article and some horrible sing-a-long
-song comes streaming out out your speakers, and you can't find the
-volume button, because there isn't one, and people are starting to look
-at you, and you try to stop the program, but you can't, and you can't
-find the program to control the volume, and everybody else in the room
-suddenly decides to look at you disdainfully, and you'll feel rather
-stupid.)
+default.  Set @code{gnus-show-mime} to @code{t} if you want to use
+@sc{mime} all the time; it might be best just use the toggling functions
+from the summary buffer to avoid getting nasty surprises (for instance,
+you enter the group @samp{alt.sing-a-long} and, before you know it,
+@sc{mime} has decoded the sounds file in the article and some horrible
+sing-a-long song comes streaming out out your speakers, and you can't
+find the volume button, because there isn't one, and people are starting
+to look at you, and you try to stop the program, but you can't, and you
+can't find the program to control the volume, and everybody else in the
+room suddenly decides to look at you disdainfully, and you'll feel
+rather stupid.)
 
 Any similarity to real events and people is purely coincidental.  Ahem.
 
@@ -4756,18 +5115,18 @@ section is designed to give general pointers on how to customize Gnus
 for some quite common situations.
 
 @menu
-* Slow NNTP Connection::      You run a local Emacs and get the news elsewhere.
+* Slow @sc{nntp} Connection::      You run a local Emacs and get the news elsewhere.
 * Slow Terminal Connection::  You run a remote Emacs.
 * Little Disk Space::         You feel that having large setup files is icky.
 * Slow Machine::              You feel like buying a faster machine.
 @end menu
 
-@node Slow NNTP Connection
-@section Slow NNTP Connection
+@node Slow @sc{nntp} Connection
+@section Slow @sc{nntp} Connection
 
 If you run Emacs on a machine locally, and get your news from a machine
 over some very thin strings, you want to cut down on the amount of data
-Gnus has to get from the NNTP server.
+Gnus has to get from the @sc{nntp} server.
 
 @table @code
 @item gnus-read-active-file
@@ -4779,8 +5138,8 @@ doesn't suddenly decide to fetch the active file anyway.  Note that this
 will make it difficult for you to get hold of new groups.
 @item gnus-nov-is-evil
 This one has to be @code{nil}.  If not, grabbing article headers from
-the NNTP server will not be very fast.  Not all NNTP servers support
-@sc{XOVER}; Gnus will detect this by itself.
+the @sc{nntp} server will not be very fast.  Not all @sc{nntp} servers
+support @sc{xover}; Gnus will detect this by itself.
 @end table
 
 @node Slow Terminal Connection
@@ -4834,10 +5193,20 @@ use any other newsreaders than Gnus.
 If this is @code{nil}, Gnus will not save the list of dead groups.  That
 means that Gnus will not know whether groups are new or old, which makes
 automatic handling of new groups impossible.  You should also set
-@code{gnus-check-new-newsgroups} and @code{gnus-check-bogus-newsgroups}
-to @code{nil} if you set this variable to @code{nil}.
+@code{gnus-check-new-newsgroups} to @code{ask-server} and
+@code{gnus-check-bogus-newsgroups} to @code{nil} if you set this
+variable to @code{nil}.
 @end table
 
+@vindex gnus-kill-file-name
+A kill file for the group @samp{soc.motss} is normally called
+@file{soc.motss.KILL}.  The suffix appended to the group name to get
+this file name is detailed by the @code{gnus-kill-file-name} variable.
+The "global" kill file (not in the score file sense of "global", of
+course) is called just @file{KILL}.
+
+In short: avoid killing.  Scoring is more fun.
+
 @node Slow Machine
 @section Slow Machine
 
@@ -4866,9 +5235,15 @@ Ahem.
 Make sure your computer is switched on.
 
 Make sure that you really load the current Gnus version.  If you have
-been running @sc{GNUS}, you need to exit Emacs and start it up again before
+been running @sc{gnus}, you need to exit Emacs and start it up again before
 Gnus will work.
 
+Try doing an @kbd{M-x gnus-version}.  If you get something that looks
+like @samp{(ding) Gnus v0.46; nntp 4.0} you have the right files loaded.
+If, on the other hand, you get something like @samp{NNTP 3.x} or
+@samp{nntp flee}, you have some old .el files lying around.  Delete
+these. 
+
 Read the help group (@kbd{M h} in the group buffer) for a FAQ and a
 how-to.