Synch with the emacs-25 branch; the changes will be merged to the trunk (soon?)
[gnus] / lisp / gnus-group.el
index 7596c7e..b1a4933 100644 (file)
@@ -1,7 +1,6 @@
 ;;; gnus-group.el --- group mode commands for Gnus
 
-;; Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
-;;   2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
+;; Copyright (C) 1996-2015 Free Software Foundation, Inc.
 
 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
 ;; Keywords: news
 
 ;;; Code:
 
-;; For Emacs < 22.2.
-(eval-and-compile
-  (unless (fboundp 'declare-function) (defmacro declare-function (&rest r))))
-
 (eval-when-compile
   (require 'cl))
 (defvar tool-bar-mode)
@@ -55,7 +50,9 @@
 (autoload 'gnus-agent-total-fetched-for "gnus-agent")
 (autoload 'gnus-cache-total-fetched-for "gnus-cache")
 
-(defcustom gnus-no-groups-message "No Gnus is good news"
+(autoload 'gnus-group-make-nnir-group "nnir")
+
+(defcustom gnus-no-groups-message "No news is good news"
   "*Message displayed by Gnus when no groups are available."
   :group 'gnus-start
   :type 'string)
@@ -117,10 +114,11 @@ If nil, only list groups that have unread articles."
   :type 'boolean)
 
 (defcustom gnus-group-default-list-level gnus-level-subscribed
-  "*Default listing level.
+  "Default listing level.
 Ignored if `gnus-group-use-permanent-levels' is non-nil."
   :group 'gnus-group-listing
-  :type 'integer)
+  :type '(choice (integer :tag "Level")
+                 (function :tag "Function returning level")))
 
 (defcustom gnus-group-list-inactive-groups t
   "*If non-nil, inactive groups will be listed."
@@ -157,7 +155,7 @@ list."
                         (function-item gnus-group-sort-by-rank)
                         (function :tag "other" nil))))
 
-(defcustom gnus-group-line-format "%M\%S\%p\%P\%5y:%B%(%g%)\n"
+(defcustom gnus-group-line-format "%M\ %S\ %p\ %P\ %5y:%B%(%g%)\n"
   "*Format of group lines.
 It works along the same lines as a normal formatting string,
 with some simple extensions.
@@ -215,7 +213,7 @@ See Info node `(gnus)Formatting Variables'."
   :group 'gnus-group-visual
   :type 'string)
 
-(defcustom gnus-group-mode-line-format "Gnus: %%b {%M\%:%S}"
+(defcustom gnus-group-mode-line-format "Gnus: %%b {%M\ %:%S}"
   "*The format specification for the group mode line.
 It works along the same lines as a normal formatting string,
 with some simple extensions:
@@ -360,7 +358,7 @@ If you want to modify the group buffer, you can use this hook."
      gnus-group-news-low))
   "*Controls the highlighting of group buffer lines.
 
-Below is a list of `Form'/`Face' pairs.  When deciding how a a
+Below is a list of `Form'/`Face' pairs.  When deciding how a
 particular group line should be displayed, each form is
 evaluated.  The content of the face field after the first true form is
 used.  You can change how those group lines are displayed by
@@ -447,7 +445,7 @@ If non-nil, the value should be a string or an alist.  If it is a string,
 e.g. \"nnml:\", in which case `gnus-group-jump-to-group' offers \"Group:
 nnml:\" in the minibuffer prompt.
 
-If it is an alist, it must consist of \(NUMBER .  PROMPT\) pairs, for example:
+If it is an alist, it must consist of \(NUMBER .  PROMPT) pairs, for example:
 \((1 .  \"\") (2 .  \"nnfolder+archive:\")).  The element with number 0 is
 used when no prefix argument is given to `gnus-group-jump-to-group'."
   :version "22.1"
@@ -480,6 +478,26 @@ simple manner.")
 
 (defvar gnus-group-edit-buffer nil)
 
+(defvar gnus-tmp-news-method)
+(defvar gnus-tmp-colon)
+(defvar gnus-tmp-news-server)
+(defvar gnus-tmp-decoded-group)
+(defvar gnus-tmp-header)
+(defvar gnus-tmp-process-marked)
+(defvar gnus-tmp-summary-live)
+(defvar gnus-tmp-news-method-string)
+(defvar gnus-tmp-group-icon)
+(defvar gnus-tmp-moderated-string)
+(defvar gnus-tmp-newsgroup-description)
+(defvar gnus-tmp-comment)
+(defvar gnus-tmp-qualified-group)
+(defvar gnus-tmp-subscribed)
+(defvar gnus-tmp-number-of-read)
+(defvar gnus-inhibit-demon)
+(defvar gnus-pick-mode)
+(defvar gnus-tmp-marked-mark)
+(defvar gnus-tmp-number-of-unread)
+
 (defvar gnus-group-line-format-alist
   `((?M gnus-tmp-marked-mark ?c)
     (?S gnus-tmp-subscribed ?c)
@@ -569,7 +587,6 @@ simple manner.")
   "p" gnus-group-prev-unread-group
   "\177" gnus-group-prev-unread-group
   [delete] gnus-group-prev-unread-group
-  [backspace] gnus-group-prev-unread-group
   "N" gnus-group-next-group
   "P" gnus-group-prev-group
   "\M-n" gnus-group-next-unread-group-same-level
@@ -653,6 +670,7 @@ simple manner.")
   "D" gnus-group-enter-directory
   "f" gnus-group-make-doc-group
   "w" gnus-group-make-web-group
+  "G" gnus-group-make-nnir-group
   "M" gnus-group-read-ephemeral-group
   "r" gnus-group-rename-group
   "R" gnus-group-make-rss-group
@@ -694,7 +712,8 @@ simple manner.")
   "M" gnus-group-list-all-matching
   "l" gnus-group-list-level
   "c" gnus-group-list-cached
-  "?" gnus-group-list-dormant)
+  "?" gnus-group-list-dormant
+  "!" gnus-group-list-ticked)
 
 (gnus-define-keys (gnus-group-list-limit-map "/" gnus-group-list-map)
   "k"  gnus-group-list-limit
@@ -706,7 +725,8 @@ simple manner.")
   "M"  gnus-group-list-limit
   "l"  gnus-group-list-limit
   "c"  gnus-group-list-limit
-  "?"  gnus-group-list-limit)
+  "?"  gnus-group-list-limit
+  "!"  gnus-group-list-limit)
 
 (gnus-define-keys (gnus-group-list-flush-map "f" gnus-group-list-map)
   "k"  gnus-group-list-flush
@@ -718,7 +738,8 @@ simple manner.")
   "M"  gnus-group-list-flush
   "l"  gnus-group-list-flush
   "c"  gnus-group-list-flush
-  "?"  gnus-group-list-flush)
+  "?"  gnus-group-list-flush
+  "!"  gnus-group-list-flush)
 
 (gnus-define-keys (gnus-group-list-plus-map "p" gnus-group-list-map)
   "k"  gnus-group-list-plus
@@ -730,14 +751,14 @@ simple manner.")
   "M"  gnus-group-list-plus
   "l"  gnus-group-list-plus
   "c"  gnus-group-list-plus
-  "?"  gnus-group-list-plus)
+  "?"  gnus-group-list-plus
+  "!"  gnus-group-list-plus)
 
 (gnus-define-keys (gnus-group-score-map "W" gnus-group-mode-map)
   "f" gnus-score-flush-cache
   "e" gnus-score-edit-all-score)
 
 (gnus-define-keys (gnus-group-help-map "H" gnus-group-mode-map)
-  "C" gnus-group-fetch-control
   "d" gnus-group-describe-group
   "v" gnus-version)
 
@@ -757,7 +778,6 @@ simple manner.")
        (symbol-value 'gnus-topic-mode)))
 
 (defun gnus-group-make-menu-bar ()
-  (gnus-turn-off-edit-menu 'group)
   (unless (boundp 'gnus-group-reading-menu)
 
     (easy-menu-define
@@ -804,10 +824,6 @@ simple manner.")
        ["Describe" gnus-group-describe-group :active (gnus-group-group-name)
        ,@(if (featurep 'xemacs) nil
            '(:help "Display description of the current group"))]
-       ["Fetch control message" gnus-group-fetch-control
-       :active (gnus-group-group-name)
-       ,@(if (featurep 'xemacs) nil
-           '(:help "Display the archived control message for the current group"))]
        ;; Actually one should check, if any of the marked groups gives t for
        ;; (gnus-check-backend-function 'request-expire-articles ...)
        ["Expire articles" gnus-group-expire-articles
@@ -852,7 +868,8 @@ simple manner.")
        ["List all groups matching..." gnus-group-list-all-matching t]
        ["List active file" gnus-group-list-active t]
        ["List groups with cached" gnus-group-list-cached t]
-       ["List groups with dormant" gnus-group-list-dormant t])
+       ["List groups with dormant" gnus-group-list-dormant t]
+       ["List groups with ticked" gnus-group-list-ticked t])
        ("Sort"
        ["Default sort" gnus-group-sort-groups t]
        ["Sort by method" gnus-group-sort-groups-by-method t]
@@ -905,6 +922,7 @@ simple manner.")
        ["Add the help group" gnus-group-make-help-group t]
        ["Make a doc group..." gnus-group-make-doc-group t]
        ["Make a web group..." gnus-group-make-web-group t]
+       ["Make a search group..." gnus-group-make-nnir-group t]
        ["Make a virtual group..." gnus-group-make-empty-virtual t]
        ["Add a group to a virtual..." gnus-group-add-to-virtual t]
        ["Make an ephemeral group..." gnus-group-read-ephemeral-group t]
@@ -989,7 +1007,7 @@ Setter function for custom variables."
                                 'gnus-group-tool-bar-retro)
   "Specifies the Gnus group tool bar.
 
-It can be either a list or a symbol refering to a list.  See
+It can be either a list or a symbol referring to a list.  See
 `gmm-tool-bar-from-list' for the format of the list.  The
 default key map is `gnus-group-mode-map'.
 
@@ -1008,10 +1026,10 @@ Pre-defined symbols include `gnus-group-tool-bar-gnome' and
   '((gnus-group-post-news "mail/compose")
     ;; Some useful agent icons?  I don't use the agent so agent users should
     ;; suggest useful commands:
-    (gnus-agent-toggle-plugged "disconnect" t
+    (gnus-agent-toggle-plugged "unplugged" t
                               :help "Gnus is currently unplugged.  Click to work online."
                               :visible (and gnus-agent (not gnus-plugged)))
-    (gnus-agent-toggle-plugged "connect" t
+    (gnus-agent-toggle-plugged "plugged" t
                               :help "Gnus is currently plugged.  Click to work offline."
                               :visible (and gnus-agent gnus-plugged))
     ;; FIXME: gnus-agent-toggle-plugged (in gnus-agent-group-make-menu-bar)
@@ -1086,8 +1104,7 @@ When FORCE, rebuild the tool bar."
   (when (and (not (featurep 'xemacs))
             (boundp 'tool-bar-mode)
             tool-bar-mode
-            ;; The Gnus 5.10.6 code checked (default-value 'tool-bar-mode).
-            ;; Why?  --rsteib
+             (display-graphic-p)
             (or (not gnus-group-tool-bar-map) force))
     (let* ((load-path
            (gmm-image-load-path-for-library "gnus"
@@ -1103,7 +1120,7 @@ When FORCE, rebuild the tool bar."
          (set (make-local-variable 'tool-bar-map) map))))
   gnus-group-tool-bar-map)
 
-(defun gnus-group-mode ()
+(define-derived-mode gnus-group-mode fundamental-mode "Group"
   "Major mode for reading news.
 
 All normal editing commands are switched off.
@@ -1120,17 +1137,12 @@ For more in-depth information on this mode, read the manual (`\\[gnus-info-find-
 The following commands are available:
 
 \\{gnus-group-mode-map}"
-  (interactive)
-  (kill-all-local-variables)
   (when (gnus-visual-p 'group-menu 'menu)
     (gnus-group-make-menu-bar)
     (gnus-group-make-tool-bar))
   (gnus-simplify-mode-line)
-  (setq major-mode 'gnus-group-mode)
-  (setq mode-name "Group")
   (gnus-group-set-mode-line)
   (setq mode-line-process nil)
-  (use-local-map gnus-group-mode-map)
   (buffer-disable-undo)
   (setq truncate-lines t)
   (setq buffer-read-only t
@@ -1141,16 +1153,14 @@ The following commands are available:
   (when gnus-use-undo
     (gnus-undo-mode 1))
   (when gnus-slave
-    (gnus-slave-mode))
-  (gnus-run-mode-hooks 'gnus-group-mode-hook))
+    (gnus-slave-mode)))
 
 (defun gnus-update-group-mark-positions ()
   (save-excursion
     (let ((gnus-process-mark ?\200)
          (gnus-group-update-hook nil)
          (gnus-group-marked '("dummy.group"))
-         (gnus-active-hashtb (make-vector 10 0))
-         (topic ""))
+         (gnus-active-hashtb (make-vector 10 0)))
       (gnus-set-active "dummy.group" '(0 . 0))
       (gnus-set-work-buffer)
       (gnus-group-insert-group-line "dummy.group" 0 nil 0 nil)
@@ -1166,6 +1176,12 @@ The following commands are available:
   (mouse-set-point e)
   (gnus-group-read-group nil))
 
+(defun gnus-group-default-list-level ()
+  "Return the real value for `gnus-group-default-list-level'."
+  (if (functionp gnus-group-default-list-level)
+      (funcall gnus-group-default-list-level)
+    gnus-group-default-list-level))
+
 ;; Look at LEVEL and find out what the level is really supposed to be.
 ;; If LEVEL is non-nil, LEVEL will be returned, if not, what happens
 ;; will depend on whether `gnus-group-use-permanent-levels' is used.
@@ -1175,37 +1191,41 @@ The following commands are available:
     (or (setq gnus-group-use-permanent-levels
              (or level (if (numberp gnus-group-use-permanent-levels)
                            gnus-group-use-permanent-levels
-                         (or gnus-group-default-list-level
+                         (or (gnus-group-default-list-level)
                              gnus-level-subscribed))))
-       gnus-group-default-list-level gnus-level-subscribed))
+       (gnus-group-default-list-level) gnus-level-subscribed))
    (number-or-nil
     level)
    (t
-    (or level gnus-group-default-list-level gnus-level-subscribed))))
+    (or level (gnus-group-default-list-level) gnus-level-subscribed))))
 
 (defun gnus-group-setup-buffer ()
   (set-buffer (gnus-get-buffer-create gnus-group-buffer))
-  (unless (eq major-mode 'gnus-group-mode)
-    (gnus-group-mode)
-    (when gnus-carpal
-      (gnus-carpal-setup-buffer 'group))))
+  (unless (derived-mode-p 'gnus-group-mode)
+    (gnus-group-mode)))
 
 (defun gnus-group-name-charset (method group)
-  (if (null method)
-      (setq method (gnus-find-method-for-group group)))
-  (let ((item (or (assoc method gnus-group-name-charset-method-alist)
-                 (and (consp method)
-                      (assoc (list (car method) (cadr method))
-                             gnus-group-name-charset-method-alist))))
-       (alist gnus-group-name-charset-group-alist)
-       result)
-    (if item
-       (cdr item)
-      (while (setq item (pop alist))
-       (if (string-match (car item) group)
-           (setq alist nil
-                 result (cdr item))))
-      result)))
+  (unless method
+    (setq method (gnus-find-method-for-group group)))
+  (when (stringp method)
+    (setq method (gnus-server-to-method method)))
+  (if (eq (car method) 'nnimap)
+      ;; IMAP groups should not be encoded, since they do the encoding
+      ;; in utf7 in the protocol.
+      'utf-8
+    (let ((item (or (assoc method gnus-group-name-charset-method-alist)
+                   (and (consp method)
+                        (assoc (list (car method) (cadr method))
+                               gnus-group-name-charset-method-alist))))
+         (alist gnus-group-name-charset-group-alist)
+         result)
+      (if item
+         (cdr item)
+       (while (setq item (pop alist))
+         (if (string-match (car item) group)
+             (setq alist nil
+                   result (cdr item))))
+       result))))
 
 (defun gnus-group-name-decode (string charset)
   ;; Fixme: Don't decode in unibyte mode.
@@ -1229,7 +1249,7 @@ Also see the `gnus-group-use-permanent-levels' variable."
             (prefix-numeric-value current-prefix-arg)
           (or
            (gnus-group-default-level nil t)
-           gnus-group-default-list-level
+           (gnus-group-default-list-level)
            gnus-level-subscribed))))
   (unless level
     (setq level (car gnus-group-list-mode)
@@ -1342,14 +1362,14 @@ if it is a string, only list groups matching REGEXP."
                     (predicate t)      ; We list all groups?
                     (t
                      (or
-                      (if (eq unread t) ; Unactivated?
+                      (if (eq unread t) ; Inactive?
                           gnus-group-list-inactive-groups
-                                       ; We list unactivated
+                                       ; We list inactive
                         (and (numberp unread) (> unread 0)))
                                        ; We list groups with unread articles
                       (and gnus-list-groups-with-ticked-articles
                            (cdr (assq 'tick (gnus-info-marks info))))
-                                       ; And groups with tickeds
+                                       ; And groups with ticked articles
                       ;; Check for permanent visibility.
                       (and gnus-permanently-visible-groups
                            (string-match gnus-permanently-visible-groups
@@ -1434,7 +1454,8 @@ if it is a string, only list groups matching REGEXP."
           (gnus-dribble-enter
            (concat "(gnus-group-set-info '"
                    (gnus-prin1-to-string (nth 2 entry))
-                   ")")))
+                   ")")
+           (concat "^(gnus-group-set-info '(\"" (regexp-quote group) "\"")))
       (setq gnus-group-indentation (gnus-group-group-indentation))
       (gnus-delete-line)
       (gnus-group-insert-group-line-info group)
@@ -1550,7 +1571,7 @@ if it is a string, only list groups matching REGEXP."
              ?m ? ))
         (gnus-tmp-moderated-string
          (if (eq gnus-tmp-moderated ?m) "(m)" ""))
-         (gnus-tmp-group-icon (gnus-group-get-icon gnus-tmp-qualified-group))
+         (gnus-tmp-group-icon (gnus-group-get-icon gnus-tmp-group))
         (gnus-tmp-news-server (or (cadr gnus-tmp-method) ""))
         (gnus-tmp-news-method (or (car gnus-tmp-method) ""))
         (gnus-tmp-news-method-string
@@ -1572,7 +1593,7 @@ if it is a string, only list groups matching REGEXP."
              gnus-process-mark ? ))
         (buffer-read-only nil)
         beg end
-        header gnus-tmp-header)        ; passed as parameter to user-funcs.
+         gnus-tmp-header)      ; passed as parameter to user-funcs.
     (beginning-of-line)
     (setq beg (point))
     (gnus-add-text-properties
@@ -1590,22 +1611,31 @@ if it is a string, only list groups matching REGEXP."
                  gnus-indentation ,gnus-group-indentation
                  gnus-level ,gnus-tmp-level))
     (setq end (point))
-    (when gnus-group-update-tool-bar
-      (gnus-put-text-property beg end 'point-entered
-                             'gnus-tool-bar-update)
-      (gnus-put-text-property beg end 'point-left
-                             'gnus-tool-bar-update))
+    (gnus-group--setup-tool-bar-update beg end)
     (forward-line -1)
     (when (inline (gnus-visual-p 'group-highlight 'highlight))
       (gnus-group-highlight-line gnus-tmp-group beg end))
     (gnus-run-hooks 'gnus-group-update-hook)
-    (forward-line)
-    ;; Allow XEmacs to remove front-sticky text properties.
-    (gnus-group-remove-excess-properties)))
+    (forward-line)))
+
+(defun gnus-group--setup-tool-bar-update (beg end)
+  (when gnus-group-update-tool-bar
+    (if (fboundp 'cursor-sensor-mode)
+        (progn
+          (unless (bound-and-true-p cursor-sensor-mode)
+            (cursor-sensor-mode 1))
+          (gnus-put-text-property beg end 'cursor-sensor-functions
+                                  '(gnus-tool-bar-update)))
+      (gnus-put-text-property beg end 'point-entered
+                              #'gnus-tool-bar-update)
+      (gnus-put-text-property beg end 'point-left
+                              #'gnus-tool-bar-update))))
 
 (defun gnus-group-update-eval-form (group list)
   "Eval `car' of each element of LIST, and return the first that return t.
 Some value are bound so the form can use them."
+  (defvar group-age) (defvar ticked) (defvar score) (defvar level)
+  (defvar mailp) (defvar total) (defvar unread)
   (when list
     (let* ((entry (gnus-group-entry group))
            (unread (if (numberp (car entry)) (car entry) 0))
@@ -1656,7 +1686,7 @@ and ends at END."
   (let ((face (cdar (gnus-group-update-eval-form
                       group
                       gnus-group-highlight))))
-    (unless (eq face (get-text-property beg 'face))
+    (unless (eq face (gnus-get-text-property-excluding-characters-with-faces beg 'face))
       (let ((inhibit-read-only t))
         (gnus-put-text-property-excluding-characters-with-faces
          beg end 'face
@@ -1677,10 +1707,18 @@ and ends at END."
           " "))
     " "))
 
-(defun gnus-group-update-group (group &optional visible-only)
+
+(defun gnus-group-refresh-group (group)
+  (gnus-activate-group group)
+  (gnus-get-unread-articles-in-group (gnus-get-info group)
+                                    (gnus-active group))
+  (gnus-group-update-group group))
+
+(defun gnus-group-update-group (group &optional visible-only
+                                     info-unchanged)
   "Update all lines where GROUP appear.
 If VISIBLE-ONLY is non-nil, the group won't be displayed if it isn't
-already."
+already.  If INFO-UNCHANGED is non-nil, dribble buffer is not updated."
   (with-current-buffer gnus-group-buffer
     (save-excursion
       ;; The buffer may be narrowed.
@@ -1689,14 +1727,17 @@ already."
         (let ((ident (gnus-intern-safe group gnus-active-hashtb))
               (loc (point-min))
               found buffer-read-only)
-          ;; Enter the current status into the dribble buffer.
-          (let ((entry (gnus-group-entry group)))
-            (when (and entry
-                       (not (gnus-ephemeral-group-p group)))
-              (gnus-dribble-enter
-               (concat "(gnus-group-set-info '"
-                       (gnus-prin1-to-string (nth 2 entry))
-                       ")"))))
+         (unless info-unchanged
+           ;; Enter the current status into the dribble buffer.
+           (let ((entry (gnus-group-entry group)))
+             (when (and entry
+                        (not (gnus-ephemeral-group-p group)))
+               (gnus-dribble-enter
+                (concat "(gnus-group-set-info '"
+                        (gnus-prin1-to-string (nth 2 entry))
+                        ")")
+                (concat "^(gnus-group-set-info '(\""
+                        (regexp-quote group) "\"")))))
           ;; Find all group instances.  If topics are in use, each group
           ;; may be listed in more than once.
           (while (setq loc (text-property-any
@@ -1773,7 +1814,9 @@ already."
   "Get the name of the newsgroup on the current line."
   (let ((group (get-text-property (point-at-bol) 'gnus-group)))
     (when group
-      (symbol-name group))))
+      (if (stringp group)
+         group
+       (symbol-name group)))))
 
 (defun gnus-group-group-level ()
   "Get the level of the newsgroup on the current line."
@@ -1890,7 +1933,7 @@ If FIRST-TOO, the current line is also eligible as a target."
       (unless no-advance
        (gnus-group-next-group 1))
       (decf n))
-    (gnus-summary-position-point)
+    (gnus-group-position-point)
     n))
 
 (defun gnus-group-unmark-group (n)
@@ -2125,13 +2168,13 @@ be permanent."
 
 (defun gnus-group-name-at-point ()
   "Return a group name from around point if it exists, or nil."
-  (if (eq major-mode 'gnus-group-mode)
+  (if (derived-mode-p 'gnus-group-mode)
       (let ((group (gnus-group-group-name)))
        (when group
          (gnus-group-decoded-name group)))
     (let ((regexp "[][\C-@-\t\v-*,/:-@\\^`{-\C-?]*\
 \\(nn[a-z]+\\(?:\\+[^][\C-@-*,/:-@\\^`{-\C-?]+\\)?:\
-\[^][\C-@-*,./:-@\\^`{-\C-?]+\\(?:\\.[^][\C-@-*,./:-@\\^`{-\C-?]+\\)*\
+[^][\C-@-*,./:-@\\^`{-\C-?]+\\(?:\\.[^][\C-@-*,./:-@\\^`{-\C-?]+\\)*\
 \\|[^][\C-@-*,./:-@\\^`{-\C-?]+\\(?:\\.[^][\C-@-*,./:-@\\^`{-\C-?]+\\)+\\)")
          (start (point))
          (case-fold-search nil))
@@ -2165,26 +2208,40 @@ be permanent."
        (goto-char start)))))
 
 (defun gnus-group-completing-read (&optional prompt collection
-                                             require-match initial-input hist def)
+                                            require-match initial-input hist
+                                            def)
   "Read a group name with completion.  Non-ASCII group names are allowed.
 The arguments are the same as `completing-read' except that COLLECTION
 and HIST default to `gnus-active-hashtb' and `gnus-group-history'
-respectively if they are omitted."
-  (let* ((collection (or collection (or gnus-active-hashtb [0])))
-        (choices (mapcar (lambda (symbol)
-                            (let ((group (symbol-name symbol)))
-                              (if (string-match "[^\000-\177]" group)
-                                  (gnus-group-decoded-name group)
-                                group)))
-                          (gnus-remove-if-not 'symbolp collection)))
-         (group
-          (gnus-completing-read (or prompt "Group") choices
-                                require-match initial-input
-                                (or hist 'gnus-group-history)
-                                def)))
-    (if (symbol-value (intern-soft group collection))
-       group
-      (mm-encode-coding-string group (gnus-group-name-charset nil group)))))
+respectively if they are omitted.  Regards COLLECTION as a hash table
+if it is not a list."
+  (or collection (setq collection gnus-active-hashtb))
+  (let (choices group)
+    (if (listp collection)
+       (dolist (symbol collection)
+         (setq group (symbol-name symbol))
+         (push (if (string-match "[^\000-\177]" group)
+                   (gnus-group-decoded-name group)
+                 group)
+               choices))
+      (mapatoms (lambda (symbol)
+                 (setq group (symbol-name symbol))
+                 (push (if (string-match "[^\000-\177]" group)
+                           (gnus-group-decoded-name group)
+                         group)
+                       choices))
+               collection))
+    (setq group (gnus-completing-read (or prompt "Group") (nreverse choices)
+                                     require-match initial-input
+                                     (or hist 'gnus-group-history)
+                                     def))
+    (unless (if (listp collection)
+               (member group (mapcar 'symbol-name collection))
+             (symbol-value (intern-soft group collection)))
+      (setq group
+           (mm-encode-coding-string
+            group (gnus-group-name-charset nil group))))
+    (gnus-replace-in-string group "\n" "")))
 
 ;;;###autoload
 (defun gnus-fetch-group (group &optional articles)
@@ -2241,8 +2298,8 @@ confirmation is required."
                                              number)
   "Read GROUP from METHOD as an ephemeral group.
 If ACTIVATE, request the group first.
-If QUIT-CONFIG, use that window configuration when exiting from the
-ephemeral group.
+If QUIT-CONFIG, use that Gnus window configuration name when
+exiting from the ephemeral group.
 If REQUEST-ONLY, don't actually read the group; just request it.
 If SELECT-ARTICLES, only select those articles.
 If PARAMETERS, use those as the group parameters.
@@ -2253,31 +2310,49 @@ Return the name of the group if selection was successful."
    (list
     ;; (gnus-read-group "Group name: ")
     (gnus-group-completing-read)
-    (gnus-read-method "From method: ")))
+    (gnus-read-method "From method")))
+  (unless (gnus-alive-p)
+    (nnheader-init-server-buffer)
+    ;; Necessary because of funky inlining.
+    (require 'gnus-cache)
+    (setq gnus-newsrc-hashtb (gnus-make-hashtable)))
   ;; Transform the select method into a unique server.
   (when (stringp method)
     (setq method (gnus-server-to-method method)))
-  (setq method
-       `(,(car method) ,(concat (cadr method) "-ephemeral")
-         (,(intern (format "%s-address" (car method))) ,(cadr method))
-         ,@(cddr method)))
+  (let ((address-slot
+        (intern (format "%s-address" (car method)))))
+    (setq method
+         (if (assq address-slot (cddr method))
+             `(,(car method) ,(concat (cadr method) "-ephemeral")
+               ,@(cddr method))
+           `(,(car method) ,(concat (cadr method) "-ephemeral")
+             (,address-slot ,(cadr method))
+             ,@(cddr method)))))
   (let ((group (if (gnus-group-foreign-p group) group
                 (gnus-group-prefixed-name (gnus-group-real-name group)
                                           method))))
+    (gnus-set-active group nil)
     (gnus-sethash
      group
      `(-1 nil (,group
               ,gnus-level-default-subscribed nil nil ,method
               ,(cons
-                (if quit-config
-                    (cons 'quit-config quit-config)
-                  (cons 'quit-config
+                (cons 'quit-config
+                      (cond
+                       (quit-config
+                        quit-config)
+                       ((assq gnus-current-window-configuration
+                              gnus-buffer-configuration)
                         (cons gnus-summary-buffer
-                              gnus-current-window-configuration)))
+                              gnus-current-window-configuration))
+                       (t
+                        (cons (current-buffer)
+                              (current-window-configuration)))))
                 parameters)))
      gnus-newsrc-hashtb)
     (push method gnus-ephemeral-servers)
-    (set-buffer gnus-group-buffer)
+    (when (gnus-buffer-live-p gnus-group-buffer)
+      (set-buffer gnus-group-buffer))
     (unless (gnus-check-server method)
       (error "Unable to contact server: %s" (gnus-status-message method)))
     (when activate
@@ -2293,9 +2368,10 @@ Return the name of the group if selection was successful."
                       gnus-fetch-old-ephemeral-headers))
                  (gnus-group-read-group (or number t) t group select-articles))
            group)
-       ;;(error nil)
        (quit
-        (message "Quit reading the ephemeral group")
+        (if debug-on-quit
+            (debug "Quit")
+          (message "Quit reading the ephemeral group"))
         nil)))))
 
 (defcustom gnus-gmane-group-download-format
@@ -2334,7 +2410,7 @@ specified by `gnus-gmane-group-download-format'."
               group start (+ start range)))
       (write-region (point-min) (point-max) tmpfile)
       (gnus-group-read-ephemeral-group
-       (format "%s.start-%s.range-%s" group start range)
+       (format "nndoc+ephemeral:%s.start-%s.range-%s" group start range)
        `(nndoc ,tmpfile
               (nndoc-article-type mbox))))
     (delete-file tmpfile)))
@@ -2359,7 +2435,7 @@ Valid input formats include:
      ;; URLs providing `group', `start' and `range':
      ((string-match
        ;; http://thread.gmane.org/gmane.emacs.devel/86326/focus=86525
-       "^http://thread\.gmane\.org/\\([^/]+\\)/\\([0-9]+\\)/focus=\\([0-9]+\\)$"
+       "^http://thread\\.gmane\\.org/\\([^/]+\\)/\\([0-9]+\\)/focus=\\([0-9]+\\)$"
        url)
       (setq group (match-string 1 url)
            start (string-to-number (match-string 2 url))
@@ -2370,15 +2446,15 @@ Valid input formats include:
      ;; URLs providing `group' and `start':
      ((or (string-match
           ;; http://article.gmane.org/gmane.comp.gnu.make.bugs/3584
-          "^http://\\(?:thread\\|article\\|permalink\\)\.gmane\.org/\\([^/]+\\)/\\([0-9]+\\)"
+          "^http://\\(?:thread\\|article\\|permalink\\)\\.gmane\\.org/\\([^/]+\\)/\\([0-9]+\\)"
           url)
          (string-match
           ;; Don't advertise these in the doc string yet:
-          "^\\(?:nntp\\|news\\)://news\.gmane\.org/\\([^/]+\\)/\\([0-9]+\\)"
+          "^\\(?:nntp\\|news\\)://news\\.gmane\\.org/\\([^/]+\\)/\\([0-9]+\\)"
           url)
          (string-match
           ;; http://news.gmane.org/group/gmane.emacs.gnus.general/thread=65099/force_load=t
-          "^http://news\.gmane\.org/group/\\([^/]+\\)/thread=\\([0-9]+\\)"
+          "^http://news\\.gmane\\.org/group/\\([^/]+\\)/thread=\\([0-9]+\\)"
           url))
       (setq group (match-string 1 url)
            start (string-to-number (match-string 2 url))))
@@ -2387,41 +2463,51 @@ Valid input formats include:
     (gnus-read-ephemeral-gmane-group group start range)))
 
 (defcustom gnus-bug-group-download-format-alist
-  '((emacs . "http://debbugs.gnu.org/%s;mbox=yes")
+  '((emacs . "http://debbugs.gnu.org/cgi/bugreport.cgi?bug=%s;mboxmaint=yes;mboxstat=yes")
     (debian
-     . "http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=%s&mbox=yes"))
+     . "http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=%s&mbox=yes;mboxmaint=yes"))
   "Alist of symbols for bug trackers and the corresponding URL format string.
 The URL format string must contain a single \"%s\", specifying
 the bug number, and browsing the URL must return mbox output."
   :group 'gnus-group-foreign
-  :version "23.2" ;; No Gnus
+  ;; Added mboxmaint=yes.  This gets the version with the messages as
+  ;; they went out, not as they came in.
+  ;; Eg bug-gnu-emacs is replaced by ###@debbugs.
+  :version "24.1"
   :type '(repeat (cons (symbol) (string :tag "URL format string"))))
 
-(defun gnus-read-ephemeral-bug-group (number mbox-url)
+(defun gnus-read-ephemeral-bug-group (ids mbox-url &optional window-conf)
   "Browse bug NUMBER as ephemeral group."
   (interactive (list (read-string "Enter bug number: "
                                  (thing-at-point 'word) nil)
                     ;; FIXME: Add completing-read from
                     ;; `gnus-emacs-bug-group-download-format' ...
                     (cdr (assoc 'emacs gnus-bug-group-download-format-alist))))
-  (when (stringp number)
-    (setq number (string-to-number number)))
+  (when (stringp ids)
+    (setq ids (string-to-number ids)))
+  (unless (listp ids)
+    (setq ids (list ids)))
   (let ((tmpfile (mm-make-temp-file "gnus-temp-group-")))
-    (with-temp-file tmpfile
-      (url-insert-file-contents (format mbox-url number))
-      (goto-char (point-min))
-      ;; Add the debbugs address so that we can respond to reports easily.
-      (while (re-search-forward "^To: " nil t)
-       (end-of-line)
-       (insert (format ", %s@%s" number
-                       (replace-regexp-in-string
-                        "/.*$" ""
-                        (replace-regexp-in-string "^http://" "" mbox-url)))))
-      (write-region (point-min) (point-max) tmpfile)
-      (gnus-group-read-ephemeral-group
-       "gnus-read-ephemeral-bug"
-       `(nndoc ,tmpfile
-              (nndoc-article-type mbox))))
+    (let ((coding-system-for-write 'binary)
+         (coding-system-for-read 'binary))
+      (with-temp-file tmpfile
+       (mm-disable-multibyte)
+       (dolist (id ids)
+         (url-insert-file-contents (format mbox-url id)))
+       (goto-char (point-min))
+       ;; Add the debbugs address so that we can respond to reports easily.
+       (while (re-search-forward "^To: " nil t)
+         (end-of-line)
+         (insert (format ", %s@%s" (car ids)
+                         (gnus-replace-in-string
+                          (gnus-replace-in-string mbox-url "^http://" "")
+                          "/.*$" ""))))))
+    (gnus-group-read-ephemeral-group
+     (format "nndoc+ephemeral:bug#%s"
+            (mapconcat 'number-to-string ids ","))
+     `(nndoc ,tmpfile
+            (nndoc-article-type mbox))
+     nil window-conf)
     (delete-file tmpfile)))
 
 (defun gnus-read-ephemeral-debian-bug-group (number)
@@ -2432,13 +2518,23 @@ the bug number, and browsing the URL must return mbox output."
    number
    (cdr (assoc 'debian gnus-bug-group-download-format-alist))))
 
-(defun gnus-read-ephemeral-emacs-bug-group (number)
-  "Browse Emacs bug NUMBER as ephemeral group."
-  (interactive (list (read-string "Enter bug number: "
-                                 (thing-at-point 'word) nil)))
+(defvar debbugs-gnu-bug-number)                ; debbugs-gnu
+
+(defun gnus-read-ephemeral-emacs-bug-group (ids &optional window-conf)
+  "Browse Emacs bugs IDS as an ephemeral group."
+  (interactive (list (string-to-number
+                     (read-string "Enter bug number: "
+                                  (thing-at-point 'word) nil))))
+  (unless (listp ids)
+    (setq ids (list ids)))
   (gnus-read-ephemeral-bug-group
-   number
-   (cdr (assoc 'emacs gnus-bug-group-download-format-alist))))
+   ids
+   (cdr (assoc 'emacs gnus-bug-group-download-format-alist))
+   window-conf)
+  (when (fboundp 'debbugs-gnu-summary-mode)
+    (with-current-buffer (window-buffer (selected-window))
+      (debbugs-gnu-summary-mode 1)
+      (set (make-local-variable 'debbugs-gnu-bug-number) (car ids)))))
 
 (defun gnus-group-jump-to-group (group &optional prompt)
   "Jump to newsgroup GROUP.
@@ -2447,7 +2543,7 @@ If PROMPT (the prefix) is a number, use the prompt specified in
 `gnus-group-jump-to-group-prompt'."
   (interactive
    (list (gnus-group-completing-read
-          nil nil (gnus-read-active-file-p)
+          nil nil nil
           (if current-prefix-arg
               (cdr (assq current-prefix-arg gnus-group-jump-to-group-prompt))
             (or (and (stringp gnus-group-jump-to-group-prompt)
@@ -2653,11 +2749,14 @@ The user will be prompted for GROUP."
   "Add a new newsgroup.
 The user will be prompted for a NAME, for a select METHOD, and an
 ADDRESS.  NAME should be a human-readable string (i.e., not be encoded
-even if it contains non-ASCII characters) unless ENCODED is non-nil."
+even if it contains non-ASCII characters) unless ENCODED is non-nil.
+
+If the backend supports it, the group will also be created on the
+server."
   (interactive
    (list
     (gnus-read-group "Group name: ")
-    (gnus-read-method "From method: ")))
+    (gnus-read-method "Select method for new group (use tab for completion)")))
 
   (when (stringp method)
     (setq method (or (gnus-server-to-method method) method)))
@@ -2686,7 +2785,8 @@ even if it contains non-ASCII characters) unless ENCODED is non-nil."
     (unless (gnus-ephemeral-group-p name)
       (gnus-dribble-enter
        (concat "(gnus-group-set-info '"
-              (gnus-prin1-to-string (cdr info)) ")")))
+              (gnus-prin1-to-string (cdr info)) ")")
+       (concat "^(gnus-group-set-info '(\"" (regexp-quote name) "\"")))
     ;; Insert the line.
     (gnus-group-insert-group-line-info nname)
     (forward-line -1)
@@ -2717,6 +2817,22 @@ even if it contains non-ASCII characters) unless ENCODED is non-nil."
        (lambda (group)
          (gnus-group-delete-group group nil t))))))
 
+(defun gnus-group-delete-articles (group &optional oldp)
+  "Delete all articles in the current group.
+If OLDP (the prefix), only delete articles that are \"old\",
+according to the expiry settings.  Note that this will delete old
+not-expirable articles, too."
+  (interactive (list (gnus-group-group-name)
+                    current-prefix-arg))
+  (let ((articles (gnus-uncompress-range (gnus-active group))))
+    (when (gnus-yes-or-no-p
+          (format "Do you really want to delete these %d articles forever? "
+                  (length articles)))
+      (gnus-request-expire-articles articles group
+                                   (if current-prefix-arg
+                                       nil
+                                     'force)))))
+
 (defun gnus-group-delete-group (group &optional force no-prompt)
   "Delete the current group.  Only meaningful with editable groups.
 If FORCE (the prefix) is non-nil, all the articles in the group will
@@ -2834,7 +2950,7 @@ and NEW-NAME will be prompted for."
            (gnus-info-params info))
           (t info))
      ;; The proper documentation.
-     (format
+     (gnus-format-message
       "Editing the %s for `%s'."
       (cond
        ((eq part 'method) "select method")
@@ -3014,17 +3130,17 @@ If SOLID (the prefix), create a solid group."
                  (nnweb-ephemeral-p t))))
     (if solid
        (progn
-         (gnus-pull 'nnweb-ephemeral-p method)
+         (gnus-alist-pull 'nnweb-ephemeral-p method)
          (gnus-group-make-group group method))
       (gnus-group-read-ephemeral-group
        group method t
        (cons (current-buffer)
-            (if (eq major-mode 'gnus-summary-mode) 'summary 'group))))))
+            (if (derived-mode-p 'gnus-summary-mode) 'summary 'group))))))
 
 (defvar nnrss-group-alist)
 (eval-when-compile
-  (defun nnrss-discover-feed (arg))
-  (defun nnrss-save-server-data (arg)))
+  (defun nnrss-discover-feed (_arg))
+  (defun nnrss-save-server-data (_arg)))
 (defun gnus-group-make-rss-group (&optional url)
   "Given a URL, discover if there is an RSS feed.
 If there is, use Gnus to create an nnrss group"
@@ -3070,7 +3186,7 @@ The user will be prompted for a directory.  The contents of this
 directory will be used as a newsgroup.  The directory should contain
 mail messages or news articles in files that have numeric names."
   (interactive
-   (list (read-file-name "Create group from directory: ")))
+   (list (read-directory-name "Create group from directory: ")))
   (unless (file-exists-p dir)
     (error "No such directory"))
   (unless (file-directory-p dir)
@@ -3134,7 +3250,7 @@ mail messages or news articles in files that have numeric names."
     (unless (gnus-group-read-ephemeral-group
             name method t
             (cons (current-buffer)
-                  (if (eq major-mode 'gnus-summary-mode)
+                  (if (derived-mode-p 'gnus-summary-mode)
                       'summary 'group)))
       (error "Couldn't enter %s" dir))))
 
@@ -3162,7 +3278,8 @@ mail messages or news articles in files that have numeric names."
       (error "%s is not an nnimap group" group))
     (unless (setq acl (nnimap-acl-get mailbox (cadr method)))
       (error "Server does not support ACL's"))
-    (gnus-edit-form acl (format "Editing the access control list for `%s'.
+    (gnus-edit-form acl (gnus-format-message "\
+Editing the access control list for `%s'.
 
    An access control list is a list of (identifier . rights) elements.
 
@@ -3171,7 +3288,7 @@ mail messages or news articles in files that have numeric names."
 
    Rights is a string listing a (possibly empty) set of alphanumeric
    characters, each character listing a set of operations which is being
-   controlled.  Letters are reserved for ``standard'' rights, listed
+   controlled.  Letters are reserved for \"standard\" rights, listed
    below.  Digits are reserved for implementation or site defined rights.
 
    l - lookup (mailbox is visible to LIST/LSUB commands)
@@ -3406,13 +3523,14 @@ sort in reverse order."
   "Clear all marks and read ranges from the current group.
 Obeys the process/prefix convention."
   (interactive "P")
-  (gnus-group-iterate arg
-    (lambda (group)
-      (let (info)
-       (gnus-info-clear-data (setq info (gnus-get-info group)))
-       (gnus-get-unread-articles-in-group info (gnus-active group) t)
-       (when (gnus-group-goto-group group)
-         (gnus-group-update-group-line))))))
+  (when (gnus-y-or-n-p "Really clear data? ")
+    (gnus-group-iterate arg
+      (lambda (group)
+       (let (info)
+         (gnus-info-clear-data (setq info (gnus-get-info group)))
+         (gnus-get-unread-articles-in-group info (gnus-active group) t)
+         (when (gnus-group-goto-group group)
+           (gnus-group-update-group-line)))))))
 
 (defun gnus-group-clear-data-on-native-groups ()
   "Clear all marks and read ranges from all native groups."
@@ -3502,6 +3620,8 @@ Cross references (Xref: header) of articles are ignored."
   (interactive "P")
   (gnus-group-catchup-current n 'all))
 
+(declare-function gnus-sequence-of-unread-articles "gnus-sum" (group))
+
 (defun gnus-group-catchup (group &optional all)
   "Mark all articles in GROUP as read.
 If ALL is non-nil, all articles are marked as read.
@@ -3529,7 +3649,8 @@ or nil if no action could be taken."
        (gnus-add-marked-articles group 'tick nil nil 'force)
        (gnus-add-marked-articles group 'dormant nil nil 'force))
       ;; Do auto-expirable marks if that's required.
-      (when (gnus-group-auto-expirable-p group)
+      (when (and (gnus-group-auto-expirable-p group)
+                (not (gnus-group-read-only-p group)))
         (gnus-range-map
         (lambda (article)
           (gnus-add-marked-articles group 'expire (list article))
@@ -3562,6 +3683,10 @@ Uses the process/prefix convention."
           (expirable (if (gnus-group-total-expirable-p group)
                          (cons nil (gnus-list-of-read-articles group))
                        (assq 'expire (gnus-info-marks info))))
+          (articles-to-expire
+           (gnus-list-range-difference
+            (gnus-uncompress-sequence (cdr expirable))
+            (cdr (assq 'unexist (gnus-info-marks info)))))
           (expiry-wait (gnus-group-find-parameter group 'expiry-wait))
           (nnmail-expiry-target
            (or (gnus-group-find-parameter group 'expiry-target)
@@ -3576,11 +3701,9 @@ Uses the process/prefix convention."
              ;; parameter.
              (let ((nnmail-expiry-wait-function nil)
                    (nnmail-expiry-wait expiry-wait))
-               (gnus-request-expire-articles
-                (gnus-uncompress-sequence (cdr expirable)) group))
+               (gnus-request-expire-articles articles-to-expire group))
            ;; Just expire using the normal expiry values.
-           (gnus-request-expire-articles
-            (gnus-uncompress-sequence (cdr expirable)) group))))
+           (gnus-request-expire-articles articles-to-expire group))))
        (gnus-close-group group))
       (gnus-message 6 "Expiring articles in %s...done"
                    (gnus-group-decoded-name group))
@@ -3664,10 +3787,10 @@ If given numerical prefix, toggle the N next groups."
 Killed newsgroups are subscribed.  If SILENT, don't try to update the
 group line."
   (interactive (list (gnus-group-completing-read
-                     nil (gnus-read-active-file-p))))
+                     nil nil (gnus-read-active-file-p))))
   (let ((newsrc (gnus-group-entry group)))
     (cond
-     ((string-match "^[ \t]*$" group)
+     ((string-match "\\`[ \t]*\\'" group)
       (error "Empty group name"))
      (newsrc
       ;; Toggle subscription flag.
@@ -3764,6 +3887,8 @@ of groups killed."
                  gnus-list-of-killed-groups))
          (gnus-group-change-level
           (if entry entry group) gnus-level-killed (if entry nil level))
+         (when (numberp (gnus-group-unread group))
+           (gnus-request-update-group-status group 'unsubscribe))
          (message "Killed group %s" (gnus-group-decoded-name group)))
       ;; If there are lots and lots of groups to be killed, we use
       ;; this thing instead.
@@ -3786,7 +3911,9 @@ of groups killed."
          (setq gnus-zombie-list (delete group gnus-zombie-list))))
        ;; There may be more than one instance displayed.
        (while (gnus-group-goto-group group)
-         (gnus-delete-line)))
+         (gnus-delete-line))
+       (when (numberp (gnus-group-unread group))
+         (gnus-request-update-group-status group 'unsubscribe)))
       (gnus-make-hashtable-from-newsrc-alist))
 
     (gnus-group-position-point)
@@ -3814,6 +3941,7 @@ yanked) a list of yanked groups is returned."
        (and prev (gnus-group-entry prev))
        t)
       (gnus-group-insert-group-line-info group)
+      (gnus-request-update-group-status group 'subscribe)
       (gnus-undo-register
        `(when (gnus-group-goto-group ,group)
           (gnus-group-kill-group 1))))
@@ -3928,11 +4056,13 @@ entail asking the server for the groups."
        (gnus-activate-foreign-newsgroups level))
     (gnus-group-get-new-news)))
 
-(defun gnus-group-get-new-news (&optional arg)
+(defun gnus-group-get-new-news (&optional arg one-level)
   "Get newly arrived articles.
 If ARG is a number, it specifies which levels you are interested in
 re-scanning.  If ARG is non-nil and not a number, this will force
-\"hard\" re-reading of the active files from all servers."
+\"hard\" re-reading of the active files from all servers.
+If ONE-LEVEL is not nil, then re-scan only the specified level,
+otherwise all levels below ARG will be scanned too."
   (interactive "P")
   (require 'nnmail)
   (let ((gnus-inhibit-demon t)
@@ -3946,7 +4076,8 @@ re-scanning.  If ARG is non-nil and not a number, this will force
     (unless gnus-slave
       (gnus-master-read-slave-newsrc))
 
-    (gnus-get-unread-articles arg)
+    (gnus-get-unread-articles (gnus-group-default-level arg t)
+                             nil one-level)
 
     ;; If the user wants it, we scan for new groups.
     (when (eq gnus-check-new-newsgroups 'always)
@@ -3966,7 +4097,7 @@ If DONT-SCAN is non-nil, scan non-activated groups as well."
   (let* ((groups (gnus-group-process-prefix n))
         (ret (if (numberp n) (- n (length groups)) 0))
         (beg (unless n
-               (point)))
+               (point-marker)))
         group method
         (gnus-inhibit-demon t)
         ;; Binding this variable will inhibit multiple fetchings
@@ -3977,7 +4108,9 @@ If DONT-SCAN is non-nil, scan non-activated groups as well."
       (gnus-group-remove-mark group)
       ;; Bypass any previous denials from the server.
       (gnus-remove-denial (setq method (gnus-find-method-for-group group)))
-      (if (gnus-activate-group group (if dont-scan nil 'scan) nil method)
+      (if (or (and (not dont-scan)
+                  (gnus-request-group-scan group (gnus-get-info group)))
+             (gnus-activate-group group (if dont-scan nil 'scan) nil method))
          (let ((info (gnus-get-info group))
                (active (gnus-active group)))
            (when info
@@ -3988,44 +4121,15 @@ If DONT-SCAN is non-nil, scan non-activated groups as well."
            (when gnus-agent
              (gnus-agent-save-group-info
               method (gnus-group-real-name group) active))
-           (gnus-group-update-group group))
-       (if (eq (gnus-server-status (gnus-find-method-for-group group))
-               'denied)
-           (gnus-error 3 "Server denied access")
-         (gnus-error 3 "%s error: %s" group (gnus-status-message group)))))
+           (gnus-group-update-group group nil t))
+       (gnus-error 3 "%s error: %s" group (gnus-status-message group))))
     (when beg
       (goto-char beg))
     (when gnus-goto-next-group-when-activating
       (gnus-group-next-unread-group 1 t))
-    (gnus-summary-position-point)
+    (gnus-group-position-point)
     ret))
 
-(defun gnus-group-fetch-control (group)
-  "Fetch the archived control messages for the current group.
-If given a prefix argument, prompt for a group."
-  (interactive
-   (list (or (when current-prefix-arg
-              (gnus-group-completing-read))
-            (gnus-group-group-name)
-            gnus-newsgroup-name)))
-  (unless group
-    (error "No group name given"))
-  (let ((name (gnus-group-real-name group))
-       hierarchy)
-    (when (string-match "\\(^[^\\.]+\\)\\..*" name)
-      (setq hierarchy (match-string 1 name))
-      (if gnus-group-fetch-control-use-browse-url
-         (browse-url (concat "ftp://ftp.isc.org/usenet/control/"
-                             hierarchy "/" name ".gz"))
-       (let ((enable-local-variables nil))
-         (gnus-group-read-ephemeral-group
-          group
-          `(nndoc ,group (nndoc-address
-                          ,(find-file-noselect
-                            (concat "/ftp@ftp.isc.org:/usenet/control/"
-                                    hierarchy "/" name ".gz")))
-                  (nndoc-article-type mbox)) t nil nil))))))
-
 (defun gnus-group-describe-group (force &optional group)
   "Display a description of the current newsgroup."
   (interactive (list current-prefix-arg (gnus-group-group-name)))
@@ -4195,8 +4299,14 @@ groups.
 With 2 C-u's, use most complete method possible to query the server
 for new groups, and subscribe the new groups as zombies."
   (interactive "p")
-  (gnus-find-new-newsgroups (or arg 1))
-  (gnus-group-list-groups))
+  (let ((new-groups (gnus-find-new-newsgroups (or arg 1)))
+       current-group)
+    (gnus-group-list-groups)
+    (setq current-group (gnus-group-group-name))
+    (dolist (group new-groups)
+      (gnus-group-jump-to-group group))
+    (when current-group
+      (gnus-group-jump-to-group current-group))))
 
 (defun gnus-group-edit-global-kill (&optional article group)
   "Edit the global kill file.
@@ -4233,10 +4343,15 @@ The hook `gnus-suspend-gnus-hook' is called before actually suspending."
       (unless (or (eq buf group-buf)
                  (eq buf gnus-dribble-buffer)
                  (with-current-buffer buf
-                   (eq major-mode 'message-mode)))
+                   (derived-mode-p 'message-mode)))
        (gnus-kill-buffer buf)))
     (setq gnus-backlog-articles nil)
     (gnus-kill-gnus-frames)
+    ;; Closing all the backends is useful (for instance) when when the
+    ;; IP addresses have changed and you need to reconnect.
+    (dolist (elem gnus-opened-servers)
+      (gnus-close-server (car elem))
+      (setcar (cdr elem) 'closed))
     (when group-buf
       (bury-buffer group-buf)
       (delete-windows-on group-buf t))))
@@ -4302,13 +4417,14 @@ The hook `gnus-exit-gnus-hook' is called before actually exiting."
 (defun gnus-group-browse-foreign-server (method)
   "Browse a foreign news server.
 If called interactively, this function will ask for a select method
- (nntp, nnspool, etc.) and a server address (eg. nntp.some.where).
+ (nntp, nnspool, etc.) and a server address (e.g., nntp.some.where).
 If not, METHOD should be a list where the first element is the method
 and the second element is the address."
   (interactive
    (list (let ((how (gnus-completing-read
                     "Which back end"
-                    (mapcar 'car (append gnus-valid-select-methods gnus-server-alist))
+                    (mapcar 'car (append gnus-valid-select-methods
+                                         gnus-server-alist))
                     t (cons "nntp" 0) 'gnus-method-history)))
           ;; We either got a back end name or a virtual server name.
           ;; If the first, we also need an address.
@@ -4317,7 +4433,12 @@ and the second element is the address."
                     ;; Suggested by mapjph@bath.ac.uk.
                     (gnus-completing-read
                      "Address"
-                     gnus-secondary-servers))
+                     ;; FIXME? gnus-secondary-servers is obsolete,
+                     ;; and it is not obvious that there is anything
+                     ;; sensible to use instead in this particular case.
+                     (if (boundp 'gnus-secondary-servers)
+                         gnus-secondary-servers
+                       (cdr gnus-select-method))))
             ;; We got a server name.
             how))))
   (gnus-browse-foreign-server method))
@@ -4376,11 +4497,20 @@ and the second element is the address."
                             (gnus-list-of-unread-articles (car info))))))
        (error "No such group: %s" (gnus-info-group info))))))
 
-(defun gnus-group-set-method-info (group select-method)
-  (gnus-group-set-info select-method group 'method))
-
-(defun gnus-group-set-params-info (group params)
-  (gnus-group-set-info params group 'params))
+;; Ad-hoc function for inserting data from a different newsrc.eld
+;; file.  Use with caution, if at all.
+(defun gnus-import-other-newsrc-file (file)
+  (with-temp-buffer
+    (insert-file-contents file)
+    (let (form)
+      (while (ignore-errors
+              (setq form (read (current-buffer))))
+       (when (and (consp form)
+                  (eq (cadr form) 'gnus-newsrc-alist))
+         (let ((infos (cadr (nth 2 form))))
+           (dolist (info infos)
+             (when (gnus-get-info (car info))
+               (gnus-set-info (car info) info)))))))))
 
 (defun gnus-add-marked-articles (group type articles &optional info force)
   ;; Add ARTICLES of TYPE to the info of GROUP.
@@ -4408,6 +4538,8 @@ and the second element is the address."
                     (sort (nconc (gnus-uncompress-range (cdr m))
                                  (copy-sequence articles)) '<) t))))))
 
+(declare-function gnus-summary-add-mark "gnus-sum" (article type))
+
 (defun gnus-add-mark (group mark article)
   "Mark ARTICLE in GROUP with MARK, whether the group is displayed or not."
   (let ((buffer (gnus-summary-buffer-name group)))
@@ -4502,6 +4634,28 @@ This command may read the active file."
   (goto-char (point-min))
   (gnus-group-position-point))
 
+(defun gnus-group-list-ticked (level &optional lowest)
+  "List all groups with ticked articles.
+If the prefix LEVEL is non-nil, it should be a number that says which
+level to cut off listing groups.
+If LOWEST, don't list groups with level lower than LOWEST.
+
+This command may read the active file."
+  (interactive "P")
+  (when level
+    (setq level (prefix-numeric-value level)))
+  (when (or (not level) (>= level gnus-level-zombie))
+    (gnus-cache-open))
+  (funcall gnus-group-prepare-function
+          (or level gnus-level-subscribed)
+          #'(lambda (info)
+              (let ((marks (gnus-info-marks info)))
+                (assq 'tick marks)))
+          lowest
+          'ignore)
+  (goto-char (point-min))
+  (gnus-group-position-point))
+
 (defun gnus-group-listed-groups ()
   "Return a list of listed groups."
   (let (point groups)
@@ -4540,11 +4694,19 @@ This command may read the active file."
     (gnus-group-list-plus args)))
 
 (defun gnus-group-list-limit (&optional args)
-  "List groups limited within the current selection."
+  "List groups limited within the current selection.
+If you've limited the groups, you can further limit the selection
+with this command.  If you've first limited to groups with
+dormant articles with `A ?', you can then further limit with
+`A / c', which will then limit to groups with cached articles, giving
+you the groups that have both dormant articles and cached articles."
   (interactive "P")
   (let ((gnus-group-list-option 'limit))
     (gnus-group-list-plus args)))
 
+(declare-function gnus-mark-article-as-read "gnus-sum" (article &optional mark))
+(declare-function gnus-group-make-articles-read "gnus-sum" (group articles))
+
 (defun gnus-group-mark-article-read (group article)
   "Mark ARTICLE read."
   (let ((buffer (gnus-summary-buffer-name group))
@@ -4560,6 +4722,8 @@ This command may read the active file."
              (setq mark gnus-expirable-mark))
            (setq mark (gnus-request-update-mark
                        group article mark))
+           (gnus-request-set-mark
+            group (list (list (list article) 'add '(read))))
            (gnus-mark-article-as-read article mark)
            (setq gnus-newsgroup-active (gnus-active group))
            (when active
@@ -4569,10 +4733,11 @@ This command may read the active file."
                  (push n gnus-newsgroup-unselected))
                (setq n (1+ n)))
              (setq gnus-newsgroup-unselected
-                   (nreverse gnus-newsgroup-unselected)))))
+                   (sort gnus-newsgroup-unselected '<)))))
       (gnus-activate-group group)
       (gnus-group-make-articles-read group (list article))
-      (when (gnus-group-auto-expirable-p group)
+      (when (and (gnus-group-auto-expirable-p group)
+                (not (gnus-group-read-only-p group)))
        (gnus-add-marked-articles
         group 'expire (list article))))))