Gnus -- minor build / warning fixes [OK For Upstream]
[gnus] / lisp / gnus-start.el
index 77d3bbf..52a53a7 100644 (file)
@@ -1,26 +1,24 @@
 ;;; gnus-start.el --- startup functions for Gnus
-;; Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003
-;;        Free Software Foundation, Inc.
+
+;; Copyright (C) 1996-2016 Free Software Foundation, Inc.
 
 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
 ;; Keywords: news
 
 ;; This file is part of GNU Emacs.
 
-;; GNU Emacs is free software; you can redistribute it and/or modify
+;; GNU Emacs is free software: you can redistribute it and/or modify
 ;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 2, or (at your option)
-;; any later version.
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
 
 ;; GNU Emacs is distributed in the hope that it will be useful,
 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.         See the
+;; 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.
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
 
 ;;; Commentary:
 
 (require 'gnus-spec)
 (require 'gnus-range)
 (require 'gnus-util)
+(ignore-errors (require 'gnus-cloud))
 (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")
-(eval-when-compile (require 'cl))
+
+(eval-when-compile
+  (require 'cl))
+
+(defvar gnus-agent-covered-methods)
+(defvar gnus-agent-file-loading-local)
+(defvar gnus-agent-file-loading-cache)
 
 (defcustom gnus-startup-file (nnheader-concat gnus-home-directory ".newsrc")
   "Your `.newsrc' file.
   :type 'file)
 
 (defcustom gnus-backup-startup-file 'never
-  "Whether to create backup files.
+  "Control use of version numbers for backups of `gnus-startup-file'.
 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)
@@ -57,6 +64,7 @@ variable."
 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)))
@@ -78,14 +86,6 @@ 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
@@ -111,7 +111,7 @@ 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'.
+  3. Option `gnus-read-active-file' is nil or `some'.
   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
@@ -122,9 +122,9 @@ This variable can be a list of select methods which Gnus will query with
 the `ask-server' method in addition to the primary, secondary, and archive
 servers.
 
-Eg.
+E.g.:
   (setq gnus-check-new-newsgroups
-       '((nntp \"some.server\") (nntp \"other.server\")))
+       \\='((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]."
@@ -168,8 +168,13 @@ properly with all servers."
 
 (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.")
+
+Groups with levels less than `gnus-level-subscribed', which
+should be less than this variable, are subscribed.  Groups with
+levels from `gnus-level-subscribed' (exclusive) upto this
+variable (inclusive) are unsubscribed.  See also
+`gnus-level-zombie', `gnus-level-killed' and the Info node `(gnus)Group
+Levels' for details.")
 
 (defconst gnus-level-zombie 8
   "Groups with this level are zombie groups.")
@@ -249,13 +254,13 @@ not match this regexp will be removed before saving the list."
                               (and value (not (stringp value))))
                      :value t)
                (const nil)
-               (regexp :format "%t: %v\n" :size 0)))
+               regexp))
 
 (defcustom gnus-ignored-newsgroups
   (mapconcat 'identity
             '("^to\\."                 ; not "real" groups
               "^[0-9. \t]+\\( \\|$\\)" ; all digits in name
-              "^[\"][]\"[#'()]"        ; bogus characters
+              "^[\"][\"#'()]"  ; bogus characters
               )
             "\\|")
   "*A regexp to match uninteresting newsgroups in the active file.
@@ -287,9 +292,12 @@ claim them."
                function
                (repeat function)))
 
-(defcustom gnus-subscribe-newsgroup-hooks nil
+(define-obsolete-variable-alias 'gnus-subscribe-newsgroup-hooks
+  'gnus-subscribe-newsgroup-functions "24.3")
+(defcustom gnus-subscribe-newsgroup-functions 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)
 
@@ -302,8 +310,8 @@ If, for instance, you want to subscribe to all newsgroups in the
 
 options -n no.all alt.all
 
-Gnus will the subscribe all new newsgroups in these hierarchies with
-the subscription method in this variable."
+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)
@@ -327,8 +335,17 @@ hierarchy in its entirety."
   :group 'gnus-group-new
   :type 'boolean)
 
+(defcustom gnus-auto-subscribed-categories '(mail post-mail)
+  "*New groups from methods of these categories will be subscribed automatically.
+Note that this variable only deals with new groups.  It has no
+effect whatsoever on old groups.  The default is to automatically
+subscribe all groups from mail-like backends."
+  :version "24.1"
+  :group 'gnus-group-new
+  :type '(repeat symbol))
+
 (defcustom gnus-auto-subscribed-groups
-  "^nnml\\|^nnfolder\\|^nnmbox\\|^nnmh\\|^nnbabyl\\|^nnmaildir"
+  "^nnml\\|^nnfolder\\|^nnmbox\\|^nnmh\\|^nnbabyl\\|^nnmaildir\\|^nnimap"
   "*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.
@@ -341,7 +358,7 @@ be subscribed using `gnus-subscribe-options-newsgroup-method'."
 
 (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
@@ -353,7 +370,7 @@ be subscribed using `gnus-subscribe-options-newsgroup-method'."
 
 (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
+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
@@ -378,8 +395,17 @@ This hook is called after Gnus is connected to the NNTP server."
   :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."
+  "A hook called before startup.
+This hook is called as the first thing when Gnus is started.
+See also `gnus-before-resume-hook'."
+  :group 'gnus-start
+  :type 'hook)
+
+(defcustom gnus-before-resume-hook nil
+  "A hook called before resuming Gnus after suspend.
+This hook is called as the first thing when Gnus is resumed after a suspend.
+See also `gnus-before-startup-hook'."
+  :version "24.4"
   :group 'gnus-start
   :type 'hook)
 
@@ -388,14 +414,14 @@ This hook is called as the first thing when Gnus is started."
   :group 'gnus-start
   :type 'hook)
 
-(defcustom gnus-setup-news-hook
-  '(gnus-fixup-nnimap-unread-after-getting-new-news)
+(defcustom gnus-setup-news-hook nil
   "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)
 
@@ -405,9 +431,9 @@ This hook is called as the first thing when Gnus is started."
   :type 'hook)
 
 (defcustom gnus-after-getting-new-news-hook
-  '(gnus-display-time-event-handler
-    gnus-fixup-nnimap-unread-after-getting-new-news)
+  '(gnus-display-time-event-handler)
   "*A hook run after Gnus checks for new news when Gnus is already running."
+  :version "24.1"
   :group 'gnus-group-new
   :type 'hook)
 
@@ -446,6 +472,8 @@ Can be used to turn version control on or off."
 
 ;;; Internal variables
 
+;; Fixme: deal with old emacs-mule when mm-universal-coding-system is
+;; utf-8-emacs.
 (defvar gnus-ding-file-coding-system mm-universal-coding-system
   "Coding system for ding file.")
 
@@ -489,19 +517,23 @@ Can be used to turn version control on or off."
 
 (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
              (push prefix prefixes)
              (message "Descend hierarchy %s? ([y]nsq): "
@@ -513,16 +545,18 @@ Can be used to turn version control on or off."
                         (substring prefix 1 (1- (length prefix)))))
              (cond ((= ans ?n)
                     (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))
                       (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)))
@@ -571,8 +605,7 @@ Can be used to turn version control on or off."
 (defun gnus-subscribe-hierarchically (newgroup)
   "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 (nnheader-find-file-noselect gnus-current-startup-file))
+  (with-current-buffer (nnheader-find-file-noselect gnus-current-startup-file)
     (prog1
        (let ((groupkey newgroup) before)
          (while (and (not before) groupkey)
@@ -594,7 +627,7 @@ Can be used to turn version control on or off."
   "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)))
 
@@ -608,17 +641,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-level-killed (gnus-group-entry (or next "dummy.group")))
+    (gnus-request-update-group-status newsgroup 'subscribe)
     (gnus-message 5 "Subscribe newsgroup: %s" newsgroup)
-    (run-hook-with-args 'gnus-subscribe-newsgroup-hooks newsgroup)
+    (run-hook-with-args 'gnus-subscribe-newsgroup-functions newsgroup)
     t))
 
 (defun gnus-read-active-file-p ()
@@ -628,21 +661,20 @@ the first newsgroup."
 ;;; General various misc type functions.
 
 ;; Silence byte-compiler.
-(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))
+(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."
@@ -661,6 +693,8 @@ the first newsgroup."
   (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
@@ -683,6 +717,7 @@ the first newsgroup."
        nnoo-state-alist nil
        gnus-current-select-method nil
        nnmail-split-history nil
+       gnus-extended-servers nil
        gnus-ephemeral-servers nil)
   (gnus-shutdown 'gnus)
   ;; Kill the startup file.
@@ -703,11 +738,12 @@ the first newsgroup."
 
 (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)
@@ -717,12 +753,13 @@ 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 (gnus-alive-p)
       (progn
+       (gnus-run-hooks 'gnus-before-resume-hook)
        (switch-to-buffer gnus-group-buffer)
        (gnus-group-get-new-news
         (and (numberp arg)
@@ -735,18 +772,16 @@ prompt the user for the name of an NNTP server to use."
     (nnheader-init-server-buffer)
     (setq gnus-slave slave)
     (gnus-read-init-file)
+
+    ;; Add "native" to gnus-predefined-server-alist just to have a
+    ;; name for the native select method.
+    (when gnus-select-method
+      (add-to-list 'gnus-predefined-server-alist
+                  (cons "native" gnus-select-method)))
+
     (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
@@ -756,10 +791,9 @@ prompt the user for the name of an NNTP server to use."
                    (gnus-start-news-server (and arg (not level))))))
        (if (and (not dont-connect)
                 (not did-connect))
+           ;; Couldn't connect to the server, so bail out.
            (gnus-group-quit)
          (gnus-run-hooks 'gnus-startup-hook)
-         ;; NNTP server is successfully open.
-
          ;; Find the current startup file name.
          (setq gnus-current-startup-file
                (gnus-make-newsrc-file gnus-startup-file))
@@ -768,17 +802,11 @@ 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)
+         (when gnus-agent
+           (gnus-request-create-group "queue" '(nndraft "")))
          (gnus-start-draft-setup)
          ;; Generate the group buffer.
          (gnus-group-list-groups level)
@@ -789,10 +817,14 @@ prompt the user for the name of an NNTP server to use."
 
 (defun gnus-start-draft-setup ()
   "Make sure the draft group exists."
+  (interactive)
   (gnus-request-create-group "drafts" '(nndraft ""))
-  (unless (gnus-gethash "nndraft:drafts" gnus-newsrc-hashtb)
+  (unless (gnus-group-entry "nndraft:drafts")
     (let ((gnus-level-default-subscribed 1))
       (gnus-subscribe-group "nndraft:drafts" nil '(nndraft "")))
+    (setcar (gnus-group-entry "nndraft:drafts") 0))
+  (unless (equal (gnus-group-get-parameter "nndraft:drafts" 'gnus-dummy t)
+                '((gnus-draft-mode)))
     (gnus-group-set-parameter
      "nndraft:drafts" 'gnus-dummy '((gnus-draft-mode)))))
 
@@ -813,13 +845,22 @@ prompt the user for the name of an NNTP server to use."
      gnus-current-startup-file)
    "-dribble"))
 
-(defun gnus-dribble-enter (string)
-  "Enter STRING into the dribble buffer."
+(defun gnus-dribble-enter (string &optional regexp)
+  "Enter STRING into the dribble buffer.
+If REGEXP is given, lines that match it will be deleted."
   (when (and (not gnus-dribble-ignore)
             gnus-dribble-buffer
             (buffer-name gnus-dribble-buffer))
     (let ((obuf (current-buffer)))
       (set-buffer gnus-dribble-buffer)
+      (when regexp
+       (goto-char (point-min))
+       (let (end)
+         (while (re-search-forward regexp nil t)
+           (unless (bolp) (forward-line 1))
+           (setq end (point))
+           (goto-char (match-beginning 0))
+           (delete-region (point-at-bol) end))))
       (goto-char (point-max))
       (insert string "\n")
       ;; This has been commented by Josh Huber <huber@alum.wpi.edu>
@@ -828,8 +869,7 @@ prompt the user for the name of an NNTP server to use."
       ;; 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)
+      (with-current-buffer gnus-group-buffer
        (gnus-group-set-mode-line))
       (set-buffer obuf))))
 
@@ -840,12 +880,20 @@ prompt the user for the name of an NNTP server to use."
 (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
-                       (gnus-get-buffer-create
-                        (file-name-nondirectory dribble-file))))
+    (unless (file-exists-p (file-name-directory dribble-file))
+      (make-directory (file-name-directory dribble-file) t))
+    (with-current-buffer (setq gnus-dribble-buffer
+                              (gnus-get-buffer-create
+                               (file-name-nondirectory dribble-file)))
+      (set (make-local-variable 'file-precious-flag) t)
+      (setq buffer-save-without-query t)
       (erase-buffer)
       (setq buffer-file-name dribble-file)
+      ;; The buffer may be shrunk a lot when deleting old entries.
+      ;; It caused the auto-saving to stop.
+      (if (featurep 'emacs)
+         (set (make-local-variable 'auto-save-include-big-deletions) t)
+       (set (make-local-variable 'disable-auto-save-when-buffer-shrinks) nil))
       (auto-save-mode t)
       (buffer-disable-undo)
       (bury-buffer (current-buffer))
@@ -867,7 +915,7 @@ prompt the user for the name of an NNTP server to use."
          (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))
@@ -891,8 +939,7 @@ prompt the user for the name of an NNTP server to use."
   (when (file-exists-p (gnus-dribble-file-name))
     (delete-file (gnus-dribble-file-name)))
   (when gnus-dribble-buffer
-    (save-excursion
-      (set-buffer gnus-dribble-buffer)
+    (with-current-buffer gnus-dribble-buffer
       (let ((auto (make-auto-save-file-name)))
        (when (file-exists-p auto)
          (delete-file auto))
@@ -902,14 +949,13 @@ prompt the user for the name of an NNTP server to use."
 (defun gnus-dribble-save ()
   (when (and gnus-dribble-buffer
             (buffer-name gnus-dribble-buffer))
-    (save-excursion
-      (set-buffer gnus-dribble-buffer)
-      (save-buffer))))
+    (with-current-buffer gnus-dribble-buffer
+      (when (> (buffer-size) 0)
+       (save-buffer)))))
 
 (defun gnus-dribble-clear ()
   (when (gnus-buffer-exists-p gnus-dribble-buffer)
-    (save-excursion
-      (set-buffer gnus-dribble-buffer)
+    (with-current-buffer gnus-dribble-buffer
       (erase-buffer)
       (set-buffer-modified-p nil)
       (setq buffer-saved-size (buffer-size)))))
@@ -937,45 +983,42 @@ If LEVEL is non-nil, the news will be set up at level LEVEL."
       (gnus-read-newsrc-file rawfile))
 
     ;; Make sure the archive server is available to all and sundry.
-    (when gnus-message-archive-method
-      (unless (assoc "archive" gnus-server-alist)
-       (push `("archive"
-               nnfolder
-               "archive"
-               (nnfolder-directory
-                ,(nnheader-concat message-directory "archive"))
-               (nnfolder-active-file
-                ,(nnheader-concat message-directory "archive/active"))
-               (nnfolder-get-new-mail nil)
-               (nnfolder-inhibit-expiry t))
-             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 (not method)
+                 (stringp method)
+                 (memq 'respool (assoc (format "%s" (car method))
+                                       gnus-valid-select-methods)))
+       (setq method "archive")) ;; The default.
+      (when (stringp method)
+       (setq method `(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))))
+      (if (assoc "archive" gnus-server-alist)
+         (when gnus-update-message-archive-method
+           (if method
+               (setcdr (assoc "archive" gnus-server-alist) method)
+             (setq gnus-server-alist (delq (assoc "archive" gnus-server-alist)
+                                           gnus-server-alist))))
+       (when method
+         (push (cons "archive" method) gnus-server-alist))))
 
     ;; If we don't read the complete active file, we fill in the
     ;; hashtb here.
     (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
-    ;; hash table.  The partial filling out of the hash table will be
-    ;; done in `gnus-get-unread-articles'.
-    (and gnus-read-active-file
-        (not level)
-        (gnus-read-active-file nil dont-connect))
-
     (unless gnus-active-hashtb
       (setq gnus-active-hashtb (gnus-make-hashtable 4096)))
-
     ;; Initialize the cache.
     (when gnus-use-cache
       (gnus-cache-open))
@@ -1014,18 +1057,12 @@ If LEVEL is non-nil, the news will be set up at level 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))
-      (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))))
+      (gnus-get-unread-articles level dont-connect))))
 
 (defun gnus-call-subscribe-functions (method group)
   "Call METHOD to subscribe GROUP.
@@ -1065,53 +1102,53 @@ for new groups, and subscribe the new groups as zombies."
                        'gnus-subscribe-zombies)
                  t)
                 (t gnus-check-new-newsgroups))))
