Make nnir work by default.
[gnus] / lisp / gnus-group.el
index 12239c3..7e2ea37 100644 (file)
@@ -1,34 +1,37 @@
 ;;; gnus-group.el --- group mode commands for Gnus
-;; Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003
-;;        Free Software Foundation, Inc.
+
+;; Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+;;   2005, 2006, 2007, 2008, 2009, 2010 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:
 
 ;;; Code:
 
+;; For Emacs <22.2 and XEmacs.
+(eval-and-compile
+  (unless (fboundp 'declare-function) (defmacro declare-function (&rest r))))
+
 (eval-when-compile
-  (require 'cl)
-  (defvar tool-bar-map))
+  (require 'cl))
+(defvar tool-bar-mode)
 
 (require 'gnus)
 (require 'gnus-start)
 (require 'gnus-range)
 (require 'gnus-win)
 (require 'gnus-undo)
+(require 'gmm-utils)
 (require 'time-date)
 (require 'gnus-ems)
 
-(eval-when-compile (require 'mm-url))
+(eval-when-compile
+  (require 'mm-url)
+  (let ((features (cons 'gnus-group features)))
+    (require 'gnus-sum))
+  (unless (boundp 'gnus-cache-active-hashtb)
+    (defvar gnus-cache-active-hashtb nil)))
 
-(defcustom gnus-group-archive-directory
-  "*ftp@ftp.hpc.uh.edu:/pub/emacs/ding-list/"
-  "*The address of the (ding) archives."
-  :group 'gnus-group-foreign
-  :type 'directory)
+(autoload 'gnus-agent-total-fetched-for "gnus-agent")
+(autoload 'gnus-cache-total-fetched-for "gnus-cache")
 
-(defcustom gnus-group-recent-archive-directory
-  "*ftp@ftp.hpc.uh.edu:/pub/emacs/ding-list-recent/"
-  "*The address of the most recent (ding) articles."
-  :group 'gnus-group-foreign
-  :type 'directory)
+(autoload 'gnus-group-make-nnir-group "nnir")
 
-(defcustom gnus-no-groups-message "No gnus is bad news"
+(defcustom gnus-no-groups-message "No Gnus is good news"
   "*Message displayed by Gnus when no groups are available."
   :group 'gnus-start
   :type 'string)
@@ -98,6 +100,18 @@ If nil, no groups are permanently visible."
   :group 'gnus-group-listing
   :type '(choice regexp (const nil)))
 
+(defcustom gnus-safe-html-newsgroups "\\`nnrss[+:]"
+  "Groups in which links in html articles are considered all safe.
+The value may be a regexp matching those groups, a list of group names,
+or nil.  This overrides `mm-w3m-safe-url-regexp' (which see).  This is
+effective only when emacs-w3m renders html articles, i.e., in the case
+`mm-text-html-renderer' is set to `w3m'."
+  :version "23.2"
+  :group 'gnus-group-various
+  :type '(choice regexp
+                (repeat :tag "List of group names" (string :tag "Group"))
+                (const nil)))
+
 (defcustom gnus-list-groups-with-ticked-articles t
   "*If non-nil, list groups that have only ticked articles.
 If nil, only list groups that have unread articles."
@@ -125,7 +139,7 @@ for the groups to be sorted.  Pre-made functions include
 `gnus-group-sort-by-score', `gnus-group-sort-by-method',
 `gnus-group-sort-by-server', and `gnus-group-sort-by-rank'.
 
-This variable can also be a list of sorting functions. In that case,
+This variable can also be a list of sorting functions.  In that case,
 the most significant sort function should be the last function in the
 list."
   :group 'gnus-group-listing
@@ -145,7 +159,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%)%l %O\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.
@@ -173,11 +187,11 @@ with some simple extensions.
 %O    Moderated group (string, \"(m)\" or \"\")
 %P    Topic indentation (string)
 %m    Whether there is new(ish) mail in the group (char, \"%\")
-%l    Whether there are GroupLens predictions for this group (string)
 %n    Select from where (string)
 %z    A string that look like `<%s:%n>' if a foreign select method is used
 %d    The date the group was last entered.
 %E    Icon as defined by `gnus-group-icon-list'.
+%F    The disk space used by the articles fetched by both the cache and agent.
 %u    User defined specifier.  The next character in the format string should
       be a letter.  Gnus will call the function gnus-user-format-function-X,
       where X is the letter following %u.  The function will be passed a
@@ -187,15 +201,15 @@ with some simple extensions.
 
 Note that this format specification is not always respected.  For
 reasons of efficiency, when listing killed groups, this specification
-is ignored altogether. If the spec is changed considerably, your
+is ignored altogether.  If the spec is changed considerably, your
 output may end up looking strange when listing both alive and killed
 groups.
 
 If you use %o or %O, reading the active file will be slower and quite
-a bit of extra memory will be used.  %D will also worsen performance.
-Also note that if you change the format specification to include any
-of these specs, you must probably re-start Gnus to see them go into
-effect.
+a bit of extra memory will be used.  %D and %F will also worsen
+performance.  Also note that if you change the format specification to
+include any of these specs, you must probably re-start Gnus to see
+them go into effect.
 
 General format specifiers can also be used.
 See Info node `(gnus)Formatting Variables'."
@@ -268,25 +282,22 @@ If you want to modify the group buffer, you can use this hook."
   :group 'gnus-exit
   :type 'hook)
 
-(defcustom gnus-group-update-hook '(gnus-group-highlight-line)
-  "Hook called when a group line is changed.
-The hook will not be called if `gnus-visual' is nil.
-
-The default function `gnus-group-highlight-line' will
-highlight the line according to the `gnus-group-highlight'
-variable."
+(defcustom gnus-group-update-hook nil
+  "Hook called when a group line is changed."
   :group 'gnus-group-visual
+  :version "24.1"
   :type 'hook)
 
 (defcustom gnus-useful-groups
-  '(("(ding) mailing list mirrored at sunsite.auc.dk"
-     "emacs.ding"
-     (nntp "sunsite.auc.dk"
-          (nntp-address "sunsite.auc.dk")))
-    ("gnus-bug archive"
-     "gnus-bug"
-     (nndir "/ftp@ftp.ifi.uio.no:/pub/emacs/gnus/gnus-bug/"))
-    ("Gnus help group"
+  '(("(ding) mailing list mirrored at gmane.org"
+     "gmane.emacs.gnus.general"
+     (nntp "Gmane"
+          (nntp-address "news.gmane.org")))
+    ("Gnus bug archive"
+     "gnus.gnus-bug"
+     (nntp "news.gnus.org"
+          (nntp-address "news.gnus.org")))
+    ("Local Gnus help group"
      "gnus-help"
      (nndoc "gnus-help"
            (nndoc-article-type mbox)
@@ -305,50 +316,50 @@ variable."
 (defcustom gnus-group-highlight
   '(;; Mail.
     ((and mailp (= unread 0) (eq level 1)) .
-     gnus-group-mail-1-empty-face)
+     gnus-group-mail-1-empty)
     ((and mailp (eq level 1)) .
-     gnus-group-mail-1-face)
+     gnus-group-mail-1)
     ((and mailp (= unread 0) (eq level 2)) .
-     gnus-group-mail-2-empty-face)
+     gnus-group-mail-2-empty)
     ((and mailp (eq level 2)) .
-     gnus-group-mail-2-face)
+     gnus-group-mail-2)
     ((and mailp (= unread 0) (eq level 3)) .
-     gnus-group-mail-3-empty-face)
+     gnus-group-mail-3-empty)
     ((and mailp (eq level 3)) .
-     gnus-group-mail-3-face)
+     gnus-group-mail-3)
     ((and mailp (= unread 0)) .
-     gnus-group-mail-low-empty-face)
+     gnus-group-mail-low-empty)
     ((and mailp) .
-     gnus-group-mail-low-face)
+     gnus-group-mail-low)
     ;; News.
     ((and (= unread 0) (eq level 1)) .
-     gnus-group-news-1-empty-face)
+     gnus-group-news-1-empty)
     ((and (eq level 1)) .
-     gnus-group-news-1-face)
+     gnus-group-news-1)
     ((and (= unread 0) (eq level 2)) .
-     gnus-group-news-2-empty-face)
+     gnus-group-news-2-empty)
     ((and (eq level 2)) .
-     gnus-group-news-2-face)
+     gnus-group-news-2)
     ((and (= unread 0) (eq level 3)) .
-     gnus-group-news-3-empty-face)
+     gnus-group-news-3-empty)
     ((and (eq level 3)) .
-     gnus-group-news-3-face)
+     gnus-group-news-3)
     ((and (= unread 0) (eq level 4)) .
-     gnus-group-news-4-empty-face)
+     gnus-group-news-4-empty)
     ((and (eq level 4)) .
-     gnus-group-news-4-face)
+     gnus-group-news-4)
     ((and (= unread 0) (eq level 5)) .
-     gnus-group-news-5-empty-face)
+     gnus-group-news-5-empty)
     ((and (eq level 5)) .
-     gnus-group-news-5-face)
+     gnus-group-news-5)
     ((and (= unread 0) (eq level 6)) .
-     gnus-group-news-6-empty-face)
+     gnus-group-news-6-empty)
     ((and (eq level 6)) .
-     gnus-group-news-6-face)
+     gnus-group-news-6)
     ((and (= unread 0)) .
-     gnus-group-news-low-empty-face)
+     gnus-group-news-low-empty)
     (t .
-     gnus-group-news-low-face))
+     gnus-group-news-low))
   "*Controls the highlighting of group buffer lines.
 
 Below is a list of `Form'/`Face' pairs.  When deciding how a a
@@ -371,6 +382,7 @@ score: The score of the group.
 ticked: The number of ticked articles."
   :group 'gnus-group-visual
   :type '(repeat (cons (sexp :tag "Form") face)))
+(put 'gnus-group-highlight 'risky-local-variable t)
 
 (defcustom gnus-new-mail-mark ?%
   "Mark used for groups with new mail."
@@ -378,7 +390,7 @@ ticked: The number of ticked articles."
   :type 'character)
 
 (defgroup gnus-group-icons nil
-  "Add Icons to your group buffer.  "
+  "Add Icons to your group buffer."
   :group 'gnus-group-visual)
 
 (defcustom gnus-group-icon-list
@@ -402,12 +414,12 @@ group: The name of the group.
 unread: The number of unread articles in the group.
 method: The select method used.
 mailp: Whether it's a mail group or not.
-newsp: Whether it's a news group or not
 level: The level of the group.
 score: The score of the group.
 ticked: The number of ticked articles."
   :group 'gnus-group-icons
   :type '(repeat (cons (sexp :tag "Form") file)))
+(put 'gnus-group-icon-list 'risky-local-variable t)
 
 (defcustom gnus-group-name-charset-method-alist nil
   "Alist of method and the charset for group names.
@@ -432,12 +444,20 @@ For example:
 
 (defcustom gnus-group-jump-to-group-prompt nil
   "Default prompt for `gnus-group-jump-to-group'.
-If non-nil, the value should be a string, e.g. \"nnml:\",
-in which case `gnus-group-jump-to-group' offers \"Group: nnml:\"
-in the minibuffer prompt."
+
+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:
+\((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"
   :group 'gnus-group-various
   :type '(choice (string :tag "Prompt string")
-                (const :tag "Empty" nil)))
+                (const :tag "Empty" nil)
+                (repeat (cons (integer :tag "Argument")
+                              (string :tag "Prompt string")))))
 
 (defvar gnus-group-listing-limit 1000
   "*A limit of the number of groups when listing.
@@ -474,31 +494,44 @@ simple manner.")
                   (gnus-range-length (cdr (assq 'tick gnus-tmp-marked))))))
              (t number)) ?s)
     (?R gnus-tmp-number-of-read ?s)
-    (?U (gnus-number-of-unseen-articles-in-group gnus-tmp-group) ?d)
+    (?U (if (gnus-active gnus-tmp-group)
+           (gnus-number-of-unseen-articles-in-group gnus-tmp-group)
+         "*")
+       ?s)
     (?t gnus-tmp-number-total ?d)
     (?y gnus-tmp-number-of-unread ?s)
     (?I (gnus-range-length (cdr (assq 'dormant gnus-tmp-marked))) ?d)
     (?T (gnus-range-length (cdr (assq 'tick gnus-tmp-marked))) ?d)
     (?i (+ (gnus-range-length (cdr (assq 'dormant gnus-tmp-marked)))
           (gnus-range-length (cdr (assq 'tick gnus-tmp-marked)))) ?d)
-    (?g gnus-tmp-group ?s)
+    (?g (if (boundp 'gnus-tmp-decoded-group)
+           gnus-tmp-decoded-group
+         gnus-tmp-group)
+       ?s)
     (?G gnus-tmp-qualified-group ?s)
-    (?c (gnus-short-group-name gnus-tmp-group) ?s)
+    (?c (gnus-short-group-name (if (boundp 'gnus-tmp-decoded-group)
+                                  gnus-tmp-decoded-group
+                                gnus-tmp-group))
+       ?s)
     (?C gnus-tmp-comment ?s)
     (?D gnus-tmp-newsgroup-description ?s)
     (?o gnus-tmp-moderated ?c)
     (?O gnus-tmp-moderated-string ?s)
     (?p gnus-tmp-process-marked ?c)
     (?s gnus-tmp-news-server ?s)
-    (?n gnus-tmp-news-method ?s)
+    (?n ,(if (featurep 'xemacs)
+            '(symbol-name gnus-tmp-news-method)
+          'gnus-tmp-news-method)
+       ?s)
     (?P gnus-group-indentation ?s)
     (?E gnus-tmp-group-icon ?s)
     (?B gnus-tmp-summary-live ?c)
-    (?l gnus-tmp-grouplens ?s)
     (?z gnus-tmp-news-method-string ?s)
     (?m (gnus-group-new-mail gnus-tmp-group) ?c)
     (?d (gnus-group-timestamp-string gnus-tmp-group) ?s)
-    (?u gnus-tmp-user-defined ?s)))
+    (?u gnus-tmp-user-defined ?s)
+    (?F (gnus-total-fetched-for gnus-tmp-group) ?s)
+    ))
 
 (defvar gnus-group-mode-line-format-alist
   `((?S gnus-tmp-news-server ?s)
@@ -517,8 +550,6 @@ simple manner.")
 (defvar gnus-group-list-mode nil)
 
 
-(defvar gnus-group-icon-cache nil)
-
 (defvar gnus-group-listed-groups nil)
 (defvar gnus-group-list-option nil)
 
@@ -528,207 +559,200 @@ simple manner.")
 
 (put 'gnus-group-mode 'mode-class 'special)
 
-(when t
-  (gnus-define-keys gnus-group-mode-map
-    " " gnus-group-read-group
-    "=" gnus-group-select-group
-    "\r" gnus-group-select-group
-    "\M-\r" gnus-group-quick-select-group
-    "\M- " gnus-group-visible-select-group
-    [(meta control return)] gnus-group-select-group-ephemerally
-    "j" gnus-group-jump-to-group
-    "n" gnus-group-next-unread-group
-    "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
-    "\M-p" gnus-group-prev-unread-group-same-level
-    "," gnus-group-best-unread-group
-    "." gnus-group-first-unread-group
-    "u" gnus-group-unsubscribe-current-group
-    "U" gnus-group-unsubscribe-group
-    "c" gnus-group-catchup-current
-    "C" gnus-group-catchup-current-all
-    "\M-c" gnus-group-clear-data
-    "l" gnus-group-list-groups
-    "L" gnus-group-list-all-groups
-    "m" gnus-group-mail
-    "i" gnus-group-news
-    "g" gnus-group-get-new-news
-    "\M-g" gnus-group-get-new-news-this-group
-    "R" gnus-group-restart
-    "r" gnus-group-read-init-file
-    "B" gnus-group-browse-foreign-server
-    "b" gnus-group-check-bogus-groups
-    "F" gnus-group-find-new-groups
-    "\C-c\C-d" gnus-group-describe-group
-    "\M-d" gnus-group-describe-all-groups
-    "\C-c\C-a" gnus-group-apropos
-    "\C-c\M-\C-a" gnus-group-description-apropos
-    "a" gnus-group-post-news
-    "\ek" gnus-group-edit-local-kill
-    "\eK" gnus-group-edit-global-kill
-    "\C-k" gnus-group-kill-group
-    "\C-y" gnus-group-yank-group
-    "\C-w" gnus-group-kill-region
-    "\C-x\C-t" gnus-group-transpose-groups
-    "\C-c\C-l" gnus-group-list-killed
-    "\C-c\C-x" gnus-group-expire-articles
-    "\C-c\M-\C-x" gnus-group-expire-all-groups
-    "V" gnus-version
-    "s" gnus-group-save-newsrc
-    "z" gnus-group-suspend
-    "q" gnus-group-exit
-    "Q" gnus-group-quit
-    "?" gnus-group-describe-briefly
-    "\C-c\C-i" gnus-info-find-node
-    "\M-e" gnus-group-edit-group-method
-    "^" gnus-group-enter-server-mode
-    gnus-mouse-2 gnus-mouse-pick-group
-    "<" beginning-of-buffer
-    ">" end-of-buffer
-    "\C-c\C-b" gnus-bug
-    "\C-c\C-s" gnus-group-sort-groups
-    "t" gnus-topic-mode
-    "\C-c\M-g" gnus-activate-all-groups
-    "\M-&" gnus-group-universal-argument
-    "#" gnus-group-mark-group
-    "\M-#" gnus-group-unmark-group)
-
-  (gnus-define-keys (gnus-group-mark-map "M" gnus-group-mode-map)
-    "m" gnus-group-mark-group
-    "u" gnus-group-unmark-group
-    "w" gnus-group-mark-region
-    "b" gnus-group-mark-buffer
-    "r" gnus-group-mark-regexp
-    "U" gnus-group-unmark-all-groups)
-
-  (gnus-define-keys (gnus-group-sieve-map "D" gnus-group-mode-map)
-    "u" gnus-sieve-update
-    "g" gnus-sieve-generate)
-
-  (gnus-define-keys (gnus-group-group-map "G" gnus-group-mode-map)
-    "d" gnus-group-make-directory-group
-    "h" gnus-group-make-help-group
-    "u" gnus-group-make-useful-group
-    "a" gnus-group-make-archive-group
-    "k" gnus-group-make-kiboze-group
-    "l" gnus-group-nnimap-edit-acl
-    "m" gnus-group-make-group
-    "E" gnus-group-edit-group
-    "e" gnus-group-edit-group-method
-    "p" gnus-group-edit-group-parameters
-    "v" gnus-group-add-to-virtual
-    "V" gnus-group-make-empty-virtual
-    "D" gnus-group-enter-directory
-    "f" gnus-group-make-doc-group
-    "w" gnus-group-make-web-group
-    "r" gnus-group-rename-group
-    "R" gnus-group-make-rss-group
-    "c" gnus-group-customize
-    "x" gnus-group-nnimap-expunge
-    "\177" gnus-group-delete-group
-    [delete] gnus-group-delete-group)
-
-  (gnus-define-keys (gnus-group-soup-map "s" gnus-group-group-map)
-    "b" gnus-group-brew-soup
-    "w" gnus-soup-save-areas
-    "s" gnus-soup-send-replies
-    "p" gnus-soup-pack-packet
-    "r" nnsoup-pack-replies)
-
-  (gnus-define-keys (gnus-group-sort-map "S" gnus-group-group-map)
-    "s" gnus-group-sort-groups
-    "a" gnus-group-sort-groups-by-alphabet
-    "u" gnus-group-sort-groups-by-unread
-    "l" gnus-group-sort-groups-by-level
-    "v" gnus-group-sort-groups-by-score
-    "r" gnus-group-sort-groups-by-rank
-    "m" gnus-group-sort-groups-by-method
-    "n" gnus-group-sort-groups-by-real-name)
-
-  (gnus-define-keys (gnus-group-sort-selected-map "P" gnus-group-group-map)
-    "s" gnus-group-sort-selected-groups
-    "a" gnus-group-sort-selected-groups-by-alphabet
-    "u" gnus-group-sort-selected-groups-by-unread
-    "l" gnus-group-sort-selected-groups-by-level
-    "v" gnus-group-sort-selected-groups-by-score
-    "r" gnus-group-sort-selected-groups-by-rank
-    "m" gnus-group-sort-selected-groups-by-method
-    "n" gnus-group-sort-selected-groups-by-real-name)
-
-  (gnus-define-keys (gnus-group-list-map "A" gnus-group-mode-map)
-    "k" gnus-group-list-killed
-    "z" gnus-group-list-zombies
-    "s" gnus-group-list-groups
-    "u" gnus-group-list-all-groups
-    "A" gnus-group-list-active
-    "a" gnus-group-apropos
-    "d" gnus-group-description-apropos
-    "m" gnus-group-list-matching
-    "M" gnus-group-list-all-matching
-    "l" gnus-group-list-level
-    "c" gnus-group-list-cached
-    "?" gnus-group-list-dormant)
-
-  (gnus-define-keys (gnus-group-list-limit-map "/" gnus-group-list-map)
-    "k"  gnus-group-list-limit
-    "z"  gnus-group-list-limit
-    "s"  gnus-group-list-limit
-    "u"  gnus-group-list-limit
-    "A"  gnus-group-list-limit
-    "m"  gnus-group-list-limit
-    "M"  gnus-group-list-limit
-    "l"  gnus-group-list-limit
-    "c"  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
-    "z"  gnus-group-list-flush
-    "s"  gnus-group-list-flush
-    "u"  gnus-group-list-flush
-    "A"  gnus-group-list-flush
-    "m"  gnus-group-list-flush
-    "M"  gnus-group-list-flush
-    "l"  gnus-group-list-flush
-    "c"  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
-    "z"  gnus-group-list-plus
-    "s"  gnus-group-list-plus
-    "u"  gnus-group-list-plus
-    "A"  gnus-group-list-plus
-    "m"  gnus-group-list-plus
-    "M"  gnus-group-list-plus
-    "l"  gnus-group-list-plus
-    "c"  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)
-
-  (gnus-define-keys (gnus-group-help-map "H" gnus-group-mode-map)
-    "c" gnus-group-fetch-charter
-    "C" gnus-group-fetch-control
-    "d" gnus-group-describe-group
-    "f" gnus-group-fetch-faq
-    "v" gnus-version)
-
-  (gnus-define-keys (gnus-group-sub-map "S" gnus-group-mode-map)
-    "l" gnus-group-set-current-level
-    "t" gnus-group-unsubscribe-current-group
-    "s" gnus-group-unsubscribe-group
-    "k" gnus-group-kill-group
-    "y" gnus-group-yank-group
-    "w" gnus-group-kill-region
-    "\C-k" gnus-group-kill-level
-    "z" gnus-group-kill-all-zombies))
+(gnus-define-keys gnus-group-mode-map
+  " " gnus-group-read-group
+  "=" gnus-group-select-group
+  "\r" gnus-group-select-group
+  "\M-\r" gnus-group-quick-select-group
+  "\M- " gnus-group-visible-select-group
+  [(meta control return)] gnus-group-select-group-ephemerally
+  "j" gnus-group-jump-to-group
+  "n" gnus-group-next-unread-group
+  "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
+  "\M-p" gnus-group-prev-unread-group-same-level
+  "," gnus-group-best-unread-group
+  "." gnus-group-first-unread-group
+  "u" gnus-group-unsubscribe-current-group
+  "U" gnus-group-unsubscribe-group
+  "c" gnus-group-catchup-current
+  "C" gnus-group-catchup-current-all
+  "\M-c" gnus-group-clear-data
+  "l" gnus-group-list-groups
+  "L" gnus-group-list-all-groups
+  "m" gnus-group-mail
+  "i" gnus-group-news
+  "g" gnus-group-get-new-news
+  "\M-g" gnus-group-get-new-news-this-group
+  "R" gnus-group-restart
+  "r" gnus-group-read-init-file
+  "B" gnus-group-browse-foreign-server
+  "b" gnus-group-check-bogus-groups
+  "F" gnus-group-find-new-groups
+  "\C-c\C-d" gnus-group-describe-group
+  "\M-d" gnus-group-describe-all-groups
+  "\C-c\C-a" gnus-group-apropos
+  "\C-c\M-\C-a" gnus-group-description-apropos
+  "a" gnus-group-post-news
+  "\ek" gnus-group-edit-local-kill
+  "\eK" gnus-group-edit-global-kill
+  "\C-k" gnus-group-kill-group
+  "\C-y" gnus-group-yank-group
+  "\C-w" gnus-group-kill-region
+  "\C-x\C-t" gnus-group-transpose-groups
+  "\C-c\C-l" gnus-group-list-killed
+  "\C-c\C-x" gnus-group-expire-articles
+  "\C-c\M-\C-x" gnus-group-expire-all-groups
+  "V" gnus-version
+  "s" gnus-group-save-newsrc
+  "z" gnus-group-suspend
+  "q" gnus-group-exit
+  "Q" gnus-group-quit
+  "?" gnus-group-describe-briefly
+  "\C-c\C-i" gnus-info-find-node
+  "\M-e" gnus-group-edit-group-method
+  "^" gnus-group-enter-server-mode
+  gnus-mouse-2 gnus-mouse-pick-group
+  [follow-link] mouse-face
+  "<" beginning-of-buffer
+  ">" end-of-buffer
+  "\C-c\C-b" gnus-bug
+  "\C-c\C-s" gnus-group-sort-groups
+  "t" gnus-topic-mode
+  "\C-c\M-g" gnus-activate-all-groups
+  "\M-&" gnus-group-universal-argument
+  "#" gnus-group-mark-group
+  "\M-#" gnus-group-unmark-group)
+
+(gnus-define-keys (gnus-group-mark-map "M" gnus-group-mode-map)
+  "m" gnus-group-mark-group
+  "u" gnus-group-unmark-group
+  "w" gnus-group-mark-region
+  "b" gnus-group-mark-buffer
+  "r" gnus-group-mark-regexp
+  "U" gnus-group-unmark-all-groups)
+
+(gnus-define-keys (gnus-group-sieve-map "D" gnus-group-mode-map)
+  "u" gnus-sieve-update
+  "g" gnus-sieve-generate)
+
+(gnus-define-keys (gnus-group-group-map "G" gnus-group-mode-map)
+  "d" gnus-group-make-directory-group
+  "h" gnus-group-make-help-group
+  "u" gnus-group-make-useful-group
+  "l" gnus-group-nnimap-edit-acl
+  "m" gnus-group-make-group
+  "E" gnus-group-edit-group
+  "e" gnus-group-edit-group-method
+  "p" gnus-group-edit-group-parameters
+  "v" gnus-group-add-to-virtual
+  "V" gnus-group-make-empty-virtual
+  "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
+  "c" gnus-group-customize
+  "z" gnus-group-compact-group
+  "x" gnus-group-expunge-group
+  "\177" gnus-group-delete-group
+  [delete] gnus-group-delete-group)
+
+(gnus-define-keys (gnus-group-sort-map "S" gnus-group-group-map)
+  "s" gnus-group-sort-groups
+  "a" gnus-group-sort-groups-by-alphabet
+  "u" gnus-group-sort-groups-by-unread
+  "l" gnus-group-sort-groups-by-level
+  "v" gnus-group-sort-groups-by-score
+  "r" gnus-group-sort-groups-by-rank
+  "m" gnus-group-sort-groups-by-method
+  "n" gnus-group-sort-groups-by-real-name)
+
+(gnus-define-keys (gnus-group-sort-selected-map "P" gnus-group-group-map)
+  "s" gnus-group-sort-selected-groups
+  "a" gnus-group-sort-selected-groups-by-alphabet
+  "u" gnus-group-sort-selected-groups-by-unread
+  "l" gnus-group-sort-selected-groups-by-level
+  "v" gnus-group-sort-selected-groups-by-score
+  "r" gnus-group-sort-selected-groups-by-rank
+  "m" gnus-group-sort-selected-groups-by-method
+  "n" gnus-group-sort-selected-groups-by-real-name)
+
+(gnus-define-keys (gnus-group-list-map "A" gnus-group-mode-map)
+  "k" gnus-group-list-killed
+  "z" gnus-group-list-zombies
+  "s" gnus-group-list-groups
+  "u" gnus-group-list-all-groups
+  "A" gnus-group-list-active
+  "a" gnus-group-apropos
+  "d" gnus-group-description-apropos
+  "m" gnus-group-list-matching
+  "M" gnus-group-list-all-matching
+  "l" gnus-group-list-level
+  "c" gnus-group-list-cached
+  "?" gnus-group-list-dormant)
+
+(gnus-define-keys (gnus-group-list-limit-map "/" gnus-group-list-map)
+  "k"  gnus-group-list-limit
+  "z"  gnus-group-list-limit
+  "s"  gnus-group-list-limit
+  "u"  gnus-group-list-limit
+  "A"  gnus-group-list-limit
+  "m"  gnus-group-list-limit
+  "M"  gnus-group-list-limit
+  "l"  gnus-group-list-limit
+  "c"  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
+  "z"  gnus-group-list-flush
+  "s"  gnus-group-list-flush
+  "u"  gnus-group-list-flush
+  "A"  gnus-group-list-flush
+  "m"  gnus-group-list-flush
+  "M"  gnus-group-list-flush
+  "l"  gnus-group-list-flush
+  "c"  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
+  "z"  gnus-group-list-plus
+  "s"  gnus-group-list-plus
+  "u"  gnus-group-list-plus
+  "A"  gnus-group-list-plus
+  "m"  gnus-group-list-plus
+  "M"  gnus-group-list-plus
+  "l"  gnus-group-list-plus
+  "c"  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)
+
+(gnus-define-keys (gnus-group-sub-map "S" gnus-group-mode-map)
+  "l" gnus-group-set-current-level
+  "t" gnus-group-unsubscribe-current-group
+  "s" gnus-group-unsubscribe-group
+  "k" gnus-group-kill-group
+  "y" gnus-group-yank-group
+  "w" gnus-group-kill-region
+  "\C-k" gnus-group-kill-level
+  "z" gnus-group-kill-all-zombies)
 
 (defun gnus-topic-mode-p ()
   "Return non-nil in `gnus-topic-mode'."
@@ -736,7 +760,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
@@ -783,11 +806,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 FAQ" gnus-group-fetch-faq (gnus-group-group-name)]
-       ["Fetch charter" gnus-group-fetch-charter
-       :active (gnus-group-group-name)
-       ,@(if (featurep 'xemacs) nil
-           '(:help "Display the charter of the current group"))]
        ["Fetch control message" gnus-group-fetch-control
        :active (gnus-group-group-name)
        ,@(if (featurep 'xemacs) nil
@@ -806,6 +824,8 @@ simple manner.")
        (gnus-group-group-name)]
        ["Select quick" gnus-group-quick-select-group (gnus-group-group-name)]
        ["Customize" gnus-group-customize (gnus-group-group-name)]
+       ["Compact" gnus-group-compact-group
+       :active (gnus-group-group-name)]
        ("Edit"
        ["Parameters" gnus-group-edit-group-parameters
         :included (not (gnus-topic-mode-p))
@@ -885,12 +905,13 @@ simple manner.")
        ["Make a foreign group..." gnus-group-make-group t]
        ["Add a directory group..." gnus-group-make-directory-group t]
        ["Add the help group" gnus-group-make-help-group t]
-       ["Add the archive group" gnus-group-make-archive-group t]
        ["Make a doc group..." gnus-group-make-doc-group t]
        ["Make a web group..." gnus-group-make-web-group t]
-       ["Make a kiboze group..." gnus-group-make-kiboze-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]
+       ["Make an RSS group..." gnus-group-make-rss-group t]
        ["Rename group..." gnus-group-rename-group
         (gnus-check-backend-function
          'request-rename-group (gnus-group-group-name))]
@@ -920,13 +941,6 @@ simple manner.")
     (easy-menu-define
      gnus-group-misc-menu gnus-group-mode-map ""
      `("Gnus"
-       ("SOUP"
-       ["Pack replies" nnsoup-pack-replies (fboundp 'nnsoup-request-group)]
-       ["Send replies" gnus-soup-send-replies
-        (fboundp 'gnus-soup-pack-packet)]
-       ["Pack packet" gnus-soup-pack-packet (fboundp 'gnus-soup-pack-packet)]
-       ["Save areas" gnus-soup-save-areas (fboundp 'gnus-soup-pack-packet)]
-       ["Brew SOUP" gnus-group-brew-soup (fboundp 'gnus-soup-pack-packet)])
        ["Send a mail" gnus-group-mail t]
        ["Send a message (mail or news)" gnus-group-post-news t]
        ["Create a local message" gnus-group-news t]
@@ -944,7 +958,6 @@ simple manner.")
        ["Browse foreign server..." gnus-group-browse-foreign-server t]
        ["Enter server buffer" gnus-group-enter-server-mode t]
        ["Expire all expirable articles" gnus-group-expire-all-groups t]
-       ["Generate any kiboze groups" nnkiboze-generate-groups t]
        ["Gnus version" gnus-version t]
        ["Save .newsrc files" gnus-group-save-newsrc t]
        ["Suspend Gnus" gnus-group-suspend t]
@@ -960,43 +973,145 @@ simple manner.")
 
     (gnus-run-hooks 'gnus-group-menu-hook)))
 
-(defvar gnus-group-toolbar-map nil)
-
-;; Emacs 21 tool bar.  Should be no-op otherwise.
-(defun gnus-group-make-tool-bar ()
-  (if (and
-       (condition-case nil (require 'tool-bar) (error nil))
-       (fboundp 'tool-bar-add-item-from-menu)
-       (default-value 'tool-bar-mode)
-       (not gnus-group-toolbar-map))
-      (setq gnus-group-toolbar-map
-           (let ((tool-bar-map (make-sparse-keymap))
-                 (load-path (mm-image-load-path)))
-             (tool-bar-add-item-from-menu
-              'gnus-group-get-new-news "get-news" gnus-group-mode-map)
-             (tool-bar-add-item-from-menu
-              'gnus-group-get-new-news-this-group "gnntg" gnus-group-mode-map)
-             (tool-bar-add-item-from-menu
-              'gnus-group-catchup-current "catchup" gnus-group-mode-map)
-             (tool-bar-add-item-from-menu
-              'gnus-group-describe-group "describe-group" gnus-group-mode-map)
-             (tool-bar-add-item "subscribe" 'gnus-group-subscribe 'subscribe
-                                :help "Subscribe to the current group")
-             (tool-bar-add-item "unsubscribe" 'gnus-group-unsubscribe
-                                'unsubscribe
-                                :help "Unsubscribe from the current group")
-             (tool-bar-add-item-from-menu
-              'gnus-group-exit "exit-gnus" gnus-group-mode-map)
-             tool-bar-map)))
-  (if gnus-group-toolbar-map
-      (set (make-local-variable 'tool-bar-map) gnus-group-toolbar-map)))
+
+(defvar gnus-group-tool-bar-map nil)
+
+(defun gnus-group-tool-bar-update (&optional symbol value)
+  "Update group buffer toolbar.
+Setter function for custom variables."
+  (when symbol
+    (set-default symbol value))
+  ;; (setq-default gnus-group-tool-bar-map nil)
+  ;; (use-local-map gnus-group-mode-map)
+  (when (gnus-alive-p)
+    (with-current-buffer gnus-group-buffer
+      (gnus-group-make-tool-bar t))))
+
+(defcustom gnus-group-tool-bar (if (eq gmm-tool-bar-style 'gnome)
+                                  'gnus-group-tool-bar-gnome
+                                '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
+`gmm-tool-bar-from-list' for the format of the list.  The
+default key map is `gnus-group-mode-map'.
+
+Pre-defined symbols include `gnus-group-tool-bar-gnome' and
+`gnus-group-tool-bar-retro'."
+  :type '(choice (const :tag "GNOME style" gnus-group-tool-bar-gnome)
+                (const :tag "Retro look" gnus-group-tool-bar-retro)
+                (repeat :tag "User defined list" gmm-tool-bar-item)
+                (symbol))
+  :version "23.1" ;; No Gnus
+  :initialize 'custom-initialize-default
+  :set 'gnus-group-tool-bar-update
+  :group 'gnus-group)
+
+(defcustom gnus-group-tool-bar-gnome
+  '((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
+                              :help "Gnus is currently unplugged.  Click to work online."
+                              :visible (and gnus-agent (not gnus-plugged)))
+    (gnus-agent-toggle-plugged "connect" 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)
+    ;; should have a better help text.
+    (gnus-group-send-queue "mail/outbox" t
+                          :visible (and gnus-agent gnus-plugged)
+                          :help "Send articles from the queue group")
+    (gnus-group-get-new-news "mail/inbox" nil
+                            :visible (or (not gnus-agent)
+                                         gnus-plugged))
+    ;; FIXME: gnus-*-read-group should have a better help text.
+    (gnus-topic-read-group "open" nil
+                          :visible (and (boundp 'gnus-topic-mode)
+                                        gnus-topic-mode))
+    (gnus-group-read-group "open" nil
+                          :visible (not (and (boundp 'gnus-topic-mode)
+                                             gnus-topic-mode)))
+    ;; (gnus-group-find-new-groups "???" nil)
+    (gnus-group-save-newsrc "save")
+    (gnus-group-describe-group "describe")
+    (gnus-group-unsubscribe-current-group "gnus/toggle-subscription")
+    (gnus-group-prev-unread-group "left-arrow")
+    (gnus-group-next-unread-group "right-arrow")
+    (gnus-group-exit "exit")
+    (gmm-customize-mode "preferences" t :help "Edit mode preferences")
+    (gnus-info-find-node "help"))
+  "List of functions for the group tool bar (GNOME style).
+
+See `gmm-tool-bar-from-list' for the format of the list."
+  :type '(repeat gmm-tool-bar-item)
+  :version "23.1" ;; No Gnus
+  :initialize 'custom-initialize-default
+  :set 'gnus-group-tool-bar-update
+  :group 'gnus-group)
+
+(defcustom gnus-group-tool-bar-retro
+  '((gnus-group-get-new-news "gnus/get-news")
+    (gnus-group-get-new-news-this-group "gnus/gnntg")
+    (gnus-group-catchup-current "gnus/catchup")
+    (gnus-group-describe-group "gnus/describe-group")
+    (gnus-group-subscribe "gnus/subscribe" t
+                         :help "Subscribe to the current group")
+    (gnus-group-unsubscribe "gnus/unsubscribe" t
+                           :help "Unsubscribe from the current group")
+    (gnus-group-exit "gnus/exit-gnus" gnus-group-mode-map))
+  "List of functions for the group tool bar (retro look).
+
+See `gmm-tool-bar-from-list' for the format of the list."
+  :type '(repeat gmm-tool-bar-item)
+  :version "23.1" ;; No Gnus
+  :initialize 'custom-initialize-default
+  :set 'gnus-group-tool-bar-update
+  :group 'gnus-group)
+
+(defcustom gnus-group-tool-bar-zap-list t
+  "List of icon items from the global tool bar.
+These items are not displayed in the Gnus group mode tool bar.
+
+See `gmm-tool-bar-from-list' for the format of the list."
+  :type 'gmm-tool-bar-zap-list
+  :version "23.1" ;; No Gnus
+  :initialize 'custom-initialize-default
+  :set 'gnus-group-tool-bar-update
+  :group 'gnus-group)
+
+(defvar image-load-path)
+(defvar tool-bar-map)
+
+(defun gnus-group-make-tool-bar (&optional force)
+  "Make a group mode tool bar from `gnus-group-tool-bar'.
+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
+            (or (not gnus-group-tool-bar-map) force))
+    (let* ((load-path
+           (gmm-image-load-path-for-library "gnus"
+                                            "gnus/toggle-subscription.xpm"
+                                            nil t))
+           (image-load-path (cons (car load-path)
+                                  (when (boundp 'image-load-path)
+                                    image-load-path)))
+          (map (gmm-tool-bar-from-list gnus-group-tool-bar
+                                       gnus-group-tool-bar-zap-list
+                                       'gnus-group-mode-map)))
+      (if map
+         (set (make-local-variable 'tool-bar-map) map))))
+  gnus-group-tool-bar-map)
 
 (defun gnus-group-mode ()
   "Major mode for reading news.
 
 All normal editing commands are switched off.
 \\<gnus-group-mode-map>
-The group buffer lists (some of) the groups available. For instance,
+The group buffer lists (some of) the groups available.  For instance,
 `\\[gnus-group-list-groups]' will list all subscribed groups with unread articles, while `\\[gnus-group-list-zombies]'
 lists all zombie groups.
 
@@ -1021,7 +1136,8 @@ The following commands are available:
   (use-local-map gnus-group-mode-map)
   (buffer-disable-undo)
   (setq truncate-lines t)
-  (setq buffer-read-only t)
+  (setq buffer-read-only t
+       show-trailing-whitespace nil)
   (gnus-set-default-directory)
   (gnus-update-format-specifications nil 'group 'group-mode)
   (gnus-update-group-mark-positions)
@@ -1029,7 +1145,7 @@ The following commands are available:
     (gnus-undo-mode 1))
   (when gnus-slave
     (gnus-slave-mode))
-  (gnus-run-hooks 'gnus-group-mode-hook))
+  (gnus-run-mode-hooks 'gnus-group-mode-hook))
 
 (defun gnus-update-group-mark-positions ()
   (save-excursion
@@ -1043,8 +1159,9 @@ The following commands are available:
       (gnus-group-insert-group-line "dummy.group" 0 nil 0 nil)
       (goto-char (point-min))
       (setq gnus-group-mark-positions
-           (list (cons 'process (and (search-forward "\200" nil t)
-                                     (- (point) 2))))))))
+           (list (cons 'process (and (search-forward
+                                      (mm-string-to-multibyte "\200") nil t)
+                                     (- (point) (point-min) 1))))))))
 
 (defun gnus-mouse-pick-group (e)
   "Enter the group under the mouse pointer."
@@ -1072,14 +1189,15 @@ The following commands are available:
 (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))))
+    (gnus-group-mode)))
 
 (defun gnus-group-name-charset (method group)
   (if (null method)
       (setq method (gnus-find-method-for-group group)))
-  (let ((item (assoc method gnus-group-name-charset-method-alist))
+  (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
@@ -1121,7 +1239,7 @@ Also see the `gnus-group-use-permanent-levels' variable."
   (gnus-group-setup-buffer)
   (gnus-update-format-specifications nil 'group 'group-mode)
   (let ((case-fold-search nil)
-       (props (text-properties-at (gnus-point-at-bol)))
+       (props (text-properties-at (point-at-bol)))
        (empty (= (point-min) (point-max)))
        (group (gnus-group-group-name))
        number)
@@ -1131,7 +1249,7 @@ Also see the `gnus-group-use-permanent-levels' variable."
                   (zerop number))
              (zerop (buffer-size)))
       ;; No groups in the buffer.
-      (gnus-message 5 gnus-no-groups-message))
+      (gnus-message 5 "%s" gnus-no-groups-message))
     ;; We have some groups displayed.
     (goto-char (point-max))
     (when (or (not gnus-group-goto-next-group-function)
@@ -1153,7 +1271,7 @@ Also see the `gnus-group-use-permanent-levels' variable."
                     (point-min) (point-max)
                     'gnus-group (gnus-intern-safe
                                  group gnus-active-hashtb))))
-         (let ((newsrc (cdddr (gnus-gethash group gnus-newsrc-hashtb))))
+         (let ((newsrc (cdddr (gnus-group-entry group))))
            (while (and newsrc
                        (not (gnus-goto-char
                              (text-property-any
@@ -1208,12 +1326,13 @@ if it is a string, only list groups matching REGEXP."
              group (gnus-info-group info)
              params (gnus-info-params info)
              newsrc (cdr newsrc)
-             unread (car (gnus-gethash group gnus-newsrc-hashtb)))
+             unread (gnus-group-unread group))
        (when not-in-list
          (setq not-in-list (delete group not-in-list)))
        (when (gnus-group-prepare-logic
               group
-              (and unread              ; This group might be unchecked
+              (and (or unread          ; This group might be unchecked
+                       predicate)      ; Check if this group should be listed
                    (or (not (stringp regexp))
                        (string-match regexp group))
                    (<= (setq clevel (gnus-info-level info)) level)
@@ -1227,7 +1346,7 @@ if it is a string, only list groups matching REGEXP."
                       (if (eq unread t) ; Unactivated?
                           gnus-group-list-inactive-groups
                                        ; We list unactivated
-                        (> unread 0))
+                        (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))))
@@ -1308,7 +1427,7 @@ if it is a string, only list groups matching REGEXP."
   "Update the current line in the group buffer."
   (let* ((buffer-read-only nil)
         (group (gnus-group-group-name))
-        (entry (and group (gnus-gethash group gnus-newsrc-hashtb)))
+        (entry (and group (gnus-group-entry group)))
         gnus-group-indentation)
     (when group
       (and entry
@@ -1325,7 +1444,7 @@ if it is a string, only list groups matching REGEXP."
 
 (defun gnus-group-insert-group-line-info (group)
   "Insert GROUP on the current line."
-  (let ((entry (gnus-gethash group gnus-newsrc-hashtb))
+  (let ((entry (gnus-group-entry group))
        (gnus-group-indentation (gnus-group-group-indentation))
        active info)
     (if entry
@@ -1359,6 +1478,36 @@ if it is a string, only list groups matching REGEXP."
                (gnus-range-difference (list active) (gnus-info-read info))
                seen))))))
 
+;; Moving through the Group buffer (in topic mode) e.g. with C-n doesn't
+;; update the state (enabled/disabled) of the icon `gnus-group-describe-group'
+;; automatically.  After `C-l' the state is correct.  See the following report
+;; on emacs-devel
+;; <http://thread.gmane.org/v9acdmrcse.fsf@marauder.physik.uni-ulm.de>:
+;; From: Reiner Steib
+;; Subject: tool bar icons not updated according to :active condition
+;; Newsgroups: gmane.emacs.devel
+;; Date: Mon, 23 Jan 2006 19:59:13 +0100
+;; Message-ID: <v9acdmrcse.fsf@marauder.physik.uni-ulm.de>
+
+(defcustom gnus-group-update-tool-bar
+  (and (not (featurep 'xemacs))
+       (boundp 'tool-bar-mode)
+       tool-bar-mode
+       ;; Using `redraw-frame' (see `gnus-tool-bar-update') in Emacs might
+       ;; be confusing, so maybe we shouldn't call it by default.
+       (fboundp 'force-window-update))
+  "Force updating the group buffer tool bar."
+  :group 'gnus-group
+  :version "22.1"
+  :initialize 'custom-initialize-default
+  :set (lambda (symbol value)
+        (set-default symbol value)
+        (when (gnus-alive-p)
+          (with-current-buffer gnus-group-buffer
+            ;; FIXME: Is there a better way to redraw the group buffer?
+            (gnus-group-get-new-news 0))))
+  :type 'boolean)
+
 (defun gnus-group-insert-group-line (gnus-tmp-group gnus-tmp-level
                                                    gnus-tmp-marked number
                                                    gnus-tmp-method)
@@ -1402,7 +1551,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-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
@@ -1422,143 +1571,177 @@ if it is a string, only list groups matching REGEXP."
         (gnus-tmp-process-marked
          (if (member gnus-tmp-group gnus-group-marked)
              gnus-process-mark ? ))
-        (gnus-tmp-grouplens
-         (or (and gnus-use-grouplens
-                  (bbb-grouplens-group-p gnus-tmp-group))
-             ""))
         (buffer-read-only nil)
+        beg end
         header gnus-tmp-header)        ; passed as parameter to user-funcs.
     (beginning-of-line)
+    (setq beg (point))
     (gnus-add-text-properties
      (point)
      (prog1 (1+ (point))
        ;; Insert the text.
-       (let ((gnus-tmp-group (gnus-group-name-decode
-                             gnus-tmp-group group-name-charset)))
+       (let ((gnus-tmp-decoded-group (gnus-group-name-decode
+                                     gnus-tmp-group group-name-charset)))
         (eval gnus-group-line-format-spec)))
      `(gnus-group ,(gnus-intern-safe gnus-tmp-group gnus-active-hashtb)
                  gnus-unread ,(if (numberp number)
-                                  (string-to-int gnus-tmp-number-of-unread)
+                                  (string-to-number gnus-tmp-number-of-unread)
                                 t)
                  gnus-marked ,gnus-tmp-marked-mark
                  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))
     (forward-line -1)
     (when (inline (gnus-visual-p 'group-highlight 'highlight))
-      (gnus-run-hooks 'gnus-group-update-hook))
+      (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)))
 
-(defun gnus-group-highlight-line ()
-  "Highlight the current line according to `gnus-group-highlight'."
-  (let* ((list gnus-group-highlight)
-        (p (point))
-        (end (gnus-point-at-eol))
-        ;; now find out where the line starts and leave point there.
-        (beg (progn (beginning-of-line) (point)))
-        (group (gnus-group-group-name))
-        (entry (gnus-group-entry group))
-        (unread (if (numberp (car entry)) (car entry) 0))
-        (active (gnus-active group))
-        (total (if active (1+ (- (cdr active) (car active))) 0))
-        (info (nth 2 entry))
-        (method (gnus-server-get-method group (gnus-info-method info)))
-        (marked (gnus-info-marks info))
-        (mailp (apply 'append
-                      (mapcar
-                       (lambda (x)
-                         (memq x (assoc (symbol-name
-                                         (car (or method gnus-select-method)))
-                                        gnus-valid-select-methods)))
-                       '(mail post-mail))))
-        (level (or (gnus-info-level info) gnus-level-killed))
-        (score (or (gnus-info-score info) 0))
-        (ticked (gnus-range-length (cdr (assq 'tick marked))))
-        (group-age (gnus-group-timestamp-delta group))
-        (inhibit-read-only t))
-    ;; Eval the cars of the lists until we find a match.
-    (while (and list
-               (not (eval (caar list))))
-      (setq list (cdr list)))
-    (let ((face (cdar list)))
-      (unless (eq face (get-text-property beg 'face))
-       (gnus-put-text-property-excluding-characters-with-faces
-        beg end 'face
-        (setq face (if (boundp face) (symbol-value face) face)))
-       (gnus-extent-start-open beg)))
-    (goto-char p)))
+(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."
+  (when list
+    (let* ((entry (gnus-group-entry group))
+           (unread (if (numberp (car entry)) (car entry) 0))
+           (active (gnus-active group))
+           (total (if active (1+ (- (cdr active) (car active))) 0))
+           (info (nth 2 entry))
+           (method (inline (gnus-server-get-method group (gnus-info-method info))))
+           (marked (gnus-info-marks info))
+           (mailp (apply 'append
+                         (mapcar
+                          (lambda (x)
+                            (memq x (assoc (symbol-name
+                                            (car (or method gnus-select-method)))
+                                           gnus-valid-select-methods)))
+                          '(mail post-mail))))
+           (level (or (gnus-info-level info) gnus-level-killed))
+           (score (or (gnus-info-score info) 0))
+           (ticked (gnus-range-length (cdr (assq 'tick marked))))
+           (group-age (gnus-group-timestamp-delta group)))
+      ;; FIXME: http://thread.gmane.org/gmane.emacs.gnus.general/65451/focus=65465
+      ;; ======================================================================
+      ;; From: Richard Stallman
+      ;; Subject: Re: Rewriting gnus-group-highlight-line (was: [...])
+      ;; Cc: ding@gnus.org
+      ;; Date: Sat, 27 Oct 2007 19:41:20 -0400
+      ;; Message-ID: <E1IlvHM-0006TS-7t@fencepost.gnu.org>
+      ;;
+      ;; [...]
+      ;; The kludge is that the alist elements contain expressions that refer
+      ;; to local variables with short names.  Perhaps write your own tiny
+      ;; evaluator that handles just `and', `or', and numeric comparisons
+      ;; and just a few specific variables.
+      ;; ======================================================================
+      ;;
+      ;; Similar for other evaluated variables.  Grep for risky-local-variable
+      ;; to find them!  -- rsteib
+      ;;
+      ;; Eval the cars of the lists until we find a match.
+      (while (and list
+                  (not (eval (caar list))))
+        (setq list (cdr list)))
+      list)))
+
+(defun gnus-group-highlight-line (group beg end)
+  "Highlight the current line according to `gnus-group-highlight'.
+GROUP is current group, and the line to highlight starts at BEG
+and ends at END."
+  (let ((face (cdar (gnus-group-update-eval-form
+                      group
+                      gnus-group-highlight))))
+    (unless (eq face (get-text-property beg 'face))
+      (let ((inhibit-read-only t))
+        (gnus-put-text-property-excluding-characters-with-faces
+         beg end 'face
+         (if (boundp face) (symbol-value face) face)))
+      (gnus-extent-start-open beg))))
+
+(defun gnus-group-get-icon (group)
+  "Return an icon for GROUP according to `gnus-group-icon-list'."
+  (if gnus-group-icon-list
+      (let ((image-path
+             (cdar (gnus-group-update-eval-form group gnus-group-icon-list))))
+        (if image-path
+            (propertize " "
+                        'display
+                        (append
+                         (gnus-create-image (expand-file-name image-path))
+                         '(:ascent center)))
+          " "))
+    " "))
 
 (defun gnus-group-update-group (group &optional visible-only)
   "Update all lines where GROUP appear.
 If VISIBLE-ONLY is non-nil, the group won't be displayed if it isn't
 already."
-  ;; Can't use `save-excursion' here, so we do it manually.
-  (let ((buf (current-buffer))
-       mark)
-    (set-buffer gnus-group-buffer)
-    (setq mark (point-marker))
-    ;; The buffer may be narrowed.
-    (save-restriction
-      (widen)
-      (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-gethash group gnus-newsrc-hashtb)))
-         (when (and entry
-                    (not (gnus-ephemeral-group-p group)))
-           (gnus-dribble-enter
-            (concat "(gnus-group-set-info '"
-                    (gnus-prin1-to-string (nth 2 entry))
-                    ")"))))
-       ;; Find all group instances.  If topics are in use, each group
-       ;; may be listed in more than once.
-       (while (setq loc (text-property-any
-                         loc (point-max) 'gnus-group ident))
-         (setq found t)
-         (goto-char loc)
-         (let ((gnus-group-indentation (gnus-group-group-indentation)))
-           (gnus-delete-line)
-           (gnus-group-insert-group-line-info group)
-           (save-excursion
-             (forward-line -1)
-             (gnus-run-hooks 'gnus-group-update-group-hook)))
-         (setq loc (1+ loc)))
-       (unless (or found visible-only)
-         ;; No such line in the buffer, find out where it's supposed to
-         ;; go, and insert it there (or at the end of the buffer).
-         (if gnus-goto-missing-group-function
-             (funcall gnus-goto-missing-group-function group)
-           (let ((entry (cddr (gnus-gethash group gnus-newsrc-hashtb))))
-             (while (and entry (car entry)
-                         (not
-                          (gnus-goto-char
-                           (text-property-any
-                            (point-min) (point-max)
-                            'gnus-group (gnus-intern-safe
-                                         (caar entry) gnus-active-hashtb)))))
-               (setq entry (cdr entry)))
-             (or entry (goto-char (point-max)))))
-         ;; Finally insert the line.
-         (let ((gnus-group-indentation (gnus-group-group-indentation)))
-           (gnus-group-insert-group-line-info group)
-           (save-excursion
-             (forward-line -1)
-             (gnus-run-hooks 'gnus-group-update-group-hook))))
-       (when gnus-group-update-group-function
-         (funcall gnus-group-update-group-function group))
-       (gnus-group-set-mode-line)))
-    (goto-char mark)
-    (set-marker mark nil)
-    (set-buffer buf)))
+  (with-current-buffer gnus-group-buffer
+    (save-excursion
+      ;; The buffer may be narrowed.
+      (save-restriction
+        (widen)
+        (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))
+                       ")"))))
+          ;; Find all group instances.  If topics are in use, each group
+          ;; may be listed in more than once.
+          (while (setq loc (text-property-any
+                            loc (point-max) 'gnus-group ident))
+            (setq found t)
+            (goto-char loc)
+            (let ((gnus-group-indentation (gnus-group-group-indentation)))
+              (gnus-delete-line)
+              (gnus-group-insert-group-line-info group)
+              (save-excursion
+                (forward-line -1)
+                (gnus-run-hooks 'gnus-group-update-group-hook)))
+            (setq loc (1+ loc)))
+          (unless (or found visible-only)
+            ;; No such line in the buffer, find out where it's supposed to
+            ;; go, and insert it there (or at the end of the buffer).
+            (if gnus-goto-missing-group-function
+                (funcall gnus-goto-missing-group-function group)
+              (let ((entry (cddr (gnus-group-entry group))))
+                (while (and entry (car entry)
+                            (not
+                             (gnus-goto-char
+                              (text-property-any
+                               (point-min) (point-max)
+                               'gnus-group (gnus-intern-safe
+                                            (caar entry)
+                                            gnus-active-hashtb)))))
+                  (setq entry (cdr entry)))
+                (or entry (goto-char (point-max)))))
+            ;; Finally insert the line.
+            (let ((gnus-group-indentation (gnus-group-group-indentation)))
+              (gnus-group-insert-group-line-info group)
+              (save-excursion
+                (forward-line -1)
+                (gnus-run-hooks 'gnus-group-update-group-hook))))
+          (when gnus-group-update-group-function
+            (funcall gnus-group-update-group-function group))
+          (gnus-group-set-mode-line))))))
 
 (defun gnus-group-set-mode-line ()
   "Update the mode line in the group buffer."
   (when (memq 'group gnus-updated-mode-lines)
     ;; Yes, we want to keep this mode line updated.
-    (save-excursion
-      (set-buffer gnus-group-buffer)
+    (with-current-buffer gnus-group-buffer
       (let* ((gformat (or gnus-group-mode-line-format-spec
                          (gnus-set-format 'group-mode)))
             (gnus-tmp-news-server (cadr gnus-select-method))
@@ -1571,8 +1754,7 @@ already."
              (and gnus-dribble-buffer
                   (buffer-name gnus-dribble-buffer)
                   (buffer-modified-p gnus-dribble-buffer)
-                  (save-excursion
-                    (set-buffer gnus-dribble-buffer)
+                  (with-current-buffer gnus-dribble-buffer
                     (not (zerop (buffer-size))))))
             (mode-string (eval gformat)))
        ;; Say whether the dribble buffer has been modified.
@@ -1590,24 +1772,24 @@ already."
 
 (defun gnus-group-group-name ()
   "Get the name of the newsgroup on the current line."
-  (let ((group (get-text-property (gnus-point-at-bol) 'gnus-group)))
+  (let ((group (get-text-property (point-at-bol) 'gnus-group)))
     (when group
       (symbol-name group))))
 
 (defun gnus-group-group-level ()
   "Get the level of the newsgroup on the current line."
-  (get-text-property (gnus-point-at-bol) 'gnus-level))
+  (get-text-property (point-at-bol) 'gnus-level))
 
 (defun gnus-group-group-indentation ()
   "Get the indentation of the newsgroup on the current line."
-  (or (get-text-property (gnus-point-at-bol) 'gnus-indentation)
+  (or (get-text-property (point-at-bol) 'gnus-indentation)
       (and gnus-group-indentation-function
           (funcall gnus-group-indentation-function))
       ""))
 
 (defun gnus-group-group-unread ()
   "Get the number of unread articles of the newsgroup on the current line."
-  (get-text-property (gnus-point-at-bol) 'gnus-unread))
+  (get-text-property (point-at-bol) 'gnus-unread))
 
 (defun gnus-group-new-mail (group)
   (if (nnmail-new-mail-p (gnus-group-real-name group))
@@ -1665,6 +1847,18 @@ If FIRST-TOO, the current line is also eligible as a target."
       (goto-char (or pos beg))
       (and pos t))))
 
+(defun gnus-total-fetched-for (group)
+  (let* ((size-in-cache (or (gnus-cache-total-fetched-for group) 0))
+        (size-in-agent (or (gnus-agent-total-fetched-for group) 0))
+        (size (+ size-in-cache size-in-agent))
+        (suffix '("B" "K" "M" "G"))
+        (scale 1024.0)
+        (cutoff scale))
+    (while (> size cutoff)
+      (setq size (/ size scale)
+           suffix (cdr suffix)))
+    (format "%5.1f%s" size (car suffix))))
+
 ;;; Gnus group mode commands
 
 ;; Group marking.
@@ -1686,19 +1880,18 @@ If FIRST-TOO, the current line is also eligible as a target."
        ;; Go to the mark position.
        (beginning-of-line)
        (forward-char (or (cdr (assq 'process gnus-group-mark-positions)) 2))
-       (subst-char-in-region
-        (point) (1+ (point)) (char-after)
-        (if unmark
-            (progn
-              (setq gnus-group-marked (delete group gnus-group-marked))
-              ? )
+       (delete-char 1)
+       (if unmark
+           (progn
+             (setq gnus-group-marked (delete group gnus-group-marked))
+             (insert-char ? 1 t))
           (setq gnus-group-marked
                 (cons group (delete group gnus-group-marked)))
-          gnus-process-mark)))
+          (insert-char gnus-process-mark 1 t)))
       (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)
@@ -1710,10 +1903,8 @@ If FIRST-TOO, the current line is also eligible as a target."
 (defun gnus-group-unmark-all-groups ()
   "Unmark all groups."
   (interactive)
-  (let ((groups gnus-group-marked))
-    (save-excursion
-      (while groups
-       (gnus-group-remove-mark (pop groups)))))
+  (save-excursion
+    (mapc 'gnus-group-remove-mark gnus-group-marked))
   (gnus-group-position-point))
 
 (defun gnus-group-mark-region (unmark beg end)
@@ -1736,9 +1927,11 @@ If UNMARK, remove the mark instead."
   (interactive "sMark (regexp): ")
   (let ((alist (cdr gnus-newsrc-alist))
        group)
-    (while alist
-      (when (string-match regexp (setq group (gnus-info-group (pop alist))))
-       (gnus-group-set-mark group))))
+    (save-excursion
+      (while alist
+       (when (string-match regexp (setq group (gnus-info-group (pop alist))))
+         (gnus-group-jump-to-group group)
+         (gnus-group-set-mark group)))))
   (gnus-group-position-point))
 
 (defun gnus-group-remove-mark (group &optional test-marked)
@@ -1844,10 +2037,15 @@ and with point over the group in question."
 (defun gnus-group-read-group (&optional all no-article group select-articles)
   "Read news in this newsgroup.
 If the prefix argument ALL is non-nil, already read articles become
-readable.  IF ALL is a number, fetch this number of articles.  If the
-optional argument NO-ARTICLE is non-nil, no article will be
-auto-selected upon group entry.         If GROUP is non-nil, fetch that
-group."
+readable.
+
+If ALL is a positive number, fetch this number of the latest
+articles in the group.  If ALL is a negative number, fetch this
+number of the earliest articles in the group.
+
+If the optional argument NO-ARTICLE is non-nil, no article will
+be auto-selected upon group entry.  If GROUP is non-nil, fetch
+that group."
   (interactive "P")
   (let ((no-display (eq all 0))
        (group (or group (gnus-group-group-name)))
@@ -1857,8 +2055,7 @@ group."
     (unless group
       (error "No group on current line"))
     (setq marked (gnus-info-marks
-                 (nth 2 (setq entry (gnus-gethash
-                                     group gnus-newsrc-hashtb)))))
+                 (nth 2 (setq entry (gnus-group-entry group)))))
     ;; This group might be a dead group.  In that case we have to get
     ;; the number of unread articles from `gnus-active-hashtb'.
     (setq number
@@ -1879,17 +2076,20 @@ group."
 No article is selected automatically.
 If the group is opened, just switch the summary buffer.
 If ALL is non-nil, already read articles become readable.
-If ALL is a number, fetch this number of articles."
+If ALL is a positive number, fetch this number of the latest
+articles in the group.
+If ALL is a negative number, fetch this number of the earliest
+articles in the group."
   (interactive "P")
   (when (and (eobp) (not (gnus-group-group-name)))
     (forward-line -1))
   (gnus-group-read-group all t))
 
-(defun gnus-group-quick-select-group (&optional all)
-  "Select the current group \"quickly\".
-This means that no highlighting or scoring will be performed.
-If ALL (the prefix argument) is 0, don't even generate the summary
-buffer.
+(defun gnus-group-quick-select-group (&optional all group)
+  "Select the GROUP \"quickly\".
+This means that no highlighting or scoring will be performed.  If
+ALL (the prefix argument) is 0, don't even generate the summary
+buffer.  If GROUP is nil, use current group.
 
 This might be useful if you want to toggle threading
 before entering the group."
@@ -1900,7 +2100,7 @@ before entering the group."
        gnus-home-score-file
        gnus-apply-kill-hook
        gnus-summary-expunge-below)
-    (gnus-group-read-group all t)))
+    (gnus-group-read-group all t group)))
 
 (defun gnus-group-visible-select-group (&optional all)
   "Select the current group without hiding any articles."
@@ -1924,14 +2124,94 @@ be permanent."
     (gnus-group-read-ephemeral-group
      (gnus-group-prefixed-name group method) method)))
 
+(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)
+      (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-?]+\\)+\\)")
+         (start (point))
+         (case-fold-search nil))
+      (prog1
+         (if (or (and (not (or (eobp)
+                               (looking-at "[][\C-@-*,/;-@\\^`{-\C-?]")))
+                      (prog1 t
+                        (skip-chars-backward "^][\C-@-\t\v-*,/;-@\\^`{-\C-?"
+                                             (point-at-bol))))
+                 (and (looking-at "[][\C-@-\t\v-*,/;-@\\^`{-\C-?]*$")
+                      (prog1 t
+                        (skip-chars-backward "][\C-@-\t\v-*,/;-@\\^`{-\C-?")
+                        (skip-chars-backward "^][\C-@-\t\v-*,/;-@\\^`{-\C-?"
+                                             (point-at-bol))))
+                 (string-match "\\`[][\C-@-\t\v-*,/;-@\\^`{-\C-?]*\\'"
+                               (buffer-substring (point-at-bol) (point))))
+             (when (looking-at regexp)
+               (match-string 1))
+           (let (group distance)
+             (when (looking-at regexp)
+               (setq group (match-string 1)
+                     distance (- (match-beginning 1) (match-beginning 0))))
+             (skip-chars-backward "][\C-@-\t\v-*,/;-@\\^`{-\C-?")
+             (skip-chars-backward "^][\C-@-\t\v-*,/;-@\\^`{-\C-?"
+                                  (point-at-bol))
+             (if (looking-at regexp)
+                 (if (and group (<= distance (- start (match-end 0))))
+                     group
+                   (match-string 1))
+               group)))
+       (goto-char start)))))
+
+(defun gnus-group-completing-read (&optional prompt collection
+                                            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.  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))))
+    (replace-regexp-in-string "\n" "" group)))
+
 ;;;###autoload
 (defun gnus-fetch-group (group &optional articles)
   "Start Gnus if necessary and enter GROUP.
+If ARTICLES, display those articles.
 Returns whether the fetching was successful or not."
-  (interactive (list (completing-read "Group name: " gnus-active-hashtb)))
-  (unless (get-buffer gnus-group-buffer)
+  (interactive (list (gnus-group-completing-read nil
+                                                nil nil
+                                                (gnus-group-name-at-point))))
+  (unless (gnus-alive-p)
     (gnus-no-server))
-  (gnus-group-read-group articles nil group))
+  (gnus-group-read-group (if articles nil t) nil group articles))
 
 ;;;###autoload
 (defun gnus-fetch-group-other-frame (group)
@@ -1946,8 +2226,6 @@ Returns whether the fetching was successful or not."
           (other-frame 1))))
   (gnus-fetch-group group))
 
-(defvar gnus-ephemeral-group-server 0)
-
 (defcustom gnus-large-ephemeral-newsgroup 200
   "The number of articles which indicates a large ephemeral newsgroup.
 Same as `gnus-large-newsgroup', but only used for ephemeral newsgroups.
@@ -1955,12 +2233,14 @@ Same as `gnus-large-newsgroup', but only used for ephemeral newsgroups.
 If the number of articles in a newsgroup is greater than this value,
 confirmation is required for selecting the newsgroup.  If it is nil, no
 confirmation is required."
+  :version "22.1"
   :group 'gnus-group-select
   :type '(choice (const :tag "No limit" nil)
                 integer))
 
 (defcustom gnus-fetch-old-ephemeral-headers nil
   "Same as `gnus-fetch-old-headers', but only used for ephemeral newsgroups."
+  :version "22.1"
   :group 'gnus-thread
   :type '(choice (const :tag "off" nil)
                 (const some)
@@ -1972,7 +2252,8 @@ confirmation is required."
 (defun gnus-group-read-ephemeral-group (group method &optional activate
                                              quit-config request-only
                                              select-articles
-                                             parameters)
+                                             parameters
+                                             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
@@ -1980,8 +2261,14 @@ 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.
+If NUMBER, fetch this number of articles.
 
 Return the name of the group if selection was successful."
+  (interactive
+   (list
+    ;; (gnus-read-group "Group name: ")
+    (gnus-group-completing-read)
+    (gnus-read-method "From method: ")))
   ;; Transform the select method into a unique server.
   (when (stringp method)
     (setq method (gnus-server-to-method method)))
@@ -2019,22 +2306,169 @@ Return the name of the group if selection was successful."
          (when (let ((gnus-large-newsgroup gnus-large-ephemeral-newsgroup)
                      (gnus-fetch-old-headers
                       gnus-fetch-old-ephemeral-headers))
-                 (gnus-group-read-group t t group select-articles))
+                 (gnus-group-read-group (or number t) t group select-articles))
            group)
        ;;(error nil)
        (quit
         (message "Quit reading the ephemeral group")
         nil)))))
 
-(defun gnus-group-jump-to-group (group)
-  "Jump to newsgroup GROUP."
+(defcustom gnus-gmane-group-download-format
+  "http://download.gmane.org/%s/%s/%s"
+  "URL for downloading mbox files.
+It must contain three \"%s\".  They correspond to the group, the
+minimal and maximal article numbers, respectively."
+  :group 'gnus-group-foreign
+  :version "23.1" ;; No Gnus
+  :type 'string)
+
+(autoload 'url-insert-file-contents "url-handlers")
+;; FIXME:
+;; - Add documentation, menu, key bindings, ...
+
+(defun gnus-read-ephemeral-gmane-group (group start &optional range)
+  "Read articles from Gmane group GROUP as an ephemeral group.
+START is the first article.  RANGE specifies how many articles
+are fetched.  The articles are downloaded via HTTP using the URL
+specified by `gnus-gmane-group-download-format'."
+  ;; See <http://gmane.org/export.php> for more information.
+  (interactive
+   (list
+    (gnus-group-completing-read "Gmane group")
+    (read-number "Start article number: ")
+    (read-number "How many articles: ")))
+  (unless range (setq range 500))
+  (when (< range 1)
+    (error "Invalid range: %s" range))
+  (let ((tmpfile (mm-make-temp-file
+                 (format "%s.start-%s.range-%s." group start range)))
+       (gnus-thread-sort-functions '(gnus-thread-sort-by-number)))
+    (with-temp-file tmpfile
+      (url-insert-file-contents
+       (format 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)
+       `(nndoc ,tmpfile
+              (nndoc-article-type mbox))))
+    (delete-file tmpfile)))
+
+(defun gnus-read-ephemeral-gmane-group-url (url)
+  "Create an ephemeral Gmane group from URL.
+
+Valid input formats include:
+\"http://thread.gmane.org/gmane.foo.bar/12300/focus=12399\",
+\"http://thread.gmane.org/gmane.foo.bar/12345/\",
+\"http://article.gmane.org/gmane.foo.bar/12345/\",
+\"http://news.gmane.org/group/gmane.foo.bar/thread=12345\""
+  ;; - Feel free to add other useful Gmane URLs here!  Maybe the URLs should
+  ;;   be customizable?
+  ;; - The URLs should be added to `gnus-button-alist'.  Probably we should
+  ;;   prompt the user to decide: "View via `browse-url' or in Gnus? "
+  ;;   (`gnus-read-ephemeral-gmane-group-url')
+  (interactive
+   (list (gnus-group-completing-read "Gmane URL")))
+  (let (group start range)
+    (cond
+     ;; 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]+\\)$"
+       url)
+      (setq group (match-string 1 url)
+           start (string-to-number (match-string 2 url))
+           ;; Ensure that `range' is large enough to ensure focus article is
+           ;; included.
+           range (- (string-to-number (match-string 3 url))
+                    start -1)))
+     ;; 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]+\\)"
+          url)
+         (string-match
+          ;; Don't advertise these in the doc string yet:
+          "^\\(?: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]+\\)"
+          url))
+      (setq group (match-string 1 url)
+           start (string-to-number (match-string 2 url))))
+     (t
+      (error "Can't parse URL %s" url)))
+    (gnus-read-ephemeral-gmane-group group start range)))
+
+(defcustom gnus-bug-group-download-format-alist
+  '((emacs . "http://debbugs.gnu.org/%s;mbox=yes")
+    (debian
+     . "http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=%s&mbox=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
+  :type '(repeat (cons (symbol) (string :tag "URL format string"))))
+
+(defun gnus-read-ephemeral-bug-group (number mbox-url)
+  "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)))
+  (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))))
+    (delete-file tmpfile)))
+
+(defun gnus-read-ephemeral-debian-bug-group (number)
+  "Browse Debian bug NUMBER as ephemeral group."
+  (interactive (list (read-string "Enter bug number: "
+                                 (thing-at-point 'word) nil)))
+  (gnus-read-ephemeral-bug-group
+   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)))
+  (gnus-read-ephemeral-bug-group
+   number
+   (cdr (assoc 'emacs gnus-bug-group-download-format-alist))))
+
+(defun gnus-group-jump-to-group (group &optional prompt)
+  "Jump to newsgroup GROUP.
+
+If PROMPT (the prefix) is a number, use the prompt specified in
+`gnus-group-jump-to-group-prompt'."
   (interactive
-   (list (mm-string-make-unibyte
-         (completing-read
-          "Group: " gnus-active-hashtb nil
-          (gnus-read-active-file-p)
-          gnus-group-jump-to-group-prompt
-          'gnus-group-history))))
+   (list (gnus-group-completing-read
+          nil nil (gnus-read-active-file-p)
+          (if current-prefix-arg
+              (cdr (assq current-prefix-arg gnus-group-jump-to-group-prompt))
+            (or (and (stringp gnus-group-jump-to-group-prompt)
+                     gnus-group-jump-to-group-prompt)
+                (let ((p (cdr (assq 0 gnus-group-jump-to-group-prompt))))
+                  (and (stringp p) p)))))))
 
   (when (equal group "")
     (error "Empty group name"))
@@ -2182,6 +2616,25 @@ If EXCLUDE-GROUP, do not go to that group."
     (gnus-group-position-point)
     (and best-point (gnus-group-group-name))))
 
+;; Is there something like an after-point-motion-hook?
+;; (inhibit-point-motion-hooks?).  Is there a tool-bar-update function?
+
+;; (defun gnus-group-menu-bar-update ()
+;;   (let* ((buf (list (with-current-buffer gnus-group-buffer
+;;                   (current-buffer))))
+;;      (name (buffer-name (car buf))))
+;;     (setcdr buf
+;;         (if (> (length name) 27)
+;;             (concat (substring name 0 12)
+;;                     "..."
+;;                     (substring name -12))
+;;           name))
+;;     (menu-bar-update-buffers-1 buf)))
+
+;; (defun gnus-group-position-point ()
+;;   (gnus-goto-colon)
+;;   (gnus-group-menu-bar-update))
+
 (defun gnus-group-first-unread-group ()
   "Go to the first group with unread articles."
   (interactive)
@@ -2203,10 +2656,22 @@ If EXCLUDE-GROUP, do not go to that group."
   (interactive)
   (gnus-enter-server-buffer))
 
-(defun gnus-group-make-group (name &optional method address args)
+(defun gnus-group-make-group-simple (&optional group)
+  "Add a new newsgroup.
+The user will be prompted for GROUP."
+  (interactive (list (gnus-group-completing-read)))
+  (gnus-group-make-group (gnus-group-real-name group)
+                        (gnus-group-server group)
+                        nil nil t))
+
+(defun gnus-group-make-group (name &optional method address args encoded)
   "Add a new newsgroup.
 The user will be prompted for a NAME, for a select METHOD, and an
-ADDRESS."
+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.
+
+If the backend supports it, the group will also be created on the
+server."
   (interactive
    (list
     (gnus-read-group "Group name: ")
@@ -2214,6 +2679,10 @@ ADDRESS."
 
   (when (stringp method)
     (setq method (or (gnus-server-to-method method) method)))
+  (unless encoded
+    (setq name (mm-encode-coding-string
+               name
+               (gnus-group-name-charset method name))))
   (let* ((meth (gnus-method-simplify
                (when (and method
                           (not (gnus-server-equal method gnus-select-method)))
@@ -2221,15 +2690,14 @@ ADDRESS."
                    method))))
         (nname (if method (gnus-group-prefixed-name name meth) name))
         backend info)
-    (when (gnus-gethash nname gnus-newsrc-hashtb)
-      (error "Group %s already exists" nname))
+    (when (gnus-group-entry nname)
+      (error "Group %s already exists" (gnus-group-decoded-name nname)))
     ;; Subscribe to the new group.
     (gnus-group-change-level
      (setq info (list t nname gnus-level-default-subscribed nil nil meth))
      gnus-level-default-subscribed gnus-level-killed
      (and (gnus-group-group-name)
-         (gnus-gethash (gnus-group-group-name)
-                       gnus-newsrc-hashtb))
+         (gnus-group-entry (gnus-group-group-name)))
      t)
     ;; Make it active.
     (gnus-set-active nname (cons 1 0))
@@ -2267,14 +2735,14 @@ ADDRESS."
        (lambda (group)
          (gnus-group-delete-group group nil t))))))
 
-(eval-when-compile (defvar gnus-cache-active-altered))
-
 (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
 be deleted.  This is \"deleted\" as in \"removed forever from the face
-of the Earth\".         There is no undo.  The user will be prompted before
-doing the deletion."
+of the Earth\".  There is no undo.  The user will be prompted before
+doing the deletion.
+Note that you also have to specify FORCE if you want the group to
+be removed from the server, even when it's empty."
   (interactive
    (list (gnus-group-group-name)
         current-prefix-arg))
@@ -2283,24 +2751,21 @@ doing the deletion."
   (unless (gnus-check-backend-function 'request-delete-group group)
     (error "This back end does not support group deletion"))
   (prog1
-      (if (and (not no-prompt)
-              (not (gnus-yes-or-no-p
-                    (format
-                     "Do you really want to delete %s%s? "
-                     group (if force " and all its contents" "")))))
-         ()                            ; Whew!
-       (gnus-message 6 "Deleting group %s..." group)
-       (if (not (gnus-request-delete-group group force))
-           (gnus-error 3 "Couldn't delete group %s" group)
-         (gnus-message 6 "Deleting group %s...done" group)
-         (gnus-group-goto-group group)
-         (gnus-group-kill-group 1 t)
-         (gnus-sethash group nil gnus-active-hashtb)
-         (if (boundp 'gnus-cache-active-hashtb)
-             (when gnus-cache-active-hashtb
-               (gnus-sethash group nil gnus-cache-active-hashtb)
-               (setq gnus-cache-active-altered t)))
-         t))
+      (let ((group-decoded (gnus-group-decoded-name group)))
+       (if (and (not no-prompt)
+                (not (gnus-yes-or-no-p
+                      (format
+                       "Do you really want to delete %s%s? "
+                       group-decoded (if force " and all its contents" "")))))
+           ()                          ; Whew!
+         (gnus-message 6 "Deleting group %s..." group-decoded)
+         (if (not (gnus-request-delete-group group force))
+             (gnus-error 3 "Couldn't delete group %s" group-decoded)
+           (gnus-message 6 "Deleting group %s...done" group-decoded)
+           (gnus-group-goto-group group)
+           (gnus-group-kill-group 1 t)
+           (gnus-set-active group nil)
+           t)))
     (gnus-group-position-point)))
 
 (defun gnus-group-rename-group (group new-name)
@@ -2308,14 +2773,19 @@ doing the deletion."
 When used interactively, GROUP is the group under point
 and NEW-NAME will be prompted for."
   (interactive
-   (list
-    (gnus-group-group-name)
-    (progn
-      (unless (gnus-check-backend-function
-              'request-rename-group (gnus-group-group-name))
-       (error "This back end does not support renaming groups"))
-      (gnus-read-group "Rename group to: "
-                      (gnus-group-real-name (gnus-group-group-name))))))
+   (let ((group (gnus-group-group-name))
+        method new-name)
+     (unless (gnus-check-backend-function 'request-rename-group group)
+       (error "This back end does not support renaming groups"))
+     (setq new-name (gnus-read-group
+                    "Rename group to: "
+                    (gnus-group-real-name (gnus-group-decoded-name group)))
+          method (gnus-info-method (gnus-get-info group)))
+     (list group (mm-encode-coding-string
+                 new-name
+                 (gnus-group-name-charset
+                  method
+                  (gnus-group-prefixed-name new-name method))))))
 
   (unless (gnus-check-backend-function 'request-rename-group group)
     (error "This back end does not support renaming groups"))
@@ -2334,26 +2804,34 @@ and NEW-NAME will be prompted for."
           (gnus-group-real-name new-name)
           (gnus-info-method (gnus-get-info group)))))
 
-  (gnus-message 6 "Renaming group %s to %s..." group new-name)
-  (prog1
-      (if (progn
-           (gnus-group-goto-group group)
-           (not (when (< (gnus-group-group-level) gnus-level-zombie)
-                  (gnus-request-rename-group group new-name))))
-         (gnus-error 3 "Couldn't rename group %s to %s" group new-name)
-       ;; We rename the group internally by killing it...
-       (gnus-group-kill-group)
-       ;; ... changing its name ...
-       (setcar (cdar gnus-list-of-killed-groups) new-name)
-       ;; ... and then yanking it.  Magic!
-       (gnus-group-yank-group)
-       (gnus-set-active new-name (gnus-active group))
-       (gnus-message 6 "Renaming group %s to %s...done" group new-name)
-       new-name)
-    (setq gnus-killed-list (delete group gnus-killed-list))
-    (gnus-set-active group nil)
-    (gnus-dribble-touch)
-    (gnus-group-position-point)))
+  (let ((decoded-group (gnus-group-decoded-name group))
+       (decoded-new-name (gnus-group-decoded-name new-name)))
+    (when (gnus-active new-name)
+      (error "The group %s already exists" decoded-new-name))
+
+    (gnus-message 6 "Renaming group %s to %s..."
+                 decoded-group decoded-new-name)
+    (prog1
+       (if (progn
+             (gnus-group-goto-group group)
+             (not (when (< (gnus-group-group-level) gnus-level-zombie)
+                    (gnus-request-rename-group group new-name))))
+           (gnus-error 3 "Couldn't rename group %s to %s"
+                       decoded-group decoded-new-name)
+         ;; We rename the group internally by killing it...
+         (gnus-group-kill-group)
+         ;; ... changing its name ...
+         (setcar (cdar gnus-list-of-killed-groups) new-name)
+         ;; ... and then yanking it.  Magic!
+         (gnus-group-yank-group)
+         (gnus-set-active new-name (gnus-active group))
+         (gnus-message 6 "Renaming group %s to %s...done"
+                       decoded-group decoded-new-name)
+         new-name)
+      (setq gnus-killed-list (delete group gnus-killed-list))
+      (gnus-set-active group nil)
+      (gnus-dribble-touch)
+      (gnus-group-position-point))))
 
 (defun gnus-group-edit-group (group &optional part)
   "Edit the group on the current line."
@@ -2443,10 +2921,14 @@ and NEW-NAME will be prompted for."
 (defun gnus-group-make-useful-group (group method)
   "Create one of the groups described in `gnus-useful-groups'."
   (interactive
-   (let ((entry (assoc (completing-read "Create group: " gnus-useful-groups
-                                       nil t)
+   (let ((entry (assoc (gnus-completing-read "Create group"
+                                             (mapcar 'car gnus-useful-groups)
+                                             t)
                       gnus-useful-groups)))
-     (list (cadr entry) (caddr entry))))
+     (list (cadr entry)
+          ;; Don't use `caddr' here since macros within the `interactive'
+          ;; form won't be expanded.
+          (car (cddr entry)))))
   (setq method (gnus-copy-sequence method))
   (let (entry)
     (while (setq entry (memq (assq 'eval method) method))
@@ -2463,7 +2945,7 @@ group already exists:
   (interactive)
   (let ((name (gnus-group-prefixed-name "gnus-help" '(nndoc "gnus-help")))
        (file (nnheader-find-etc-directory "gnus-tut.txt" t)))
-    (if (gnus-gethash name gnus-newsrc-hashtb)
+    (if (gnus-group-entry name)
        (cond ((eq noerror nil)
               (error "Documentation group already exists"))
              ((eq noerror t)
@@ -2483,7 +2965,9 @@ group already exists:
   (gnus-group-position-point))
 
 (defun gnus-group-make-doc-group (file type)
-  "Create a group that uses a single file as the source."
+  "Create a group that uses a single file as the source.
+
+If called with a prefix argument, ask for the file type."
   (interactive
    (list (read-file-name "File name: ")
         (and current-prefix-arg 'ask)))
@@ -2492,7 +2976,7 @@ group already exists:
          char found)
       (while (not found)
        (message
-        "%sFile type (mbox, babyl, digest, forward, mmdf, guess) [mbdfag]: "
+        "%sFile type (mbox, babyl, digest, forward, mmdf, guess) [m, b, d, f, a, g]: "
         err)
        (setq found (cond ((= (setq char (read-char)) ?m) 'mbox)
                          ((= char ?b) 'babyl)
@@ -2503,15 +2987,18 @@ group already exists:
                          (t (setq err (format "%c unknown. " char))
                             nil))))
       (setq type found)))
-  (let* ((file (expand-file-name file))
-        (name (gnus-generate-new-group-name
+  (setq file (expand-file-name file))
+  (let* ((name (gnus-generate-new-group-name
                (gnus-group-prefixed-name
-                (file-name-nondirectory file) '(nndoc "")))))
+                (file-name-nondirectory file) '(nndoc ""))))
+        (method (list 'nndoc file
+                      (list 'nndoc-address file)
+                      (list 'nndoc-article-type (or type 'guess))))
+        (coding (gnus-group-name-charset method name)))
+    (setcar (cdr method) (mm-encode-coding-string file coding))
     (gnus-group-make-group
-     (gnus-group-real-name name)
-     (list 'nndoc file
-          (list 'nndoc-address file)
-          (list 'nndoc-article-type (or type 'guess))))))
+     (mm-encode-coding-string (gnus-group-real-name name) coding)
+     method nil nil t)))
 
 (defvar nnweb-type-definition)
 (defvar gnus-group-web-type-history nil)
@@ -2528,11 +3015,11 @@ If SOLID (the prefix), create a solid group."
                           (symbol-name (caar nnweb-type-definition))))
         (type
          (gnus-string-or
-          (completing-read
-           (format "Search engine type (default %s): " default-type)
-           (mapcar (lambda (elem) (list (symbol-name (car elem))))
+          (gnus-completing-read
+           "Search engine type"
+           (mapcar (lambda (elem) (symbol-name (car elem)))
                    nnweb-type-definition)
-           nil t nil 'gnus-group-web-type-history)
+           t nil 'gnus-group-web-type-history)
           default-type))
         (search
          (read-string
@@ -2545,16 +3032,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))))))
 
-(eval-when-compile (defvar nnrss-group-alist)
-                  (defun nnrss-discover-feed (arg))
-                  (defun nnrss-save-server-data (arg)))
+(defvar nnrss-group-alist)
+(eval-when-compile
+  (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"
@@ -2564,76 +3052,40 @@ If there is, use Gnus to create an nnrss group"
       (setq url (read-from-minibuffer "URL to Search for RSS: ")))
   (let ((feedinfo (nnrss-discover-feed url)))
     (if feedinfo
-       (let ((title (read-from-minibuffer "Title: "
-                                          (cdr (assoc 'title
-                                                      feedinfo))))
-             (desc  (read-from-minibuffer "Description: "
-                                          (cdr (assoc 'description
-                                                      feedinfo))))
-             (href (cdr (assoc 'href feedinfo))))
-         (push (list title href desc)
-               nnrss-group-alist)
-         (gnus-group-unsubscribe-group
-          (concat "nnrss:" title))
+       (let* ((title (gnus-newsgroup-savable-name
+                      (read-from-minibuffer "Title: "
+                                            (gnus-newsgroup-savable-name
+                                             (mapconcat
+                                              'identity
+                                              (split-string
+                                               (or (cdr (assoc 'title
+                                                               feedinfo))
+                                                   ""))
+                                              " ")))))
+              (desc  (read-from-minibuffer "Description: "
+                                           (mapconcat
+                                            'identity
+                                            (split-string
+                                             (or (cdr (assoc 'description
+                                                             feedinfo))
+                                                 ""))
+                                            " ")))
+              (href (cdr (assoc 'href feedinfo)))
+              (coding (gnus-group-name-charset '(nnrss "") title)))
+         (when coding
+           ;; Unify non-ASCII text.
+           (setq title (mm-decode-coding-string
+                        (mm-encode-coding-string title coding)
+                        coding)))
+         (gnus-group-make-group title '(nnrss ""))
+         (push (list title href desc) nnrss-group-alist)
          (nnrss-save-server-data nil))
       (error "No feeds found for %s" url))))
 
-(defvar nnwarchive-type-definition)
-(defvar gnus-group-warchive-type-history nil)
-(defvar gnus-group-warchive-login-history nil)
-(defvar gnus-group-warchive-address-history nil)
-
-(defun gnus-group-make-warchive-group ()
-  "Create a nnwarchive group."
-  (interactive)
-  (require 'nnwarchive)
-  (let* ((group (gnus-read-group "Group name: "))
-        (default-type (or (car gnus-group-warchive-type-history)
-                          (symbol-name (caar nnwarchive-type-definition))))
-        (type
-         (gnus-string-or
-          (completing-read
-           (format "Warchive type (default %s): " default-type)
-           (mapcar (lambda (elem) (list (symbol-name (car elem))))
-                   nnwarchive-type-definition)
-           nil t nil 'gnus-group-warchive-type-history)
-          default-type))
-        (address (read-string "Warchive address: "
-                              nil 'gnus-group-warchive-address-history))
-        (default-login (or (car gnus-group-warchive-login-history)
-                           user-mail-address))
-        (login
-         (gnus-string-or
-          (read-string
-           (format "Warchive login (default %s): " user-mail-address)
-           default-login 'gnus-group-warchive-login-history)
-          user-mail-address))
-        (method
-         `(nnwarchive ,address
-                      (nnwarchive-type ,(intern type))
-                      (nnwarchive-login ,login))))
-    (gnus-group-make-group group method)))
-
-(defun gnus-group-make-archive-group (&optional all)
-  "Create the (ding) Gnus archive group of the most recent articles.
-Given a prefix, create a full group."
-  (interactive "P")
-  (let ((group (gnus-group-prefixed-name
-               (if all "ding.archives" "ding.recent") '(nndir ""))))
-    (when (gnus-gethash group gnus-newsrc-hashtb)
-      (error "Archive group already exists"))
-    (gnus-group-make-group
-     (gnus-group-real-name group)
-     (list 'nndir (if all "hpc" "edu")
-          (list 'nndir-directory
-                (if all gnus-group-archive-directory
-                  gnus-group-recent-archive-directory))))
-    (gnus-group-add-parameter group (cons 'to-address "ding@gnus.org"))))
-
 (defun gnus-group-make-directory-group (dir)
   "Create an nndir group.
 The user will be prompted for a directory.  The contents of this
-directory will be used as a newsgroup. The directory should contain
+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: ")))
@@ -2644,7 +3096,7 @@ mail messages or news articles in files that have numeric names."
   (let ((ext "")
        (i 0)
        group)
-    (while (or (not group) (gnus-gethash group gnus-newsrc-hashtb))
+    (while (or (not group) (gnus-group-entry group))
       (setq group
            (gnus-group-prefixed-name
             (expand-file-name ext dir)
@@ -2654,45 +3106,12 @@ mail messages or news articles in files that have numeric names."
      (gnus-group-real-name group)
      (list 'nndir (gnus-group-real-name group) (list 'nndir-directory dir)))))
 
-(eval-when-compile (defvar nnkiboze-score-file))
-(defun gnus-group-make-kiboze-group (group address scores)
-  "Create an nnkiboze group.
-The user will be prompted for a name, a regexp to match groups, and
-score file entries for articles to include in the group."
-  (interactive
-   (list
-    (read-string "nnkiboze group name: ")
-    (read-string "Source groups (regexp): ")
-    (let ((headers (mapcar (lambda (group) (list group))
-                          '("subject" "from" "number" "date" "message-id"
-                            "references" "chars" "lines" "xref"
-                            "followup" "all" "body" "head")))
-         scores header regexp regexps)
-      (while (not (equal "" (setq header (completing-read
-                                         "Match on header: " headers nil t))))
-       (setq regexps nil)
-       (while (not (equal "" (setq regexp (read-string
-                                           (format "Match on %s (regexp): "
-                                                   header)))))
-         (push (list regexp nil nil 'r) regexps))
-       (push (cons header regexps) scores))
-      scores)))
-  (gnus-group-make-group group "nnkiboze" address)
-  (let* ((nnkiboze-current-group group)
-        (score-file (car (nnkiboze-score-file "")))
-        (score-dir (file-name-directory score-file)))
-    (unless (file-exists-p score-dir)
-      (make-directory score-dir))
-    (with-temp-file score-file
-      (let (emacs-lisp-mode-hook)
-       (pp scores (current-buffer))))))
-
 (defun gnus-group-add-to-virtual (n vgroup)
   "Add the current group to a virtual group."
   (interactive
    (list current-prefix-arg
-        (completing-read "Add to virtual group: " gnus-newsrc-hashtb nil t
-                         "nnvirtual:")))
+        (gnus-group-completing-read "Add to virtual group"
+                                     nil t "nnvirtual:")))
   (unless (eq (car (gnus-find-method-for-group vgroup)) 'nnvirtual)
     (error "%s is not an nnvirtual group" vgroup))
   (gnus-close-group vgroup)
@@ -2714,7 +3133,7 @@ score file entries for articles to include in the group."
   (let* ((method (list 'nnvirtual "^$"))
         (pgroup (gnus-group-prefixed-name group method)))
     ;; Check whether it exists already.
-    (when (gnus-gethash pgroup gnus-newsrc-hashtb)
+    (when (gnus-group-entry pgroup)
       (error "Group %s already exists" pgroup))
     ;; Subscribe the new group after the group on the current line.
     (gnus-subscribe-group pgroup (gnus-group-group-name) method)
@@ -2737,22 +3156,17 @@ score file entries for articles to include in the group."
                       'summary 'group)))
       (error "Couldn't enter %s" dir))))
 
-(eval-and-compile
-  (autoload 'nnimap-expunge "nnimap")
-  (autoload 'nnimap-acl-get "nnimap")
-  (autoload 'nnimap-acl-edit "nnimap"))
-
-(defun gnus-group-nnimap-expunge (group)
+(defun gnus-group-expunge-group (group)
   "Expunge deleted articles in current nnimap GROUP."
   (interactive (list (gnus-group-group-name)))
-  (let ((mailbox (gnus-group-real-name group)) method)
-    (unless group
-      (error "No group on current line"))
-    (unless (gnus-get-info group)
-      (error "Killed group; can't be edited"))
-    (unless (eq 'nnimap (car (setq method (gnus-find-method-for-group group))))
-      (error "%s is not an nnimap group" group))
-    (nnimap-expunge mailbox (cadr method))))
+  (let ((method (gnus-find-method-for-group group)))
+    (if (not (gnus-check-backend-function
+             'request-expunge-group (car method)))
+       (error "%s does not support expunging" (car method))
+      (gnus-request-expunge-group group method))))
+
+(autoload 'nnimap-acl-get "nnimap")
+(autoload 'nnimap-acl-edit "nnimap")
 
 (defun gnus-group-nnimap-edit-acl (group)
   "Edit the Access Control List of current nnimap GROUP."
@@ -2886,7 +3300,7 @@ If REVERSE, sort in reverse order."
   (let (entries infos)
     ;; First find all the group entries for these groups.
     (while groups
-      (push (nthcdr 2 (gnus-gethash (pop groups) gnus-newsrc-hashtb))
+      (push (nthcdr 2 (gnus-group-entry (pop groups)))
            entries))
     ;; Then sort the infos.
     (setq infos
@@ -2967,8 +3381,8 @@ sort in reverse order."
 
 (defun gnus-group-sort-by-unread (info1 info2)
   "Sort by number of unread articles."
-  (let ((n1 (car (gnus-gethash (gnus-info-group info1) gnus-newsrc-hashtb)))
-       (n2 (car (gnus-gethash (gnus-info-group info2) gnus-newsrc-hashtb))))
+  (let ((n1 (gnus-group-unread (gnus-info-group info1)))
+       (n2 (gnus-group-unread (gnus-info-group info2))))
     (< (or (and (numberp n1) n1) 0)
        (or (and (numberp n2) n2) 0))))
 
@@ -3007,7 +3421,8 @@ sort in reverse order."
 ;;; Clearing data
 
 (defun gnus-group-clear-data (&optional arg)
-  "Clear all marks and read ranges from the current group."
+  "Clear all marks and read ranges from the current group.
+Obeys the process/prefix convention."
   (interactive "P")
   (gnus-group-iterate arg
     (lambda (group)
@@ -3077,7 +3492,7 @@ up is returned."
                   "Do you really want to mark all articles in %s as read? "
                 "Mark all unread articles in %s as read? ")
               (if (= (length groups) 1)
-                  (car groups)
+                  (gnus-group-decoded-name (car groups))
                 (format "these %d groups" (length groups)))))))
        n
       (while (setq group (pop groups))
@@ -3087,13 +3502,15 @@ up is returned."
          (when (eq 'nnvirtual (car method))
            (nnvirtual-catchup-group
             (gnus-group-real-name group) (nth 1 method) all)))
-       (if (>= (gnus-group-level group) gnus-level-zombie)
-           (gnus-message 2 "Dead groups can't be caught up")
-         (if (prog1
-                 (gnus-group-goto-group group)
-               (gnus-group-catchup group all))
-             (gnus-group-update-group-line)
-           (setq ret (1+ ret)))))
+       (cond
+        ((>= (gnus-group-level group) gnus-level-zombie)
+         (gnus-message 2 "Dead groups can't be caught up"))
+        ((prog1
+             (gnus-group-goto-group group)
+           (gnus-group-catchup group all))
+         (gnus-group-update-group-line))
+        (t
+         (setq ret (1+ ret)))))
       (gnus-group-next-unread-group 1)
       ret)))
 
@@ -3108,10 +3525,10 @@ Cross references (Xref: header) of articles are ignored."
 If ALL is non-nil, all articles are marked as read.
 The return value is the number of articles that were marked as read,
 or nil if no action could be taken."
-  (let* ((entry (gnus-gethash group gnus-newsrc-hashtb))
+  (let* ((entry (gnus-group-entry group))
         (num (car entry))
-        (marks (nth 3 (nth 2 entry)))
-        (unread (gnus-list-of-unread-articles group)))
+        (marks (gnus-info-marks (nth 2 entry)))
+        (unread (gnus-sequence-of-unread-articles group)))
     ;; Remove entries for this group.
     (nnmail-purge-split-history (gnus-group-real-name group))
     ;; Do the updating only if the newsgroup isn't killed.
@@ -3124,16 +3541,19 @@ or nil if no action could be taken."
                                                 'del '(tick))
                                           (list (cdr (assq 'dormant marks))
                                                 'del '(dormant))))
-       (setq unread (gnus-uncompress-range
-                     (gnus-range-add (gnus-range-add
-                                      unread (cdr (assq 'dormant marks)))
-                                     (cdr (assq 'tick marks)))))
+       (setq unread (gnus-range-add (gnus-range-add
+                                      unread (cdr (assq 'dormant marks)))
+                                     (cdr (assq 'tick marks))))
        (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)
-       (gnus-add-marked-articles group 'expire unread)
-       (gnus-request-set-mark group (list (list unread 'add '(expire)))))
+        (gnus-range-map
+        (lambda (article)
+          (gnus-add-marked-articles group 'expire (list article))
+          (gnus-request-set-mark group (list (list (list article)
+                                                   'add '(expire)))))
+        unread))
       (let ((gnus-newsgroup-name group))
        (gnus-run-hooks 'gnus-group-catchup-group-hook))
       num)))
@@ -3154,7 +3574,8 @@ Uses the process/prefix convention."
 
 (defun gnus-group-expire-articles-1 (group)
   (when (gnus-check-backend-function 'request-expire-articles group)
-    (gnus-message 6 "Expiring articles in %s..." group)
+    (gnus-message 6 "Expiring articles in %s..."
+                 (gnus-group-decoded-name group))
     (let* ((info (gnus-get-info group))
           (expirable (if (gnus-group-total-expirable-p group)
                          (cons nil (gnus-list-of-read-articles group))
@@ -3179,7 +3600,8 @@ Uses the process/prefix convention."
            (gnus-request-expire-articles
             (gnus-uncompress-sequence (cdr expirable)) group))))
        (gnus-close-group group))
-      (gnus-message 6 "Expiring articles in %s...done" group)
+      (gnus-message 6 "Expiring articles in %s...done"
+                   (gnus-group-decoded-name group))
       ;; Return the list of un-expired articles.
       (cdr expirable))))
 
@@ -3199,27 +3621,29 @@ Uses the process/prefix convention."
   (interactive
    (list
     current-prefix-arg
-    (string-to-int
-     (let ((s (read-string
-              (format "Level (default %s): "
-                      (or (gnus-group-group-level)
-                          gnus-level-default-subscribed)))))
-       (if (string-match "^\\s-*$" s)
-          (int-to-string (or (gnus-group-group-level)
-                             gnus-level-default-subscribed))
-        s)))))
+    (progn
+      (unless (gnus-group-process-prefix current-prefix-arg)
+       (error "No group on the current line"))
+      (string-to-number
+       (let ((s (read-string
+                (format "Level (default %s): "
+                        (or (gnus-group-group-level)
+                            gnus-level-default-subscribed)))))
+        (if (string-match "^\\s-*$" s)
+            (int-to-string (or (gnus-group-group-level)
+                               gnus-level-default-subscribed))
+          s))))))
   (unless (and (>= level 1) (<= level gnus-level-killed))
     (error "Invalid level: %d" level))
-  (let ((groups (gnus-group-process-prefix n))
-       group)
-    (while (setq group (pop groups))
-      (gnus-group-remove-mark group)
-      (gnus-message 6 "Changed level of %s from %d to %d"
-                   group (or (gnus-group-group-level) gnus-level-killed)
-                   level)
-      (gnus-group-change-level
-       group level (or (gnus-group-group-level) gnus-level-killed))
-      (gnus-group-update-group-line)))
+  (dolist (group (gnus-group-process-prefix n))
+    (gnus-group-remove-mark group)
+    (gnus-message 6 "Changed level of %s from %d to %d"
+                 (gnus-group-decoded-name group)
+                 (or (gnus-group-group-level) gnus-level-killed)
+                 level)
+    (gnus-group-change-level
+     group level (or (gnus-group-group-level) gnus-level-killed))
+    (gnus-group-update-group-line))
   (gnus-group-position-point))
 
 (defun gnus-group-unsubscribe (&optional n)
@@ -3257,13 +3681,9 @@ If given numerical prefix, toggle the N next groups."
   "Toggle subscription to GROUP.
 Killed newsgroups are subscribed.  If SILENT, don't try to update the
 group line."
-  (interactive
-   (list (completing-read
-         "Group: " gnus-active-hashtb nil
-         (gnus-read-active-file-p)
-         nil
-         'gnus-group-history)))
-  (let ((newsrc (gnus-gethash group gnus-newsrc-hashtb)))
+  (interactive (list (gnus-group-completing-read
+                     nil nil (gnus-read-active-file-p))))
+  (let ((newsrc (gnus-group-entry group)))
     (cond
      ((string-match "^[ \t]*$" group)
       (error "Empty group name"))
@@ -3287,7 +3707,7 @@ group line."
                gnus-level-zombie)
           gnus-level-killed)
        (when (gnus-group-group-name)
-        (gnus-gethash (gnus-group-group-name) gnus-newsrc-hashtb)))
+        (gnus-group-entry (gnus-group-group-name))))
       (unless silent
        (gnus-group-update-group group)))
      (t (error "No such newsgroup: %s" group)))
@@ -3295,7 +3715,7 @@ group line."
 
 (defun gnus-group-transpose-groups (n)
   "Move the current newsgroup up N places.
-If given a negative prefix, move down instead. The difference between
+If given a negative prefix, move down instead.  The difference between
 N and the number of steps taken is returned."
   (interactive "p")
   (unless (gnus-group-group-name)
@@ -3326,12 +3746,10 @@ The killed newsgroups can be yanked by using \\[gnus-group-yank-group]."
           (count-lines
            (progn
              (goto-char begin)
-             (beginning-of-line)
-             (point))
+             (point-at-bol))
            (progn
              (goto-char end)
-             (beginning-of-line)
-             (point))))))
+             (point-at-bol))))))
     (goto-char begin)
     (beginning-of-line)                        ;Important when LINES < 1
     (gnus-group-kill-group lines)))
@@ -3355,7 +3773,7 @@ of groups killed."
          (setq level (gnus-group-group-level))
          (gnus-delete-line)
          (when (and (not discard)
-                    (setq entry (gnus-gethash group gnus-newsrc-hashtb)))
+                    (setq entry (gnus-group-entry group)))
            (gnus-undo-register
              `(progn
                 (gnus-group-goto-group ,(gnus-group-group-name))
@@ -3364,7 +3782,7 @@ of groups killed."
                  gnus-list-of-killed-groups))
          (gnus-group-change-level
           (if entry entry group) gnus-level-killed (if entry nil level))
-         (message "Killed group %s" group))
+         (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.
       (dolist (group (nreverse groups))
@@ -3378,7 +3796,7 @@ of groups killed."
          (funcall gnus-group-change-level-function
                   group gnus-level-killed 3))
        (cond
-        ((setq entry (gnus-gethash group gnus-newsrc-hashtb))
+        ((setq entry (gnus-group-entry group))
          (push (cons (car entry) (nth 2 entry))
                gnus-list-of-killed-groups)
          (setcdr (cdr entry) (cdddr entry)))
@@ -3411,7 +3829,7 @@ yanked) a list of yanked groups is returned."
       (setq prev (gnus-group-group-name))
       (gnus-group-change-level
        info (gnus-info-level (cdr info)) gnus-level-killed
-       (and prev (gnus-gethash prev gnus-newsrc-hashtb))
+       (and prev (gnus-group-entry prev))
        t)
       (gnus-group-insert-group-line-info group)
       (gnus-undo-register
@@ -3492,7 +3910,7 @@ entail asking the server for the groups."
   ;; First we make sure that we have really read the active file.
   (unless (gnus-read-active-file-p)
     (let ((gnus-read-active-file t)
-         (gnus-agent nil))             ; Trick the agent into ignoring the active file.
+         (gnus-agent gnus-plugged)); If we're actually plugged, store the active file in the agent.
       (gnus-read-active-file)))
   ;; Find all groups and sort them.
   (let ((groups
@@ -3546,27 +3964,13 @@ re-scanning.  If ARG is non-nil and not a number, this will force
     (unless gnus-slave
       (gnus-master-read-slave-newsrc))
 
-    ;; We might read in new NoCeM messages here.
-    (when (and gnus-use-nocem
-              (null arg))
-      (gnus-nocem-scan-groups))
-    ;; If ARG is not a number, then we read the active file.
-    (when (and arg (not (numberp arg)))
-      (let ((gnus-read-active-file t))
-       (gnus-read-active-file))
-      (setq arg nil)
-
-      ;; If the user wants it, we scan for new groups.
-      (when (eq gnus-check-new-newsgroups 'always)
-       (gnus-find-new-newsgroups)))
-
-    (setq arg (gnus-group-default-level arg t))
-    (if (and gnus-read-active-file (not arg))
-       (progn
-         (gnus-read-active-file)
-         (gnus-get-unread-articles arg))
-      (let ((gnus-read-active-file (if arg nil gnus-read-active-file)))
-       (gnus-get-unread-articles arg)))
+    (gnus-get-unread-articles arg)
+
+    ;; If the user wants it, we scan for new groups.
+    (when (eq gnus-check-new-newsgroups 'always)
+      (gnus-find-new-newsgroups))
+
+    (gnus-check-reasonable-setup)
     (gnus-run-hooks 'gnus-after-getting-new-news-hook)
     (gnus-group-list-groups (and (numberp arg)
                                 (max (car gnus-group-list-mode) arg)))))
@@ -3574,7 +3978,8 @@ re-scanning.  If ARG is non-nil and not a number, this will force
 (defun gnus-group-get-new-news-this-group (&optional n dont-scan)
   "Check for newly arrived news in the current group (and the N-1 next groups).
 The difference between N and the number of newsgroup checked is returned.
-If N is negative, this group and the N-1 previous groups will be checked."
+If N is negative, this group and the N-1 previous groups will be checked.
+If DONT-SCAN is non-nil, scan non-activated groups as well."
   (interactive "P")
   (let* ((groups (gnus-group-process-prefix n))
         (ret (if (numberp n) (- n (length groups)) 0))
@@ -3590,15 +3995,17 @@ If N is negative, this group and the N-1 previous groups will be checked."
       (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))
-         (progn
-           (gnus-get-unread-articles-in-group
-            (gnus-get-info group) (gnus-active group) t)
+      (if (gnus-activate-group group (if dont-scan nil 'scan) nil method)
+         (let ((info (gnus-get-info group))
+               (active (gnus-active group)))
+           (when info
+             (gnus-request-update-info info method))
+           (gnus-get-unread-articles-in-group info active)
            (unless (gnus-virtual-group-p group)
              (gnus-close-group group))
            (when gnus-agent
              (gnus-agent-save-group-info
-              method (gnus-group-real-name group) (gnus-active group)))
+              method (gnus-group-real-name group) active))
            (gnus-group-update-group group))
        (if (eq (gnus-server-status (gnus-find-method-for-group group))
                'denied)
@@ -3608,71 +4015,15 @@ If N is negative, this group and the N-1 previous groups will be checked."
       (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-faq (group &optional faq-dir)
-  "Fetch the FAQ for the current group.
-If given a prefix argument, prompt for the FAQ dir
-to use."
-  (interactive
-   (list
-    (gnus-group-group-name)
-    (when current-prefix-arg
-      (completing-read
-       "FAQ dir: " (and (listp gnus-group-faq-directory)
-                       (mapcar #'list
-                               gnus-group-faq-directory))))))
-  (unless group
-    (error "No group name given"))
-  (let ((dirs (or faq-dir gnus-group-faq-directory))
-       dir found file)
-    (unless (listp dirs)
-      (setq dirs (list dirs)))
-    (while (and (not found)
-               (setq dir (pop dirs)))
-      (let ((name (gnus-group-real-name group)))
-       (setq file (expand-file-name name dir)))
-      (if (not (file-exists-p file))
-         (gnus-message 1 "No such file: %s" file)
-       (let ((enable-local-variables nil))
-         (find-file file)
-         (setq found t))))))
-
-(defun gnus-group-fetch-charter (group)
-  "Fetch the charter for the current group.
-If given a prefix argument, prompt for a group."
-  (interactive
-   (list (or (when current-prefix-arg
-              (completing-read "Group: " gnus-active-hashtb))
-            (gnus-group-group-name)
-            gnus-newsgroup-name)))
-  (unless group
-    (error "No group name given"))
-  (require 'mm-url)
-  (condition-case nil (require 'url-http) (error nil))
-  (let ((name (mm-url-form-encode-xwfu (gnus-group-real-name group)))
-       url hierarchy)
-    (when (string-match "\\(^[^\\.]+\\)\\..*" name)
-      (setq hierarchy (match-string 1 name))
-      (if (and (setq url (cdr (assoc hierarchy gnus-group-charter-alist)))
-              (if (fboundp 'url-http-file-exists-p)
-                  (url-http-file-exists-p (eval url))
-                t))
-         (browse-url (eval url))
-       (setq url (concat "http://" hierarchy
-                         ".news-admin.org/charters/" name))
-       (if (and (fboundp 'url-http-file-exists-p)
-                (url-http-file-exists-p url))
-           (browse-url url)
-         (gnus-group-fetch-control group))))))
-
 (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
-              (completing-read "Group: " gnus-active-hashtb))
+              (gnus-group-completing-read))
             (gnus-group-group-name)
             gnus-newsgroup-name)))
   (unless group
@@ -3683,14 +4034,14 @@ If given a prefix argument, prompt for a group."
       (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 ".Z"))
+                             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 ".Z")))
+                                    hierarchy "/" name ".gz")))
                   (nndoc-article-type mbox)) t nil nil))))))
 
 (defun gnus-group-describe-group (force &optional group)
@@ -3710,7 +4061,7 @@ If given a prefix argument, prompt for a group."
                   (gnus-gethash mname gnus-description-hashtb))
              (setq desc (gnus-group-get-description group))
              (gnus-read-descriptions-file method))
-      (gnus-message 1
+      (gnus-message 1 "%s"
                    (or desc (gnus-gethash group gnus-description-hashtb)
                        "No description available")))))
 
@@ -3871,11 +4222,9 @@ If GROUP, edit that local kill file instead."
   (interactive "P")
   (setq gnus-current-kill-article article)
   (gnus-kill-file-edit-file group)
-  (gnus-message
-   6
-   (substitute-command-keys
-    (format "Editing a %s kill file (Type \\[gnus-kill-file-exit] to exit)"
-           (if group "local" "global")))))
+  (gnus-message 6 "Editing a %s kill file (Type %s to exit)"
+               (if group "local" "global")
+               (substitute-command-keys "\\[gnus-kill-file-exit]")))
 
 (defun gnus-group-edit-local-kill (article group)
   "Edit a local kill file."
@@ -3887,6 +4236,8 @@ If GROUP, edit that local kill file instead."
   (interactive)
   (gnus-save-newsrc-file))
 
+(defvar gnus-backlog-articles)
+
 (defun gnus-group-suspend ()
   "Suspend the current Gnus session.
 In fact, cleanup buffers except for group mode buffer.
@@ -3896,14 +4247,13 @@ The hook `gnus-suspend-gnus-hook' is called before actually suspending."
   (gnus-offer-save-summaries)
   ;; Kill Gnus buffers except for group mode buffer.
   (let ((group-buf (get-buffer gnus-group-buffer)))
-    (mapcar (lambda (buf)
-             (unless (or (member buf (list group-buf gnus-dribble-buffer))
-                         (progn
-                           (save-excursion
-                             (set-buffer buf)
-                             (eq major-mode 'message-mode))))
-               (gnus-kill-buffer buf)))
-           (gnus-buffers))
+    (dolist (buf (gnus-buffers))
+      (unless (or (eq buf group-buf)
+                 (eq buf gnus-dribble-buffer)
+                 (with-current-buffer buf
+                   (eq major-mode 'message-mode)))
+       (gnus-kill-buffer buf)))
+    (setq gnus-backlog-articles nil)
     (gnus-kill-gnus-frames)
     (when group-buf
       (bury-buffer group-buf)
@@ -3951,8 +4301,7 @@ The hook `gnus-exit-gnus-hook' is called before actually exiting."
     (gnus-run-hooks 'gnus-exit-gnus-hook)
     (gnus-configure-windows 'group t)
     (when (and (gnus-buffer-live-p gnus-dribble-buffer)
-              (not (zerop (save-excursion
-                           (set-buffer gnus-dribble-buffer)
+              (not (zerop (with-current-buffer gnus-dribble-buffer
                            (buffer-size)))))
       (gnus-dribble-enter
        ";;; Gnus was exited on purpose without saving the .newsrc files."))
@@ -3966,7 +4315,7 @@ The hook `gnus-exit-gnus-hook' is called before actually exiting."
 (defun gnus-group-describe-briefly ()
   "Give a one line description of the group mode commands."
   (interactive)
-  (gnus-message 7 (substitute-command-keys "\\<gnus-group-mode-map>\\[gnus-group-read-group]:Select  \\[gnus-group-next-unread-group]:Forward  \\[gnus-group-prev-unread-group]:Backward  \\[gnus-group-exit]:Exit  \\[gnus-info-find-node]:Run Info  \\[gnus-group-describe-briefly]:This help")))
+  (gnus-message 7 "%s" (substitute-command-keys "\\<gnus-group-mode-map>\\[gnus-group-read-group]:Select  \\[gnus-group-next-unread-group]:Forward  \\[gnus-group-prev-unread-group]:Backward  \\[gnus-group-exit]:Exit  \\[gnus-info-find-node]:Run Info  \\[gnus-group-describe-briefly]:This help")))
 
 (defun gnus-group-browse-foreign-server (method)
   "Browse a foreign news server.
@@ -3975,28 +4324,27 @@ If called interactively, this function will ask for a select method
 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 (completing-read
-                    "Which back end: "
-                    (append gnus-valid-select-methods gnus-server-alist)
-                    nil t (cons "nntp" 0) 'gnus-method-history)))
+   (list (let ((how (gnus-completing-read
+                    "Which back end"
+                    (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.
           (if (assoc how gnus-valid-select-methods)
               (list (intern how)
                     ;; Suggested by mapjph@bath.ac.uk.
-                    (completing-read
-                     "Address: "
-                     (mapcar (lambda (server) (list server))
-                             gnus-secondary-servers)))
+                    (gnus-completing-read
+                     "Address"
+                     gnus-secondary-servers))
             ;; We got a server name.
             how))))
   (gnus-browse-foreign-server method))
 
 (defun gnus-group-set-info (info &optional method-only-group part)
   (when (or info part)
-    (let* ((entry (gnus-gethash
-                  (or method-only-group (gnus-info-group info))
-                  gnus-newsrc-hashtb))
+    (let* ((entry (gnus-group-entry
+                  (or method-only-group (gnus-info-group info))))
           (part-info info)
           (info (if method-only-group (nth 2 entry) info))
           method)
@@ -4004,7 +4352,7 @@ and the second element is the address."
        (unless entry
          (error "Trying to change non-existent group %s" method-only-group))
        ;; We have received parts of the actual group info - either the
-       ;; select method or the group parameters.        We first check
+       ;; select method or the group parameters.  We first check
        ;; whether we have to extend the info, and if so, do that.
        (let ((len (length info))
              (total (if (eq part 'method) 5 6)))
@@ -4015,13 +4363,11 @@ and the second element is the address."
          (setcar (nthcdr (1- total) info) part-info)))
       (unless entry
        ;; This is a new group, so we just create it.
-       (save-excursion
-         (set-buffer gnus-group-buffer)
+       (with-current-buffer gnus-group-buffer
          (setq method (gnus-info-method info))
          (when (gnus-server-equal method "native")
            (setq method nil))
-         (save-excursion
-           (set-buffer gnus-group-buffer)
+         (with-current-buffer gnus-group-buffer
            (if method
                ;; It's a foreign group...
                (gnus-group-make-group
@@ -4029,15 +4375,15 @@ and the second element is the address."
                 (if (stringp method) method
                   (prin1-to-string (car method)))
                 (and (consp method)
-                     (nth 1 (gnus-info-method info))))
+                     (nth 1 (gnus-info-method info)))
+                nil t)
              ;; It's a native group.
-             (gnus-group-make-group (gnus-info-group info))))
+             (gnus-group-make-group (gnus-info-group info) nil nil nil t)))
          (gnus-message 6 "Note: New group created")
          (setq entry
-               (gnus-gethash (gnus-group-prefixed-name
-                              (gnus-group-real-name (gnus-info-group info))
-                              (or (gnus-info-method info) gnus-select-method))
-                             gnus-newsrc-hashtb))))
+               (gnus-group-entry (gnus-group-prefixed-name
+                                  (gnus-group-real-name (gnus-info-group info))
+                                  (or (gnus-info-method info) gnus-select-method))))))
       ;; Whether it was a new group or not, we now have the entry, so we
       ;; can do the update.
       (if entry
@@ -4085,8 +4431,7 @@ and the second element is the address."
   "Mark ARTICLE in GROUP with MARK, whether the group is displayed or not."
   (let ((buffer (gnus-summary-buffer-name group)))
     (if (gnus-buffer-live-p buffer)
-       (save-excursion
-         (set-buffer (get-buffer buffer))
+       (with-current-buffer (get-buffer buffer)
          (gnus-summary-add-mark article mark))
       (gnus-add-marked-articles group (cdr (assq mark gnus-article-mark-lists))
                                (list article)))))
@@ -4250,6 +4595,40 @@ This command may read the active file."
        (gnus-add-marked-articles
         group 'expire (list article))))))
 
+
+;;;
+;;; Group compaction. -- dvl
+;;;
+
+(defun gnus-group-compact-group (group)
+  "Compact the current group.
+Compaction means removing gaps between article numbers.  Hence, this
+operation is only meaningful for back ends using one file per article
+\(e.g. nnml).
+
+Note: currently only implemented in nnml."
+  (interactive (list (gnus-group-group-name)))
+  (unless group
+    (error "No group to compact"))
+  (unless (gnus-check-backend-function 'request-compact-group group)
+    (error "This back end does not support group compaction"))
+  (let ((group-decoded (gnus-group-decoded-name group)))
+    (gnus-message 6 "\
+Compacting group %s... (this may take a long time)"
+                 group-decoded)
+    (prog1
+       (if (not (gnus-request-compact-group group))
+           (gnus-error 3 "Couldn't compact group %s" group-decoded)
+         (gnus-message 6 "Compacting group %s...done" group-decoded)
+         t)
+      ;; Invalidate the "original article" buffer which might be out of date.
+      ;; #### NOTE: Yes, this might be a bit rude, but since compaction
+      ;; #### will not happen very often, I think this is acceptable.
+      (let ((original (get-buffer gnus-original-article-buffer)))
+       (and original (gnus-kill-buffer original)))
+      ;; Update the group line to reflect new information (art number etc).
+      (gnus-group-update-group-line))))
+
 (provide 'gnus-group)
 
 ;;; gnus-group.el ends here