Merge from gnus--rel--5.10
[gnus] / lisp / gnus-start.el
index 3abb42c..cd3c21d 100644 (file)
@@ -1,7 +1,9 @@
 ;;; gnus-start.el --- startup functions for Gnus
-;; Copyright (C) 1996 Free Software Foundation, Inc.
 
-;; Author: Lars Magne Ingebrigtsen <larsi@ifi.uio.no>
+;; Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+;;   2005, 2006 Free Software Foundation, Inc.
+
+;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
 ;; Keywords: news
 
 ;; This file is part of GNU Emacs.
 
 ;; 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
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 ;; GNU General Public License for more details.
 
 ;; You should have received a copy of the GNU General Public License
 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
-;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-;; Boston, MA 02111-1307, USA.
+;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+;; Boston, MA 02110-1301, USA.
 
 ;;; Commentary:
 
 ;;; Code:
 
-(require 'gnus-load)
 (require 'gnus)
 (require 'gnus-win)
 (require 'gnus-int)
 (require 'gnus-spec)
 (require 'gnus-range)
-(require 'message)
-
-(defvar gnus-startup-file "~/.newsrc"
-  "*Your `.newsrc' file.
-`.newsrc-SERVER' will be used instead if that exists.")
-
-(defvar gnus-init-file "~/.gnus"
-  "*Your Gnus elisp startup file.
-If a file with the .el or .elc suffixes exist, it will be read
-instead.")
+(require 'gnus-util)
+(autoload 'message-make-date "message")
+(autoload 'gnus-agent-read-servers-validate "gnus-agent")
+(autoload 'gnus-agent-save-local "gnus-agent")
+(autoload 'gnus-agent-possibly-alter-active "gnus-agent")
 
-(defvar gnus-default-subscribed-newsgroups nil
-  "*This variable lists what newsgroups should be subscribed the first time Gnus is used.
-It should be a list of strings.
-If it is `t', Gnus will not do anything special the first time it is
-started; it'll just use the normal newsgroups subscription methods.")
-
-(defvar gnus-use-dribble-file t
+(eval-when-compile
+  (require 'cl)
+
+  (defvar gnus-agent-covered-methods nil)
+  (defvar gnus-agent-file-loading-local nil)
+  (defvar gnus-agent-file-loading-cache nil))
+
+(defcustom gnus-startup-file (nnheader-concat gnus-home-directory ".newsrc")
+  "Your `.newsrc' file.
+`.newsrc-SERVER' will be used instead if that exists."
+  :group 'gnus-start
+  :type 'file)
+
+(defcustom gnus-backup-startup-file 'never
+  "Whether to create backup files.
+This variable takes the same values as the `version-control'
+variable."
+  :version "22.1"
+  :group 'gnus-start
+  :type '(choice (const :tag "Never" never)
+                (const :tag "If existing" nil)
+                (other :tag "Always" t)))
+
+(defcustom gnus-save-startup-file-via-temp-buffer t
+  "Whether to write the startup file contents to a buffer then save
+the buffer or write directly to the file.  The buffer is faster
+because all of the contents are written at once.  The direct write
+uses considerably less memory."
+  :version "22.1"
+  :group 'gnus-start
+  :type '(choice (const :tag "Write via buffer" t)
+                 (const :tag "Write directly to file" nil)))
+
+(defcustom gnus-init-file (nnheader-concat gnus-home-directory ".gnus")
+  "Your Gnus Emacs-Lisp startup file name.
+If a file with the `.el' or `.elc' suffixes exists, it will be read instead."
+  :group 'gnus-start
+  :type 'file)
+
+(defcustom gnus-site-init-file
+  (condition-case nil
+      (concat (file-name-directory
+              (directory-file-name installation-directory))
+             "site-lisp/gnus-init")
+    (error nil))
+  "The site-wide Gnus Emacs-Lisp startup file name, or nil if none.
+If a file with the `.el' or `.elc' suffixes exists, it will be read instead."
+  :group 'gnus-start
+  :type '(choice file (const nil)))
+
+(defcustom gnus-default-subscribed-newsgroups nil
+  "List of newsgroups to subscribe, when a user runs Gnus the first time.
+The value should be a list of strings.
+If it is t, Gnus will not do anything special the first time it is
+started; it'll just use the normal newsgroups subscription methods."
+  :group 'gnus-start
+  :type '(choice (repeat string) (const :tag "Nothing special" t)))
+
+(defcustom gnus-use-dribble-file t
   "*Non-nil means that Gnus will use a dribble file to store user updates.
 If Emacs should crash without saving the .newsrc files, complete
-information can be restored from the dribble file.")
+information can be restored from the dribble file."
+  :group 'gnus-dribble-file
+  :type 'boolean)
 
-(defvar gnus-dribble-directory nil
+(defcustom gnus-dribble-directory nil
   "*The directory where dribble files will be saved.
 If this variable is nil, the directory where the .newsrc files are
-saved will be used.")
+saved will be used."
+  :group 'gnus-dribble-file
+  :type '(choice directory (const nil)))
 
-(defvar gnus-check-new-newsgroups t
-  "*Non-nil means that Gnus will run gnus-find-new-newsgroups at startup.
+(defcustom gnus-check-new-newsgroups 'ask-server
+  "*Non-nil means that Gnus will run `gnus-find-new-newsgroups' at startup.
 This normally finds new newsgroups by comparing the active groups the
 servers have already reported with those Gnus already knows, either alive
 or killed.
 
-When any of the following are true, gnus-find-new-newsgroups will instead
+When any of the following are true, `gnus-find-new-newsgroups' will instead
 ask the servers (primary, secondary, and archive servers) to list new
 groups since the last time it checked:
   1. This variable is `ask-server'.
   2. This variable is a list of select methods (see below).
   3. `gnus-read-active-file' is nil or `some'.
-  4. A prefix argument is given to gnus-find-new-newsgroups interactively.
+  4. A prefix argument is given to `gnus-find-new-newsgroups' interactively.
 
 Thus, if this variable is `ask-server' or a list of select methods or
 `gnus-read-active-file' is nil or `some', then the killed list is no
@@ -85,14 +137,24 @@ Eg.
        '((nntp \"some.server\") (nntp \"other.server\")))
 
 If this variable is nil, then you have to tell Gnus explicitly to
-check for new newsgroups with \\<gnus-group-mode-map>\\[gnus-find-new-newsgroups].")
-
-(defvar gnus-check-bogus-newsgroups nil
+check for new newsgroups with \\<gnus-group-mode-map>\\[gnus-find-new-newsgroups]."
+  :group 'gnus-start
+  :type '(choice (const :tag "no" nil)
+                (const :tag "by brute force" t)
+                (const :tag "ask servers" ask-server)
+                (repeat :menu-tag "ask additional servers"
+                        :tag "ask additional servers"
+                        :value ((nntp ""))
+                        (sexp :format "%v"))))
+
+(defcustom gnus-check-bogus-newsgroups nil
   "*Non-nil means that Gnus will check and remove bogus newsgroup at startup.
 If this variable is nil, then you have to tell Gnus explicitly to
-check for bogus newsgroups with \\<gnus-group-mode-map>\\[gnus-group-check-bogus-groups].")
+check for bogus newsgroups with \\<gnus-group-mode-map>\\[gnus-group-check-bogus-groups]."
+  :group 'gnus-start-server
+  :type 'boolean)
 
-(defvar gnus-read-active-file t
+(defcustom gnus-read-active-file 'some
   "*Non-nil means that Gnus will read the entire active file at startup.
 If this variable is nil, Gnus will only know about the groups in your
 `.newsrc' file.
@@ -105,34 +167,44 @@ generally be faster than both the t and nil value.
 If you set this variable to nil or `some', 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.")
+properly with all servers."
+  :group 'gnus-start-server
+  :type '(choice (const nil)
+                (const some)
+                (const t)))
 
-(defvar gnus-level-subscribed 5
-  "*Groups with levels less than or equal to this variable are subscribed.")
+(defconst gnus-level-subscribed 5
+  "Groups with levels less than or equal to this variable are subscribed.")
 
-(defvar gnus-level-unsubscribed 7
-  "*Groups with levels less than or equal to this variable are unsubscribed.
+(defconst gnus-level-unsubscribed 7
+  "Groups with levels less than or equal to this variable are unsubscribed.
 Groups with levels less than `gnus-level-subscribed', which should be
 less than this variable, are subscribed.")
 
-(defvar gnus-level-zombie 8
-  "*Groups with this level are zombie groups.")
+(defconst gnus-level-zombie 8
+  "Groups with this level are zombie groups.")
 
-(defvar gnus-level-killed 9
-  "*Groups with this level are killed.")
+(defconst gnus-level-killed 9
+  "Groups with this level are killed.")
 
-(defvar gnus-level-default-subscribed 3
-  "*New subscribed groups will be subscribed at this level.")
+(defcustom gnus-level-default-subscribed 3
+  "*New subscribed groups will be subscribed at this level."
+  :group 'gnus-group-levels
+  :type 'integer)
 
-(defvar gnus-level-default-unsubscribed 6
-  "*New unsubscribed groups will be unsubscribed at this level.")
+(defcustom gnus-level-default-unsubscribed 6
+  "*New unsubscribed groups will be unsubscribed at this level."
+  :group 'gnus-group-levels
+  :type 'integer)
 
-(defvar gnus-activate-level (1+ gnus-level-subscribed)
+(defcustom gnus-activate-level (1+ gnus-level-subscribed)
   "*Groups higher than this level won't be activated on startup.
-Setting this variable to something log might save lots of time when
-you have many groups that you aren't interested in.")
+Setting this variable to something low might save lots of time when
+you have many groups that you aren't interested in."
+  :group 'gnus-group-levels
+  :type 'integer)
 
-(defvar gnus-activate-foreign-newsgroups 4
+(defcustom gnus-activate-foreign-newsgroups 4
   "*If nil, Gnus will not check foreign newsgroups at startup.
 If it is non-nil, it should be a number between one and nine.  Foreign
 newsgroups that have a level lower or equal to this number will be
@@ -143,17 +215,33 @@ subscribed newsgroups, but not the rest, you'd set this variable to
 If you subscribe to lots of newsgroups from different servers, startup
 might take a while.  By setting this variable to nil, you'll save time,
 but you won't be told how many unread articles there are in the
-groups.")
+groups."
+  :group 'gnus-group-levels
+  :type '(choice integer
+                (const :tag "none" nil)))
+
+(defcustom gnus-read-newsrc-file t
+  "*Non-nil means that Gnus will read the `.newsrc' file.
+Gnus always reads its own startup file, which is called
+\".newsrc.eld\".  The file called \".newsrc\" is in a format that can
+be readily understood by other newsreaders.  If you don't plan on
+using other newsreaders, set this variable to nil to save some time on
+entry."
+  :version "21.1"
+  :group 'gnus-newsrc
+  :type 'boolean)
 
-(defvar gnus-save-newsrc-file t
+(defcustom gnus-save-newsrc-file t
   "*Non-nil means that Gnus will save the `.newsrc' file.
 Gnus always saves its own startup file, which is called
 \".newsrc.eld\".  The file called \".newsrc\" is in a format that can
 be readily understood by other newsreaders.  If you don't plan on
 using other newsreaders, set this variable to nil to save some time on
-exit.")
+exit."
+  :group 'gnus-newsrc
+  :type 'boolean)
 
-(defvar gnus-save-killed-list t
+(defcustom gnus-save-killed-list t
   "*If non-nil, save the list of killed groups to the startup file.
 If you set this variable to nil, you'll save both time (when starting
 and quitting) and space (both memory and disk), but it will also mean
@@ -161,45 +249,84 @@ that Gnus has no record of which groups are new and which are old, so
 the automatic new newsgroups subscription methods become meaningless.
 
 You should always set `gnus-check-new-newsgroups' to `ask-server' or
-nil if you set this variable to nil.")
-
-(defvar gnus-ignored-newsgroups
-  (purecopy (mapconcat 'identity
-                      '("^to\\."       ; not "real" groups
-                        "^[0-9. \t]+ " ; all digits in name
-                        "[][\"#'()]"   ; bogus characters
-                        )
-                      "\\|"))
+nil if you set this variable to nil.
+
+This variable can also be a regexp.  In that case, all groups that do
+not match this regexp will be removed before saving the list."
+  :group 'gnus-newsrc
+  :type '(radio (sexp :format "Non-nil\n"
+                     :match (lambda (widget value)
+                              (and value (not (stringp value))))
+                     :value t)
+               (const nil)
+               regexp))
+
+(defcustom gnus-ignored-newsgroups
+  (mapconcat 'identity
+            '("^to\\."                 ; not "real" groups
+              "^[0-9. \t]+\\( \\|$\\)" ; all digits in name
+              "^[\"][]\"[#'()]"        ; bogus characters
+              )
+            "\\|")
   "*A regexp to match uninteresting newsgroups in the active file.
 Any lines in the active file matching this regular expression are
 removed from the newsgroup list before anything else is done to it,
-thus making them effectively non-existent.")
+thus making them effectively non-existent."
+  :group 'gnus-group-new
+  :type 'regexp)
 
-(defvar gnus-subscribe-newsgroup-method 'gnus-subscribe-zombies
-  "*Function called with a group name when new group is detected.
+(defcustom gnus-subscribe-newsgroup-method 'gnus-subscribe-zombies
+  "*Function(s) called with a group name when new group is detected.
 A few pre-made functions are supplied: `gnus-subscribe-randomly'
 inserts new groups at the beginning of the list of groups;
 `gnus-subscribe-alphabetically' inserts new groups in strict
 alphabetic order; `gnus-subscribe-hierarchically' inserts new groups
 in hierarchical newsgroup order; `gnus-subscribe-interactively' asks
 for your decision; `gnus-subscribe-killed' kills all new groups;
-`gnus-subscribe-zombies' will make all new groups into zombies.")
-
-;; 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)
-  "*This function is called to subscribe newsgroups mentioned on \"options -n\" lines.
+`gnus-subscribe-zombies' will make all new groups into zombies;
+`gnus-subscribe-topics' will enter groups into the topics that
+claim them."
+  :group 'gnus-group-new
+  :type '(radio (function-item gnus-subscribe-randomly)
+               (function-item gnus-subscribe-alphabetically)
+               (function-item gnus-subscribe-hierarchically)
+               (function-item gnus-subscribe-interactively)
+               (function-item gnus-subscribe-killed)
+               (function-item gnus-subscribe-zombies)
+               (function-item gnus-subscribe-topics)
+               function
+               (repeat function)))
+
+(defcustom gnus-subscribe-newsgroup-hooks nil
+  "*Hooks run after you subscribe to a new group.
+The hooks will be called with new group's name as argument."
+  :version "22.1"
+  :group 'gnus-group-new
+  :type 'hook)
+
+(defcustom gnus-subscribe-options-newsgroup-method
+  'gnus-subscribe-alphabetically
+  "*Function(s) called to subscribe newsgroups mentioned on \"options -n\" lines.
 If, for instance, you want to subscribe to all newsgroups in the
 \"no\" and \"alt\" hierarchies, you'd put the following in your
 .newsrc file:
 
 options -n no.all alt.all
 
-Gnus will the subscribe all new newsgroups in these hierarchies with
-the subscription method in this variable.")
-
-(defvar gnus-subscribe-hierarchical-interactive nil
+Gnus will then subscribe all new newsgroups in these hierarchies
+with the subscription method in this variable."
+  :group 'gnus-group-new
+  :type '(radio (function-item gnus-subscribe-randomly)
+               (function-item gnus-subscribe-alphabetically)
+               (function-item gnus-subscribe-hierarchically)
+               (function-item gnus-subscribe-interactively)
+               (function-item gnus-subscribe-killed)
+               (function-item gnus-subscribe-zombies)
+               (function-item gnus-subscribe-topics)
+               function
+               (repeat function)))
+
+(defcustom gnus-subscribe-hierarchical-interactive nil
   "*If non-nil, Gnus will offer to subscribe hierarchically.
 When a new hierarchy appears, Gnus will ask the user:
 
@@ -207,63 +334,133 @@ When a new hierarchy appears, Gnus will ask the user:
 
 If the user pressed `d', Gnus will descend the hierarchy, `y' will
 subscribe to all newsgroups in the hierarchy and `s' will skip this
-hierarchy in its entirety.")
+hierarchy in its entirety."
+  :group 'gnus-group-new
+  :type 'boolean)
 
-(defvar gnus-auto-subscribed-groups
-  "^nnml\\|^nnfolder\\|^nnmbox\\|^nnmh\\|^nnbabyl"
+(defcustom gnus-auto-subscribed-groups
+  "^nnml\\|^nnfolder\\|^nnmbox\\|^nnmh\\|^nnbabyl\\|^nnmaildir"
   "*All new groups that match this regexp will be subscribed automatically.
 Note that this variable only deals with new groups.  It has no effect
 whatsoever on old groups.
 
 New groups that match this regexp will not be handled by
 `gnus-subscribe-newsgroup-method'.  Instead, they will
-be subscribed using `gnus-subscribe-options-newsgroup-method'.")
+be subscribed using `gnus-subscribe-options-newsgroup-method'."
+  :group 'gnus-group-new
+  :type 'regexp)
 
-(defvar gnus-options-subscribe nil
+(defcustom gnus-options-subscribe nil
   "*All new groups matching this regexp will be subscribed unconditionally.
-Note that this variable deals only with new newsgroups.         This variable
+Note that this variable deals only with new newsgroups.  This variable
 does not affect old newsgroups.
 
 New groups that match this regexp will not be handled by
 `gnus-subscribe-newsgroup-method'.  Instead, they will
-be subscribed using `gnus-subscribe-options-newsgroup-method'.")
+be subscribed using `gnus-subscribe-options-newsgroup-method'."
+  :group 'gnus-group-new
+  :type '(choice regexp
+                (const :tag "none" nil)))
 
-(defvar gnus-options-not-subscribe nil
+(defcustom gnus-options-not-subscribe nil
   "*All new groups matching this regexp will be ignored.
-Note that this variable deals only with new newsgroups.         This variable
-does not affect old (already subscribed) newsgroups.")
-
-(defvar gnus-modtime-botch nil
-  "*Non-nil means .newsrc should be deleted prior to save.  
+Note that this variable deals only with new newsgroups.  This variable
+does not affect old (already subscribed) newsgroups."
+  :group 'gnus-group-new
+  :type '(choice regexp
+                (const :tag "none" nil)))
+
+(defcustom gnus-modtime-botch nil
+  "*Non-nil means .newsrc should be deleted prior to save.
 Its use is due to the bogus appearance that .newsrc was modified on
-disc.")
-
-(defvar gnus-check-bogus-groups-hook nil
-  "A hook run after removing bogus groups.")
-
-(defvar gnus-startup-hook nil
-  "*A hook called at startup.
-This hook is called after Gnus is connected to the NNTP server.")
-
-(defvar gnus-get-new-news-hook nil
-  "*A hook run just before Gnus checks for new news.")
-
-(defvar gnus-after-getting-new-news-hook nil
-  "*A hook run after Gnus checks for new news.")
-
-(defvar gnus-save-newsrc-hook nil
-  "*A hook called before saving any of the newsrc files.")
-
-(defvar gnus-save-quick-newsrc-hook nil
-  "*A hook called just before saving the quick newsrc file.
-Can be used to turn version control on or off.")
-
-(defvar gnus-save-standard-newsrc-hook nil
-  "*A hook called just before saving the standard newsrc file.
-Can be used to turn version control on or off.")
+disc."
+  :group 'gnus-newsrc
+  :type 'boolean)
+
+(defcustom gnus-check-bogus-groups-hook nil
+  "A hook run after removing bogus groups."
+  :group 'gnus-start-server
+  :type 'hook)
+
+(defcustom gnus-startup-hook nil
+  "A hook called at startup.
+This hook is called after Gnus is connected to the NNTP server."
+  :group 'gnus-start
+  :type 'hook)
+
+(defcustom gnus-before-startup-hook nil
+  "A hook called at before startup.
+This hook is called as the first thing when Gnus is started."
+  :group 'gnus-start
+  :type 'hook)
+
+(defcustom gnus-started-hook nil
+  "A hook called as the last thing after startup."
+  :group 'gnus-start
+  :type 'hook)
+
+(defcustom gnus-setup-news-hook
+  '(gnus-fixup-nnimap-unread-after-getting-new-news)
+  "A hook after reading the .newsrc file, but before generating the buffer."
+  :group 'gnus-start
+  :type 'hook)
+
+(defcustom gnus-get-top-new-news-hook nil
+  "A hook run just before Gnus checks for new news globally."
+  :version "22.1"
+  :group 'gnus-group-new
+  :type 'hook)
+
+(defcustom gnus-get-new-news-hook nil
+  "A hook run just before Gnus checks for new news."
+  :group 'gnus-group-new
+  :type 'hook)
+
+(defcustom gnus-after-getting-new-news-hook
+  '(gnus-display-time-event-handler
+    gnus-fixup-nnimap-unread-after-getting-new-news)
+  "*A hook run after Gnus checks for new news when Gnus is already running."
+  :group 'gnus-group-new
+  :type 'hook)
+
+(defcustom gnus-read-newsrc-el-hook nil
+  "A hook called after reading the newsrc.eld? file."
+  :group 'gnus-newsrc
+  :type 'hook)
+
+(defcustom gnus-save-newsrc-hook nil
+  "A hook called before saving any of the newsrc files."
+  :group 'gnus-newsrc
+  :type 'hook)
+
+(defcustom gnus-save-quick-newsrc-hook nil
+  "A hook called just before saving the quick newsrc file.
+Can be used to turn version control on or off."
+  :group 'gnus-newsrc
+  :type 'hook)
+
+(defcustom gnus-save-standard-newsrc-hook nil
+  "A hook called just before saving the standard newsrc file.
+Can be used to turn version control on or off."
+  :group 'gnus-newsrc
+  :type 'hook)
+
+(defcustom gnus-group-mode-hook nil
+  "Hook for Gnus group mode."
+  :group 'gnus-group-various
+  :options '(gnus-topic-mode)
+  :type 'hook)
+
+(defcustom gnus-always-read-dribble-file nil
+  "Unconditionally read the dribble file."
+  :group 'gnus-newsrc
+  :type 'boolean)
 
 ;;; Internal variables
 
+(defvar gnus-ding-file-coding-system mm-universal-coding-system
+  "Coding system for ding file.")
+
 (defvar gnus-newsrc-file-version nil)
 (defvar gnus-override-subscribe-method nil)
 (defvar gnus-dribble-buffer nil)
@@ -285,58 +482,65 @@ Can be used to turn version control on or off.")
 ;; Suggested by Brian Edmonds <edmonds@cs.ubc.ca>.
 (defvar gnus-init-inhibit nil)
 (defun gnus-read-init-file (&optional inhibit-next)
-  (if gnus-init-inhibit
-      (setq gnus-init-inhibit nil)
-    (setq gnus-init-inhibit inhibit-next)
-    (and 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")))
-        (condition-case var
-            (load gnus-init-file nil t)
-          (error
-           (error "Error in %s: %s" gnus-init-file var))))))
+  ;; Don't load .gnus if the -q option was used.
+  (when init-file-user
+    (if gnus-init-inhibit
+       (setq gnus-init-inhibit nil)
+      (setq gnus-init-inhibit inhibit-next)
+      (dolist (file (list gnus-site-init-file gnus-init-file))
+       (when (and file
+                  (locate-library file))
+         (if (or debug-on-error debug-on-quit)
+             (load file nil t)
+           (condition-case var
+               (load file nil t)
+             (error
+              (error "Error in %s: %s" file (cadr var))))))))))
 
 ;; For subscribing new newsgroup
 
 (defun gnus-subscribe-hierarchical-interactive (groups)
   (let ((groups (sort groups 'string<))
-       prefixes prefix start ans group starts)
+       prefixes prefix start ans group starts real-group)
     (while groups
       (setq prefixes (list "^"))
       (while (and groups prefixes)
-       (while (not (string-match (car prefixes) (car groups)))
+       (while (not (string-match (car prefixes)
+                                 (gnus-group-real-name (car groups))))
          (setq prefixes (cdr prefixes)))
        (setq prefix (car prefixes))
        (setq start (1- (length prefix)))
-       (if (and (string-match "[^\\.]\\." (car groups) start)
+       (if (and (string-match "[^\\.]\\." (gnus-group-real-name (car groups))
+                              start)
                 (cdr groups)
                 (setq prefix
-                      (concat "^" (substring (car groups) 0 (match-end 0))))
-                (string-match prefix (cadr groups)))
+                      (concat "^" (substring
+                                   (gnus-group-real-name (car groups))
+                                   0 (match-end 0))))
+                (string-match prefix (gnus-group-real-name (cadr groups))))
            (progn
-             (setq prefixes (cons prefix prefixes))
+             (push prefix prefixes)
              (message "Descend hierarchy %s? ([y]nsq): "
                       (substring prefix 1 (1- (length prefix))))
-             (while (not (memq (setq ans (read-char)) '(?y ?\n ?n ?s ?q)))
+             (while (not (memq (setq ans (read-char-exclusive))
+                               '(?y ?\n ?\r ?n ?s ?q)))
                (ding)
                (message "Descend hierarchy %s? ([y]nsq): "
                         (substring prefix 1 (1- (length prefix)))))
              (cond ((= ans ?n)
                     (while (and groups
-                                (string-match prefix
-                                              (setq group (car groups))))
-                      (setq gnus-killed-list
-                            (cons group gnus-killed-list))
+                                (setq group (car groups)
+                                      real-group (gnus-group-real-name group))
+                                (string-match prefix real-group))
+                      (push group gnus-killed-list)
                       (gnus-sethash group group gnus-killed-hashtb)
                       (setq groups (cdr groups)))
                     (setq starts (cdr starts)))
                    ((= ans ?s)
                     (while (and groups
-                                (string-match prefix
-                                              (setq group (car groups))))
+                                (setq group (car groups)
+                                      real-group (gnus-group-real-name group))
+                                (string-match prefix real-group))
                       (gnus-sethash group group gnus-killed-hashtb)
                       (gnus-subscribe-alphabetically (car groups))
                       (setq groups (cdr groups)))
@@ -344,12 +548,13 @@ Can be used to turn version control on or off.")
                    ((= ans ?q)
                     (while groups
                       (setq group (car groups))
-                      (setq gnus-killed-list (cons group gnus-killed-list))
+                      (push group gnus-killed-list)
                       (gnus-sethash group group gnus-killed-hashtb)
                       (setq groups (cdr groups))))
                    (t nil)))
          (message "Subscribe %s? ([n]yq)" (car groups))
-         (while (not (memq (setq ans (read-char)) '(?y ?\n ?q ?n)))
+         (while (not (memq (setq ans (read-char-exclusive))
+                           '(?y ?\n ?\r ?q ?n)))
            (ding)
            (message "Subscribe %s? ([n]yq)" (car groups)))
          (setq group (car groups))
@@ -359,11 +564,11 @@ Can be used to turn version control on or off.")
                ((= ans ?q)
                 (while groups
                   (setq group (car groups))
-                  (setq gnus-killed-list (cons group gnus-killed-list))
+                  (push group gnus-killed-list)
                   (gnus-sethash group group gnus-killed-hashtb)
                   (setq groups (cdr groups))))
                (t
-                (setq gnus-killed-list (cons group gnus-killed-list))
+                (push group gnus-killed-list)
                 (gnus-sethash group group gnus-killed-hashtb)))
          (setq groups (cdr groups)))))))
 
@@ -372,7 +577,7 @@ Can be used to turn version control on or off.")
   (gnus-subscribe-newsgroup newsgroup))
 
 (defun gnus-subscribe-alphabetically (newgroup)
-  "Subscribe new NEWSGROUP and insert it in alphabetical order."
+  "Subscribe new NEWGROUP and insert it in alphabetical order."
   (let ((groups (cdr gnus-newsrc-alist))
        before)
     (while (and (not before) groups)
@@ -382,32 +587,32 @@ Can be used to turn version control on or off.")
     (gnus-subscribe-newsgroup newgroup before)))
 
 (defun gnus-subscribe-hierarchically (newgroup)
-  "Subscribe new NEWSGROUP and insert it in hierarchical newsgroup order."
+  "Subscribe new NEWGROUP and insert it in hierarchical newsgroup order."
   ;; Basic ideas by mike-w@cs.aukuni.ac.nz (Mike Williams)
   (save-excursion
-    (set-buffer (find-file-noselect gnus-current-startup-file))
-    (let ((groupkey newgroup)
-         before)
-      (while (and (not before) groupkey)
-       (goto-char (point-min))
-       (let ((groupkey-re
-              (concat "^\\(" (regexp-quote groupkey) ".*\\)[!:]")))
-         (while (and (re-search-forward groupkey-re nil t)
-                     (progn
-                       (setq before (match-string 1))
-                       (string< before newgroup)))))
-       ;; Remove tail of newsgroup name (eg. a.b.c -> a.b)
-       (setq groupkey
-             (if (string-match "^\\(.*\\)\\.[^.]+$" groupkey)
-                 (substring groupkey (match-beginning 1) (match-end 1)))))
-      (gnus-subscribe-newsgroup newgroup before))
-    (kill-buffer (current-buffer))))
+    (set-buffer (nnheader-find-file-noselect gnus-current-startup-file))
+    (prog1
+       (let ((groupkey newgroup) before)
+         (while (and (not before) groupkey)
+           (goto-char (point-min))
+           (let ((groupkey-re
+                  (concat "^\\(" (regexp-quote groupkey) ".*\\)[!:]")))
+             (while (and (re-search-forward groupkey-re nil t)
+                         (progn
+                           (setq before (match-string 1))
+                           (string< before newgroup)))))
+           ;; Remove tail of newsgroup name (eg. a.b.c -> a.b)
+           (setq groupkey
+                 (when (string-match "^\\(.*\\)\\.[^.]+$" groupkey)
+                   (substring groupkey (match-beginning 1) (match-end 1)))))
+         (gnus-subscribe-newsgroup newgroup before))
+      (kill-buffer (current-buffer)))))
 
 (defun gnus-subscribe-interactively (group)
   "Subscribe the new GROUP interactively.
 It is inserted in hierarchical newsgroup order if subscribed.  If not,
 it is killed."
-  (if (gnus-y-or-n-p (format "Subscribe new newsgroup: %s " group))
+  (if (gnus-y-or-n-p (format "Subscribe new newsgroup %s? " group))
       (gnus-subscribe-hierarchically group)
     (push group gnus-killed-list)))
 
@@ -421,16 +626,17 @@ it is killed."
 
 (defun gnus-subscribe-newsgroup (newsgroup &optional next)
   "Subscribe new NEWSGROUP.
-If NEXT is non-nil, it is inserted before NEXT.         Otherwise it is made
+If NEXT is non-nil, it is inserted before NEXT.  Otherwise it is made
 the first newsgroup."
   (save-excursion
     (goto-char (point-min))
     ;; We subscribe the group by changing its level to `subscribed'.
     (gnus-group-change-level
      newsgroup gnus-level-default-subscribed
-     gnus-level-killed (gnus-gethash (or next "dummy.group")
-                                    gnus-newsrc-hashtb))
-    (gnus-message 5 "Subscribe newsgroup: %s" newsgroup)))
+     gnus-level-killed (gnus-group-entry (or next "dummy.group")))
+    (gnus-message 5 "Subscribe newsgroup: %s" newsgroup)
+    (run-hook-with-args 'gnus-subscribe-newsgroup-hooks newsgroup)
+    t))
 
 (defun gnus-read-active-file-p ()
   "Say whether the active file has been read from `gnus-select-method'."
@@ -439,33 +645,49 @@ the first newsgroup."
 ;;; General various misc type functions.
 
 ;; Silence byte-compiler.
-(defvar gnus-current-headers)
-(defvar gnus-thread-indent-array)
-(defvar gnus-newsgroup-name)
-(defvar gnus-newsgroup-headers)
-(defvar gnus-group-list-mode)
-(defvar gnus-group-mark-positions)
-(defvar gnus-newsgroup-data)
-(defvar gnus-newsgroup-unreads)
-(defvar nnoo-state-alist)
-(defvar gnus-current-select-method)
+(eval-when-compile
+  (defvar gnus-current-headers)
+  (defvar gnus-thread-indent-array)
+  (defvar gnus-newsgroup-name)
+  (defvar gnus-newsgroup-headers)
+  (defvar gnus-group-list-mode)
+  (defvar gnus-group-mark-positions)
+  (defvar gnus-newsgroup-data)
+  (defvar gnus-newsgroup-unreads)
+  (defvar nnoo-state-alist)
+  (defvar gnus-current-select-method)
+  (defvar mail-sources)
+  (defvar nnmail-scan-directory-mail-source-once)
+  (defvar nnmail-split-history)
+  (defvar nnmail-spool-file))
+
+(defun gnus-close-all-servers ()
+  "Close all servers."
+  (interactive)
+  (dolist (server gnus-opened-servers)
+    (gnus-close-server (car server))))
+
 (defun gnus-clear-system ()
   "Clear all variables and buffers."
   ;; Clear Gnus variables.
-  (let ((variables gnus-variable-list))
+  (let ((variables (remove 'gnus-format-specs gnus-variable-list)))
     (while variables
       (set (car variables) nil)
       (setq variables (cdr variables))))
   ;; Clear other internal variables.
   (setq gnus-list-of-killed-groups nil
        gnus-have-read-active-file nil
+        gnus-agent-covered-methods nil
+        gnus-agent-file-loading-local nil
+        gnus-agent-file-loading-cache nil
+        gnus-server-method-cache nil
        gnus-newsrc-alist nil
        gnus-newsrc-hashtb nil
        gnus-killed-list nil
        gnus-zombie-list nil
        gnus-killed-hashtb nil
        gnus-active-hashtb nil
-       gnus-moderated-list nil
+       gnus-moderated-hashtb nil
        gnus-description-hashtb nil
        gnus-current-headers nil
        gnus-thread-indent-array nil
@@ -478,7 +700,9 @@ the first newsgroup."
        gnus-newsgroup-data nil
        gnus-newsgroup-unreads nil
        nnoo-state-alist nil
-       gnus-current-select-method nil)
+       gnus-current-select-method nil
+       nnmail-split-history nil
+       gnus-ephemeral-servers nil)
   (gnus-shutdown 'gnus)
   ;; Kill the startup file.
   (and gnus-current-startup-file
@@ -491,18 +715,19 @@ the first newsgroup."
     (kill-buffer (get-file-buffer (gnus-newsgroup-kill-file nil))))
   (gnus-kill-buffer nntp-server-buffer)
   ;; Kill Gnus buffers.
-  (while gnus-buffer-list
-    (gnus-kill-buffer (pop gnus-buffer-list)))
+  (dolist (buffer (gnus-buffers))
+    (gnus-kill-buffer buffer))
   ;; Remove Gnus frames.
   (gnus-kill-gnus-frames))
 
 (defun gnus-no-server-1 (&optional arg slave)
   "Read network news.
-If ARG is a positive number, Gnus will use that as the
-startup level. If ARG is nil, Gnus will be started at level 2.
-If ARG is non-nil and not a positive number, Gnus will
-prompt the user for the name of an NNTP server to use.
-As opposed to `gnus', this command will not connect to the local server."
+If ARG is a positive number, Gnus will use that as the startup
+level.  If ARG is nil, Gnus will be started at level 2
+\(`gnus-level-default-subscribed' minus one).  If ARG is non-nil
+and not a positive number, Gnus will prompt the user for the name
+of an NNTP server to use.  As opposed to \\[gnus], this command
+will not connect to the local server."
   (interactive "P")
   (let ((val (or arg (1- gnus-level-default-subscribed))))
     (gnus val t slave)
@@ -512,38 +737,54 @@ As opposed to `gnus', this command will not connect to the local server."
 (defun gnus-1 (&optional arg dont-connect slave)
   "Read network news.
 If ARG is non-nil and a positive number, Gnus will use that as the
-startup level. If ARG is non-nil and not a positive number, Gnus will
+startup level.  If ARG is non-nil and not a positive number, Gnus will
 prompt the user for the name of an NNTP server to use."
   (interactive "P")
 
-  (if (and (get-buffer gnus-group-buffer)
-          (save-excursion
-            (set-buffer gnus-group-buffer)
-            (eq major-mode 'gnus-group-mode)))
+  (if (gnus-alive-p)
       (progn
        (switch-to-buffer gnus-group-buffer)
-       (gnus-group-get-new-news))
+       (gnus-group-get-new-news
+        (and (numberp arg)
+             (> arg 0)
+             (max (car gnus-group-list-mode) arg))))
 
-    (gnus-splash)
     (gnus-clear-system)
+    (gnus-splash)
+    (gnus-run-hooks 'gnus-before-startup-hook)
     (nnheader-init-server-buffer)
-    (gnus-read-init-file)
     (setq gnus-slave slave)
+    (gnus-read-init-file)
 
-    (when (string-match "xemacs" (emacs-version))
-      (gnus-splash))
+    ;; Add "native" to gnus-predefined-server-alist just to have a
+    ;; name for the native select method.
+    (when gnus-select-method
+      (push (cons "native" gnus-select-method)
+           gnus-predefined-server-alist))
+    
+    (if gnus-agent
+       (gnus-agentize))
+
+    (when gnus-simple-splash
+      (setq gnus-simple-splash nil)
+      (cond
+       ((featurep 'xemacs)
+       (gnus-xmas-splash))
+       ((and window-system
+            (= (frame-height) (1+ (window-height))))
+       (gnus-x-splash))))
 
     (let ((level (and (numberp arg) (> arg 0) arg))
          did-connect)
       (unwind-protect
          (progn
-           (or dont-connect
-               (setq did-connect
-                     (gnus-start-news-server (and arg (not level))))))
+           (unless dont-connect
+             (setq did-connect
+                   (gnus-start-news-server (and arg (not level))))))
        (if (and (not dont-connect)
                 (not did-connect))
            (gnus-group-quit)
-         (run-hooks 'gnus-startup-hook)
+         (gnus-run-hooks 'gnus-startup-hook)
          ;; NNTP server is successfully open.
 
          ;; Find the current startup file name.
@@ -554,32 +795,31 @@ prompt the user for the name of an NNTP server to use."
          (when (or gnus-slave gnus-use-dribble-file)
            (gnus-dribble-read-file))
 
-         ;; Allow using GroupLens predictions.
-         (when gnus-use-grouplens
-           (bbb-login)
-           (add-hook 'gnus-summary-mode-hook 'gnus-grouplens-mode))
-
          ;; Do the actual startup.
+         (if gnus-agent
+             (gnus-request-create-group "queue" '(nndraft "")))
+         (gnus-request-create-group "drafts" '(nndraft ""))
          (gnus-setup-news nil level dont-connect)
+         (gnus-run-hooks 'gnus-setup-news-hook)
+         (gnus-start-draft-setup)
          ;; Generate the group buffer.
          (gnus-group-list-groups level)
          (gnus-group-first-unread-group)
          (gnus-configure-windows 'group)
-         (gnus-group-set-mode-line))))))
-
-;;;###autoload
-(defun gnus-unload ()
-  "Unload all Gnus features."
-  (interactive)
-  (or (boundp 'load-history)
-      (error "Sorry, `gnus-unload' is not implemented in this Emacs version."))
-  (let ((history load-history)
-       feature)
-    (while history
-      (and (string-match "^\\(gnus\\|nn\\)" (caar history))
-          (setq feature (cdr (assq 'provide (car history))))
-          (unload-feature feature 'force))
-      (setq history (cdr history)))))
+         (gnus-group-set-mode-line)
+         (gnus-run-hooks 'gnus-started-hook))))))
+
+(defun gnus-start-draft-setup ()
+  "Make sure the draft group exists."
+  (gnus-request-create-group "drafts" '(nndraft ""))
+  (unless (gnus-group-entry "nndraft:drafts")
+    (let ((gnus-level-default-subscribed 1))
+      (gnus-subscribe-group "nndraft:drafts" nil '(nndraft ""))))
+  (unless (equal (gnus-group-get-parameter "nndraft:drafts" 'gnus-dummy t)
+                '((gnus-draft-mode)))
+    (gnus-message 3 "Setting up drafts group")
+    (gnus-group-set-parameter
+     "nndraft:drafts" 'gnus-dummy '((gnus-draft-mode)))))
 
 \f
 ;;;
@@ -600,49 +840,69 @@ prompt the user for the name of an NNTP server to use."
 
 (defun gnus-dribble-enter (string)
   "Enter STRING into the dribble buffer."
-  (if (and (not gnus-dribble-ignore)
-          gnus-dribble-buffer
-          (buffer-name gnus-dribble-buffer))
-      (let ((obuf (current-buffer)))
-       (set-buffer gnus-dribble-buffer)
-       (insert string "\n")
-       (set-window-point (get-buffer-window (current-buffer)) (point-max))
-       (bury-buffer gnus-dribble-buffer)
-       (set-buffer obuf))))
+  (when (and (not gnus-dribble-ignore)
+            gnus-dribble-buffer
+            (buffer-name gnus-dribble-buffer))
+    (let ((obuf (current-buffer)))
+      (set-buffer gnus-dribble-buffer)
+      (goto-char (point-max))
+      (insert string "\n")
+      ;; This has been commented by Josh Huber <huber@alum.wpi.edu>
+      ;; It causes problems with both XEmacs and Emacs 21, and doesn't
+      ;; seem to be of much value. (FIXME: remove this after we make sure
+      ;; it's not needed).
+      ;; (set-window-point (get-buffer-window (current-buffer)) (point-max))
+      (bury-buffer gnus-dribble-buffer)
+      (save-excursion
+       (set-buffer gnus-group-buffer)
+       (gnus-group-set-mode-line))
+      (set-buffer obuf))))
+
+(defun gnus-dribble-touch ()
+  "Touch the dribble buffer."
+  (gnus-dribble-enter ""))
 
 (defun gnus-dribble-read-file ()
   "Read the dribble file from disk."
   (let ((dribble-file (gnus-dribble-file-name)))
     (save-excursion
       (set-buffer (setq gnus-dribble-buffer
-                       (get-buffer-create
+                       (gnus-get-buffer-create
                         (file-name-nondirectory dribble-file))))
-      (gnus-add-current-to-buffer-list)
+      (set (make-local-variable 'file-precious-flag) t)
       (erase-buffer)
       (setq buffer-file-name dribble-file)
       (auto-save-mode t)
-      (buffer-disable-undo (current-buffer))
+      (buffer-disable-undo)
       (bury-buffer (current-buffer))
       (set-buffer-modified-p nil)
       (let ((auto (make-auto-save-file-name))
            (gnus-dribble-ignore t)
+           (purpose nil)
            modes)
        (when (or (file-exists-p auto) (file-exists-p dribble-file))
          ;; Load whichever file is newest -- the auto save file
          ;; or the "real" file.
          (if (file-newer-than-file-p auto dribble-file)
-             (insert-file-contents auto)
-           (insert-file-contents dribble-file))
+             (nnheader-insert-file-contents auto)
+           (nnheader-insert-file-contents dribble-file))
          (unless (zerop (buffer-size))
            (set-buffer-modified-p t))
          ;; Set the file modes to reflect the .newsrc file modes.
          (save-buffer)
          (when (and (file-exists-p gnus-current-startup-file)
+                    (file-exists-p dribble-file)
                     (setq modes (file-modes gnus-current-startup-file)))
-           (set-file-modes dribble-file modes))
+           (gnus-set-file-modes dribble-file modes))
+         (goto-char (point-min))
+         (when (search-forward "Gnus was exited on purpose" nil t)
+           (setq purpose t))
          ;; Possibly eval the file later.
-         (when (gnus-y-or-n-p
-                "Auto-save file exists.  Do you want to read it? ")
+         (when (or gnus-always-read-dribble-file
+                   (gnus-y-or-n-p
+                    (if purpose
+                        "Gnus exited on purpose without saving; read auto-save file anyway? "
+                    "Gnus auto-save file exists.  Do you want to read it? ")))
            (setq gnus-dribble-eval-file t)))))))
 
 (defun gnus-dribble-eval-file ()
@@ -660,8 +920,8 @@ prompt the user for the name of an NNTP server to use."
     (save-excursion
       (set-buffer gnus-dribble-buffer)
       (let ((auto (make-auto-save-file-name)))
-       (if (file-exists-p auto)
-           (delete-file auto))
+       (when (file-exists-p auto)
+         (delete-file auto))
        (erase-buffer)
        (set-buffer-modified-p nil)))))
 
@@ -689,25 +949,59 @@ prompt the user for the name of an NNTP server to use."
   "Setup news information.
 If RAWFILE is non-nil, the .newsrc file will also be read.
 If LEVEL is non-nil, the news will be set up at level LEVEL."
-  (let ((init (not (and gnus-newsrc-alist gnus-active-hashtb (not rawfile)))))
+  (require 'nnmail)
+  (let ((init (not (and gnus-newsrc-alist gnus-active-hashtb (not rawfile))))
+       ;; Binding this variable will inhibit multiple fetchings
+       ;; of the same mail source.
+       (nnmail-fetched-sources (list t)))
 
-    (when init 
+    (when init
       ;; Clear some variables to re-initialize news information.
       (setq gnus-newsrc-alist nil
            gnus-active-hashtb nil)
       ;; Read the newsrc file and create `gnus-newsrc-hashtb'.
       (gnus-read-newsrc-file rawfile))
 
-    (when (and (not (assoc "archive" gnus-server-alist))
-              (gnus-archive-server-wanted-p))
-      (push (cons "archive" gnus-message-archive-method)
-           gnus-server-alist))
+    ;; Make sure the archive server is available to all and sundry.
+    (when gnus-message-archive-method
+      (unless (assoc "archive" gnus-server-alist)
+       (let ((method (or (and (stringp gnus-message-archive-method)
+                              (gnus-server-to-method
+                               gnus-message-archive-method))
+                         gnus-message-archive-method)))
+         ;; Check whether the archive method is writable.
+         (unless (or (stringp method)
+                     (memq 'respool (assoc (format "%s" (car method))
+                                           gnus-valid-select-methods)))
+           (setq method "archive")) ;; The default.
+         (push (if (stringp method)
+                   `("archive"
+                     nnfolder
+                     ,method
+                     (nnfolder-directory
+                      ,(nnheader-concat message-directory method))
+                     (nnfolder-active-file
+                      ,(nnheader-concat message-directory
+                                        (concat method "/active")))
+                     (nnfolder-get-new-mail nil)
+                     (nnfolder-inhibit-expiry t))
+                 (cons "archive" method))
+               gnus-server-alist))))
 
     ;; If we don't read the complete active file, we fill in the
     ;; hashtb here.
-    (if (or (null gnus-read-active-file)
-           (eq gnus-read-active-file 'some))
-       (gnus-update-active-hashtb-from-killed))
+    (when (or (null gnus-read-active-file)
+             (eq gnus-read-active-file 'some))
+      (gnus-update-active-hashtb-from-killed))
+
+    ;; Validate agent covered methods now that gnus-server-alist has
+    ;; been initialized.
+    ;; NOTE: This is here for one purpose only.  By validating the
+    ;; agentized server's, it converts the old 5.10.3, and earlier,
+    ;; format to the current format.  That enables the agent code
+    ;; within gnus-read-active-file to function correctly.
+    (if gnus-agent
+        (gnus-agent-read-servers-validate))
 
     ;; Read the active file and create `gnus-active-hashtb'.
     ;; If `gnus-read-active-file' is nil, then we just create an empty
@@ -715,17 +1009,19 @@ If LEVEL is non-nil, the news will be set up at level LEVEL."
     ;; done in `gnus-get-unread-articles'.
     (and gnus-read-active-file
         (not level)
-        (gnus-read-active-file))
+        (gnus-read-active-file nil dont-connect))
 
-    (or gnus-active-hashtb
-       (setq gnus-active-hashtb (make-vector 4095 0)))
+    (unless gnus-active-hashtb
+      (setq gnus-active-hashtb (gnus-make-hashtable 4096)))
 
     ;; Initialize the cache.
     (when gnus-use-cache
       (gnus-cache-open))
 
     ;; Possibly eval the dribble file.
-    (and init (or gnus-use-dribble-file gnus-slave) (gnus-dribble-eval-file))
+    (and init
+        (or gnus-use-dribble-file gnus-slave)
+        (gnus-dribble-eval-file))
 
     ;; Slave Gnusii should then clear the dribble buffer.
     (when (and init gnus-slave)
@@ -734,43 +1030,82 @@ If LEVEL is non-nil, the news will be set up at level LEVEL."
     (gnus-update-format-specifications)
 
     ;; See whether we need to read the description file.
-    (if (and (string-match "%[-,0-9]*D" gnus-group-line-format)
-            (not gnus-description-hashtb)
-            (not dont-connect)
-            gnus-read-active-file)
-       (gnus-read-all-descriptions-files))
+    (when (and (boundp 'gnus-group-line-format)
+              (stringp gnus-group-line-format)
+              (let ((case-fold-search nil))
+                (string-match "%[-,0-9]*D" gnus-group-line-format))
+              (not gnus-description-hashtb)
+              (not dont-connect)
+              gnus-read-active-file)
+      (gnus-read-all-descriptions-files))
 
     ;; Find new newsgroups and treat them.
-    (if (and init gnus-check-new-newsgroups (not level)
-            (gnus-check-server gnus-select-method))
-       (gnus-find-new-newsgroups))
+    (when (and init gnus-check-new-newsgroups (not level)
+              (gnus-check-server gnus-select-method)
+              (not gnus-slave)
+              gnus-plugged)
+      (gnus-find-new-newsgroups))
+
+    ;; Check and remove bogus newsgroups.
+    (when (and init gnus-check-bogus-newsgroups
+              gnus-read-active-file (not level)
+              (gnus-server-opened gnus-select-method))
+      (gnus-check-bogus-newsgroups))
 
     ;; We might read in new NoCeM messages here.
-    (when (and gnus-use-nocem 
-              (not level)
-              (not dont-connect))
+    (when (and (not dont-connect)
+              gnus-use-nocem
+              (or (and (numberp gnus-use-nocem)
+                       (numberp level)
+                       (>= level gnus-use-nocem))
+                  (not level)))
       (gnus-nocem-scan-groups))
 
+    ;; Read any slave files.
+    (gnus-master-read-slave-newsrc)
+
     ;; Find the number of unread articles in each non-dead group.
     (let ((gnus-read-active-file (and (not level) gnus-read-active-file)))
-      (gnus-get-unread-articles level))
-
-    (if (and init gnus-check-bogus-newsgroups
-            gnus-read-active-file (not level)
-            (gnus-server-opened gnus-select-method))
-       (gnus-check-bogus-newsgroups))))
+      (gnus-get-unread-articles level))))
+
+(defun gnus-call-subscribe-functions (method group)
+  "Call METHOD to subscribe GROUP.
+If no function returns `non-nil', call `gnus-subscribe-zombies'."
+  (unless (cond
+          ((functionp method)
+           (funcall method group))
+          ((listp method)
+           (catch 'found
+             (dolist (func method)
+               (if (funcall func group)
+                   (throw 'found t)))
+             nil))
+          (t nil))
+    (gnus-subscribe-zombies group)))
 
 (defun gnus-find-new-newsgroups (&optional arg)
   "Search for new newsgroups and add them.
-Each new newsgroup will be treated with `gnus-subscribe-newsgroup-method.'
+Each new newsgroup will be treated with `gnus-subscribe-newsgroup-method'.
 The `-n' option line from .newsrc is respected.
-If ARG (the prefix), use the `ask-server' method to query
-the server for new groups."
-  (interactive "P")
-  (let ((check (if (or (and arg (not (listp gnus-check-new-newsgroups)))
-                      (null gnus-read-active-file)
-                      (eq gnus-read-active-file 'some))
-                  'ask-server gnus-check-new-newsgroups)))
+
+With 1 C-u, use the `ask-server' method to query the server for new
+groups.
+With 2 C-u's, use most complete method possible to query the server
+for new groups, and subscribe the new groups as zombies."
+  (interactive "p")
+  (let* ((gnus-subscribe-newsgroup-method
+         gnus-subscribe-newsgroup-method)
+        (check (cond
+                ((or (and (= (or arg 1) 4)
+                          (not (listp gnus-check-new-newsgroups)))
+                     (null gnus-read-active-file)
+                     (eq gnus-read-active-file 'some))
+                 'ask-server)
+                ((= (or arg 1) 16)
+                 (setq gnus-subscribe-newsgroup-method
+                       'gnus-subscribe-zombies)
+                 t)
+                (t gnus-check-new-newsgroups))))
     (unless (gnus-check-first-time-used)
       (if (or (consp check)
              (eq check 'ask-server))
@@ -782,7 +1117,7 @@ the server for new groups."
          (gnus-message 5 "Looking for new newsgroups...")
          (unless gnus-have-read-active-file
            (gnus-read-active-file))
-         (setq gnus-newsrc-last-checked-date (current-time-string))
+         (setq gnus-newsrc-last-checked-date (message-make-date))
          (unless gnus-killed-hashtb
            (gnus-make-hashtable-from-killed))
          ;; Go though every newsgroup in `gnus-active-hashtb' and compare
@@ -800,23 +1135,24 @@ the server for new groups."
                  ((eq do-sub 'subscribe)
                   (setq groups (1+ groups))
                   (gnus-sethash group group gnus-killed-hashtb)
-                  (funcall gnus-subscribe-options-newsgroup-method group))
+                  (gnus-call-subscribe-functions
+                   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)))))))
+                      (push group new-newsgroups)
+                    (gnus-call-subscribe-functions
+                     gnus-subscribe-newsgroup-method group)))))))
           gnus-active-hashtb)
          (when new-newsgroups
            (gnus-subscribe-hierarchical-interactive new-newsgroups))
-         ;; Suggested by Per Abrahamsen <amanda@iesd.auc.dk>.
          (if (> groups 0)
-             (gnus-message 6 "%d new newsgroup%s arrived."
+             (gnus-message 5 "%d new newsgroup%s arrived."
                            groups (if (> groups 1) "s have" " has"))
-           (gnus-message 6 "No new newsgroups.")))))))
+           (gnus-message 5 "No new newsgroups.")))))))
 
 (defun gnus-matches-options-n (group)
   ;; Returns `subscribe' if the group is to be unconditionally
@@ -846,7 +1182,8 @@ the server for new groups."
       (and regs (cdar regs))))))
 
 (defun gnus-ask-server-for-new-groups ()
-  (let* ((date (or gnus-newsrc-last-checked-date (current-time-string)))
+  (let* ((new-date (message-make-date))
+        (date (or gnus-newsrc-last-checked-date new-date))
         (methods (cons gnus-select-method
                        (nconc
                         (when (gnus-archive-server-wanted-p)
@@ -856,19 +1193,20 @@ the server for new groups."
                               gnus-check-new-newsgroups)
                          gnus-secondary-select-methods))))
         (groups 0)
-        (new-date (current-time-string))
         group new-newsgroups got-new method hashtb
         gnus-override-subscribe-method)
+    (unless gnus-killed-hashtb
+      (gnus-make-hashtable-from-killed))
     ;; Go through both primary and secondary select methods and
     ;; request new newsgroups.
     (while (setq method (gnus-server-get-method nil (pop methods)))
-      (setq new-newsgroups nil)
-      (setq gnus-override-subscribe-method method)
+      (setq new-newsgroups nil
+           gnus-override-subscribe-method method)
       (when (and (gnus-check-server method)
                 (gnus-request-newgroups date method))
        (save-excursion
-         (setq got-new t)
-         (setq hashtb (gnus-make-hashtable 100))
+         (setq got-new t
+               hashtb (gnus-make-hashtable 100))
          (set-buffer nntp-server-buffer)
          ;; Enter all the new groups into a hashtable.
          (gnus-active-to-gnus-format method hashtb 'ignore))
@@ -892,7 +1230,8 @@ the server for new groups."
                ((eq do-sub 'subscribe)
                 (incf groups)
                 (gnus-sethash group group gnus-killed-hashtb)
-                (funcall gnus-subscribe-options-newsgroup-method group))
+                (gnus-call-subscribe-functions
+                 gnus-subscribe-options-newsgroup-method group))
                ((eq do-sub 'ignore)
                 nil)
                (t
@@ -900,61 +1239,75 @@ the server for new groups."
                 (gnus-sethash group group gnus-killed-hashtb)
                 (if gnus-subscribe-hierarchical-interactive
                     (push group new-newsgroups)
-                  (funcall gnus-subscribe-newsgroup-method group)))))))
+                  (gnus-call-subscribe-functions
+                   gnus-subscribe-newsgroup-method group)))))))
         hashtb))
       (when new-newsgroups
        (gnus-subscribe-hierarchical-interactive new-newsgroups)))
-    ;; Suggested by Per Abrahamsen <amanda@iesd.auc.dk>.
-    (when (> groups 0)
-      (gnus-message 6 "%d new newsgroup%s arrived."
-                   groups (if (> groups 1) "s have" " has")))
-    (and got-new (setq gnus-newsrc-last-checked-date new-date))
+    (if (> groups 0)
+       (gnus-message 5 "%d new newsgroup%s arrived"
+                     groups (if (> groups 1) "s have" " has"))
+      (gnus-message 5 "No new newsgroups"))
+    (when got-new
+      (setq gnus-newsrc-last-checked-date new-date))
     got-new))
 
 (defun gnus-check-first-time-used ()
-  (if (or (> (length gnus-newsrc-alist) 1)
-         (file-exists-p gnus-startup-file)
-         (file-exists-p (concat gnus-startup-file ".el"))
-         (file-exists-p (concat gnus-startup-file ".eld")))
-      nil
+  (catch 'ended
+    ;; First check if any of the following files exist.  If they do,
+    ;; it's not the first time the user has used Gnus.
+    (dolist (file (list (concat gnus-current-startup-file ".el")
+                       (concat gnus-current-startup-file ".eld")
+                       (concat gnus-startup-file ".el")
+                       (concat gnus-startup-file ".eld")))
+      (when (file-exists-p file)
+       (throw 'ended nil)))
     (gnus-message 6 "First time user; subscribing you to default groups")
     (unless (gnus-read-active-file-p)
-      (gnus-read-active-file))
-    (setq gnus-newsrc-last-checked-date (current-time-string))
-    (let ((groups gnus-default-subscribed-newsgroups)
+      (let ((gnus-read-active-file t))
+       (gnus-read-active-file)))
+    (setq gnus-newsrc-last-checked-date (message-make-date))
+    ;; Subscribe to the default newsgroups.
+    (let ((groups (or gnus-default-subscribed-newsgroups
+                     gnus-backup-default-subscribed-newsgroups))
          group)
       (if (eq groups t)
-         nil
-       (setq groups (or groups gnus-backup-default-subscribed-newsgroups))
-       (mapatoms
-        (lambda (sym)
-          (if (null (setq group (symbol-name sym)))
-              ()
-            (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-active (car groups))
-             (gnus-group-change-level
-              (car groups) gnus-level-default-subscribed gnus-level-killed))
-         (setq groups (cdr groups)))
-       (gnus-group-make-help-group)
-       (and gnus-novice-user
-            (gnus-message 7 "`A k' to list killed groups"))))))
-
-(defun gnus-subscribe-group (group previous &optional method)
+         ;; If t, we subscribe (or not) all groups as if they were new.
+         (mapatoms
+          (lambda (sym)
+            (when (setq group (symbol-name sym))
+              (let ((do-sub (gnus-matches-options-n group)))
+                (cond
+                 ((eq do-sub 'subscribe)
+                  (gnus-sethash group group gnus-killed-hashtb)
+                  (gnus-call-subscribe-functions
+                   gnus-subscribe-options-newsgroup-method group))
+                 ((eq do-sub 'ignore)
+                  nil)
+                 (t
+                  (push group gnus-killed-list))))))
+          gnus-active-hashtb)
+       (dolist (group groups)
+         ;; Only subscribe the default groups that are activated.
+         (when (gnus-active group)
+           (gnus-group-change-level
+            group gnus-level-default-subscribed gnus-level-killed)))
+       (save-excursion
+         (set-buffer gnus-group-buffer)
+         ;; Don't error if the group already exists. This happens when a
+         ;; first-time user types 'F'. -- didier
+         (gnus-group-make-help-group t))
+       (when gnus-novice-user
+         (gnus-message 7 "`A k' to list killed groups"))))))
+
+(defun gnus-subscribe-group (group &optional previous method)
+  "Subscribe GROUP and put it after PREVIOUS."
   (gnus-group-change-level
    (if method
        (list t group gnus-level-default-subscribed nil nil method)
      group)
-   gnus-level-default-subscribed gnus-level-killed previous t))
+   gnus-level-default-subscribed gnus-level-killed previous t)
+  t)
 
 ;; `gnus-group-change-level' is the fundamental function for changing
 ;; subscription levels of newsgroups.  This might mean just changing
@@ -980,27 +1333,27 @@ the server for new groups."
        (if fromkilled (setq group (nth 1 entry))
          (setq group (car (nth 2 entry))))
       (setq group entry))
-    (if (and (stringp entry)
-            oldlevel
-            (< oldlevel gnus-level-zombie))
-       (setq entry (gnus-gethash entry gnus-newsrc-hashtb)))
+    (when (and (stringp entry)
+              oldlevel
+              (< oldlevel gnus-level-zombie))
+      (setq entry (gnus-group-entry entry)))
     (if (and (not oldlevel)
             (consp entry))
        (setq oldlevel (gnus-info-level (nth 2 entry)))
-      (setq oldlevel (or oldlevel 9)))
-    (if (stringp previous)
-       (setq previous (gnus-gethash previous gnus-newsrc-hashtb)))
+      (setq oldlevel (or oldlevel gnus-level-killed)))
+    (when (stringp previous)
+      (setq previous (gnus-group-entry previous)))
 
     (if (and (>= oldlevel gnus-level-zombie)
-            (gnus-gethash group gnus-newsrc-hashtb))
+            (gnus-group-entry group))
        ;; We are trying to subscribe a group that is already
        ;; subscribed.
        ()                              ; Do nothing.
 
-      (or (gnus-ephemeral-group-p group)
-         (gnus-dribble-enter
-          (format "(gnus-group-change-level %S %S %S %S %S)"
-                  group level oldlevel (car (nth 2 previous)) fromkilled)))
+      (unless (gnus-ephemeral-group-p group)
+       (gnus-dribble-enter
+        (format "(gnus-group-change-level %S %S %S %S %S)"
+                group level oldlevel (car (nth 2 previous)) fromkilled)))
 
       ;; Then we remove the newgroup from any old structures, if needed.
       ;; If the group was killed, we remove it from the killed or zombie
@@ -1008,24 +1361,22 @@ the server for new groups."
       ;; it from the newsrc hash table and assoc.
       (cond
        ((>= oldlevel gnus-level-zombie)
-       (if (= oldlevel gnus-level-zombie)
-           (setq gnus-zombie-list (delete group gnus-zombie-list))
-         (setq gnus-killed-list (delete group gnus-killed-list))))
+       ;; oldlevel could be wrong.
+       (setq gnus-zombie-list (delete group gnus-zombie-list))
+       (setq gnus-killed-list (delete group gnus-killed-list)))
        (t
-       (if (and (>= level gnus-level-zombie)
-                entry)
-           (progn
-             (gnus-sethash (car (nth 2 entry)) nil gnus-newsrc-hashtb)
-             (if (nth 3 entry)
-                 (setcdr (gnus-gethash (car (nth 3 entry))
-                                       gnus-newsrc-hashtb)
-                         (cdr entry)))
-             (setcdr (cdr entry) (cdddr entry))))))
+       (when (and (>= level gnus-level-zombie)
+                  entry)
+         (gnus-sethash (car (nth 2 entry)) nil gnus-newsrc-hashtb)
+         (when (nth 3 entry)
+           (setcdr (gnus-group-entry (car (nth 3 entry)))
+                   (cdr entry)))
+         (setcdr (cdr entry) (cdddr entry)))))
 
       ;; Finally we enter (if needed) the list where it is supposed to
       ;; go, and change the subscription level.  If it is to be killed,
       ;; we enter it into the killed or zombie list.
-      (cond 
+      (cond
        ((>= level gnus-level-zombie)
        ;; Remove from the hash table.
        (gnus-sethash group nil gnus-newsrc-hashtb)
@@ -1033,8 +1384,12 @@ the server for new groups."
        ;; groups.
        (unless (gnus-group-foreign-p group)
          (if (= level gnus-level-zombie)
-             (setq gnus-zombie-list (cons group gnus-zombie-list))
-           (setq gnus-killed-list (cons group gnus-killed-list)))))
+             (push group gnus-zombie-list)
+           (if (= oldlevel gnus-level-killed)
+               ;; Remove from active hashtb.
+               (unintern group gnus-active-hashtb)
+             ;; Don't add it into killed-list if it was killed.
+             (push 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
@@ -1050,14 +1405,14 @@ the server for new groups."
            (setq active (gnus-active group))
            (setq num
                  (if active (- (1+ (cdr active)) (car active)) t))
-           ;; Check whether the group is foreign.  If so, the
-           ;; foreign select method has to be entered into the
-           ;; info.
-           (let ((method (or gnus-override-subscribe-method
-                             (gnus-group-method group))))
-             (if (eq method gnus-select-method)
-                 (setq info (list group level nil))
-               (setq info (list group level nil nil method)))))
+           ;; Shorten the select method if possible, if we need to
+           ;; store it at all (native groups).
+           (let ((method (gnus-method-simplify
+                          (or gnus-override-subscribe-method
+                              (gnus-group-method group)))))
+             (if method
+                 (setq info (list group level nil nil method))
+               (setq info (list group level nil)))))
          (unless previous
            (setq previous
                  (let ((p gnus-newsrc-alist))
@@ -1074,14 +1429,18 @@ the server for new groups."
            (gnus-sethash group (cons num previous)
                          gnus-newsrc-hashtb))
          (when (cdr entry)
-           (setcdr (gnus-gethash (caadr entry) gnus-newsrc-hashtb) entry)))))
+           (setcdr (gnus-group-entry (caadr entry)) entry))
+         (gnus-dribble-enter
+          (format
+           "(gnus-group-set-info '%S)" info)))))
       (when gnus-group-change-level-function
-       (funcall gnus-group-change-level-function group level oldlevel)))))
+       (funcall gnus-group-change-level-function
+                group level oldlevel previous)))))
 
 (defun gnus-kill-newsgroup (newsgroup)
   "Obsolete function.  Kills a newsgroup."
   (gnus-group-change-level
-   (gnus-gethash newsgroup gnus-newsrc-hashtb) gnus-level-killed))
+   (gnus-group-entry newsgroup) gnus-level-killed))
 
 (defun gnus-check-bogus-newsgroups (&optional confirm)
   "Remove bogus newsgroups.
@@ -1091,26 +1450,34 @@ newsgroup."
        bogus group entry info)
     (gnus-message 5 "Checking bogus newsgroups...")
     (unless (gnus-read-active-file-p)
-      (gnus-read-active-file))
+      (gnus-read-active-file t))
     (when (gnus-read-active-file-p)
       ;; Find all bogus newsgroup that are subscribed.
       (while newsrc
        (setq info (pop newsrc)
              group (gnus-info-group info))
        (unless (or (gnus-active group) ; Active
-                   (gnus-info-method info) ; Foreign
-                   (and confirm
-                        (not (gnus-y-or-n-p
-                              (format "Remove bogus newsgroup: %s " group)))))
+                   (and (gnus-info-method info)
+                        (not (gnus-secondary-method-p
+                              (gnus-info-method info))))) ; Foreign
          ;; Found a bogus newsgroup.
          (push group bogus)))
-      ;; Remove all bogus subscribed groups by first killing them, and
-      ;; then removing them from the list of killed groups.
-      (while bogus
-       (when (setq entry (gnus-gethash (setq group (pop bogus))
-                                       gnus-newsrc-hashtb))
-         (gnus-group-change-level entry gnus-level-killed)
-         (setq gnus-killed-list (delete group gnus-killed-list))))
+      (if confirm
+         (map-y-or-n-p
+          "Remove bogus group %s? "
+          (lambda (group)
+            ;; Remove all bogus subscribed groups by first killing them, and
+            ;; then removing them from the list of killed groups.
+            (when (setq entry (gnus-group-entry group))
+              (gnus-group-change-level entry gnus-level-killed)
+              (setq gnus-killed-list (delete group gnus-killed-list))))
+          bogus '("group" "groups" "remove"))
+       (while (setq group (pop bogus))
+         ;; Remove all bogus subscribed groups by first killing them, and
+         ;; then removing them from the list of killed groups.
+         (when (setq entry (gnus-group-entry group))
+           (gnus-group-change-level entry gnus-level-killed)
+           (setq gnus-killed-list (delete group gnus-killed-list)))))
       ;; Then we remove all bogus groups from the list of killed and
       ;; zombie groups.  They are removed without confirmation.
       (let ((dead-lists '(gnus-killed-list gnus-zombie-list))
@@ -1124,7 +1491,7 @@ newsgroup."
              (set (car dead-lists)
                   (delete group (symbol-value (car dead-lists))))))
          (setq dead-lists (cdr dead-lists))))
-      (run-hooks 'gnus-check-bogus-groups-hook)
+      (gnus-run-hooks 'gnus-check-bogus-groups-hook)
       (gnus-message 5 "Checking bogus newsgroups...done"))))
 
 (defun gnus-check-duplicate-killed-groups ()
@@ -1143,26 +1510,82 @@ newsgroup."
     "Alter the ACTIVE info for GROUP to reflect the articles in the cache."
     (when gnus-cache-active-hashtb
       (let ((cache-active (gnus-gethash group gnus-cache-active-hashtb)))
-       (and cache-active 
-            (< (car cache-active) (car active))
-            (setcar active (car cache-active)))
-       (and cache-active
-            (> (cdr cache-active) (cdr active))
-            (setcdr active (cdr cache-active)))))))
+       (when cache-active
+         (when (< (car cache-active) (car active))
+           (setcar active (car cache-active)))
+         (when (> (cdr cache-active) (cdr active))
+           (setcdr active (cdr cache-active))))))))
+
+(defun gnus-activate-group (group &optional scan dont-check method)
+  "Check whether a group has been activated or not.
+If SCAN, request a scan of that group as well."
+  (let ((method (or method (inline (gnus-find-method-for-group group))))
+       active)
+    (and (inline (gnus-check-server method))
+        ;; We escape all bugs and quit here to make it possible to
+        ;; continue if a group is so out-there that it reports bugs
+        ;; and stuff.
+        (progn
+          (and scan
+               (gnus-check-backend-function 'request-scan (car method))
+               (gnus-request-scan group method))
+          t)
+        (if (or debug-on-error debug-on-quit)
+            (inline (gnus-request-group group dont-check method))
+          (condition-case nil
+              (inline (gnus-request-group group dont-check method))
+            ;;(error nil)
+            (quit
+             (message "Quit activating %s" group)
+             nil)))
+        (unless dont-check
+          (setq active (gnus-parse-active))
+          ;; If there are no articles in the group, the GROUP
+          ;; command may have responded with the `(0 . 0)'.  We
+          ;; ignore this if we already have an active entry
+          ;; for the group.
+          (if (and (zerop (or (car active) 0))
+                   (zerop (or (cdr active) 0))
+                   (gnus-active group))
+              (gnus-active group)
+
+             ;; If a cache is present, we may have to alter the active info.
+             (when gnus-use-cache
+               (inline (gnus-cache-possibly-alter-active
+                        group active)))
+
+             ;; If the agent is enabled, we may have to alter the active info.
+             (when gnus-agent
+               (gnus-agent-possibly-alter-active group active))
+
+            (gnus-set-active group active)
+            ;; Return the new active info.
+            active)))))
 
 (defun gnus-get-unread-articles-in-group (info active &optional update)
-  (when active
+  (when (and info active)
     ;; Allow the backend to update the info in the group.
-    (when (and update 
+    (when (and update
               (gnus-request-update-info
-               info (gnus-find-method-for-group (gnus-info-group info))))
+               info (inline (gnus-find-method-for-group
+                             (gnus-info-group info)))))
       (gnus-activate-group (gnus-info-group info) nil t))
+
     (let* ((range (gnus-info-read info))
           (num 0))
+
+      ;; These checks are present in gnus-activate-group but skipped
+      ;; due to setting dont-check in the preceeding call.
+
       ;; If a cache is present, we may have to alter the active info.
       (when (and gnus-use-cache info)
-       (inline (gnus-cache-possibly-alter-active 
+       (inline (gnus-cache-possibly-alter-active
                 (gnus-info-group info) active)))
+
+      ;; If the agent is enabled, we may have to alter the active info.
+      (when (and gnus-agent info)
+       (gnus-agent-possibly-alter-active (gnus-info-group info) active info))
+
       ;; Modify the list of read articles according to what articles
       ;; are available; then tally the unread articles and add the
       ;; number to the group hash table entry.
@@ -1199,26 +1622,27 @@ newsgroup."
                        (cdadr range))))
          (setcdr range (cddr range)))
        ;; Adjust the first element to be the same as the lower limit.
-       (if (and (not (atom (car range)))
-                (< (cdar range) (car active)))
-           (setcdr (car range) (1- (car active))))
+       (when (and (not (atom (car range)))
+                  (< (cdar 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))
-         ;; Go past all legal elements.
+         ;; Go past all valid elements.
          (while (and (cdr srange)
                      (<= (or (and (atom (cadr srange))
                                   (cadr srange))
-                             (caadr srange)) (cdr active)))
+                             (caadr srange))
+                         (cdr active)))
            (setq srange (cdr srange)))
-         (if (cdr srange)
-             ;; Nuke all remaining illegal elements.
-             (setcdr srange nil))
+         (when (cdr srange)
+           ;; Nuke all remaining invalid elements.
+           (setcdr srange nil))
 
          ;; Adjust the final element.
-         (if (and (not (atom (car srange)))
-                  (> (cdar srange) (cdr active)))
-             (setcdr (car srange) (cdr active))))
+         (when (and (not (atom (car srange)))
+                    (> (cdar srange) (cdr active)))
+           (setcdr (car srange) (cdr active))))
        ;; Compute the number of unread articles.
        (while range
          (setq num (+ num (- (1+ (or (and (atom (car range)) (car range))
@@ -1228,13 +1652,15 @@ newsgroup."
          (setq range (cdr range)))
        (setq num (max 0 (- (cdr active) num)))))
       ;; Set the number of unread articles.
-      (when info
-       (setcar (gnus-gethash (gnus-info-group info) gnus-newsrc-hashtb) num))
+      (when (and info
+                (gnus-group-entry (gnus-info-group info)))
+       (setcar (gnus-group-entry (gnus-info-group info)) num))
       num)))
 
 ;; Go though `gnus-newsrc-alist' and compare with `gnus-active-hashtb'
 ;; and compute how many unread articles there are in each group.
 (defun gnus-get-unread-articles (&optional level)
+  (setq gnus-server-method-cache nil)
   (let* ((newsrc (cdr gnus-newsrc-alist))
         (level (or level gnus-activate-level (1+ gnus-level-subscribed)))
         (foreign-level
@@ -1246,8 +1672,11 @@ newsgroup."
                  gnus-activate-foreign-newsgroups)
                 (t 0))
           level))
-        info group active method)
-    (gnus-message 5 "Checking new news...")
+        (methods-cache nil)
+        (type-cache nil)
+        scanned-methods info group active method retrieve-groups cmethod
+        method-type ignore)
+    (gnus-message 6 "Checking new news...")
 
     (while newsrc
       (setq active (gnus-active (setq group (gnus-info-group
@@ -1258,41 +1687,129 @@ newsgroup."
       ;; be reached) we just set the number of unread articles in this
       ;; newsgroup to t.  This means that Gnus thinks that there are
       ;; unread articles, but it has no idea how many.
-      (if (and (setq method (gnus-info-method info))
-              (not (gnus-server-equal
-                    gnus-select-method
-                    (setq method (gnus-server-get-method nil method))))
-              (not (gnus-secondary-method-p method)))
-         ;; These groups are foreign.  Check the level.
-         (when (<= (gnus-info-level info) foreign-level)
-           (setq active (gnus-activate-group group 'scan))
-           (unless (inline (gnus-virtual-group-p group))
-             (inline (gnus-close-group group)))
-           (when (fboundp (intern (concat (symbol-name (car method))
-                                          "-request-update-info")))
-             (inline (gnus-request-update-info info method))))
-       ;; These groups are native or secondary.
-       (when (and (<= (gnus-info-level info) level)
-                  (not gnus-read-active-file))
-         (setq active (gnus-activate-group group 'scan))
-         (inline (gnus-close-group group))))
+
+      ;; To be more explicit:
+      ;; >0 for an active group with messages
+      ;; 0 for an active group with no unread messages
+      ;; nil for non-foreign groups that the user has requested not be checked
+      ;; t for unchecked foreign groups or bogus groups, or groups that can't
+      ;;   be checked, for one reason or other.
+      (when (setq method (gnus-info-method info))
+       (if (setq cmethod (assoc method methods-cache))
+           (setq method (cdr cmethod))
+         (setq cmethod (inline (gnus-server-get-method nil method)))
+         (push (cons method cmethod) methods-cache)
+         (setq method cmethod)))
+      (when (and method
+                (not (setq method-type (cdr (assoc method type-cache)))))
+       (setq method-type
+             (cond
+              ((gnus-secondary-method-p method)
+               'secondary)
+              ((inline (gnus-server-equal gnus-select-method method))
+               'primary)
+              (t
+               'foreign)))
+       (push (cons method method-type) type-cache))
+
+      (setq ignore nil)
+      (cond ((and method (eq method-type 'foreign))
+            ;; These groups are foreign.  Check the level.
+            (if (<= (gnus-info-level info) foreign-level)
+                (when (setq active (gnus-activate-group group 'scan))
+                  ;; Let the Gnus agent save the active file.
+                  (when (and gnus-agent active (gnus-online method))
+                    (gnus-agent-save-group-info
+                     method (gnus-group-real-name group) active))
+                  (unless (inline (gnus-virtual-group-p group))
+                    (inline (gnus-close-group group)))
+                  (when (fboundp (intern (concat (symbol-name (car method))
+                                                 "-request-update-info")))
+                    (inline (gnus-request-update-info info method))))
+              (setq ignore t)))
+           ;; These groups are native or secondary.
+           ((> (gnus-info-level info) level)
+            ;; We don't want these groups.
+            (setq active 'ignore))
+           ;; Activate groups.
+           ((not gnus-read-active-file)
+            (if (gnus-check-backend-function 'retrieve-groups group)
+                ;; if server support gnus-retrieve-groups we push
+                ;; the group onto retrievegroups for later checking
+                (if (assoc method retrieve-groups)
+                    (setcdr (assoc method retrieve-groups)
+                            (cons group (cdr (assoc method retrieve-groups))))
+                  (push (list method group) retrieve-groups))
+              ;; hack: `nnmail-get-new-mail' changes the mail-source depending
+              ;; on the group, so we must perform a scan for every group
+              ;; if the users has any directory mail sources.
+              ;; hack: if `nnmail-scan-directory-mail-source-once' is non-nil,
+              ;; for it scan all spool files even when the groups are
+              ;; not required.
+              (if (and
+                   (or nnmail-scan-directory-mail-source-once
+                       (null (assq 'directory
+                                   (or mail-sources
+                                       (if (listp nnmail-spool-file)
+                                           nnmail-spool-file
+                                         (list nnmail-spool-file))))))
+                   (member method scanned-methods))
+                  (setq active (gnus-activate-group group))
+                (setq active (gnus-activate-group group 'scan))
+                (push method scanned-methods))
+              (when active
+                (gnus-close-group group)))))
 
       ;; Get the number of unread articles in the group.
-      (if active
-         (inline (gnus-get-unread-articles-in-group info active))
+      (cond
+       ((eq active 'ignore)
+       ;; Don't do anything.
+       )
+       ((and active ignore)
+       ;; The level of the foreign group is higher than the specified
+       ;; value.
+       )
+       (active
+       (inline (gnus-get-unread-articles-in-group info active t)))
+       (t
        ;; The group couldn't be reached, so we nix out the number of
        ;; unread articles and stuff.
        (gnus-set-active group nil)
-       (setcar (gnus-gethash group gnus-newsrc-hashtb) t)))
+       (let ((tmp (gnus-group-entry group)))
+         (when tmp
+           (setcar tmp t))))))
+
+    ;; iterate through groups on methods which support gnus-retrieve-groups
+    ;; and fetch a partial active file and use it to find new news.
+    (dolist (rg retrieve-groups)
+      (let ((method (or (car rg) gnus-select-method))
+           (groups (cdr rg)))
+       (when (gnus-check-server method)
+         ;; Request that the backend scan its incoming messages.
+         (when (gnus-check-backend-function 'request-scan (car method))
+           (gnus-request-scan nil method))
+         (gnus-read-active-file-2
+          (mapcar (lambda (group) (gnus-group-real-name group)) groups)
+          method)
+         (dolist (group groups)
+           (cond
+            ((setq active (gnus-active (gnus-info-group
+                                        (setq info (gnus-get-info group)))))
+             (inline (gnus-get-unread-articles-in-group info active t)))
+            (t
+             ;; The group couldn't be reached, so we nix out the number of
+             ;; unread articles and stuff.
+             (gnus-set-active group nil)
+             (setcar (gnus-group-entry group) t)))))))
 
-    (gnus-message 5 "Checking new news...done")))
+    (gnus-message 6 "Checking new news...done")))
 
 ;; Create a hash table out of the newsrc alist.  The `car's of the
 ;; alist elements are used as keys.
 (defun gnus-make-hashtable-from-newsrc-alist ()
   (let ((alist gnus-newsrc-alist)
        (ohashtb gnus-newsrc-hashtb)
-       prev)
+       prev info method rest methods)
     (setq gnus-newsrc-hashtb (gnus-make-hashtable (length alist)))
     (setq alist
          (setq prev (setq gnus-newsrc-alist
@@ -1301,13 +1818,26 @@ newsgroup."
                               gnus-newsrc-alist
                             (cons (list "dummy.group" 0 nil) alist)))))
     (while alist
+      (setq info (car alist))
+      ;; Make the same select-methods identical Lisp objects.
+      (when (setq method (gnus-info-method info))
+       (if (setq rest (member method methods))
+           (gnus-info-set-method info (car rest))
+         (push method methods)))
       (gnus-sethash
-       (caar alist)
-       (cons (and ohashtb (car (gnus-gethash (caar alist) ohashtb)))
+       (car info)
+       ;; Preserve number of unread articles in groups.
+       (cons (and ohashtb (car (gnus-gethash (car info) ohashtb)))
             prev)
        gnus-newsrc-hashtb)
       (setq prev alist
-           alist (cdr alist)))))
+           alist (cdr alist)))
+    ;; Make the same select-methods in `gnus-server-alist' identical
+    ;; as well.
+    (while methods
+      (setq method (pop methods))
+      (when (setq rest (rassoc method gnus-server-alist))
+       (setcdr rest method)))))
 
 (defun gnus-make-hashtable-from-killed ()
   "Create a hash table from the killed and zombie lists."
@@ -1316,33 +1846,11 @@ newsgroup."
     (setq gnus-killed-hashtb
          (gnus-make-hashtable
           (+ (length gnus-killed-list) (length gnus-zombie-list))))
-    (while (setq list (pop lists))
-      (setq list (symbol-value list))
+    (while lists
+      (setq list (symbol-value (pop lists)))
       (while list
        (gnus-sethash (car list) (pop list) gnus-killed-hashtb)))))
 
-(defun gnus-activate-group (group &optional scan dont-check method)
-  ;; Check whether a group has been activated or not.
-  ;; If SCAN, request a scan of that group as well.
-  (let ((method (or method (gnus-find-method-for-group group)))
-       active)
-    (and (gnus-check-server method)
-        ;; We escape all bugs and quit here to make it possible to
-        ;; continue if a group is so out-there that it reports bugs
-        ;; and stuff.
-        (progn
-          (and scan
-               (gnus-check-backend-function 'request-scan (car method))
-               (gnus-request-scan group method))
-          t)
-        (condition-case ()
-            (gnus-request-group group dont-check method)
-                                       ;   (error nil)
-          (quit nil))
-        (gnus-set-active group (setq active (gnus-parse-active)))
-        ;; Return the new active info.
-        active)))
-
 (defun gnus-parse-active ()
   "Parse active info in the nntp server buffer."
   (save-excursion
@@ -1356,23 +1864,96 @@ newsgroup."
 
 (defun gnus-make-articles-unread (group articles)
   "Mark ARTICLES in GROUP as unread."
-  (let* ((info (nth 2 (or (gnus-gethash group gnus-newsrc-hashtb)
-                         (gnus-gethash (gnus-group-real-name group)
-                                       gnus-newsrc-hashtb))))
+  (let* ((info (nth 2 (or (gnus-group-entry group)
+                         (gnus-group-entry
+                          (gnus-group-real-name group)))))
         (ranges (gnus-info-read info))
         news article)
     (while articles
       (when (gnus-member-of-range
             (setq article (pop articles)) ranges)
-       (setq news (cons article news))))
+       (push article news)))
     (when news
+      ;; Enter this list into the group info.
       (gnus-info-set-read
        info (gnus-remove-from-range (gnus-info-read info) (nreverse news)))
+
+      ;; Set the number of unread articles in gnus-newsrc-hashtb.
+      (gnus-get-unread-articles-in-group info (gnus-active group))
+
+      ;; Insert the change into the group buffer and the dribble file.
+      (gnus-group-update-group group t))))
+
+(defun gnus-make-ascending-articles-unread (group articles)
+  "Mark ascending ARTICLES in GROUP as unread."
+  (let* ((entry (or (gnus-group-entry group)
+                    (gnus-group-entry (gnus-group-real-name group))))
+         (info (nth 2 entry))
+        (ranges (gnus-info-read info))
+         (r ranges)
+        modified)
+
+    (while articles
+      (let ((article (pop articles))) ; get the next article to remove from ranges
+        (while (let ((range (car ranges))) ; note the current range
+                 (if (atom range)       ; single value range
+                     (cond ((not range)
+                            ;; the articles extend past the end of the ranges
+                            ;; OK - I'm done
+                            (setq articles nil))
+                           ((< range article)
+                            ;; this range preceeds the article. Leave the range unmodified.
+                            (pop ranges)
+                            ranges)
+                           ((= range article)
+                            ;; this range exactly matches the article; REMOVE THE RANGE.
+                            ;; NOTE: When the range being removed is the last range, the list is corrupted by inserting null at its end.
+                            (setcar ranges (cadr ranges))
+                            (setcdr ranges (cddr ranges))
+                            (setq modified (if (car ranges) t 'remove-null))
+                            nil))
+                   (let ((min (car range))
+                         (max (cdr range)))
+                     ;; I have a min/max range to consider
+                     (cond ((> min max) ; invalid range introduced by splitter
+                            (setcar ranges (cadr ranges))
+                            (setcdr ranges (cddr ranges))
+                            (setq modified (if (car ranges) t 'remove-null))
+                            ranges)
+                           ((= min max)
+                            ;; replace min/max range with a single-value range
+                            (setcar ranges min)
+                            ranges)
+                           ((< max article)
+                            ;; this range preceeds the article. Leave the range unmodified.
+                            (pop ranges)
+                            ranges)
+                           ((< article min)
+                            ;; this article preceeds the range.  Return null to move to the
+                            ;; next article
+                            nil)
+                           (t
+                            ;; this article splits the range into two parts
+                            (setcdr ranges (cons (cons (1+ article) max) (cdr ranges)))
+                            (setcdr range (1- article))
+                            (setq modified t)
+                            ranges))))))))
+
+    (when modified
+      (when (eq modified 'remove-null)
+        (setq r (delq nil r)))
+      ;; Enter this list into the group info.
+      (gnus-info-set-read info r)
+
+      ;; Set the number of unread articles in gnus-newsrc-hashtb.
+      (gnus-get-unread-articles-in-group info (gnus-active group))
+
+      ;; Insert the change into the group buffer and the dribble file.
       (gnus-group-update-group group t))))
 
 ;; Enter all dead groups into the hashtb.
 (defun gnus-update-active-hashtb-from-killed ()
-  (let ((hashtb (setq gnus-active-hashtb (make-vector 4095 0)))
+  (let ((hashtb (setq gnus-active-hashtb (gnus-make-hashtable 4096)))
        (lists (list gnus-killed-list gnus-zombie-list))
        killed)
     (while lists
@@ -1388,7 +1969,8 @@ newsgroup."
   (unless (gnus-read-active-file-p)
     (let ((gnus-read-active-file t))
       (gnus-read-active-file)))
-  (or gnus-killed-hashtb (gnus-make-hashtable-from-killed))
+  (unless gnus-killed-hashtb
+    (gnus-make-hashtable-from-killed))
   ;; Go through all newsgroups that are known to Gnus - enlarge kill list.
   (mapatoms
    (lambda (sym)
@@ -1402,82 +1984,107 @@ newsgroup."
           (if (or (eq do-sub 'subscribe) (eq do-sub 'ignore))
               ()
             (setq groups (1+ groups))
-            (setq gnus-killed-list
-                  (cons group gnus-killed-list))
+            (push group gnus-killed-list)
             (gnus-sethash group group gnus-killed-hashtb))))))
-   gnus-active-hashtb))
+   gnus-active-hashtb)
+  (gnus-dribble-touch))
 
 ;; Get the active file(s) from the backend(s).
-(defun gnus-read-active-file ()
+(defun gnus-read-active-file (&optional force not-native)
   (gnus-group-set-mode-line)
-  (let ((methods 
-        (append
-         (if (gnus-check-server gnus-select-method)
-             ;; The native server is available.
-             (cons gnus-select-method gnus-secondary-select-methods)
-           ;; The native server is down, so we just do the
-           ;; secondary ones.
-           gnus-secondary-select-methods)
-         ;; Also read from the archive server.
-         (when (gnus-archive-server-wanted-p)
-           (list "archive"))))
-       list-type)
+  (let ((methods
+        (mapcar
+         (lambda (m) (if (stringp m) (gnus-server-get-method nil m) m))
+         (append
+          (if (and (not not-native)
+                   (gnus-check-server gnus-select-method))
+              ;; The native server is available.
+              (cons gnus-select-method gnus-secondary-select-methods)
+            ;; The native server is down, so we just do the
+            ;; secondary ones.
+            gnus-secondary-select-methods)
+          ;; Also read from the archive server.
+          (when (gnus-archive-server-wanted-p)
+            (list "archive")))))
+       method)
     (setq gnus-have-read-active-file nil)
     (save-excursion
       (set-buffer nntp-server-buffer)
-      (while methods
-       (let* ((method (if (stringp (car methods))
-                          (gnus-server-get-method nil (car methods))
-                        (car methods)))
-              (where (nth 1 method))
-              (mesg (format "Reading active file%s via %s..."
-                            (if (and where (not (zerop (length where))))
-                                (concat " from " where) "")
-                            (car method))))
+      (while (setq method (pop methods))
+       ;; Only do each method once, in case the methods appear more
+       ;; than once in this list.
+       (unless (member method methods)
+         (if (or debug-on-error debug-on-quit)
+             (gnus-read-active-file-1 method force)
+           (condition-case ()
+               (gnus-read-active-file-1 method force)
+             ;; We catch C-g so that we can continue past servers
+             ;; that do not respond.
+             (quit
+              (message "Quit reading the active file")
+              nil))))))))
+
+(defun gnus-read-active-file-1 (method force)
+  (let (where mesg)
+    (setq where (nth 1 method)
+         mesg (format "Reading active file%s via %s..."
+                      (if (and where (not (zerop (length where))))
+                          (concat " from " where) "")
+                      (car method)))
+    (gnus-message 5 mesg)
+    (when (gnus-check-server method)
+      ;; Request that the backend scan its incoming messages.
+      (when (gnus-check-backend-function 'request-scan (car method))
+       (gnus-request-scan nil method))
+      (cond
+       ((and (eq gnus-read-active-file 'some)
+            (gnus-check-backend-function 'retrieve-groups (car method))
+            (not force))
+       (let ((newsrc (cdr gnus-newsrc-alist))
+             (gmethod (gnus-server-get-method nil method))
+             groups info)
+         (while (setq info (pop newsrc))
+           (when (inline
+                   (gnus-server-equal
+                         (inline
+                           (gnus-find-method-for-group
+                                 (gnus-info-group info) info))
+                         gmethod))
+             (push (gnus-group-real-name (gnus-info-group info))
+                   groups)))
+         (gnus-read-active-file-2 groups method)))
+       ((null method)
+       t)
+       (t
+       (if (not (gnus-request-list method))
+           (unless (equal method gnus-message-archive-method)
+             (gnus-error 1 "Cannot read active file from %s server"
+                         (car method)))
          (gnus-message 5 mesg)
-         (when (gnus-check-server method)
-           ;; Request that the backend scan its incoming messages.
-           (and (gnus-check-backend-function 'request-scan (car method))
-                (gnus-request-scan nil method))
-           (cond
-            ((and (eq gnus-read-active-file 'some)
-                  (gnus-check-backend-function 'retrieve-groups (car method)))
-             (let ((newsrc (cdr gnus-newsrc-alist))
-                   (gmethod (gnus-server-get-method nil method))
-                   groups info)
-               (while (setq info (pop newsrc))
-                 (when (gnus-server-equal
-                        (gnus-find-method-for-group 
-                         (gnus-info-group info) info)
-                        gmethod)
-                   (push (gnus-group-real-name (gnus-info-group info)) 
-                         groups)))
-               (when groups
-                 (gnus-check-server method)
-                 (setq list-type (gnus-retrieve-groups groups method))
-                 (cond
-                  ((not list-type)
-                   (gnus-error
-                    1.2 "Cannot read partial active file from %s server."
-                    (car method)))
-                  ((eq list-type 'active)
-                   (gnus-active-to-gnus-format method gnus-active-hashtb))
-                  (t
-                   (gnus-groups-to-gnus-format method gnus-active-hashtb))))))
-            (t
-             (if (not (gnus-request-list method))
-                 (unless (equal method gnus-message-archive-method)
-                   (gnus-error 1 "Cannot read active file from %s server."
-                               (car method)))
-               (gnus-message 5 mesg)
-               (gnus-active-to-gnus-format method gnus-active-hashtb)
-               ;; We mark this active file as read.
-               (push method gnus-have-read-active-file)
-               (gnus-message 5 "%sdone" mesg))))))
-       (setq methods (cdr methods))))))
+         (gnus-active-to-gnus-format method gnus-active-hashtb nil t)
+         ;; We mark this active file as read.
+         (push method gnus-have-read-active-file)
+         (gnus-message 5 "%sdone" mesg)))))))
+
+(defun gnus-read-active-file-2 (groups method)
+  "Read an active file for GROUPS in METHOD using `gnus-retrieve-groups'."
+  (when groups
+    (save-excursion
+      (set-buffer nntp-server-buffer)
+      (gnus-check-server method)
+      (let ((list-type (gnus-retrieve-groups groups method)))
+       (cond ((not list-type)
+              (gnus-error
+               1.2 "Cannot read partial active file from %s server."
+               (car method)))
+             ((eq list-type 'active)
+              (gnus-active-to-gnus-format method gnus-active-hashtb nil t))
+             (t
+              (gnus-groups-to-gnus-format method gnus-active-hashtb t)))))))
 
 ;; Read an active file and place the results in `gnus-active-hashtb'.
-(defun gnus-active-to-gnus-format (&optional method hashtb ignore-errors)
+(defun gnus-active-to-gnus-format (&optional method hashtb ignore-errors
+                                            real-active)
   (unless method
     (setq method gnus-select-method))
   (let ((cur (current-buffer))
@@ -1492,96 +2099,80 @@ newsgroup."
                              (gnus-make-hashtable 4096)))))))
     ;; Delete unnecessary lines.
     (goto-char (point-min))
-    (while (search-forward "\nto." nil t)
-      (delete-region (1+ (match-beginning 0))
-                    (progn (forward-line 1) (point))))
-    (or (string= gnus-ignored-newsgroups "")
-       (progn
-         (goto-char (point-min))
-         (delete-matching-lines gnus-ignored-newsgroups)))
-    ;; Make the group names readable as a lisp expression even if they
-    ;; contain special characters.
-    ;; Fix by Luc Van Eycken <Luc.VanEycken@esat.kuleuven.ac.be>.
-    (goto-char (point-max))
-    (while (re-search-backward "[][';?()#]" nil t)
-      (insert ?\\))
+    (cond
+     ((string= gnus-ignored-newsgroups "")
+      (delete-matching-lines "^to\\."))
+     (t
+      (delete-matching-lines (concat "^to\\.\\|" gnus-ignored-newsgroups))))
+
+    (goto-char (point-min))
+    (unless (re-search-forward "[\\\"]" nil t)
+      ;; Make the group names readable as a lisp expression even if they
+      ;; contain special characters.
+      (goto-char (point-max))
+      (while (re-search-backward "[][';?()#]" nil t)
+       (insert ?\\)))
+
+    ;; Let the Gnus agent save the active file.
+    (when (and gnus-agent real-active (gnus-online method))
+      (gnus-agent-save-active method))
+
     ;; If these are groups from a foreign select method, we insert the
     ;; group prefix in front of the group names.
-    (and method (not (gnus-server-equal
-                     (gnus-server-get-method nil method)
-                     (gnus-server-get-method nil gnus-select-method)))
-        (let ((prefix (gnus-group-prefixed-name "" method)))
-          (goto-char (point-min))
-          (while (and (not (eobp))
-                      (progn (insert prefix)
-                             (zerop (forward-line 1)))))))
+    (when (not (gnus-server-equal
+               (gnus-server-get-method nil method)
+               (gnus-server-get-method nil gnus-select-method)))
+      (let ((prefix (gnus-group-prefixed-name "" method)))
+       (goto-char (point-min))
+       (while (and (not (eobp))
+                   (progn
+                     (when (= (following-char) ?\")
+                       (forward-char 1))
+                     (insert prefix)
+                     (zerop (forward-line 1)))))))
     ;; Store the active file in a hash table.
     (goto-char (point-min))
-    (if (string-match "%[oO]" gnus-group-line-format)
-       ;; Suggested by Brian Edmonds <edmonds@cs.ubc.ca>.
-       ;; If we want information on moderated groups, we use this
-       ;; loop...
-       (let* ((mod-hashtb (make-vector 7 0))
-              (m (intern "m" mod-hashtb))
-              group max min)
-         (while (not (eobp))
-           (condition-case nil
-               (progn
-                 (narrow-to-region (point) (gnus-point-at-eol))
-                 (setq group (let ((obarray hashtb)) (read cur)))
-                 (if (and (numberp (setq max (read cur)))
-                          (numberp (setq min (read cur)))
-                          (progn
-                            (skip-chars-forward " \t")
-                            (not
-                             (or (= (following-char) ?=)
-                                 (= (following-char) ?x)
-                                 (= (following-char) ?j)))))
-                     (set group (cons min max))
-                   (set group nil))
-                 ;; Enter moderated groups into a list.
-                 (if (eq (let ((obarray mod-hashtb)) (read cur)) m)
-                     (setq gnus-moderated-list
-                           (cons (symbol-name group) gnus-moderated-list))))
-             (error
-              (and group
-                   (symbolp group)
-                   (set group nil))))
-           (widen)
-           (forward-line 1)))
-      ;; And if we do not care about moderation, we use this loop,
-      ;; which is faster.
-      (let (group max min)
-       (while (not (eobp))
-         (condition-case ()
-             (progn
-               (narrow-to-region (point) (gnus-point-at-eol))
-               ;; group gets set to a symbol interned in the hash table
-               ;; (what a hack!!) - jwz
-               (setq group (let ((obarray hashtb)) (read cur)))
-               (if (and (numberp (setq max (read cur)))
-                        (numberp (setq min (read cur)))
-                        (progn
-                          (skip-chars-forward " \t")
-                          (not
-                           (or (= (following-char) ?=)
-                               (= (following-char) ?x)
-                               (= (following-char) ?j)))))
+    (let (group max min)
+      (while (not (eobp))
+       (condition-case ()
+           (progn
+             (narrow-to-region (point) (point-at-eol))
+             ;; group gets set to a symbol interned in the hash table
+             ;; (what a hack!!) - jwz
+             (setq group (let ((obarray hashtb)) (read cur)))
+             ;; ### The extended group name scheme makes
+             ;; the previous optimization strategy sort of pointless...
+             (when (stringp group)
+               (setq group (intern group hashtb)))
+             (if (and (numberp (setq max (read cur)))
+                      (numberp (setq min (read cur)))
+                      (progn
+                        (skip-chars-forward " \t")
+                        (not
+                         (or (eq (char-after) ?=)
+                             (eq (char-after) ?x)
+                             (eq (char-after) ?j)))))
+                 (progn
                    (set group (cons min max))
-                 (set group nil)))
-           (error
-            (progn
-              (and group
-                   (symbolp group)
-                   (set group nil))
-              (or ignore-errors
-                  (gnus-message 3 "Warning - illegal active: %s"
-                                (buffer-substring
-                                 (gnus-point-at-bol) (gnus-point-at-eol)))))))
-         (widen)
-         (forward-line 1))))))
-
-(defun gnus-groups-to-gnus-format (method &optional hashtb)
+                   ;; if group is moderated, stick in moderation table
+                   (when (eq (char-after) ?m)
+                     (unless gnus-moderated-hashtb
+                       (setq gnus-moderated-hashtb (gnus-make-hashtable)))
+                     (gnus-sethash (symbol-name group) t
+                                   gnus-moderated-hashtb)))
+               (set group nil)))
+         (error
+          (and group
+               (symbolp group)
+               (set group nil))
+          (unless ignore-errors
+            (gnus-message 3 "Warning - invalid active: %s"
+                          (buffer-substring
+                           (point-at-bol) (point-at-eol))))))
+       (widen)
+       (forward-line 1)))))
+
+(defun gnus-groups-to-gnus-format (method &optional hashtb real-active)
   ;; Parse a "groups" active file.
   (let ((cur (current-buffer))
        (hashtb (or hashtb
@@ -1596,43 +2187,51 @@ newsgroup."
                           (gnus-server-get-method nil gnus-select-method)))
                     (gnus-group-prefixed-name "" method))))
 
-    (goto-char (point-min))
-    ;; We split this into to separate loops, one with the prefix
-    ;; and one without to speed the reading up somewhat.
-    (if prefix
-       (let (min max opoint group)
+    ;; Let the Gnus agent save the active file.
+    (if (and gnus-agent
+            real-active
+            (gnus-online method)
+            (gnus-agent-method-p method))
+       (progn
+         (gnus-agent-save-active method)
+         (gnus-active-to-gnus-format method hashtb nil real-active))
+
+      (goto-char (point-min))
+      ;; We split this into to separate loops, one with the prefix
+      ;; and one without to speed the reading up somewhat.
+      (if prefix
+         (let (min max opoint group)
+           (while (not (eobp))
+             (condition-case ()
+                 (progn
+                   (read cur) (read cur)
+                   (setq min (read cur)
+                         max (read cur)
+                         opoint (point))
+                   (skip-chars-forward " \t")
+                   (insert prefix)
+                   (goto-char opoint)
+                   (set (let ((obarray hashtb)) (read cur))
+                        (cons min max)))
+               (error (and group (symbolp group) (set group nil))))
+             (forward-line 1)))
+       (let (min max group)
          (while (not (eobp))
            (condition-case ()
-               (progn
+               (when (eq (char-after) ?2)
                  (read cur) (read cur)
                  (setq min (read cur)
-                       max (read cur)
-                       opoint (point))
-                 (skip-chars-forward " \t")
-                 (insert prefix)
-                 (goto-char opoint)
-                 (set (let ((obarray hashtb)) (read cur))
+                       max (read cur))
+                 (set (setq group (let ((obarray hashtb)) (read cur)))
                       (cons min max)))
              (error (and group (symbolp group) (set group nil))))
-           (forward-line 1)))
-      (let (min max group)
-       (while (not (eobp))
-         (condition-case ()
-             (if (= (following-char) ?2)
-                 (progn
-                   (read cur) (read cur)
-                   (setq min (read cur)
-                         max (read cur))
-                   (set (setq group (let ((obarray hashtb)) (read cur)))
-                        (cons min max))))
-           (error (and group (symbolp group) (set group nil))))
-         (forward-line 1))))))
+           (forward-line 1)))))))
 
 (defun gnus-read-newsrc-file (&optional force)
   "Read startup file.
 If FORCE is non-nil, the .newsrc file is read."
   ;; Reset variables that might be defined in the .newsrc.eld file.
-  (let ((variables gnus-variable-list))
+  (let ((variables (remove 'gnus-format-specs gnus-variable-list)))
     (while variables
       (set (car variables) nil)
       (setq variables (cdr variables))))
@@ -1644,46 +2243,120 @@ If FORCE is non-nil, the .newsrc file is read."
       ;; file (ticked articles, killed groups, foreign methods, etc.)
       (gnus-read-newsrc-el-file quick-file)
 
-      (if (and (file-exists-p gnus-current-startup-file)
-              (or force
-                  (and (file-newer-than-file-p newsrc-file quick-file)
-                       (file-newer-than-file-p newsrc-file
-                                               (concat quick-file "d")))
-                  (not gnus-newsrc-alist)))
-         ;; We read the .newsrc file.  Note that if there if a
-         ;; .newsrc.eld file exists, it has already been read, and
-         ;; the `gnus-newsrc-hashtb' has been created.  While reading
-         ;; the .newsrc file, Gnus will only use the information it
-         ;; can find there for changing the data already read -
-         ;; ie. reading the .newsrc file will not trash the data
-         ;; already read (except for read articles).
-         (save-excursion
-           (gnus-message 5 "Reading %s..." newsrc-file)
-           (set-buffer (find-file-noselect newsrc-file))
-           (buffer-disable-undo (current-buffer))
-           (gnus-newsrc-to-gnus-format)
-           (kill-buffer (current-buffer))
-           (gnus-message 5 "Reading %s...done" newsrc-file)))
-
-      ;; Read any slave files.
-      (unless gnus-slave
-       (gnus-master-read-slave-newsrc))
-      
+      (when (and gnus-read-newsrc-file
+                (file-exists-p gnus-current-startup-file)
+                (or force
+                    (and (file-newer-than-file-p newsrc-file quick-file)
+                         (file-newer-than-file-p newsrc-file
+                                                 (concat quick-file "d")))
+                    (not gnus-newsrc-alist)))
+       ;; We read the .newsrc file.  Note that if there if a
+       ;; .newsrc.eld file exists, it has already been read, and
+       ;; the `gnus-newsrc-hashtb' has been created.  While reading
+       ;; the .newsrc file, Gnus will only use the information it
+       ;; can find there for changing the data already read -
+       ;; i. e., reading the .newsrc file will not trash the data
+       ;; already read (except for read articles).
+       (save-excursion
+         (gnus-message 5 "Reading %s..." newsrc-file)
+         (set-buffer (nnheader-find-file-noselect newsrc-file))
+         (buffer-disable-undo)
+         (gnus-newsrc-to-gnus-format)
+         (kill-buffer (current-buffer))
+         (gnus-message 5 "Reading %s...done" newsrc-file)))
+
       ;; Convert old to new.
       (gnus-convert-old-newsrc))))
 
 (defun gnus-convert-old-newsrc ()
-  "Convert old newsrc into the new format, if needed."
+  "Convert old newsrc formats into the current format, if needed."
   (let ((fcv (and gnus-newsrc-file-version
-                 (gnus-continuum-version gnus-newsrc-file-version))))
-    (cond
-     ;; No .newsrc.eld file was loaded.
-     ((null fcv) nil)
-     ;; Gnus 5 .newsrc.eld was loaded.
-     ((< fcv (gnus-continuum-version "September Gnus v0.1"))
-      (gnus-convert-old-ticks)))))
-
-(defun gnus-convert-old-ticks ()
+                 (gnus-continuum-version gnus-newsrc-file-version)))
+       (gcv (gnus-continuum-version)))
+    (when fcv
+      ;; A newsrc file was loaded.
+      (let (prompt-displayed
+            (converters
+             (sort
+              (mapcar (lambda (date-func)
+                        (cons (gnus-continuum-version (car date-func))
+                              date-func))
+                      ;; This is a list of converters that must be run
+                      ;; to bring the newsrc file up to the current
+                      ;; version.  If you create an incompatibility
+                      ;; with older versions, you should create an
+                      ;; entry here.  The entry should consist of the
+                      ;; current gnus version (hardcoded so that it
+                      ;; doesn't change with each release) and the
+                      ;; function that must be applied to convert the
+                      ;; previous version into the current version.
+                      '(("September Gnus v0.1" nil
+                         gnus-convert-old-ticks)
+                        ("Oort Gnus v0.08"     "legacy-gnus-agent"
+                         gnus-agent-convert-to-compressed-agentview)
+                        ("Gnus v5.10.7"        "legacy-gnus-agent"
+                         gnus-agent-unlist-expire-days)
+                        ("Gnus v5.10.7"        "legacy-gnus-agent"
+                         gnus-agent-unhook-expire-days)))
+              #'car-less-than-car)))
+        ;; Skip converters older than the file version
+        (while (and converters (>= fcv (caar converters)))
+          (pop converters))
+
+        ;; Perform converters to bring older version up to date.
+       (when (and converters (< fcv (caar converters)))
+         (while (and converters (< fcv (caar converters))
+                     (<= (caar converters) gcv))
+            (let* ((converter-spec  (pop converters))
+                   (convert-to      (nth 1 converter-spec))
+                   (load-from       (nth 2 converter-spec))
+                   (func            (nth 3 converter-spec)))
+              (when (and load-from
+                         (not (fboundp func)))
+                (load load-from t))
+              (or prompt-displayed
+                  (not (gnus-convert-converter-needs-prompt func))
+                  (while (let (c
+                               (cursor-in-echo-area t)
+                               (echo-keystrokes 0))
+                           (message "Convert gnus from version '%s' to '%s'? (n/y/?)"
+                                    gnus-newsrc-file-version gnus-version)
+                           (setq c (read-char-exclusive))
+
+                           (cond ((or (eq c ?n) (eq c ?N))
+                                  (error "Can not start gnus without converting"))
+                                 ((or (eq c ?y) (eq c ?Y))
+                                  (setq prompt-displayed t)
+                                  nil)
+                                 ((eq c ?\?)
+                                  (message "This conversion is irreversible. \
+ To be safe, you should backup your files before proceeding.")
+                                  (sit-for 5)
+                                  t)
+                                 (t
+                                  (gnus-message 3 "Ignoring unexpected input")
+                                  (sit-for 3)
+                                  t)))))
+
+              (funcall func convert-to)))
+          (gnus-dribble-enter
+           (format ";Converted gnus from version '%s' to '%s'."
+                   gnus-newsrc-file-version gnus-version)))))))
+
+(defun gnus-convert-mark-converter-prompt (converter no-prompt)
+  "Indicate whether CONVERTER requires gnus-convert-old-newsrc to
+  display the conversion prompt.  NO-PROMPT may be nil (prompt),
+  t (no prompt), or any form that can be called as a function.
+  The form should return either t or nil."
+  (put converter 'gnus-convert-no-prompt no-prompt))
+
+(defun gnus-convert-converter-needs-prompt (converter)
+  (let ((no-prompt (get converter 'gnus-convert-no-prompt)))
+    (not (if (memq no-prompt '(t nil))
+            no-prompt
+          (funcall no-prompt)))))
+
+(defun gnus-convert-old-ticks (converting-to)
   (let ((newsrc (cdr gnus-newsrc-alist))
        marks info dormant ticked)
     (while (setq info (pop newsrc))
@@ -1698,24 +2371,48 @@ If FORCE is non-nil, the .newsrc file is read."
            (nconc (gnus-uncompress-range dormant)
                   (gnus-uncompress-range ticked)))))))))
 
+(defun gnus-load (file)
+  "Load FILE, but in such a way that read errors can be reported."
+  (with-temp-buffer
+    (insert-file-contents file)
+    (while (not (eobp))
+      (condition-case type
+         (let ((form (read (current-buffer))))
+           (eval form))
+       (error
+        (unless (eq (car type) 'end-of-file)
+          (let ((error (format "Error in %s line %d" file
+                               (count-lines (point-min) (point)))))
+            (ding)
+            (unless (gnus-yes-or-no-p (concat error "; continue? "))
+              (error "%s" error)))))))))
+
 (defun gnus-read-newsrc-el-file (file)
   (let ((ding-file (concat file "d")))
-    ;; We always, always read the .eld file.
-    (gnus-message 5 "Reading %s..." ding-file)
-    (let (gnus-newsrc-assoc)
-      (condition-case nil
-         (load ding-file t t t)
-       (error
-        (gnus-error 1 "Error in %s" ding-file)))
-      (when gnus-newsrc-assoc
-       (setq gnus-newsrc-alist gnus-newsrc-assoc)))
+    (when (file-exists-p ding-file)
+      ;; We always, always read the .eld file.
+      (gnus-message 5 "Reading %s..." ding-file)
+      (let (gnus-newsrc-assoc)
+       (let ((coding-system-for-read gnus-ding-file-coding-system))
+         (gnus-load ding-file))
+       ;; Older versions of `gnus-format-specs' are no longer valid
+       ;; in Oort Gnus 0.01.
+       (let ((version
+              (and gnus-newsrc-file-version
+                   (gnus-continuum-version gnus-newsrc-file-version))))
+         (when (or (not version)
+                   (< version 5.090009))
+           (setq gnus-format-specs gnus-default-format-specs)))
+       (when gnus-newsrc-assoc
+         (setq gnus-newsrc-alist gnus-newsrc-assoc))))
     (gnus-make-hashtable-from-newsrc-alist)
     (when (file-newer-than-file-p file ding-file)
       ;; Old format quick file
       (gnus-message 5 "Reading %s..." file)
       ;; The .el file is newer than the .eld file, so we read that one
       ;; as well.
-      (gnus-read-old-newsrc-el-file file))))
+      (gnus-read-old-newsrc-el-file file)))
+  (gnus-run-hooks 'gnus-read-newsrc-el-hook))
 
 ;; Parse the old-style quick startup file
 (defun gnus-read-old-newsrc-el-file (file)
@@ -1724,9 +2421,8 @@ If FORCE is non-nil, the .newsrc file is read."
        (let ((gnus-killed-assoc nil)
              gnus-marked-assoc gnus-newsrc-alist gnus-newsrc-assoc)
          (prog1
-             (condition-case nil
-                 (load file t t t)
-               (error nil))
+             (ignore-errors
+               (load file t t t))
            (setq newsrc gnus-newsrc-assoc
                  killed gnus-killed-assoc
                  marked gnus-marked-assoc)))
@@ -1738,7 +2434,7 @@ If FORCE is non-nil, the .newsrc file is read."
              (gnus-info-set-level
               info (if (nth 1 group) gnus-level-default-subscribed
                      gnus-level-default-unsubscribed))
-             (setq gnus-newsrc-alist (cons info gnus-newsrc-alist)))
+             (push info gnus-newsrc-alist))
          (push (setq info
                      (list (car group)
                            (if (nth 1 group) gnus-level-default-subscribed
@@ -1750,7 +2446,7 @@ If FORCE is non-nil, the .newsrc file is read."
          (unless (nthcdr 3 info)
            (nconc info (list nil)))
          (gnus-info-set-marks
-          info (list (cons 'tick (gnus-compress-sequence 
+          info (list (cons 'tick (gnus-compress-sequence
                                   (sort (cdr m) '<) t))))))
       (setq newsrc killed)
       (while newsrc
@@ -1760,16 +2456,15 @@ If FORCE is non-nil, the .newsrc file is read."
     ;; The .el file version of this variable does not begin with
     ;; "options", while the .eld version does, so we just add it if it
     ;; isn't there.
-    (and
-     gnus-newsrc-options
-     (progn
-       (and (not (string-match "^ *options" gnus-newsrc-options))
-           (setq gnus-newsrc-options (concat "options " gnus-newsrc-options)))
-       (and (not (string-match "\n$" gnus-newsrc-options))
-           (setq gnus-newsrc-options (concat gnus-newsrc-options "\n")))
-       ;; Finally, if we read some options lines, we parse them.
-       (or (string= gnus-newsrc-options "")
-          (gnus-newsrc-parse-options gnus-newsrc-options))))
+    (when
+       gnus-newsrc-options
+      (when (not (string-match "^ *options" gnus-newsrc-options))
+       (setq gnus-newsrc-options (concat "options " gnus-newsrc-options)))
+      (when (not (string-match "\n$" gnus-newsrc-options))
+       (setq gnus-newsrc-options (concat gnus-newsrc-options "\n")))
+      ;; Finally, if we read some options lines, we parse them.
+      (unless (string= gnus-newsrc-options "")
+       (gnus-newsrc-parse-options gnus-newsrc-options)))
 
     (setq gnus-newsrc-alist (nreverse gnus-newsrc-alist))
     (gnus-make-hashtable-from-newsrc-alist)))
@@ -1781,14 +2476,15 @@ If FORCE is non-nil, the .newsrc file is read."
     (if (or (file-exists-p real-file)
            (file-exists-p (concat real-file ".el"))
            (file-exists-p (concat real-file ".eld")))
-       real-file file)))
+       real-file
+      file)))
 
 (defun gnus-newsrc-to-gnus-format ()
   (setq gnus-newsrc-options "")
   (setq gnus-newsrc-options-n nil)
 
-  (or gnus-active-hashtb
-      (setq gnus-active-hashtb (make-vector 4095 0)))
+  (unless gnus-active-hashtb
+    (setq gnus-active-hashtb (gnus-make-hashtable 4096)))
   (let ((buf (current-buffer))
        (already-read (> (length gnus-newsrc-alist) 1))
        group subscribed options-symbol newsrc Options-symbol
@@ -1824,19 +2520,20 @@ If FORCE is non-nil, the .newsrc file is read."
              ;; don't give a damn, frankly, my dear.
              (concat gnus-newsrc-options
                      (buffer-substring
-                      (gnus-point-at-bol)
+                      (point-at-bol)
                       ;; Options may continue on the next line.
                       (or (and (re-search-forward "^[^ \t]" nil 'move)
-                               (progn (beginning-of-line) (point)))
+                               (point-at-bol))
                           (point)))))
        (forward-line -1))
        (symbol
-       ;; Group names can be just numbers.  
-       (when (numberp symbol) 
+       ;; Group names can be just numbers.
+       (when (numberp symbol)
          (setq symbol (intern (int-to-string symbol) gnus-active-hashtb)))
-       (or (boundp symbol) (set symbol nil))
+       (unless (boundp symbol)
+         (set symbol nil))
        ;; It was a group name.
-       (setq subscribed (= (following-char) ?:)
+       (setq subscribed (eq (char-after) ?:)
              group (symbol-name symbol)
              reads nil)
        (if (eolp)
@@ -1852,7 +2549,7 @@ If FORCE is non-nil, the .newsrc file is read."
            (cond
             ((looking-at "[0-9]+")
              ;; We narrow and read a number instead of buffer-substring/
-             ;; string-to-int because it's faster.  narrow/widen is
+             ;; string-to-number because it's faster.  narrow/widen is
              ;; faster than save-restriction/narrow, and save-restriction
              ;; produces a garbage object.
              (setq num1 (progn
@@ -1860,7 +2557,7 @@ If FORCE is non-nil, the .newsrc file is read."
                           (read buf)))
              (widen)
              ;; If the next character is a dash, then this is a range.
-             (if (= (following-char) ?-)
+             (if (eq (char-after) ?-)
                  (progn
                    ;; We read the upper bound of the range.
                    (forward-char 1)
@@ -1868,38 +2565,36 @@ If FORCE is non-nil, the .newsrc file is read."
                        ;; This is a buggy line, by we pretend that
                        ;; it's kinda OK.  Perhaps the user should be
                        ;; dinged?
-                       (setq reads (cons num1 reads))
-                     (setq reads
-                           (cons
-                            (cons num1
-                                  (progn
-                                    (narrow-to-region (match-beginning 0)
-                                                      (match-end 0))
-                                    (read buf)))
-                            reads))
+                       (push num1 reads)
+                     (push
+                      (cons num1
+                            (progn
+                              (narrow-to-region (match-beginning 0)
+                                                (match-end 0))
+                              (read buf)))
+                      reads)
                      (widen)))
                ;; It was just a simple number, so we add it to the
                ;; list of ranges.
-               (setq reads (cons num1 reads)))
+               (push num1 reads))
              ;; If the next char in ?\n, then we have reached the end
              ;; of the line and return nil.
-             (/= (following-char) ?\n))
-            ((= (following-char) ?\n)
+             (not (eq (char-after) ?\n)))
+            ((eq (char-after) ?\n)
              ;; End of line, so we end.
              nil)
             (t
              ;; Not numbers and not eol, so this might be a buggy
              ;; line...
-             (or (eobp)
-                 ;; If it was eob instead of ?\n, we allow it.
-                 (progn
-                   ;; The line was buggy.
-                   (setq group nil)
-                   (gnus-error 3.1 "Mangled line: %s"
-                               (buffer-substring (gnus-point-at-bol)
-                                                 (gnus-point-at-eol)))))
+             (unless (eobp)
+               ;; If it was eob instead of ?\n, we allow it.
+               ;; The line was buggy.
+               (setq group nil)
+               (gnus-error 3.1 "Mangled line: %s"
+                           (buffer-substring (point-at-bol)
+                                             (point-at-eol))))
              nil))
-         ;; Skip past ", ".  Spaces are illegal in these ranges, but
+         ;; Skip past ", ".  Spaces are invalid in these ranges, but
          ;; we allow them, because it's a common mistake to put a
          ;; space after the comma.
          (skip-chars-forward ", "))
@@ -1934,7 +2629,7 @@ If FORCE is non-nil, the .newsrc file is read."
                                     (1+ gnus-level-subscribed)
                                   gnus-level-default-unsubscribed))
                               (nreverse reads))))
-           (setq newsrc (cons info newsrc))))))
+           (push info newsrc)))))
       (forward-line 1))
 
     (setq newsrc (nreverse newsrc))
@@ -1951,11 +2646,11 @@ If FORCE is non-nil, the .newsrc file is read."
            entry mentry)
        (while rc
          (or (null (nth 4 (car rc)))   ; It's a native group.
-             (assoc (caar rc) newsrc) ; It's already in the alist.
+             (assoc (caar rc) newsrc)  ; It's already in the alist.
              (if (setq entry (assoc (caar prev) newsrc))
                  (setcdr (setq mentry (memq entry newsrc))
                          (cons (car rc) (cdr mentry)))
-               (setq newsrc (cons (car rc) newsrc))))
+               (push (car rc) newsrc)))
          (setq prev rc
                rc (cdr rc)))))
 
@@ -1964,8 +2659,8 @@ If FORCE is non-nil, the .newsrc file is read."
     (gnus-make-hashtable-from-newsrc-alist)
 
     ;; Finally, if we read some options lines, we parse them.
-    (or (string= gnus-newsrc-options "")
-       (gnus-newsrc-parse-options gnus-newsrc-options))))
+    (unless (string= gnus-newsrc-options "")
+      (gnus-newsrc-parse-options gnus-newsrc-options))))
 
 ;; Parse options lines to find "options -n !all rec.all" and stuff.
 ;; The return value will be a list on the form
@@ -2006,32 +2701,45 @@ If FORCE is non-nil, the .newsrc file is read."
       (while (re-search-forward "[ \t]-n" nil t)
        (setq eol
              (or (save-excursion
-                   (and (re-search-forward "[ \t]-n" (gnus-point-at-eol) t)
+                   (and (re-search-forward "[ \t]-n" (point-at-eol) t)
                         (- (point) 2)))
-                 (gnus-point-at-eol)))
+                 (point-at-eol)))
        ;; Search for all "words"...
        (while (re-search-forward "[^ \t,\n]+" eol t)
-         (if (= (char-after (match-beginning 0)) ?!)
+         (if (eq (char-after (match-beginning 0)) ?!)
              ;; If the word begins with a bang (!), this is a "not"
              ;; spec.  We put this spec (minus the bang) and the
              ;; symbol `ignore' into the list.
-             (setq out (cons (cons (concat
-                                    "^" (buffer-substring
-                                         (1+ (match-beginning 0))
-                                         (match-end 0)))
-                                   'ignore) out))
+             (push (cons (concat
+                          "^" (buffer-substring
+                               (1+ (match-beginning 0))
+                               (match-end 0))
+                          "\\($\\|\\.\\)")
+                         'ignore)
+                   out)
            ;; There was no bang, so this is a "yes" spec.
-           (setq out (cons (cons (concat "^" (match-string 0))
-                                 'subscribe) out)))))
+           (push (cons (concat "^" (match-string 0) "\\($\\|\\.\\)")
+                       'subscribe)
+                 out))))
 
       (setq gnus-newsrc-options-n out))))
 
+(eval-and-compile
+  (defalias 'gnus-long-file-names
+    (if (fboundp 'msdos-long-file-names)
+      'msdos-long-file-names
+      (lambda () t))))
+
 (defun gnus-save-newsrc-file (&optional force)
   "Save .newsrc file."
   ;; Note: We cannot save .newsrc file if all newsgroups are removed
   ;; from the variable gnus-newsrc-alist.
   (when (and (or gnus-newsrc-alist gnus-killed-list)
             gnus-current-startup-file)
+    ;; Save agent range limits for the currently active method.
+    (when gnus-agent
+      (gnus-agent-save-local force))
+
     (save-excursion
       (if (and (or gnus-use-dribble-file gnus-slave)
               (not force)
@@ -2041,56 +2749,135 @@ If FORCE is non-nil, the .newsrc file is read."
                            (set-buffer gnus-dribble-buffer)
                            (buffer-size)))))
          (gnus-message 4 "(No changes need to be saved)")
-       (run-hooks 'gnus-save-newsrc-hook)
+       (gnus-run-hooks 'gnus-save-newsrc-hook)
        (if gnus-slave
            (gnus-slave-save-newsrc)
          ;; Save .newsrc.
          (when gnus-save-newsrc-file
-           (gnus-message 5 "Saving %s..." gnus-current-startup-file)
+           (gnus-message 8 "Saving %s..." gnus-current-startup-file)
            (gnus-gnus-to-newsrc-format)
-           (gnus-message 5 "Saving %s...done" gnus-current-startup-file))
+           (gnus-message 8 "Saving %s...done" gnus-current-startup-file))
+
          ;; Save .newsrc.eld.
-         (set-buffer (get-buffer-create " *Gnus-newsrc*"))
+         (set-buffer (gnus-get-buffer-create " *Gnus-newsrc*"))
          (make-local-variable 'version-control)
-         (setq version-control 'never)
+         (setq version-control gnus-backup-startup-file)
          (setq buffer-file-name
                (concat gnus-current-startup-file ".eld"))
          (setq default-directory (file-name-directory buffer-file-name))
-         (gnus-add-current-to-buffer-list)
-         (buffer-disable-undo (current-buffer))
+         (buffer-disable-undo)
          (erase-buffer)
-         (gnus-message 5 "Saving %s.eld..." gnus-current-startup-file)
-         (gnus-gnus-to-quick-newsrc-format)
-         (run-hooks 'gnus-save-quick-newsrc-hook)
-         (save-buffer)
-         (kill-buffer (current-buffer))
+          (gnus-message 5 "Saving %s.eld..." gnus-current-startup-file)
+
+          (if gnus-save-startup-file-via-temp-buffer
+              (let ((coding-system-for-write gnus-ding-file-coding-system)
+                    (standard-output (current-buffer)))
+                (gnus-gnus-to-quick-newsrc-format)
+                (gnus-run-hooks 'gnus-save-quick-newsrc-hook)
+                (save-buffer))
+            (let ((coding-system-for-write gnus-ding-file-coding-system)
+                  (version-control gnus-backup-startup-file)
+                  (startup-file (concat gnus-current-startup-file ".eld"))
+                  (working-dir (file-name-directory gnus-current-startup-file))
+                  working-file
+                  (i -1))
+              ;; Generate the name of a non-existent file.
+              (while (progn (setq working-file
+                                  (format
+                                   (if (and (eq system-type 'ms-dos)
+                                            (not (gnus-long-file-names)))
+                                       "%s#%d.tm#" ; MSDOS limits files to 8+3
+                                     (if (memq system-type '(vax-vms axp-vms))
+                                         "%s$tmp$%d"
+                                       "%s#tmp#%d"))
+                                   working-dir (setq i (1+ i))))
+                            (file-exists-p working-file)))
+
+              (unwind-protect
+                  (progn
+                    (gnus-with-output-to-file working-file
+                     (gnus-gnus-to-quick-newsrc-format)
+                     (gnus-run-hooks 'gnus-save-quick-newsrc-hook))
+
+                    ;; These bindings will mislead the current buffer
+                    ;; into thinking that it is visiting the startup
+                    ;; file.
+                    (let ((buffer-backed-up nil)
+                          (buffer-file-name startup-file)
+                          (file-precious-flag t)
+                          (setmodes (file-modes startup-file)))
+                      ;; Backup the current version of the startup file.
+                      (backup-buffer)
+
+                      ;; Replace the existing startup file with the temp file.
+                      (rename-file working-file startup-file t)
+                      (gnus-set-file-modes startup-file setmodes)))
+                (condition-case nil
+                    (delete-file working-file)
+                  (file-error nil)))))
+
+         (gnus-kill-buffer (current-buffer))
          (gnus-message
           5 "Saving %s.eld...done" gnus-current-startup-file))
        (gnus-dribble-delete-file)
        (gnus-group-set-mode-line)))))
 
-(defun gnus-gnus-to-quick-newsrc-format ()
-  "Insert Gnus variables such as gnus-newsrc-alist in lisp format."
-  (insert ";; Gnus startup file.\n")
-  (insert ";; Never delete this file - touch .newsrc instead to force Gnus\n")
-  (insert ";; to read .newsrc.\n")
-  (insert "(setq gnus-newsrc-file-version "
-         (prin1-to-string gnus-version) ")\n")
-  (let ((variables
-        (if gnus-save-killed-list gnus-variable-list
-          ;; Remove the `gnus-killed-list' from the list of variables
-          ;; to be saved, if required.
-          (delq 'gnus-killed-list (copy-sequence gnus-variable-list))))
-       ;; Peel off the "dummy" group.
-       (gnus-newsrc-alist (cdr gnus-newsrc-alist))
-       variable)
-    ;; Insert the variables into the file.
-    (while variables
-      (when (and (boundp (setq variable (pop variables)))
-                (symbol-value variable))
-       (insert "(setq " (symbol-name variable) " '")
-       (prin1 (symbol-value variable) (current-buffer))
-       (insert ")\n")))))
+(defun gnus-gnus-to-quick-newsrc-format (&optional minimal name &rest specific-variables)
+  "Print Gnus variables such as `gnus-newsrc-alist' in Lisp format."
+    (princ ";; -*- emacs-lisp -*-\n")
+    (if name
+       (princ (format ";; %s\n" name))
+      (princ ";; Gnus startup file.\n"))
+
+    (unless minimal
+      (princ "\
+;; Never delete this file -- if you want to force Gnus to read the
+;; .newsrc file (if you have one), touch .newsrc instead.\n")
+      (princ "(setq gnus-newsrc-file-version ")
+      (princ (gnus-prin1-to-string gnus-version))
+      (princ ")\n"))
+
+    (let* ((print-quoted t)
+           (print-readably t)
+           (print-escape-multibyte nil)
+           (print-escape-nonascii t)
+           (print-length nil)
+           (print-level nil)
+          (print-circle nil)
+           (print-escape-newlines t)
+          (gnus-killed-list
+           (if (and gnus-save-killed-list
+                    (stringp gnus-save-killed-list))
+               (gnus-strip-killed-list)
+             gnus-killed-list))
+          (variables
+           (or specific-variables
+               (if gnus-save-killed-list gnus-variable-list
+                 ;; Remove the `gnus-killed-list' from the list of variables
+                 ;; to be saved, if required.
+                 (delq 'gnus-killed-list (copy-sequence gnus-variable-list)))))
+          ;; Peel off the "dummy" group.
+          (gnus-newsrc-alist (cdr gnus-newsrc-alist))
+          variable)
+      ;; Insert the variables into the file.
+      (while variables
+       (when (and (boundp (setq variable (pop variables)))
+                  (symbol-value variable))
+         (princ "(setq ")
+          (princ (symbol-name variable))
+          (princ " '")
+         (prin1 (symbol-value variable))
+         (princ ")\n")))))
+
+(defun gnus-strip-killed-list ()
+  "Return the killed list minus the groups that match `gnus-save-killed-list'."
+  (let ((list gnus-killed-list)
+       olist)
+    (while list
+      (when (string-match gnus-save-killed-list (car list))
+       (push (car list) olist))
+      (pop list))
+    (nreverse olist)))
 
 (defun gnus-gnus-to-newsrc-format ()
   ;; Generate and save the .newsrc file.
@@ -2101,16 +2888,17 @@ If FORCE is non-nil, the .newsrc file is read."
          info ranges range method)
       (setq buffer-file-name gnus-current-startup-file)
       (setq default-directory (file-name-directory buffer-file-name))
-      (buffer-disable-undo (current-buffer))
+      (buffer-disable-undo)
       (erase-buffer)
       ;; Write options.
-      (if gnus-newsrc-options (insert gnus-newsrc-options))
+      (when gnus-newsrc-options
+       (insert gnus-newsrc-options))
       ;; Write subscribed and unsubscribed.
       (while (setq info (pop newsrc))
        ;; Don't write foreign groups to .newsrc.
        (when (or (null (setq method (gnus-info-method info)))
                  (equal method "native")
-                 (gnus-server-equal method gnus-select-method))
+                 (inline (gnus-server-equal method gnus-select-method)))
          (insert (gnus-info-group info)
                  (if (> (gnus-info-level info) gnus-level-subscribed)
                      "!" ":"))
@@ -2128,7 +2916,8 @@ If FORCE is non-nil, the .newsrc file is read."
                  (princ (car range))
                  (insert "-")
                  (princ (cdr range)))
-               (if ranges (insert ",")))))
+               (when ranges
+                 (insert ",")))))
          (insert "\n")))
       (make-local-variable 'version-control)
       (setq version-control 'never)
@@ -2142,7 +2931,7 @@ If FORCE is non-nil, the .newsrc file is read."
       (if gnus-modtime-botch
          (delete-file gnus-startup-file)
        (clear-visited-file-modtime))
-      (run-hooks 'gnus-save-standard-newsrc-hook)
+      (gnus-run-hooks 'gnus-save-standard-newsrc-hook)
       (save-buffer)
       (kill-buffer (current-buffer)))))
 
@@ -2151,12 +2940,24 @@ If FORCE is non-nil, the .newsrc file is read."
 ;;; Slave functions.
 ;;;
 
+(defvar gnus-slave-mode nil)
+
+(defun gnus-slave-mode ()
+  "Minor mode for slave Gnusae."
+  (add-minor-mode 'gnus-slave-mode " Slave" (make-sparse-keymap))
+  (gnus-run-hooks 'gnus-slave-mode-hook))
+
 (defun gnus-slave-save-newsrc ()
   (save-excursion
     (set-buffer gnus-dribble-buffer)
     (let ((slave-name
-          (make-temp-name (concat gnus-current-startup-file "-slave-"))))
-      (write-region (point-min) (point-max) slave-name nil 'nomesg))))
+          (mm-make-temp-file (concat gnus-current-startup-file "-slave-")))
+         (modes (ignore-errors
+                  (file-modes (concat gnus-current-startup-file ".eld")))))
+      (let ((coding-system-for-write gnus-ding-file-coding-system))
+       (gnus-write-buffer slave-name))
+      (when modes
+       (gnus-set-file-modes slave-name modes)))))
 
 (defun gnus-master-read-slave-newsrc ()
   (let ((slave-files
@@ -2173,8 +2974,7 @@ If FORCE is non-nil, the .newsrc file is read."
        ()                              ; There are no slave files to read.
       (gnus-message 7 "Reading slave newsrcs...")
       (save-excursion
-       (set-buffer (get-buffer-create " *gnus slave*"))
-       (buffer-disable-undo (current-buffer))
+       (set-buffer (gnus-get-buffer-create " *gnus slave*"))
        (setq slave-files
              (sort (mapcar (lambda (file)
                              (list (nth 5 (file-attributes file)) file))
@@ -2185,19 +2985,19 @@ If FORCE is non-nil, the .newsrc file is read."
        (while slave-files
          (erase-buffer)
          (setq file (nth 1 (car slave-files)))
-         (insert-file-contents file)
-         (if (condition-case ()
-                 (progn
-                   (eval-buffer (current-buffer))
-                   t)
-               (error
-                (gnus-error 3.2 "Possible error in %s" file)
-                nil))
-             (or gnus-slave ; Slaves shouldn't delete these files.
-                 (condition-case ()
-                     (delete-file file)
-                   (error nil))))
+         (nnheader-insert-file-contents file)
+         (when (condition-case ()
+                   (progn
+                     (eval-buffer (current-buffer))
+                     t)
+                 (error
+                  (gnus-error 3.2 "Possible error in %s" file)
+                  nil))
+           (unless gnus-slave          ; Slaves shouldn't delete these files.
+             (ignore-errors
+               (delete-file file))))
          (setq slave-files (cdr slave-files))))
+      (gnus-dribble-touch)
       (gnus-message 7 "Reading slave newsrcs...done"))))
 
 \f
@@ -2206,7 +3006,7 @@ If FORCE is non-nil, the .newsrc file is read."
 ;;;
 
 (defun gnus-read-all-descriptions-files ()
-  (let ((methods (cons gnus-select-method 
+  (let ((methods (cons gnus-select-method
                       (nconc
                        (when (gnus-archive-server-wanted-p)
                          (list "archive"))
@@ -2223,15 +3023,17 @@ If FORCE is non-nil, the .newsrc file is read."
       (setq method (gnus-server-to-method method)))
     ;; We create the hashtable whether we manage to read the desc file
     ;; to avoid trying to re-read after a failed read.
-    (or gnus-description-hashtb
-       (setq gnus-description-hashtb
-             (gnus-make-hashtable (length gnus-active-hashtb))))
+    (unless gnus-description-hashtb
+      (setq gnus-description-hashtb
+           (gnus-make-hashtable (length gnus-active-hashtb))))
     ;; Mark this method's desc file as read.
     (gnus-sethash (gnus-group-prefixed-name "" method) "Has read"
                  gnus-description-hashtb)
 
     (gnus-message 5 "Reading descriptions file via %s..." (car method))
     (cond
+     ((null (gnus-get-function method 'request-list-newsgroups t))
+      t)
      ((not (gnus-check-server method))
       (gnus-message 1 "Couldn't open server")
       nil)
@@ -2249,9 +3051,11 @@ If FORCE is non-nil, the .newsrc file is read."
            (narrow-to-region (point-min) (point)))
          ;; If these are groups from a foreign select method, we insert the
          ;; group prefix in front of the group names.
-         (and method (not (gnus-server-equal
-                           (gnus-server-get-method nil method)
-                           (gnus-server-get-method nil gnus-select-method)))
+         (and method (not (inline
+                            (gnus-server-equal
+                             (gnus-server-get-method nil method)
+                             (gnus-server-get-method
+                              nil gnus-select-method))))
               (let ((prefix (gnus-group-prefixed-name "" method)))
                 (goto-char (point-min))
                 (while (and (not (eobp))
@@ -2270,9 +3074,18 @@ If FORCE is non-nil, the .newsrc file is read."
                    (error 0)))
            (skip-chars-forward " \t")
            ;; ...  which leads to this line being effectively ignored.
-           (and (symbolp group)
-                (set group (buffer-substring
-                            (point) (progn (end-of-line) (point)))))
+           (when (symbolp group)
+             (let* ((str (buffer-substring
+                          (point) (progn (end-of-line) (point))))
+                    (name (symbol-name group))
+                    (charset
+                     (or (gnus-group-name-charset method name)
+                         (gnus-parameter-charset name)
+                         gnus-default-charset)))
+               ;; Fixme: Don't decode in unibyte mode.
+               (when (and str charset (featurep 'mule))
+                 (setq str (mm-decode-coding-string str charset)))
+               (set group str)))
            (forward-line 1))))
       (gnus-message 5 "Reading descriptions file...done")
       t))))
@@ -2288,11 +3101,79 @@ If FORCE is non-nil, the .newsrc file is read."
 
 ;;;###autoload
 (defun gnus-declare-backend (name &rest abilities)
-  "Declare backend NAME with ABILITIES as a Gnus backend."
+  "Declare back end NAME with ABILITIES as a Gnus back end."
   (setq gnus-valid-select-methods
        (nconc gnus-valid-select-methods
-              (list (apply 'list name abilities)))))
+              (list (apply 'list name abilities))))
+  (gnus-redefine-select-method-widget))
+
+(defun gnus-set-default-directory ()
+  "Set the default directory in the current buffer to `gnus-default-directory'.
+If this variable is nil, don't do anything."
+  (setq default-directory
+       (if (and gnus-default-directory
+                (file-exists-p gnus-default-directory))
+           (file-name-as-directory (expand-file-name gnus-default-directory))
+         default-directory)))
+
+(defun gnus-display-time-event-handler ()
+  (if (and (fboundp 'display-time-event-handler)
+          (gnus-boundp 'display-time-timer))
+      (display-time-event-handler)))
+
+;;;###autoload
+(defun gnus-fixup-nnimap-unread-after-getting-new-news ()
+  (let (server group info)
+    (mapatoms
+     (lambda (sym)
+       (when (and (setq group (symbol-name sym))
+                 (gnus-group-entry group)
+                 (setq info (symbol-value sym)))
+        (gnus-sethash group (cons (nth 2 info) (cdr (gnus-group-entry group)))
+                      gnus-newsrc-hashtb)))
+     (if (boundp 'nnimap-mailbox-info)
+        (symbol-value 'nnimap-mailbox-info)
+       (make-vector 1 0)))))
+
+(defun gnus-check-reasonable-setup ()
+  ;; Check whether nnml and nnfolder share a directory.
+  (let ((display-warn
+        (if (fboundp 'display-warning)
+            'display-warning
+          (lambda (type message)
+            (if noninteractive
+                (message "Warning (%s): %s" type message)
+              (let (window)
+                (with-current-buffer (get-buffer-create "*Warnings*")
+                  (goto-char (point-max))
+                  (unless (bolp)
+                    (insert "\n"))
+                  (insert (format "Warning (%s): %s\n" type message))
+                  (setq window (display-buffer (current-buffer)))
+                  (set-window-start
+                   window
+                   (prog2
+                       (forward-line (- 1 (window-height window)))
+                       (point)
+                     (goto-char (point-max))))))))))
+       method active actives match)
+    (dolist (server gnus-server-alist)
+      (setq method (gnus-server-to-method server)
+           active (intern (format "%s-active-file" (car method))))
+      (when (and (member (car method) '(nnml nnfolder))
+                (gnus-server-opened method)
+                (boundp active))
+       (when (setq match (assoc (symbol-value active) actives))
+         (funcall display-warn 'gnus-server
+                  (format "%s and %s share the same active file %s"
+                          (car method)
+                          (cadr match)
+                          (car match))))
+       (push (list (symbol-value active) method) actives)))))
 
 (provide 'gnus-start)
 
+;; arch-tag: f4584a22-b7b7-4853-abfc-a637329af5d2
 ;;; gnus-start.el ends here
+
+