-    (unless (gnus-check-first-time-used)
-      (if (or (consp check)
-             (eq check 'ask-server))
-         ;; Ask the server for new groups.
-         (gnus-ask-server-for-new-groups)
-       ;; Go through the active hashtb and look for new groups.
-       (let ((groups 0)
-             group new-newsgroups)
-         (gnus-message 5 "Looking for new newsgroups...")
-         (unless gnus-have-read-active-file
-           (gnus-read-active-file))
-         (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
-         ;; with `gnus-newsrc-hashtb' and `gnus-killed-hashtb'.
-         (mapatoms
-          (lambda (sym)
-            (if (or (null (setq group (symbol-name sym)))
-                    (not (boundp sym))
-                    (null (symbol-value sym))
-                    (gnus-gethash group gnus-killed-hashtb)
-                    (gnus-gethash group gnus-newsrc-hashtb))
-                ()
-              (let ((do-sub (gnus-matches-options-n group)))
-                (cond
-                 ((eq do-sub 'subscribe)
-                  (setq groups (1+ groups))
-                  (gnus-sethash group group gnus-killed-hashtb)
-                  (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
-                      (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))
-         (if (> groups 0)
-             (gnus-message 5 "%d new newsgroup%s arrived."
-                           groups (if (> groups 1) "s have" " has"))
-           (gnus-message 5 "No new newsgroups.")))))))
+    (if (or (consp check)
+            (eq check 'ask-server))
+        ;; Ask the server for new groups.
+        (gnus-ask-server-for-new-groups)
+      ;; Go through the active hashtb and look for new groups.
+      (let ((groups 0)
+            group new-newsgroups)
+        (gnus-message 5 "Looking for new newsgroups...")
+        (unless gnus-have-read-active-file
+          (gnus-read-active-file))
+        (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
+        ;; with `gnus-newsrc-hashtb' and `gnus-killed-hashtb'.
+        (mapatoms
+         (lambda (sym)
+           (if (or (null (setq group (symbol-name sym)))
+                   (not (boundp sym))
+                   (null (symbol-value sym))
+                   (gnus-gethash group gnus-killed-hashtb)
+                   (gnus-gethash group gnus-newsrc-hashtb))
+               ()
+             (let ((do-sub (gnus-matches-options-n group)))
+               (cond
+                ((eq do-sub 'subscribe)
+                 (setq groups (1+ groups))
+                 (gnus-sethash group group gnus-killed-hashtb)
+                 (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
+                     (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))
+        (if (> groups 0)
+            (gnus-message 5 "%d new newsgroup%s arrived."
+                          groups (if (> groups 1) "s have" " has"))
+          (gnus-message 5 "No new newsgroups."))
+       groups))))
 
 (defun gnus-matches-options-n (group)
   ;; Returns `subscribe' if the group is to be unconditionally
@@ -1123,6 +1160,12 @@ for new groups, and subscribe the new groups as zombies."
    ((and gnus-options-subscribe
         (string-match gnus-options-subscribe group))
     'subscribe)
+   ((let ((do-subscribe nil))
+      (dolist (category gnus-auto-subscribed-categories)
+       (when (gnus-member-of-valid category group)
+         (setq do-subscribe t)))
+      do-subscribe)
+    'subscribe)
    ((and gnus-auto-subscribed-groups
         (string-match gnus-auto-subscribed-groups group))
     'subscribe)
@@ -1209,55 +1252,7 @@ for new groups, and subscribe the new groups as zombies."
       (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 ()
-  (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)
-      (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)
-         ;; 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"))))))
+    new-newsgroups))
 
 (defun gnus-subscribe-group (group &optional previous method)
   "Subscribe GROUP and put it after PREVIOUS."
@@ -1295,16 +1290,16 @@ for new groups, and subscribe the new groups as zombies."
     (when (and (stringp entry)
               oldlevel
               (< oldlevel gnus-level-zombie))
-      (setq entry (gnus-gethash entry gnus-newsrc-hashtb)))
+      (setq entry (gnus-group-entry entry)))
     (if (and (not oldlevel)
             (consp entry))
        (setq oldlevel (gnus-info-level (nth 2 entry)))
       (setq oldlevel (or oldlevel gnus-level-killed)))
     (when (stringp previous)
-      (setq previous (gnus-gethash previous gnus-newsrc-hashtb)))
+      (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.
@@ -1328,8 +1323,7 @@ for new groups, and subscribe the new groups as zombies."
                   entry)
          (gnus-sethash (car (nth 2 entry)) nil gnus-newsrc-hashtb)
          (when (nth 3 entry)
-           (setcdr (gnus-gethash (car (nth 3 entry))
-                                 gnus-newsrc-hashtb)
+           (setcdr (gnus-group-entry (car (nth 3 entry)))
                    (cdr entry)))
          (setcdr (cdr entry) (cdddr entry)))))
 
@@ -1340,16 +1334,13 @@ for new groups, and subscribe the new groups as zombies."
        ((>= level gnus-level-zombie)
        ;; Remove from the hash table.
        (gnus-sethash group nil gnus-newsrc-hashtb)
-       ;; We do not enter foreign groups into the list of dead
-       ;; groups.
-       (unless (gnus-group-foreign-p group)
-         (if (= level gnus-level-zombie)
-             (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)))))
+       (if (= level gnus-level-zombie)
+           (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
@@ -1389,19 +1380,14 @@ for new groups, and subscribe the new groups as zombies."
            (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)))))
+          (format "(gnus-group-set-info '%S)" info)
+          (concat "^(gnus-group-set-info '(\"" (regexp-quote group) "\"")))))
       (when gnus-group-change-level-function
        (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))
-
 (defun gnus-check-bogus-newsgroups (&optional confirm)
   "Remove bogus newsgroups.
 If CONFIRM is non-nil, the user has to confirm the deletion of every
@@ -1424,18 +1410,18 @@ newsgroup."
          (push group bogus)))
       (if confirm
          (map-y-or-n-p
-          "Remove bogus group %s? "
+          (format "Remove bogus group %%s (of %d groups)? " (length bogus))
           (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-gethash group gnus-newsrc-hashtb))
+            (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-gethash group gnus-newsrc-hashtb))
+         (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
@@ -1464,8 +1450,8 @@ newsgroup."
       (setq killed (cdr killed)))))
 
 ;; We want to inline a function from gnus-cache, so we cheat here:
+(defvar gnus-cache-active-hashtb)
 (eval-when-compile
-  (defvar gnus-cache-active-hashtb)
   (defun gnus-cache-possibly-alter-active (group active)
     "Alter the ACTIVE info for GROUP to reflect the articles in the cache."
     (when gnus-cache-active-hashtb
@@ -1476,9 +1462,14 @@ newsgroup."
          (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.
+(defun gnus-activate-group (group &optional scan dont-check method
+                                 dont-sub-check)
+  "Check whether a group has been activated or not.
+If SCAN, request a scan of that group as well.  If METHOD, use
+that select method instead of determining the method based on the
+group name.  If DONT-CHECK, don't check whether the group
+actually exists.  If DONT-SUB-CHECK or DONT-CHECK, don't let the
+backend check whether the group actually exists."
   (let ((method (or method (inline (gnus-find-method-for-group group))))
        active)
     (and (inline (gnus-check-server method))
@@ -1491,12 +1482,17 @@ newsgroup."
                (gnus-request-scan group method))
           t)
         (if (or debug-on-error debug-on-quit)
-            (inline (gnus-request-group group dont-check method))
+            (inline (gnus-request-group group (or dont-sub-check dont-check)
+                                        method
+                                        (gnus-get-info group)))
           (condition-case nil
-              (inline (gnus-request-group group dont-check method))
-            ;;(error nil)
+              (inline (gnus-request-group group (or dont-sub-check dont-check)
+                                          method
+                                          (gnus-get-info group)))
             (quit
-             (message "Quit activating %s" group)
+             (if debug-on-quit
+                 (debug "Quit")
+               (message "Quit activating %s" group))
              nil)))
         (unless dont-check
           (setq active (gnus-parse-active))
@@ -1504,17 +1500,26 @@ newsgroup."
           ;; command may have responded with the `(0 . 0)'.  We
           ;; ignore this if we already have an active entry
           ;; for the group.
-          (if (and (zerop (car active))
-                   (zerop (cdr active))
+          (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
               (gnus-request-update-info
@@ -1524,6 +1529,10 @@ newsgroup."
 
     (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 preceding 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
@@ -1531,8 +1540,7 @@ newsgroup."
 
       ;; 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))
+       (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
@@ -1601,158 +1609,239 @@ newsgroup."
        (setq num (max 0 (- (cdr active) num)))))
       ;; Set the number of unread articles.
       (when (and info
-                (gnus-gethash (gnus-info-group info) gnus-newsrc-hashtb))
-       (setcar (gnus-gethash (gnus-info-group info) gnus-newsrc-hashtb) num))
+                (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)
+(defun gnus-get-unread-articles (&optional level dont-connect one-level)
   (setq gnus-server-method-cache nil)
+  (require 'gnus-agent)
   (let* ((newsrc (cdr gnus-newsrc-alist))
-        (level (or level gnus-activate-level (1+ gnus-level-subscribed)))
+        (alevel (or level gnus-activate-level (1+ gnus-level-subscribed)))
         (foreign-level
-         (min
-          (cond ((and gnus-activate-foreign-newsgroups
-                      (not (numberp gnus-activate-foreign-newsgroups)))
-                 (1+ gnus-level-subscribed))
-                ((numberp gnus-activate-foreign-newsgroups)
-                 gnus-activate-foreign-newsgroups)
-                (t 0))
-          level))
+         (or
+          level
+          (min
+           (cond ((and gnus-activate-foreign-newsgroups
+                       (not (numberp gnus-activate-foreign-newsgroups)))
+                  (1+ gnus-level-subscribed))
+                 ((numberp gnus-activate-foreign-newsgroups)
+                  gnus-activate-foreign-newsgroups)
+                 (t 0))
+           alevel)))
         (methods-cache nil)
         (type-cache nil)
-        scanned-methods info group active method retrieve-groups cmethod
-        method-type)
+        (gnus-agent-article-local-times 0)
+        (archive-method (gnus-server-to-method "archive"))
+        infos info group active method cmethod
+        method-type method-group-list entry)
     (gnus-message 6 "Checking new news...")
 
     (while newsrc
       (setq active (gnus-active (setq group (gnus-info-group
                                             (setq info (pop newsrc))))))
-
-      ;; Check newsgroups.  If the user doesn't want to check them, or
-      ;; they can't be checked (for instance, if the news server can't
-      ;; 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.
-
-      ;; 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))
+      ;; First go through all the groups, see what select methods they
+      ;; belong to, and then collect them into lists per unique select
+      ;; method.
+      (if (not (setq method (gnus-info-method info)))
+         (setq method gnus-select-method)
+       ;; There may be several similar methods.  Possibly extend the
+       ;; method.
        (if (setq cmethod (assoc method methods-cache))
            (setq method (cdr cmethod))
-         (setq cmethod (inline (gnus-server-get-method nil method)))
+         (setq cmethod (if (stringp method)
+                           (gnus-server-to-method method)
+                         (inline (gnus-find-method-for-group
+                                  (gnus-info-group info) info))))
          (push (cons method cmethod) methods-cache)
          (setq method cmethod)))
-      (when (and method
-                (not (setq method-type (cdr (assoc method type-cache)))))
+      (setq method-group-list (assoc method type-cache))
+      (unless method-group-list
        (setq method-type
              (cond
-              ((gnus-secondary-method-p method)
+              ((or (gnus-secondary-method-p method)
+                   (and (gnus-archive-server-wanted-p)
+                        (gnus-methods-equal-p archive-method method)))
                'secondary)
               ((inline (gnus-server-equal gnus-select-method method))
                'primary)
               (t
                'foreign)))
-       (push (cons method method-type) type-cache))
-      (if (and method
-              (eq method-type 'foreign))
-         ;; These groups are foreign.  Check the level.
-         (when (and (<= (gnus-info-level info) foreign-level)
-                    (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))))
-       ;; These groups are native or secondary.
-       (cond
-        ;; We don't want these groups.
-        ((> (gnus-info-level info) level)
-         (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.
-      (cond
-       ((eq active 'ignore)
-       ;; Don't do anything.
-       )
-       (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)
-       (let ((tmp (gnus-gethash group gnus-newsrc-hashtb)))
-         (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-gethash group gnus-newsrc-hashtb) t)))))))
-
+       (push (setq method-group-list (list method method-type nil nil))
+             type-cache))
+      ;; Only add groups that need updating.
+      (if (funcall (if one-level #'= #'<=) (gnus-info-level info)
+             (if (eq (cadr method-group-list) 'foreign)
+                 foreign-level
+               alevel))
+         (setcar (nthcdr 2 method-group-list)
+                 (cons info (nth 2 method-group-list)))
+       ;; The group is inactive, so we nix out the number of unread articles.
+       ;; It leads `(gnus-group-unread group)' to return t.  See also
+       ;; `gnus-group-prepare-flat'.
+       (unless active
+         (when (setq entry (gnus-group-entry group))
+           (setcar entry t)))))
+
+    ;; Sort the methods based so that the primary and secondary
+    ;; methods come first.  This is done for legacy reasons to try to
+    ;; ensure that side-effect behavior doesn't change from previous
+    ;; Gnus versions.
+    (setq type-cache
+         (sort (nreverse type-cache)
+               (lambda (c1 c2)
+                 (< (gnus-method-rank (cadr c1) (car c1))
+                    (gnus-method-rank (cadr c2) (car c2))))))
+    ;; Go through the list of servers and possibly extend methods that
+    ;; aren't equal (and that need extension; i.e., they are async).
+    (let ((methods nil))
+      (dolist (elem type-cache)
+       (destructuring-bind (method method-type infos dummy) elem
+         (let ((gnus-opened-servers methods))
+           (when (and (gnus-similar-server-opened method)
+                      (gnus-check-backend-function
+                       'retrieve-group-data-early (car method)))
+             (setq method (gnus-server-extend-method
+                           (gnus-info-group (car infos))
+                           method))
+             (setcar elem method))
+           (push (list method 'ok) methods)))))
+
+    ;; If we have primary/secondary select methods, but no groups from
+    ;; them, we still want to issue a retrieval request from them.
+    (unless dont-connect
+      (dolist (method (cons gnus-select-method
+                           gnus-secondary-select-methods))
+       (when (and (not (assoc method type-cache))
+                  (gnus-check-backend-function 'request-list (car method)))
+         (with-current-buffer nntp-server-buffer
+           (gnus-read-active-file-1 method nil)))))
+
+    ;; Clear out all the early methods.
+    (dolist (elem type-cache)
+      (destructuring-bind (method method-type infos dummy) elem
+       (when (and method
+                  infos
+                  (gnus-check-backend-function
+                   'retrieve-group-data-early (car method))
+                  (not (gnus-method-denied-p method)))
+         (when (ignore-errors (gnus-get-function method 'open-server))
+           (unless (gnus-server-opened method)
+             (gnus-open-server method))
+           (when (gnus-server-opened method)
+             ;; Just mark this server as "cleared".
+             (gnus-retrieve-group-data-early method nil))))))
+
+    ;; Start early async retrieval of data.
+    (let ((done-methods nil)
+         sanity-spec)
+      (dolist (elem type-cache)
+       (destructuring-bind (method method-type infos dummy) elem
+         (setq sanity-spec (list (car method) (cadr method)))
+         (when (and method infos
+                    (not (gnus-method-denied-p method)))
+           ;; If the open-server method doesn't exist, then the method
+           ;; itself doesn't exist, so we ignore it.
+           (if (not (ignore-errors (gnus-get-function method 'open-server)))
+               (setq type-cache (delq elem type-cache))
+             (unless (gnus-server-opened method)
+               (gnus-open-server method))
+             (when (and
+                    ;; This is a sanity check, so that we never
+                    ;; attempt to start two async requests to the
+                    ;; same server, because that will fail.  This
+                    ;; should never happen, since the methods should
+                    ;; be unique at this point, but apparently it
+                    ;; does happen in the wild with some setups.
+                    (not (member sanity-spec done-methods))
+                    (gnus-server-opened method)
+                    (gnus-check-backend-function
+                     'retrieve-group-data-early (car method)))
+               (push sanity-spec done-methods)
+               (when (gnus-check-backend-function 'request-scan (car method))
+                 (gnus-request-scan nil method))
+               ;; Store the token we get back from -early so that we
+               ;; can pass it to -finish later.
+               (setcar (nthcdr 3 elem)
+                       (gnus-retrieve-group-data-early method infos))))))))
+
+    ;; Do the rest of the retrieval.
+    (dolist (elem type-cache)
+      (destructuring-bind (method method-type infos early-data) elem
+       (when (and method infos
+                  (not (gnus-method-denied-p method)))
+         (let ((updatep (gnus-check-backend-function
+                         'request-update-info (car method))))
+           ;; See if any of the groups from this method require updating.
+           (gnus-read-active-for-groups method infos early-data)
+           (dolist (info infos)
+             (inline (gnus-get-unread-articles-in-group
+                      info (gnus-active (gnus-info-group info))
+                      updatep)))))))
     (gnus-message 6 "Checking new news...done")))
 
+(defun gnus-method-rank (type method)
+  (cond
+   ;; Get info for virtual groups last.
+   ((eq (car method) 'nnvirtual)
+    200)
+   ((eq type 'primary)
+    1)
+   ;; Compute the rank of the secondary methods based on where they
+   ;; are in the secondary select list.
+   ((eq type 'secondary)
+    (let ((i 2))
+      (block nil
+       (dolist (smethod gnus-secondary-select-methods)
+         (when (equal method smethod)
+           (return i))
+         (incf i))
+       i)))
+   ;; Just say that all foreign groups have the same rank.
+   (t
+    100)))
+
+(defun gnus-read-active-for-groups (method infos early-data)
+  (with-current-buffer nntp-server-buffer
+    (cond
+     ;; Finish up getting the data from the methods that have -early
+     ;; methods.
+     ((and
+       early-data
+       (gnus-check-backend-function 'finish-retrieve-group-infos (car method))
+       (or (not (gnus-agent-method-p method))
+          (gnus-online method)))
+      (gnus-finish-retrieve-group-infos method infos early-data)
+      ;; We may have altered the data now, so mark the dribble buffer
+      ;; as dirty so that it gets saved.
+      (gnus-dribble-touch)
+      (gnus-agent-save-active method))
+     ;; Most backends have -retrieve-groups.
+     ((gnus-check-backend-function 'retrieve-groups (car method))
+      (when (gnus-check-backend-function 'request-scan (car method))
+       (gnus-request-scan nil method))
+      (let (groups)
+       (gnus-read-active-file-2
+        (dolist (info infos (nreverse groups))
+          (push (gnus-group-real-name (gnus-info-group info)) groups))
+        method)))
+     ;; Virtually all backends have -request-list.
+     ((gnus-check-backend-function 'request-list (car method))
+      (gnus-read-active-file-1 method nil))
+     ;; Except nnvirtual and friends, where we request each group, one
+     ;; by one.
+     (t
+      (dolist (info infos)
+       (gnus-activate-group (gnus-info-group info) nil nil method t))))))
+
 ;; 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
@@ -1761,14 +1850,30 @@ newsgroup."
                               gnus-newsrc-alist
                             (cons (list "dummy.group" 0 nil) alist)))))
     (while alist
-      (gnus-sethash
-       (caar alist)
-       ;; Preserve number of unread articles in groups.
-       (cons (and ohashtb (car (gnus-gethash (caar alist) ohashtb)))
-            prev)
-       gnus-newsrc-hashtb)
-      (setq prev alist
-           alist (cdr 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)))
+      ;; Check for duplicates.
+      (if (gnus-gethash (car info) gnus-newsrc-hashtb)
+         ;; Remove this entry from the alist.
+         (setcdr prev (cddr prev))
+       (gnus-sethash
+        (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))
+      (setq 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."
@@ -1784,8 +1889,7 @@ newsgroup."
 
 (defun gnus-parse-active ()
   "Parse active info in the nntp server buffer."
-  (save-excursion
-    (set-buffer nntp-server-buffer)
+  (with-current-buffer nntp-server-buffer
     (goto-char (point-min))
     ;; Parse the result we got from `gnus-request-group'.
     (when (looking-at "[0-9]+ [0-9]+ \\([0-9]+\\) [0-9]+")
@@ -1795,9 +1899,9 @@ 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
@@ -1817,9 +1921,8 @@ newsgroup."
 
 (defun gnus-make-ascending-articles-unread (group articles)
   "Mark ascending ARTICLES in GROUP as unread."
-  (let* ((entry (or (gnus-gethash group gnus-newsrc-hashtb)
-                    (gnus-gethash (gnus-group-real-name group)
-                                  gnus-newsrc-hashtb)))
+  (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)
@@ -1834,7 +1937,7 @@ newsgroup."
                             ;; OK - I'm done
                             (setq articles nil))
                            ((< range article)
-                            ;; this range preceeds the article. Leave the range unmodified.
+                            ;; this range precedes the article. Leave the range unmodified.
                             (pop ranges)
                             ranges)
                            ((= range article)
@@ -1857,11 +1960,11 @@ newsgroup."
                             (setcar ranges min)
                             ranges)
                            ((< max article)
-                            ;; this range preceeds the article. Leave the range unmodified.
+                            ;; this range precedes the article. Leave the range unmodified.
                             (pop ranges)
                             ranges)
                            ((< article min)
-                            ;; this article preceeds the range.  Return null to move to the
+                            ;; this article precedes the range.  Return null to move to the
                             ;; next article
                             nil)
                            (t
@@ -1870,7 +1973,7 @@ newsgroup."
                             (setcdr range (1- article))
                             (setq modified t)
                             ranges))))))))
-                  
+
     (when modified
       (when (eq modified 'remove-null)
         (setq r (delq nil r)))
@@ -1891,7 +1994,7 @@ newsgroup."
     (while lists
       (setq killed (car lists))
       (while killed
-       (gnus-sethash (car killed) nil hashtb)
+       (gnus-sethash (mm-string-as-unibyte (car killed)) nil hashtb)
        (setq killed (cdr killed)))
       (setq lists (cdr lists)))))
 
@@ -1940,12 +2043,13 @@ newsgroup."
             (list "archive")))))
        method)
     (setq gnus-have-read-active-file nil)
-    (save-excursion
-      (set-buffer nntp-server-buffer)
+    (with-current-buffer nntp-server-buffer
       (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)
+       (when (and (not (member method methods))
+                  ;; Check whether the backend exists.
+                  (ignore-errors (gnus-get-function method 'open-server)))
          (if (or debug-on-error debug-on-quit)
              (gnus-read-active-file-1 method force)
            (condition-case ()
@@ -1953,7 +2057,9 @@ newsgroup."
              ;; We catch C-g so that we can continue past servers
              ;; that do not respond.
              (quit
-              (message "Quit reading the active file")
+              (if debug-on-quit
+                  (debug "Quit")
+                (message "Quit reading the active file"))
               nil))))))))
 
 (defun gnus-read-active-file-1 (method force)
@@ -1963,10 +2069,13 @@ newsgroup."
                       (if (and where (not (zerop (length where))))
                           (concat " from " where) "")
                       (car method)))
-    (gnus-message 5 mesg)
+    (gnus-message 5 "%s" mesg)
     (when (gnus-check-server method)
       ;; Request that the backend scan its incoming messages.
-      (when (gnus-check-backend-function 'request-scan (car method))
+      (when (and (or (and gnus-agent
+                         (gnus-online method))
+                    (not gnus-agent))
+                (gnus-check-backend-function 'request-scan (car method)))
        (gnus-request-scan nil method))
       (cond
        ((and (eq gnus-read-active-file 'some)
@@ -1978,10 +2087,10 @@ newsgroup."
          (while (setq info (pop newsrc))
            (when (inline
                    (gnus-server-equal
-                    (inline
-                      (gnus-find-method-for-group
-                       (gnus-info-group info) info))
-                    gmethod))
+                         (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)))
@@ -1992,17 +2101,16 @@ newsgroup."
            (unless (equal method gnus-message-archive-method)
              (gnus-error 1 "Cannot read active file from %s server"
                          (car method)))
-         (gnus-message 5 mesg)
+         (gnus-message 5 "%s" mesg)
          (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)
+         (add-to-list 'gnus-have-read-active-file method)
          (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)
+    (with-current-buffer nntp-server-buffer
       (gnus-check-server method)
       (let ((list-type (gnus-retrieve-groups groups method)))
        (cond ((not list-type)
@@ -2028,7 +2136,8 @@ newsgroup."
                            (if (equal method gnus-select-method)
                                (gnus-make-hashtable
                                 (count-lines (point-min) (point-max)))
-                             (gnus-make-hashtable 4096)))))))
+                             (gnus-make-hashtable 4096))))))
+       group max min)
     ;; Delete unnecessary lines.
     (goto-char (point-min))
     (cond
@@ -2063,8 +2172,12 @@ newsgroup."
                      (insert prefix)
                      (zerop (forward-line 1)))))))
     ;; Store the active file in a hash table.
-    (goto-char (point-min))
-    (let (group max min)
+    ;; Use a unibyte buffer in order to make `read' read non-ASCII
+    ;; group names (which have been encoded) as unibyte strings.
+    (mm-with-unibyte-buffer
+      (insert-buffer-substring cur)
+      (setq cur (current-buffer))
+      (goto-char (point-min))
       (while (not (eobp))
        (condition-case ()
            (progn
@@ -2125,7 +2238,7 @@ newsgroup."
             (gnus-online method)
             (gnus-agent-method-p method))
        (progn
-         (gnus-agent-save-groups method)
+         (gnus-agent-save-active method t)
          (gnus-active-to-gnus-format method hashtb nil real-active))
 
       (goto-char (point-min))
@@ -2198,20 +2311,102 @@ If FORCE is non-nil, the .newsrc file is read."
          (gnus-message 5 "Reading %s...done" newsrc-file)))
 
       ;; Convert old to new.
-      (gnus-convert-old-newsrc))))
+      (gnus-convert-old-newsrc)
+      (gnus-clean-old-newsrc))))
+
+(defun gnus-clean-old-newsrc (&optional force)
+  ;; Currently no cleanups.
+  )
 
 (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
+           (gnus-format-message ";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))
@@ -2236,11 +2431,11 @@ If FORCE is non-nil, the .newsrc file is read."
            (eval form))
        (error
         (unless (eq (car type) 'end-of-file)
-          (let ((error (format "Error in %s line %d" file
-                               (count-lines (point-min) (point)))))
+          (let ((errmsg (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)))))))))
+            (unless (gnus-yes-or-no-p (concat errmsg "; continue? "))
+              (error "%s" errmsg)))))))))
 
 (defun gnus-read-newsrc-el-file (file)
   (let ((ding-file (concat file "d")))
@@ -2248,8 +2443,7 @@ If FORCE is non-nil, the .newsrc file is read."
       ;; 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))
+       (gnus-load ding-file)
        ;; Older versions of `gnus-format-specs' are no longer valid
        ;; in Oort Gnus 0.01.
        (let ((version
@@ -2260,6 +2454,10 @@ If FORCE is non-nil, the .newsrc file is read."
            (setq gnus-format-specs gnus-default-format-specs)))
        (when gnus-newsrc-assoc
          (setq gnus-newsrc-alist gnus-newsrc-assoc))))
+    (dolist (elem gnus-newsrc-alist)
+      ;; Protect against broken .newsrc.el files.
+      (when (car elem)
+       (setcar elem (mm-string-as-unibyte (car elem)))))
     (gnus-make-hashtable-from-newsrc-alist)
     (when (file-newer-than-file-p file ding-file)
       ;; Old format quick file
@@ -2369,7 +2567,7 @@ If FORCE is non-nil, the .newsrc file is read."
        ((or (eq symbol options-symbol)
            (eq symbol Options-symbol))
        (setq gnus-newsrc-options
-             ;; This concating is quite inefficient, but since our
+             ;; This concatting is quite inefficient, but since our
              ;; thorough studies show that approx 99.37% of all
              ;; .newsrc files only contain a single options line, we
              ;; don't give a damn, frankly, my dear.
@@ -2378,7 +2576,7 @@ If FORCE is non-nil, the .newsrc file is read."
                       (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
@@ -2404,7 +2602,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
@@ -2585,19 +2783,23 @@ If FORCE is non-nil, the .newsrc file is read."
       'msdos-long-file-names
       (lambda () t))))
 
+(defvar gnus-save-newsrc-file-last-timestamp nil)
 (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)
               (or (not gnus-dribble-buffer)
                   (not (buffer-name gnus-dribble-buffer))
-                  (zerop (save-excursion
-                           (set-buffer gnus-dribble-buffer)
+                  (zerop (with-current-buffer gnus-dribble-buffer
                            (buffer-size)))))
          (gnus-message 4 "(No changes need to be saved)")
        (gnus-run-hooks 'gnus-save-newsrc-hook)
@@ -2608,6 +2810,7 @@ If FORCE is non-nil, the .newsrc file is read."
            (gnus-message 8 "Saving %s..." gnus-current-startup-file)
            (gnus-gnus-to-newsrc-format)
            (gnus-message 8 "Saving %s...done" gnus-current-startup-file))
+
          ;; Save .newsrc.eld.
          (set-buffer (gnus-get-buffer-create " *Gnus-newsrc*"))
          (make-local-variable 'version-control)
@@ -2619,12 +2822,29 @@ If FORCE is non-nil, the .newsrc file is read."
          (erase-buffer)
           (gnus-message 5 "Saving %s.eld..." gnus-current-startup-file)
 
+          ;; check timestamp of `gnus-current-startup-file'.eld against
+          ;; `gnus-save-newsrc-file-last-timestamp'
+          (let* ((checkfile (concat gnus-current-startup-file ".eld"))
+                 (mtime (nth 5 (file-attributes checkfile))))
+            (when (and gnus-save-newsrc-file-last-timestamp
+                       (time-less-p gnus-save-newsrc-file-last-timestamp
+                                    mtime))
+              (unless (y-or-n-p
+                       (format "%s was updated externally after %s, save?"
+                               checkfile
+                               (format-time-string
+                                "%c"
+                                gnus-save-newsrc-file-last-timestamp)))
+                (error "Couldn't save %s: updated externally" checkfile))))
+
           (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))
+                (save-buffer)
+                (setq gnus-save-newsrc-file-last-timestamp
+                            (nth 5 (file-attributes buffer-file-name))))
             (let ((coding-system-for-write gnus-ding-file-coding-system)
                   (version-control gnus-backup-startup-file)
                   (startup-file (concat gnus-current-startup-file ".eld"))
@@ -2637,9 +2857,7 @@ If FORCE is non-nil, the .newsrc file is read."
                                    (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"))
+                                    "%s#tmp#%d")
                                    working-dir (setq i (1+ i))))
                             (file-exists-p working-file)))
 
@@ -2661,7 +2879,9 @@ If FORCE is non-nil, the .newsrc file is read."
 
                       ;; Replace the existing startup file with the temp file.
                       (rename-file working-file startup-file t)
-                      (set-file-modes startup-file setmodes)))
+                      (gnus-set-file-modes startup-file setmodes)
+                      (setq gnus-save-newsrc-file-last-timestamp
+                            (nth 5 (file-attributes startup-file)))))
                 (condition-case nil
                     (delete-file working-file)
                   (file-error nil)))))
@@ -2674,7 +2894,8 @@ If FORCE is non-nil, the .newsrc file is read."
 
 (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")
+    (princ (format ";; -*- mode:emacs-lisp; coding: %s; -*-\n"
+                  gnus-ding-file-coding-system))
     (if name
        (princ (format ";; %s\n" name))
       (princ ";; Gnus startup file.\n"))
@@ -2693,6 +2914,7 @@ If FORCE is non-nil, the .newsrc file is read."
            (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
@@ -2712,7 +2934,7 @@ If FORCE is non-nil, the .newsrc file is read."
       (while variables
        (when (and (boundp (setq variable (pop variables)))
                   (symbol-value variable))
-         (princ "(setq ")
+         (princ "\n(setq ")
           (princ (symbol-name variable))
           (princ " '")
          (prin1 (symbol-value variable))
@@ -2728,10 +2950,10 @@ If FORCE is non-nil, the .newsrc file is read."
       (pop list))
     (nreverse olist)))
 
-(defun gnus-gnus-to-newsrc-format ()
+(defun gnus-gnus-to-newsrc-format (&optional foreign-ok)
+  (interactive (list (gnus-y-or-n-p "write foreign groups too? ")))
   ;; Generate and save the .newsrc file.
-  (save-excursion
-    (set-buffer (create-file-buffer gnus-current-startup-file))
+  (with-current-buffer (create-file-buffer gnus-current-startup-file)
     (let ((newsrc (cdr gnus-newsrc-alist))
          (standard-output (current-buffer))
          info ranges range method)
@@ -2739,6 +2961,10 @@ If FORCE is non-nil, the .newsrc file is read."
       (setq default-directory (file-name-directory buffer-file-name))
       (buffer-disable-undo)
       (erase-buffer)
+      ;; Use a unibyte buffer since group names are unibyte strings;
+      ;; in particular, non-ASCII group names are the ones encoded by
+      ;; a certain coding system.
+      (mm-disable-multibyte)
       ;; Write options.
       (when gnus-newsrc-options
        (insert gnus-newsrc-options))
@@ -2747,7 +2973,8 @@ If FORCE is non-nil, the .newsrc file is read."
        ;; Don't write foreign groups to .newsrc.
        (when (or (null (setq method (gnus-info-method info)))
                  (equal method "native")
-                 (inline (gnus-server-equal method gnus-select-method)))
+                 (inline (gnus-server-equal method gnus-select-method))
+                  foreign-ok)
          (insert (gnus-info-group info)
                  (if (> (gnus-info-level info) gnus-level-subscribed)
                      "!" ":"))
@@ -2781,7 +3008,8 @@ If FORCE is non-nil, the .newsrc file is read."
          (delete-file gnus-startup-file)
        (clear-visited-file-modtime))
       (gnus-run-hooks 'gnus-save-standard-newsrc-hook)
-      (save-buffer)
+      (let ((coding-system-for-write 'raw-text))
+       (save-buffer))
       (kill-buffer (current-buffer)))))
 
 \f
@@ -2793,12 +3021,13 @@ If FORCE is non-nil, the .newsrc file is read."
 
 (defun gnus-slave-mode ()
   "Minor mode for slave Gnusae."
+  ;; FIXME: gnus-slave-mode appears to never be set (i.e. it'll always be nil):
+  ;; Remove, or fix and use define-minor-mode.
   (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)
+  (with-current-buffer gnus-dribble-buffer
     (let ((slave-name
           (mm-make-temp-file (concat gnus-current-startup-file "-slave-")))
          (modes (ignore-errors
@@ -2806,7 +3035,7 @@ If FORCE is non-nil, the .newsrc file is read."
       (let ((coding-system-for-write gnus-ding-file-coding-system))
        (gnus-write-buffer slave-name))
       (when modes
-       (set-file-modes slave-name modes)))))
+       (gnus-set-file-modes slave-name modes)))))
 
 (defun gnus-master-read-slave-newsrc ()
   (let ((slave-files
@@ -2822,8 +3051,7 @@ If FORCE is non-nil, the .newsrc file is read."
     (if (not slave-files)
        ()                              ; There are no slave files to read.
       (gnus-message 7 "Reading slave newsrcs...")
-      (save-excursion
-       (set-buffer (gnus-get-buffer-create " *gnus slave*"))
+      (with-current-buffer (gnus-get-buffer-create " *gnus slave*")
        (setq slave-files
              (sort (mapcar (lambda (file)
                              (list (nth 5 (file-attributes file)) file))
@@ -2891,6 +3119,7 @@ If FORCE is non-nil, the .newsrc file is read."
       nil)
      (t
       (save-excursion
+        ;; FIXME: Shouldn't save-restriction be done after set-buffer?
        (save-restriction
          (set-buffer nntp-server-buffer)
          (goto-char (point-min))
@@ -2942,8 +3171,7 @@ If FORCE is non-nil, the .newsrc file is read."
 (defun gnus-group-get-description (group)
   "Get the description of a group by sending XGTITLE to the server."
   (when (gnus-request-group-description group)
-    (save-excursion
-      (set-buffer nntp-server-buffer)
+    (with-current-buffer nntp-server-buffer
       (goto-char (point-min))
       (when (looking-at "[^ \t]+[ \t]+\\(.*\\)")
        (match-string 1)))))
@@ -2965,30 +3193,47 @@ If this variable is nil, don't do anything."
            (file-name-as-directory (expand-file-name gnus-default-directory))
          default-directory)))
 
-(eval-and-compile
-(defalias 'gnus-display-time-event-handler
-  (if (gnus-boundp 'display-time-timer)
-      'display-time-event-handler
-    (lambda () "Does nothing as `display-time-timer' is not bound.
-Would otherwise be an alias for `display-time-event-handler'." nil))))
-
-;;;###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-display-time-event-handler ()
+  (if (and (fboundp 'display-time-event-handler)
+          (gnus-boundp 'display-time-timer))
+      (display-time-event-handler)))
+
+(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)
 
 ;;; gnus-start.el ends here
-
-