Import No Gnus v0.4.
[elisp/gnus.git-] / lisp / gnus-group.el
index d4bfd54..b601cab 100644 (file)
@@ -1,5 +1,7 @@
 ;;; gnus-group.el --- group mode commands for Gnus
-;; Copyright (C) 1996,97,98 Free Software Foundation, Inc.
+
+;; Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+;;   2005, 2006 Free Software Foundation, Inc.
 
 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
 ;; Keywords: news
 
 ;; You should have received a copy of the GNU General Public License
 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
-;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-;; Boston, MA 02111-1307, USA.
+;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+;; Boston, MA 02110-1301, USA.
 
 ;;; Commentary:
 
 ;;; Code:
 
-(eval-when-compile (require 'cl))
+(eval-when-compile
+  (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)
+  (let ((features (cons 'gnus-group features)))
+    (require 'gnus-sum))
+  (unless (boundp 'gnus-cache-active-hashtb)
+    (defvar gnus-cache-active-hashtb nil)))
+
+(autoload 'gnus-agent-total-fetched-for "gnus-agent")
+(autoload 'gnus-cache-total-fetched-for "gnus-cache")
 
 (defcustom gnus-group-archive-directory
-  "*ftp@ftp.hpc.uh.edu:/pub/emacs/ding-list/"
+  "/ftp@ftp.hpc.uh.edu:/pub/emacs/ding-list/"
   "*The address of the (ding) archives."
   :group 'gnus-group-foreign
   :type 'directory)
 
 (defcustom gnus-group-recent-archive-directory
-  "*ftp@ftp.hpc.uh.edu:/pub/emacs/ding-list-recent/"
+  "/ftp@ftp.hpc.uh.edu:/pub/emacs/ding-list-recent/"
   "*The address of the most recent (ding) articles."
   :group 'gnus-group-foreign
   :type 'directory)
 
-(defcustom gnus-no-groups-message "No news is no 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)
@@ -89,7 +106,7 @@ unread articles in the groups.
 
 If nil, no groups are permanently visible."
   :group 'gnus-group-listing
-  :type 'regexp)
+  :type '(choice regexp (const nil)))
 
 (defcustom gnus-list-groups-with-ticked-articles t
   "*If non-nil, list groups that have only ticked articles.
@@ -115,24 +132,30 @@ This function will be called with group info entries as the arguments
 for the groups to be sorted.  Pre-made functions include
 `gnus-group-sort-by-alphabet', `gnus-group-sort-by-real-name',
 `gnus-group-sort-by-unread', `gnus-group-sort-by-level',
-`gnus-group-sort-by-score', `gnus-group-sort-by-method', and
-`gnus-group-sort-by-rank'.
+`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,
 the most significant sort function should be the last function in the
 list."
   :group 'gnus-group-listing
   :link '(custom-manual "(gnus)Sorting Groups")
-  :type '(radio (function-item gnus-group-sort-by-alphabet)
-               (function-item gnus-group-sort-by-real-name)
-               (function-item gnus-group-sort-by-unread)
-               (function-item gnus-group-sort-by-level)
-               (function-item gnus-group-sort-by-score)
-               (function-item gnus-group-sort-by-method)
-               (function-item gnus-group-sort-by-rank)
-               (function :tag "other" nil)))
-
-(defcustom gnus-group-line-format "%M\%S\%p\%P\%5y: %(%g%)%l\n"
+  :type '(repeat :value-to-internal (lambda (widget value)
+                                     (if (listp value) value (list value)))
+                :match (lambda (widget value)
+                         (or (symbolp value)
+                             (widget-editable-list-match widget value)))
+                (choice (function-item gnus-group-sort-by-alphabet)
+                        (function-item gnus-group-sort-by-real-name)
+                        (function-item gnus-group-sort-by-unread)
+                        (function-item gnus-group-sort-by-level)
+                        (function-item gnus-group-sort-by-score)
+                        (function-item gnus-group-sort-by-method)
+                        (function-item gnus-group-sort-by-server)
+                        (function-item gnus-group-sort-by-rank)
+                        (function :tag "other" nil))))
+
+(defcustom gnus-group-line-format "%M\%S\%p\%P\%5y:%B%(%g%)%O\n"
   "*Format of group lines.
 It works along the same lines as a normal formatting string,
 with some simple extensions.
@@ -145,30 +168,32 @@ with some simple extensions.
 %i    Number of ticked and dormant (integer)
 %T    Number of ticked articles (integer)
 %R    Number of read articles (integer)
+%U    Number of unseen articles (integer)
 %t    Estimated total number of articles (integer)
 %y    Number of unread, unticked articles (integer)
 %G    Group name (string)
 %g    Qualified group name (string)
+%c    Short (collapsed) group name.  See `gnus-group-uncollapsed-levels'.
+%C    Group comment (string)
 %D    Group description (string)
 %s    Select method (string)
 %o    Moderated group (char, \"m\")
 %p    Process mark (char)
+%B    Whether a summary buffer for the group is open (char, \"*\")
 %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 the
-      current header as argument.  The function should return a string, which
-      will be inserted into the buffer just like information from any other
-      group specifier.
-
-Text between %( and %) will be highlighted with `gnus-mouse-face' when
-the mouse point move inside the area.  There can only be one such area.
+      where X is the letter following %u.  The function will be passed a
+      single dummy parameter as argument.  The function should return a
+      string, which will be inserted into the buffer just like information
+      from any other group specifier.
 
 Note that this format specification is not always respected.  For
 reasons of efficiency, when listing killed groups, this specification
@@ -177,10 +202,14 @@ 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'."
+  :link '(custom-manual "(gnus)Formatting Variables")
   :group 'gnus-group-visual
   :type 'string)
 
@@ -195,11 +224,10 @@ with some simple extensions:
   :group 'gnus-group-visual
   :type 'string)
 
-(defcustom gnus-group-mode-hook nil
-  "Hook for Gnus group mode."
-  :group 'gnus-group-various
-  :options '(gnus-topic-mode)
-  :type 'hook)
+;; Extracted from gnus-xmas-redefine in order to preserve user settings
+(when (featurep 'xemacs)
+  (add-hook 'gnus-group-mode-hook 'gnus-xmas-group-menu-add)
+  (add-hook 'gnus-group-mode-hook 'gnus-xmas-setup-group-toolbar))
 
 (defcustom gnus-group-menu-hook nil
   "Hook run after the creation of the group mode menu."
@@ -261,14 +289,15 @@ variable."
   :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)
@@ -285,40 +314,52 @@ variable."
                       (sexp :tag "Method"))))
 
 (defcustom gnus-group-highlight
-  '(;; News.
-    ((and (= unread 0) (not mailp) (eq level 1)) .
-     gnus-group-news-1-empty-face)
-    ((and (not mailp) (eq level 1)) .
-     gnus-group-news-1-face)
-    ((and (= unread 0) (not mailp) (eq level 2)) .
-     gnus-group-news-2-empty-face)
-    ((and (not mailp) (eq level 2)) .
-     gnus-group-news-2-face)
-    ((and (= unread 0) (not mailp) (eq level 3)) .
-     gnus-group-news-3-empty-face)
-    ((and (not mailp) (eq level 3)) .
-     gnus-group-news-3-face)
-    ((and (= unread 0) (not mailp)) .
-     gnus-group-news-low-empty-face)
-    ((and (not mailp)) .
-     gnus-group-news-low-face)
-    ;; Mail.
+  '(;; Mail.
+    ((and mailp (= unread 0) (eq level 1)) .
+     gnus-group-mail-1-empty)
+    ((and mailp (eq level 1)) .
+     gnus-group-mail-1)
+    ((and mailp (= unread 0) (eq level 2)) .
+     gnus-group-mail-2-empty)
+    ((and mailp (eq level 2)) .
+     gnus-group-mail-2)
+    ((and mailp (= unread 0) (eq level 3)) .
+     gnus-group-mail-3-empty)
+    ((and mailp (eq level 3)) .
+     gnus-group-mail-3)
+    ((and mailp (= unread 0)) .
+     gnus-group-mail-low-empty)
+    ((and mailp) .
+     gnus-group-mail-low)
+    ;; News.
     ((and (= unread 0) (eq level 1)) .
-     gnus-group-mail-1-empty-face)
-    ((eq level 1) .
-     gnus-group-mail-1-face)
+     gnus-group-news-1-empty)
+    ((and (eq level 1)) .
+     gnus-group-news-1)
     ((and (= unread 0) (eq level 2)) .
-     gnus-group-mail-2-empty-face)
-    ((eq level 2) .
-     gnus-group-mail-2-face)
+     gnus-group-news-2-empty)
+    ((and (eq level 2)) .
+     gnus-group-news-2)
     ((and (= unread 0) (eq level 3)) .
-     gnus-group-mail-3-empty-face)
-    ((eq level 3) .
-     gnus-group-mail-3-face)
-    ((= unread 0) .
-     gnus-group-mail-low-empty-face)
+     gnus-group-news-3-empty)
+    ((and (eq level 3)) .
+     gnus-group-news-3)
+    ((and (= unread 0) (eq level 4)) .
+     gnus-group-news-4-empty)
+    ((and (eq level 4)) .
+     gnus-group-news-4)
+    ((and (= unread 0) (eq level 5)) .
+     gnus-group-news-5-empty)
+    ((and (eq level 5)) .
+     gnus-group-news-5)
+    ((and (= unread 0) (eq level 6)) .
+     gnus-group-news-6-empty)
+    ((and (eq level 6)) .
+     gnus-group-news-6)
+    ((and (= unread 0)) .
+     gnus-group-news-low-empty)
     (t .
-     gnus-group-mail-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
@@ -347,8 +388,85 @@ ticked: The number of ticked articles."
   :group 'gnus-group-visual
   :type 'character)
 
+(defgroup gnus-group-icons nil
+  "Add Icons to your group buffer."
+  :group 'gnus-group-visual)
+
+(defcustom gnus-group-icon-list
+  nil
+  "*Controls the insertion of icons into group buffer lines.
+
+Below is a list of `Form'/`File' pairs.  When deciding how a
+particular group line should be displayed, each form is evaluated.
+The icon from the file field after the first true form is used.  You
+can change how those group lines are displayed by editing the file
+field.  The File will either be found in the
+`gnus-group-glyph-directory' or by designating absolute name of the
+file.
+
+It is also possible to change and add form fields, but currently that
+requires an understanding of Lisp expressions.  Hopefully this will
+change in a future release.  For now, you can use the following
+variables in the Lisp expression:
+
+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)))
+
+(defcustom gnus-group-name-charset-method-alist nil
+  "Alist of method and the charset for group names.
+
+For example:
+    (((nntp \"news.com.cn\") . cn-gb-2312))"
+  :version "21.1"
+  :group 'gnus-charset
+  :type '(repeat (cons (sexp :tag "Method") (symbol :tag "Charset"))))
+
+(defcustom gnus-group-name-charset-group-alist
+  (if (or (and (fboundp 'find-coding-system) (find-coding-system 'utf-8))
+         (mm-coding-system-p 'utf-8))
+      '((".*" . utf-8))
+    nil)
+  "Alist of group regexp and the charset for group names.
+
+For example:
+    ((\"\\.com\\.cn:\" . cn-gb-2312))"
+  :group 'gnus-charset
+  :type '(repeat (cons (regexp :tag "Group") (symbol :tag "Charset"))))
+
+(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 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)
+                (repeat (cons (integer :tag "Argument")
+                              (string :tag "Prompt string")))))
+
+(defvar gnus-group-listing-limit 1000
+  "*A limit of the number of groups when listing.
+If the number of groups is larger than the limit, list them in a
+simple manner.")
+
 ;;; Internal variables
 
+(defvar gnus-group-is-exiting-p nil)
+(defvar gnus-group-is-exiting-without-update-p nil)
 (defvar gnus-group-sort-alist-function 'gnus-group-sort-flat
   "Function for sorting the group buffer.")
 
@@ -375,27 +493,41 @@ ticked: The number of ticked articles."
                   (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)
     (?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)
-    (?l gnus-tmp-grouplens ?s)
+    (?E gnus-tmp-group-icon ?s)
+    (?B gnus-tmp-summary-live ?c)
     (?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)
@@ -413,162 +545,227 @@ ticked: The number of ticked articles."
 
 (defvar gnus-group-list-mode nil)
 
+
+(defvar gnus-group-icon-cache nil)
+
+(defvar gnus-group-listed-groups nil)
+(defvar gnus-group-list-option nil)
+
 ;;;
 ;;; Gnus group mode
 ;;;
 
 (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
-    [(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
-    "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-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
-    "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
-    "c" gnus-group-customize
-    "\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)
-
-  (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)
-
-  (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)
-
-  (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)
-    "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
+  "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
+  "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-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
+  "e" gnus-score-edit-all-score)
+
+(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)
+
+(defun gnus-topic-mode-p ()
+  "Return non-nil in `gnus-topic-mode'."
+  (and (boundp 'gnus-topic-mode)
+       (symbol-value 'gnus-topic-mode)))
 
 (defun gnus-group-make-menu-bar ()
   (gnus-turn-off-edit-menu 'group)
@@ -576,36 +773,79 @@ ticked: The number of ticked articles."
 
     (easy-menu-define
      gnus-group-reading-menu gnus-group-mode-map ""
-     '("Group"
-       ["Read" gnus-group-read-group (gnus-group-group-name)]
-       ["Select" gnus-group-select-group (gnus-group-group-name)]
+     `("Group"
+       ["Read" gnus-group-read-group
+       :included (not (gnus-topic-mode-p))
+       :active (gnus-group-group-name)]
+       ["Read " gnus-topic-read-group
+       :included (gnus-topic-mode-p)]
+       ["Select" gnus-group-select-group
+       :included (not (gnus-topic-mode-p))
+       :active (gnus-group-group-name)]
+       ["Select " gnus-topic-select-group
+       :included (gnus-topic-mode-p)]
        ["See old articles" (gnus-group-select-group 'all)
        :keys "C-u SPC" :active (gnus-group-group-name)]
-       ["Catch up" gnus-group-catchup-current (gnus-group-group-name)]
+       ["Catch up" gnus-group-catchup-current
+       :included (not (gnus-topic-mode-p))
+       :active (gnus-group-group-name)
+       ,@(if (featurep 'xemacs) nil
+           '(:help "Mark unread articles in the current group as read"))]
+       ["Catch up " gnus-topic-catchup-articles
+       :included (gnus-topic-mode-p)
+       ,@(if (featurep 'xemacs) nil
+           '(:help "Mark unread articles in the current group or topic as read"))]
        ["Catch up all articles" gnus-group-catchup-current-all
        (gnus-group-group-name)]
        ["Check for new articles" gnus-group-get-new-news-this-group
-       (gnus-group-group-name)]
+       :included (not (gnus-topic-mode-p))
+       :active (gnus-group-group-name)
+       ,@(if (featurep 'xemacs) nil
+           '(:help "Check for new messages in current group"))]
+       ["Check for new articles " gnus-topic-get-new-news-this-topic
+       :included (gnus-topic-mode-p)
+       ,@(if (featurep 'xemacs) nil
+           '(:help "Check for new messages in current group or topic"))]
        ["Toggle subscription" gnus-group-unsubscribe-current-group
        (gnus-group-group-name)]
-       ["Kill" gnus-group-kill-group (gnus-group-group-name)]
+       ["Kill" gnus-group-kill-group :active (gnus-group-group-name)
+       ,@(if (featurep 'xemacs) nil
+             '(:help "Kill (remove) current group"))]
        ["Yank" gnus-group-yank-group gnus-list-of-killed-groups]
-       ["Describe" gnus-group-describe-group (gnus-group-group-name)]
+       ["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
+           '(:help "Display the archived control message for the current group"))]
        ;; Actually one should check, if any of the marked groups gives t for
        ;; (gnus-check-backend-function 'request-expire-articles ...)
        ["Expire articles" gnus-group-expire-articles
-       (or (and (gnus-group-group-name)
-                (gnus-check-backend-function
-                 'request-expire-articles
-                 (gnus-group-group-name))) gnus-group-marked)]
-       ["Set group level" gnus-group-set-current-level
+       :included (not (gnus-topic-mode-p))
+       :active (or (and (gnus-group-group-name)
+                        (gnus-check-backend-function
+                         'request-expire-articles
+                         (gnus-group-group-name))) gnus-group-marked)]
+       ["Expire articles " gnus-topic-expire-articles
+       :included (gnus-topic-mode-p)]
+       ["Set group level..." gnus-group-set-current-level
        (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
-        (gnus-group-group-name)]
+        :included (not (gnus-topic-mode-p))
+        :active (gnus-group-group-name)]
+       ["Parameters " gnus-topic-edit-parameters
+        :included (gnus-topic-mode-p)]
        ["Select method" gnus-group-edit-group-method
         (gnus-group-group-name)]
        ["Info" gnus-group-edit-group (gnus-group-group-name)]
@@ -626,7 +866,9 @@ ticked: The number of ticked articles."
        ["Group and description apropos..." gnus-group-description-apropos t]
        ["List groups matching..." gnus-group-list-matching t]
        ["List all groups matching..." gnus-group-list-all-matching t]
-       ["List active file" gnus-group-list-active t])
+       ["List active file" gnus-group-list-active t]
+       ["List groups with cached" gnus-group-list-cached t]
+       ["List groups with dormant" gnus-group-list-dormant t])
        ("Sort"
        ["Default sort" gnus-group-sort-groups t]
        ["Sort by method" gnus-group-sort-groups-by-method t]
@@ -634,22 +876,25 @@ ticked: The number of ticked articles."
        ["Sort by score" gnus-group-sort-groups-by-score t]
        ["Sort by level" gnus-group-sort-groups-by-level t]
        ["Sort by unread" gnus-group-sort-groups-by-unread t]
-       ["Sort by name" gnus-group-sort-groups-by-alphabet t])
+       ["Sort by name" gnus-group-sort-groups-by-alphabet t]
+       ["Sort by real name" gnus-group-sort-groups-by-real-name t])
        ("Sort process/prefixed"
        ["Default sort" gnus-group-sort-selected-groups
-        (or (not (boundp 'gnus-topic-mode)) (not gnus-topic-mode))]
+        (not (gnus-topic-mode-p))]
        ["Sort by method" gnus-group-sort-selected-groups-by-method
-        (or (not (boundp 'gnus-topic-mode)) (not gnus-topic-mode))]
+        (not (gnus-topic-mode-p))]
        ["Sort by rank" gnus-group-sort-selected-groups-by-rank
-        (or (not (boundp 'gnus-topic-mode)) (not gnus-topic-mode))]
+        (not (gnus-topic-mode-p))]
        ["Sort by score" gnus-group-sort-selected-groups-by-score
-        (or (not (boundp 'gnus-topic-mode)) (not gnus-topic-mode))]
+        (not (gnus-topic-mode-p))]
        ["Sort by level" gnus-group-sort-selected-groups-by-level
-        (or (not (boundp 'gnus-topic-mode)) (not gnus-topic-mode))]
+        (not (gnus-topic-mode-p))]
        ["Sort by unread" gnus-group-sort-selected-groups-by-unread
-        (or (not (boundp 'gnus-topic-mode)) (not gnus-topic-mode))]
+        (not (gnus-topic-mode-p))]
        ["Sort by name" gnus-group-sort-selected-groups-by-alphabet
-        (or (not (boundp 'gnus-topic-mode)) (not gnus-topic-mode))])
+        (not (gnus-topic-mode-p))]
+       ["Sort by real name" gnus-group-sort-selected-groups-by-real-name
+        (not (gnus-topic-mode-p))])
        ("Mark"
        ["Mark group" gnus-group-mark-group
         (and (gnus-group-group-name)
@@ -659,27 +904,30 @@ ticked: The number of ticked articles."
              (memq (gnus-group-group-name) gnus-group-marked))]
        ["Unmark all" gnus-group-unmark-all-groups gnus-group-marked]
        ["Mark regexp..." gnus-group-mark-regexp t]
-       ["Mark region" gnus-group-mark-region t]
+       ["Mark region" gnus-group-mark-region :active (gnus-mark-active-p)]
        ["Mark buffer" gnus-group-mark-buffer t]
        ["Execute command" gnus-group-universal-argument
         (or gnus-group-marked (gnus-group-group-name))])
        ("Subscribe"
-       ["Subscribe to a group" gnus-group-unsubscribe-group t]
-       ["Kill all newsgroups in region" gnus-group-kill-region t]
+       ["Subscribe to a group..." gnus-group-unsubscribe-group t]
+       ["Kill all newsgroups in region" gnus-group-kill-region
+        :active (gnus-mark-active-p)]
        ["Kill all zombie groups" gnus-group-kill-all-zombies
         gnus-zombie-list]
        ["Kill all groups on level..." gnus-group-kill-level t])
        ("Foreign groups"
-       ["Make a foreign group" gnus-group-make-group t]
-       ["Add a directory group" gnus-group-make-directory-group t]
+       ["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 virtual group" gnus-group-make-empty-virtual t]
-       ["Add a group to a virtual" gnus-group-add-to-virtual t]
-       ["Rename group" gnus-group-rename-group
+       ["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 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))]
        ["Delete group" gnus-group-delete-group
@@ -693,9 +941,12 @@ ticked: The number of ticked articles."
        ["Next unread same level" gnus-group-next-unread-group-same-level t]
        ["Previous unread same level"
         gnus-group-prev-unread-group-same-level t]
-       ["Jump to group" gnus-group-jump-to-group t]
+       ["Jump to group..." gnus-group-jump-to-group t]
        ["First unread group" gnus-group-first-unread-group t]
        ["Best unread group" gnus-group-best-unread-group t])
+       ("Sieve"
+       ["Generate" gnus-sieve-generate t]
+       ["Generate and update" gnus-sieve-update t])
        ["Delete bogus groups" gnus-group-check-bogus-groups t]
        ["Find new newsgroups" gnus-group-find-new-groups t]
        ["Transpose" gnus-group-transpose-groups
@@ -704,7 +955,7 @@ ticked: The number of ticked articles."
 
     (easy-menu-define
      gnus-group-misc-menu gnus-group-mode-map ""
-     '("Misc"
+     `("Gnus"
        ("SOUP"
        ["Pack replies" nnsoup-pack-replies (fboundp 'nnsoup-request-group)]
        ["Send replies" gnus-soup-send-replies
@@ -712,14 +963,21 @@ ticked: The number of ticked articles."
        ["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 bug report" gnus-bug t]
        ["Send a mail" gnus-group-mail t]
-       ["Post an article..." gnus-group-post-news t]
-       ["Check for new news" gnus-group-get-new-news t]
+       ["Send a message (mail or news)" gnus-group-post-news t]
+       ["Create a local message" gnus-group-news t]
+       ["Check for new news" gnus-group-get-new-news
+       ,@(if (featurep 'xemacs) '(t)
+           '(:help "Get newly arrived articles"))
+       ]
+       ["Send queued messages" gnus-delay-send-queue
+       ,@(if (featurep 'xemacs) '(t)
+           '(:help "Send all messages that are scheduled to be sent now"))
+       ]
        ["Activate all groups" gnus-activate-all-groups t]
        ["Restart Gnus" gnus-group-restart t]
        ["Read init file" gnus-group-read-init-file t]
-       ["Browse foreign server" gnus-group-browse-foreign-server t]
+       ["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]
@@ -730,11 +988,155 @@ ticked: The number of ticked articles."
        ["Read manual" gnus-info-find-node t]
        ["Flush score cache" gnus-score-flush-cache t]
        ["Toggle topics" gnus-topic-mode t]
-       ["Exit from Gnus" gnus-group-exit t]
+       ["Send a bug report" gnus-bug t]
+       ["Exit from Gnus" gnus-group-exit
+       ,@(if (featurep 'xemacs) '(t)
+           '(:help "Quit reading news"))]
        ["Exit without saving" gnus-group-quit t]))
 
     (gnus-run-hooks 'gnus-group-menu-hook)))
 
+
+(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.0" ;; 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 "connect" t
+                              :visible (and gnus-agent (not gnus-plugged)))
+    (gnus-agent-toggle-plugged "disconnect" t
+                              :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.0" ;; 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.0" ;; No Gnus
+  :initialize 'custom-initialize-default
+  :set 'gnus-group-tool-bar-update
+  :group 'gnus-group)
+
+;; FIXME: 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'.  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-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.0" ;; No Gnus
+  :initialize 'custom-initialize-default
+  :set 'gnus-group-tool-bar-update
+  :group 'gnus-group)
+
+(defvar image-load-path)
+
+(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.
 
@@ -753,30 +1155,33 @@ The following commands are available:
 
 \\{gnus-group-mode-map}"
   (interactive)
-  (when (gnus-visual-p 'group-menu 'menu)
-    (gnus-group-make-menu-bar))
   (kill-all-local-variables)
+  (when (gnus-visual-p 'group-menu 'menu)
+    (gnus-group-make-menu-bar)
+    (gnus-group-make-tool-bar))
   (gnus-simplify-mode-line)
   (setq major-mode 'gnus-group-mode)
   (setq mode-name "Group")
   (gnus-group-set-mode-line)
   (setq mode-line-process nil)
   (use-local-map gnus-group-mode-map)
-  (buffer-disable-undo (current-buffer))
+  (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)
-  (make-local-hook 'post-command-hook)
-  (add-hook 'post-command-hook 'gnus-clear-inboxes-moved nil t)
   (when gnus-use-undo
     (gnus-undo-mode 1))
-  (gnus-run-hooks 'gnus-group-mode-hook))
+  (when gnus-slave
+    (gnus-slave-mode))
+  (gnus-run-mode-hooks 'gnus-group-mode-hook))
 
 (defun gnus-update-group-mark-positions ()
   (save-excursion
     (let ((gnus-process-mark ?\200)
+         (gnus-group-update-hook nil)
          (gnus-group-marked '("dummy.group"))
          (gnus-active-hashtb (make-vector 10 0))
          (topic ""))
@@ -785,12 +1190,10 @@ 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)
+           (list (cons 'process (and (search-forward
+                                      (mm-string-as-multibyte "\200") nil t)
                                      (- (point) 2))))))))
 
-(defun gnus-clear-inboxes-moved ()
-  (setq nnmail-moved-inboxes nil))
-
 (defun gnus-mouse-pick-group (e)
   "Enter the group under the mouse pointer."
   (interactive "e")
@@ -815,13 +1218,36 @@ The following commands are available:
     (or level gnus-group-default-list-level gnus-level-subscribed))))
 
 (defun gnus-group-setup-buffer ()
-  (set-buffer (get-buffer-create gnus-group-buffer))
+  (set-buffer (gnus-get-buffer-create gnus-group-buffer))
   (unless (eq major-mode 'gnus-group-mode)
-    (gnus-add-current-to-buffer-list)
     (gnus-group-mode)
     (when gnus-carpal
       (gnus-carpal-setup-buffer 'group))))
 
+(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))
+       (alist gnus-group-name-charset-group-alist)
+       result)
+    (if item
+       (cdr item)
+      (while (setq item (pop alist))
+       (if (string-match (car item) group)
+           (setq alist nil
+                 result (cdr item))))
+      result)))
+
+(defun gnus-group-name-decode (string charset)
+  ;; Fixme: Don't decode in unibyte mode.
+  (if (and string charset (featurep 'mule))
+      (mm-decode-coding-string string charset)
+    string))
+
+(defun gnus-group-decoded-name (string)
+  (let ((charset (gnus-group-name-charset nil string)))
+    (gnus-group-name-decode string charset)))
+
 (defun gnus-group-list-groups (&optional level unread lowest)
   "List newsgroups with level LEVEL or lower that have unread articles.
 Default is all subscribed groups.
@@ -836,8 +1262,6 @@ Also see the `gnus-group-use-permanent-levels' variable."
            (gnus-group-default-level nil t)
            gnus-group-default-list-level
            gnus-level-subscribed))))
-  ;; Just do this here, for no particular good reason.
-  (gnus-clear-inboxes-moved)
   (unless level
     (setq level (car gnus-group-list-mode)
          unread (cdr gnus-group-list-mode)))
@@ -845,7 +1269,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)
@@ -877,7 +1301,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
@@ -897,60 +1321,96 @@ If ALL (the prefix), also list groups that have no unread articles."
   (interactive "nList groups on level: \nP")
   (gnus-group-list-groups level all level))
 
-(defun gnus-group-prepare-flat (level &optional all lowest regexp)
+(defun gnus-group-prepare-logic (group test)
+  (or (and gnus-group-listed-groups
+          (null gnus-group-list-option)
+          (member group gnus-group-listed-groups))
+      (cond
+       ((null gnus-group-listed-groups) test)
+       ((null gnus-group-list-option) test)
+       (t (and (member group gnus-group-listed-groups)
+              (if (eq gnus-group-list-option 'flush)
+                  (not test)
+                test))))))
+
+(defun gnus-group-prepare-flat (level &optional predicate lowest regexp)
   "List all newsgroups with unread articles of level LEVEL or lower.
-If ALL is non-nil, list groups that have no unread articles.
+If PREDICATE is a function, list groups that the function returns non-nil;
+if it is t, list groups that have no unread articles.
 If LOWEST is non-nil, list all newsgroups of level LOWEST or higher.
-If REGEXP, only list groups matching REGEXP."
+If REGEXP is a function, list dead groups that the function returns non-nil;
+if it is a string, only list groups matching REGEXP."
   (set-buffer gnus-group-buffer)
   (let ((buffer-read-only nil)
        (newsrc (cdr gnus-newsrc-alist))
        (lowest (or lowest 1))
+       (not-in-list (and gnus-group-listed-groups
+                         (copy-sequence gnus-group-listed-groups)))
        info clevel unread group params)
     (erase-buffer)
-    (when (< lowest gnus-level-zombie)
+    (when (or (< lowest gnus-level-zombie)
+             gnus-group-listed-groups)
       ;; List living groups.
       (while newsrc
        (setq info (car newsrc)
              group (gnus-info-group info)
              params (gnus-info-params info)
              newsrc (cdr newsrc)
-             unread (car (gnus-gethash group gnus-newsrc-hashtb)))
-       (and unread                     ; This group might be bogus
-            (or (not regexp)
-                (string-match regexp group))
-            (<= (setq clevel (gnus-info-level info)) level)
-            (>= clevel lowest)
-            (or all                    ; We list all groups?
-                (if (eq unread t)      ; Unactivated?
-                    gnus-group-list-inactive-groups ; We list unactivated
-                  (> unread 0))        ; We list groups with unread articles
-                (and gnus-list-groups-with-ticked-articles
-                     (cdr (assq 'tick (gnus-info-marks info))))
+             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
+                   (or (not (stringp regexp))
+                       (string-match regexp group))
+                   (<= (setq clevel (gnus-info-level info)) level)
+                   (>= clevel lowest)
+                   (cond
+                    ((functionp predicate)
+                     (funcall predicate info))
+                    (predicate t)      ; We list all groups?
+                    (t
+                     (or
+                      (if (eq unread t) ; Unactivated?
+                          gnus-group-list-inactive-groups
+                                       ; We list unactivated
+                        (> unread 0))
+                                       ; We list groups with unread articles
+                      (and gnus-list-groups-with-ticked-articles
+                           (cdr (assq 'tick (gnus-info-marks info))))
                                        ; And groups with tickeds
-                ;; Check for permanent visibility.
-                (and gnus-permanently-visible-groups
-                     (string-match gnus-permanently-visible-groups
-                                   group))
-                (memq 'visible params)
-                (cdr (assq 'visible params)))
-            (gnus-group-insert-group-line
-             group (gnus-info-level info)
-             (gnus-info-marks info) unread (gnus-info-method info)))))
+                      ;; Check for permanent visibility.
+                      (and gnus-permanently-visible-groups
+                           (string-match gnus-permanently-visible-groups
+                                         group))
+                      (memq 'visible params)
+                      (cdr (assq 'visible params)))))))
+         (gnus-group-insert-group-line
+          group (gnus-info-level info)
+          (gnus-info-marks info) unread (gnus-info-method info)))))
 
     ;; List dead groups.
-    (and (>= level gnus-level-zombie) (<= lowest gnus-level-zombie)
-        (gnus-group-prepare-flat-list-dead
-         (setq gnus-zombie-list (sort gnus-zombie-list 'string<))
-         gnus-level-zombie ?Z
-         regexp))
-    (and (>= level gnus-level-killed) (<= lowest gnus-level-killed)
-        (gnus-group-prepare-flat-list-dead
-         (setq gnus-killed-list (sort gnus-killed-list 'string<))
-         gnus-level-killed ?K regexp))
+    (when (or gnus-group-listed-groups
+             (and (>= level gnus-level-zombie)
+                  (<= lowest gnus-level-zombie)))
+      (gnus-group-prepare-flat-list-dead
+       (setq gnus-zombie-list (sort gnus-zombie-list 'string<))
+       gnus-level-zombie ?Z
+       regexp))
+    (when not-in-list
+      (dolist (group gnus-zombie-list)
+       (setq not-in-list (delete group not-in-list))))
+    (when (or gnus-group-listed-groups
+             (and (>= level gnus-level-killed) (<= lowest gnus-level-killed)))
+      (gnus-group-prepare-flat-list-dead
+       (gnus-union
+       not-in-list
+       (setq gnus-killed-list (sort gnus-killed-list 'string<)))
+       gnus-level-killed ?K regexp))
 
     (gnus-group-set-mode-line)
-    (setq gnus-group-list-mode (cons level all))
+    (setq gnus-group-list-mode (cons level predicate))
     (gnus-run-hooks 'gnus-group-prepare-hook)
     t))
 
@@ -959,33 +1419,44 @@ If REGEXP, only list groups matching REGEXP."
   ;; suggested by Jack Vinson <vinson@unagi.cis.upenn.edu>.  It does
   ;; this by ignoring the group format specification altogether.
   (let (group)
-    (if regexp
-       ;; This loop is used when listing groups that match some
-       ;; regexp.
+    (if (> (length groups) gnus-group-listing-limit)
        (while groups
          (setq group (pop groups))
-         (when (string-match regexp group)
+         (when (gnus-group-prepare-logic
+                group
+                (or (not regexp)
+                    (and (stringp regexp) (string-match regexp group))
+                    (and (functionp regexp) (funcall regexp group))))
            (gnus-add-text-properties
             (point) (prog1 (1+ (point))
-                      (insert " " mark "     *: " group "\n"))
+                      (insert " " mark "     *: "
+                              (gnus-group-decoded-name group)
+                              "\n"))
             (list 'gnus-group (gnus-intern-safe group gnus-active-hashtb)
                   'gnus-unread t
                   'gnus-level level))))
-      ;; This loop is used when listing all groups.
       (while groups
-       (gnus-add-text-properties
-        (point) (prog1 (1+ (point))
-                  (insert " " mark "     *: "
-                          (setq group (pop groups)) "\n"))
-        (list 'gnus-group (gnus-intern-safe group gnus-active-hashtb)
-              'gnus-unread t
-              'gnus-level level))))))
+       (setq group (pop groups))
+       (when (gnus-group-prepare-logic
+              group
+              (or (not regexp)
+                  (and (stringp regexp) (string-match regexp group))
+                  (and (functionp regexp) (funcall regexp group))))
+         (gnus-group-insert-group-line
+          group level nil
+          (let ((active (gnus-active group)))
+            (if active
+                (if (zerop (cdr active))
+                    0
+                  (- (1+ (cdr active)) (car active)))
+              nil))
+          (gnus-method-simplify (gnus-find-method-for-group group))))))))
 
 (defun gnus-group-update-group-line ()
   "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
@@ -1002,7 +1473,7 @@ If REGEXP, 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
@@ -1022,13 +1493,48 @@ If REGEXP, only list groups matching REGEXP."
               0
             (- (1+ (cdr active)) (car active)))
         nil)
-       nil))))
+       (gnus-method-simplify (gnus-find-method-for-group group))))))
+
+(defun gnus-number-of-unseen-articles-in-group (group)
+  (let* ((info (nth 2 (gnus-group-entry group)))
+        (marked (gnus-info-marks info))
+        (seen (cdr (assq 'seen marked)))
+        (active (gnus-active group)))
+    (if (not active)
+       0
+      (length (gnus-uncompress-range
+              (gnus-range-difference
+               (gnus-range-difference (list active) (gnus-info-read info))
+               seen))))))
+
+(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 21 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)
   "Insert a group line in the group buffer."
-  (let* ((gnus-tmp-active (gnus-active gnus-tmp-group))
+  (let* ((gnus-tmp-method
+         (gnus-server-get-method gnus-tmp-group gnus-tmp-method))
+        (group-name-charset (gnus-group-name-charset gnus-tmp-method
+                                                     gnus-tmp-group))
+        (gnus-tmp-active (gnus-active gnus-tmp-group))
         (gnus-tmp-number-total
          (if gnus-tmp-active
              (1+ (- (cdr gnus-tmp-active) (car gnus-tmp-active)))
@@ -1045,10 +1551,17 @@ If REGEXP, only list groups matching REGEXP."
                ((<= gnus-tmp-level gnus-level-unsubscribed) ?U)
                ((= gnus-tmp-level gnus-level-zombie) ?Z)
                (t ?K)))
-        (gnus-tmp-qualified-group (gnus-group-real-name gnus-tmp-group))
+        (gnus-tmp-qualified-group
+         (gnus-group-name-decode (gnus-group-real-name gnus-tmp-group)
+                                 group-name-charset))
+        (gnus-tmp-comment
+         (or (gnus-group-get-parameter gnus-tmp-group 'comment t)
+             gnus-tmp-group))
         (gnus-tmp-newsgroup-description
          (if gnus-description-hashtb
-             (or (gnus-gethash gnus-tmp-group gnus-description-hashtb) "")
+             (or (gnus-group-name-decode
+                  (gnus-gethash gnus-tmp-group gnus-description-hashtb)
+                  group-name-charset) "")
            ""))
         (gnus-tmp-moderated
          (if (and gnus-moderated-hashtb
@@ -1056,8 +1569,7 @@ If REGEXP, only list groups matching REGEXP."
              ?m ? ))
         (gnus-tmp-moderated-string
          (if (eq gnus-tmp-moderated ?m) "(m)" ""))
-        (gnus-tmp-method
-         (gnus-server-get-method gnus-tmp-group gnus-tmp-method)) ;
+        (gnus-tmp-group-icon "==&&==")
         (gnus-tmp-news-server (or (cadr gnus-tmp-method) ""))
         (gnus-tmp-news-method (or (car gnus-tmp-method) ""))
         (gnus-tmp-news-method-string
@@ -1069,32 +1581,43 @@ If REGEXP, only list groups matching REGEXP."
                   (zerop number)
                   (cdr (assq 'tick gnus-tmp-marked)))
              ?* ? ))
+        (gnus-tmp-summary-live
+         (if (and (not gnus-group-is-exiting-p)
+                  (gnus-buffer-live-p (gnus-summary-buffer-name
+                                       gnus-tmp-group)))
+             ?* ? ))
         (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.
-       (eval gnus-group-line-format-spec))
+       (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))
-      (forward-line -1)
-      (gnus-run-hooks 'gnus-group-update-hook)
-      (forward-line))
+      (gnus-run-hooks 'gnus-group-update-hook))
+    (forward-line)
     ;; Allow XEmacs to remove front-sticky text properties.
     (gnus-group-remove-excess-properties)))
 
@@ -1102,7 +1625,7 @@ If REGEXP, only list groups matching REGEXP."
   "Highlight the current line according to `gnus-group-highlight'."
   (let* ((list gnus-group-highlight)
         (p (point))
-        (end (progn (end-of-line) (point)))
+        (end (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))
@@ -1111,11 +1634,15 @@ If REGEXP, only list groups matching REGEXP."
         (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)))
+        (method (inline (gnus-server-get-method group (gnus-info-method info))))
         (marked (gnus-info-marks info))
-        (mailp (memq 'mail (assoc (symbol-name
-                                   (car (or method gnus-select-method)))
-                                  gnus-valid-select-methods)))
+        (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))))
@@ -1127,7 +1654,7 @@ If REGEXP, only list groups matching REGEXP."
       (setq list (cdr list)))
     (let ((face (cdar list)))
       (unless (eq face (get-text-property beg 'face))
-       (gnus-put-text-property
+       (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)))
@@ -1149,8 +1676,9 @@ already."
            (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)))
+       (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))
@@ -1173,7 +1701,7 @@ already."
          ;; 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))))
+           (let ((entry (cddr (gnus-group-entry group))))
              (while (and entry (car entry)
                          (not
                           (gnus-goto-char
@@ -1233,24 +1761,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))
@@ -1308,10 +1836,28 @@ 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.
 
+(defun gnus-group-mark-line-p ()
+  (save-excursion
+    (beginning-of-line)
+    (forward-char (or (cdr (assq 'process gnus-group-mark-positions)) 2))
+    (eq (char-after) gnus-process-mark)))
+
 (defun gnus-group-mark-group (n &optional unmark no-advance)
   "Mark the current group."
   (interactive "p")
@@ -1323,15 +1869,14 @@ 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)) (following-char)
-        (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))
@@ -1347,10 +1892,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)
@@ -1373,15 +1916,17 @@ 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)
+(defun gnus-group-remove-mark (group &optional test-marked)
   "Remove the process mark from GROUP and move point there.
 Return nil if the group isn't displayed."
-  (if (gnus-group-goto-group group)
+  (if (gnus-group-goto-group group nil test-marked)
       (save-excursion
        (gnus-group-mark-group 1 'unmark t)
        t)
@@ -1429,7 +1974,7 @@ Take into consideration N (the prefix) and the list of marked groups."
          (setq n (1- n))
          (gnus-group-next-group way)))
       (nreverse groups)))
-   ((gnus-region-active-p)
+   ((and (gnus-region-active-p) (mark))
     ;; Work on the region between point and mark.
     (let ((max (max (point) (mark)))
          groups)
@@ -1460,23 +2005,25 @@ Take into consideration N (the prefix) and the list of marked groups."
     (eval
      `(defun gnus-group-iterate (arg ,function)
        "Iterate FUNCTION over all process/prefixed groups.
-FUNCTION will be called with the group name as the paremeter
+FUNCTION will be called with the group name as the parameter
 and with point over the group in question."
        (let ((,groups (gnus-group-process-prefix arg))
              (,window (selected-window))
              ,group)
-         (while (setq ,group (pop ,groups))
+         (while ,groups
+           (setq ,group (car ,groups)
+                 ,groups (cdr ,groups))
            (select-window ,window)
            (gnus-group-remove-mark ,group)
            (save-selected-window
              (save-excursion
                (funcall ,function ,group)))))))))
-  
+
 (put 'gnus-group-iterate 'lisp-indent-function 1)
 
 ;; Selecting groups.
 
-(defun gnus-group-read-group (&optional all no-article group)
+(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
@@ -1492,8 +2039,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
@@ -1507,14 +2053,20 @@ group."
                                          (cdr (assq 'tick marked)))
                                  (gnus-range-length
                                   (cdr (assq 'dormant marked)))))))
-     no-article nil no-display)))
+     no-article nil no-display nil select-articles)))
 
 (defun gnus-group-select-group (&optional all)
   "Select this newsgroup.
 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)
@@ -1553,56 +2105,100 @@ be permanent."
         gnus-summary-mode-hook gnus-select-group-hook
         (group (gnus-group-group-name))
         (method (gnus-find-method-for-group group)))
-    (setq method
-         `(,(car method) ,(concat (cadr method) "-ephemeral")
-           (,(intern (format "%s-address" (car method))) ,(cadr method))
-           ,@(cddr method)))
     (gnus-group-read-ephemeral-group
      (gnus-group-prefixed-name group method) method)))
 
 ;;;###autoload
-(defun gnus-fetch-group (group)
+(defun gnus-fetch-group (group &optional articles)
   "Start Gnus if necessary and enter GROUP.
 Returns whether the fetching was successful or not."
-  (interactive "sGroup name: ")
+  (interactive (list (completing-read "Group name: " gnus-active-hashtb)))
   (unless (get-buffer gnus-group-buffer)
     (gnus-no-server))
-  (gnus-group-read-group nil nil group))
+  (gnus-group-read-group articles nil group))
+
+;;;###autoload
+(defun gnus-fetch-group-other-frame (group)
+  "Pop up a frame and enter GROUP."
+  (interactive "P")
+  (let ((window (get-buffer-window gnus-group-buffer)))
+    (cond (window
+          (select-frame (window-frame window)))
+         ((= (length (frame-list)) 1)
+          (select-frame (make-frame)))
+         (t
+          (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.
+
+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)
+                number
+                (sexp :menu-tag "other" t)))
+
 ;; Enter a group that is not in the group buffer.  Non-nil is returned
 ;; if selection was successful.
 (defun gnus-group-read-ephemeral-group (group method &optional activate
-                                             quit-config request-only)
+                                             quit-config request-only
+                                             select-articles
+                                             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
 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 is selection was successful."
+Return the name of the group if selection was successful."
+  (interactive
+   (list
+    ;; (gnus-read-group "Group name: ")
+    (completing-read
+     "Group: " gnus-active-hashtb
+     nil nil nil
+     'gnus-group-history)
+    (gnus-read-method "From method: ")))
   ;; Transform the select method into a unique server.
   (when (stringp method)
     (setq method (gnus-server-to-method method)))
-;;;  (let ((saddr (intern (format "%s-address" (car method)))))
-;;;    (setq method (gnus-copy-sequence method))
-;;;    (require (car method))
-;;;    (when (boundp saddr)
-;;;      (unless (assq saddr method)
-;;;    (nconc method `((,saddr ,(cadr method))))
-;;;    (setf (cadr method) (format "%s-%d" (cadr method)
-;;;                                (incf gnus-ephemeral-group-server))))))
+  (setq method
+       `(,(car method) ,(concat (cadr method) "-ephemeral")
+         (,(intern (format "%s-address" (car method))) ,(cadr method))
+         ,@(cddr method)))
   (let ((group (if (gnus-group-foreign-p group) group
-                (gnus-group-prefixed-name group method))))
+                (gnus-group-prefixed-name (gnus-group-real-name group)
+                                          method))))
     (gnus-sethash
      group
      `(-1 nil (,group
               ,gnus-level-default-subscribed nil nil ,method
-              ((quit-config .
-                            ,(if quit-config quit-config
-                               (cons gnus-summary-buffer
-                                     gnus-current-window-configuration))))))
+              ,(cons
+                (if quit-config
+                    (cons 'quit-config quit-config)
+                  (cons 'quit-config
+                        (cons gnus-summary-buffer
+                              gnus-current-window-configuration)))
+                parameters)))
      gnus-newsrc-hashtb)
     (push method gnus-ephemeral-servers)
     (set-buffer gnus-group-buffer)
@@ -1616,19 +2212,33 @@ Return the name of the group is selection was successful."
     (if request-only
        group
       (condition-case ()
-         (when (gnus-group-read-group t t group)
+         (when (let ((gnus-large-newsgroup gnus-large-ephemeral-newsgroup)
+                     (gnus-fetch-old-headers
+                      gnus-fetch-old-ephemeral-headers))
+                 (gnus-group-read-group (or number t) t group select-articles))
            group)
        ;;(error nil)
-       (quit nil)))))
+       (quit
+        (message "Quit reading the ephemeral group")
+        nil)))))
+
+(defun gnus-group-jump-to-group (group &optional prompt)
+  "Jump to newsgroup GROUP.
 
-(defun gnus-group-jump-to-group (group)
-  "Jump to newsgroup GROUP."
+If PROMPT (the prefix) is a number, use the prompt specified in
+`gnus-group-jump-to-group-prompt'."
   (interactive
-   (list (completing-read
-         "Group: " gnus-active-hashtb nil
-         (gnus-read-active-file-p)
-         nil
-         'gnus-group-history)))
+   (list (mm-string-make-unibyte
+         (completing-read
+          "Group: " gnus-active-hashtb 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))))
+          'gnus-group-history))))
 
   (when (equal group "")
     (error "Empty group name"))
@@ -1642,41 +2252,56 @@ Return the name of the group is selection was successful."
   ;; Adjust cursor point.
   (gnus-group-position-point))
 
-(defun gnus-group-goto-group (group &optional far)
+(defun gnus-group-goto-group (group &optional far test-marked)
   "Goto to newsgroup GROUP.
-If FAR, it is likely that the group is not on the current line."
+If FAR, it is likely that the group is not on the current line.
+If TEST-MARKED, the line must be marked."
   (when group
-    (if far
-       (gnus-goto-char
-        (text-property-any
-         (point-min) (point-max)
-         'gnus-group (gnus-intern-safe group gnus-active-hashtb)))
-      (beginning-of-line)
-      (cond
-       ;; It's quite likely that we are on the right line, so
-       ;; we check the current line first.
-       ((eq (get-text-property (point) 'gnus-group)
-           (gnus-intern-safe group gnus-active-hashtb))
-       (point))
-       ;; Previous and next line are also likely, so we check them as well.
-       ((save-excursion
-         (forward-line -1)
-         (eq (get-text-property (point) 'gnus-group)
-             (gnus-intern-safe group gnus-active-hashtb)))
-       (forward-line -1)
-       (point))
-       ((save-excursion
-         (forward-line 1)
-         (eq (get-text-property (point) 'gnus-group)
-             (gnus-intern-safe group gnus-active-hashtb)))
-       (forward-line 1)
-       (point))
-       (t
-       ;; Search through the entire buffer.
-       (gnus-goto-char
-        (text-property-any
-         (point-min) (point-max)
-         'gnus-group (gnus-intern-safe group gnus-active-hashtb))))))))
+    (beginning-of-line)
+    (cond
+     ;; It's quite likely that we are on the right line, so
+     ;; we check the current line first.
+     ((and (not far)
+          (eq (get-text-property (point) 'gnus-group)
+              (gnus-intern-safe group gnus-active-hashtb))
+          (or (not test-marked) (gnus-group-mark-line-p)))
+      (point))
+     ;; Previous and next line are also likely, so we check them as well.
+     ((and (not far)
+          (save-excursion
+            (forward-line -1)
+            (and (eq (get-text-property (point) 'gnus-group)
+                     (gnus-intern-safe group gnus-active-hashtb))
+                 (or (not test-marked) (gnus-group-mark-line-p)))))
+      (forward-line -1)
+      (point))
+     ((and (not far)
+          (save-excursion
+            (forward-line 1)
+            (and (eq (get-text-property (point) 'gnus-group)
+                     (gnus-intern-safe group gnus-active-hashtb))
+                 (or (not test-marked) (gnus-group-mark-line-p)))))
+      (forward-line 1)
+      (point))
+     (test-marked
+      (goto-char (point-min))
+      (let (found)
+       (while (and (not found)
+                   (gnus-goto-char
+                    (text-property-any
+                     (point) (point-max)
+                     'gnus-group
+                     (gnus-intern-safe group gnus-active-hashtb))))
+         (if (gnus-group-mark-line-p)
+             (setq found t)
+           (forward-line 1)))
+       found))
+     (t
+      ;; Search through the entire buffer.
+      (gnus-goto-char
+       (text-property-any
+       (point-min) (point-max)
+       'gnus-group (gnus-intern-safe group gnus-active-hashtb)))))))
 
 (defun gnus-group-next-group (n &optional silent)
   "Go to next N'th newsgroup.
@@ -1758,9 +2383,28 @@ If EXCLUDE-GROUP, do not go to that group."
       (forward-line 1))
     (when best-point
       (goto-char best-point))
-    (gnus-summary-position-point)
+    (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)
@@ -1782,6 +2426,16 @@ If EXCLUDE-GROUP, do not go to that group."
   (interactive)
   (gnus-enter-server-buffer))
 
+(defun gnus-group-make-group-simple (&optional group)
+  "Add a new newsgroup.
+The user will be prompted for GROUP."
+  (interactive
+   (list (completing-read "Group: " gnus-active-hashtb
+                         nil nil nil 'gnus-group-history)))
+  (gnus-group-make-group
+   (gnus-group-real-name group)
+   (gnus-group-server group)))
+
 (defun gnus-group-make-group (name &optional method address args)
   "Add a new newsgroup.
 The user will be prompted for a NAME, for a select METHOD, and an
@@ -1791,21 +2445,23 @@ ADDRESS."
     (gnus-read-group "Group name: ")
     (gnus-read-method "From method: ")))
 
-  (let* ((meth (when (and method
-                         (not (gnus-server-equal method gnus-select-method)))
-                (if address (list (intern method) address)
-                  method)))
+  (when (stringp method)
+    (setq method (or (gnus-server-to-method method) method)))
+  (let* ((meth (gnus-method-simplify
+               (when (and method
+                          (not (gnus-server-equal method gnus-select-method)))
+                 (if address (list (intern method) 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))
@@ -1818,7 +2474,7 @@ ADDRESS."
     (forward-line -1)
     (gnus-group-position-point)
 
-    ;; Load the backend and try to make the backend create
+    ;; Load the back end and try to make the back end create
     ;; the group as well.
     (when (assoc (symbol-name (setq backend (car (gnus-server-get-method
                                                  nil meth))))
@@ -1826,36 +2482,54 @@ ADDRESS."
       (require backend))
     (gnus-check-server meth)
     (when (gnus-check-backend-function 'request-create-group nname)
-      (gnus-request-create-group nname nil args))
+      (unless (gnus-request-create-group nname nil args)
+       (error "Could not create group on server: %s"
+              (nnheader-get-report backend))))
     t))
 
-(defun gnus-group-delete-group (group &optional force)
-  "Delete the current group.  Only meaningful with mail groups.
+(defun gnus-group-delete-groups (&optional arg)
+  "Delete the current group.  Only meaningful with editable groups."
+  (interactive "P")
+  (let ((n (length (gnus-group-process-prefix arg))))
+    (when (gnus-yes-or-no-p
+          (if (= n 1)
+              "Delete this 1 group? "
+            (format "Delete these %d groups? " n)))
+      (gnus-group-iterate arg
+       (lambda (group)
+         (gnus-group-delete-group group nil t))))))
+
+(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))
   (unless group
-    (error "No group to rename"))
+    (error "No group to delete"))
   (unless (gnus-check-backend-function 'request-delete-group group)
-    (error "This backend does not support group deletion"))
+    (error "This back end does not support group deletion"))
   (prog1
-      (if (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)
-         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)
@@ -1868,12 +2542,12 @@ and NEW-NAME will be prompted for."
     (progn
       (unless (gnus-check-backend-function
               'request-rename-group (gnus-group-group-name))
-       (error "This backend does not support renaming groups"))
+       (error "This back end does not support renaming groups"))
       (gnus-read-group "Rename group to: "
                       (gnus-group-real-name (gnus-group-group-name))))))
 
   (unless (gnus-check-backend-function 'request-rename-group group)
-    (error "This backend does not support renaming groups"))
+    (error "This back end does not support renaming groups"))
   (unless group
     (error "No group to rename"))
   (when (equal (gnus-group-real-name group) new-name)
@@ -1889,12 +2563,17 @@ and NEW-NAME will be prompted for."
           (gnus-group-real-name new-name)
           (gnus-info-method (gnus-get-info group)))))
 
+  (when (gnus-active new-name)
+    (error "The group %s already exists" new-name))
+
   (gnus-message 6 "Renaming group %s to %s..." group new-name)
   (prog1
-      (if (not (gnus-request-rename-group group new-name))
+      (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-goto-group group)
        (gnus-group-kill-group)
        ;; ... changing its name ...
        (setcar (cdar gnus-list-of-killed-groups) new-name)
@@ -1903,6 +2582,8 @@ and NEW-NAME will be prompted for."
        (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)))
 
@@ -1931,9 +2612,19 @@ and NEW-NAME will be prompted for."
        ((eq part 'method) "select method")
        ((eq part 'params) "group parameters")
        (t "group info"))
-      group)
+      (gnus-group-decoded-name group))
      `(lambda (form)
-       (gnus-group-edit-group-done ',part ,group form)))))
+       (gnus-group-edit-group-done ',part ,group form)))
+    (local-set-key
+     "\C-c\C-i"
+     (gnus-create-info-command
+      (cond
+       ((eq part 'method)
+       "(gnus)Select Methods")
+       ((eq part 'params)
+       "(gnus)Group Parameters")
+       (t
+       "(gnus)Group Info"))))))
 
 (defun gnus-group-edit-group-method (group)
   "Edit the select method of GROUP."
@@ -1982,6 +2673,7 @@ and NEW-NAME will be prompted for."
     (gnus-group-position-point)))
 
 (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)
@@ -1993,25 +2685,39 @@ and NEW-NAME will be prompted for."
       (setcar entry (eval (cadar entry)))))
   (gnus-group-make-group group method))
 
-(defun gnus-group-make-help-group ()
-  "Create the Gnus documentation group."
+(defun gnus-group-make-help-group (&optional noerror)
+  "Create the Gnus documentation group.
+Optional argument NOERROR modifies the behavior of this function when the
+group already exists:
+- if not given, and error is signaled,
+- if t, stay silent,
+- if anything else, just print a message."
   (interactive)
   (let ((name (gnus-group-prefixed-name "gnus-help" '(nndoc "gnus-help")))
-       (file (nnheader-find-etc-directory "gnus-tut.txt" t))
-       dir)
-    (when (gnus-gethash name gnus-newsrc-hashtb)
-      (error "Documentation group already exists"))
-    (if (not file)
-       (gnus-message 1 "Couldn't find doc group")
-      (gnus-group-make-group
-       (gnus-group-real-name name)
-       (list 'nndoc "gnus-help"
-            (list 'nndoc-address file)
-            (list 'nndoc-article-type 'mbox)))))
+       (file (nnheader-find-etc-directory "gnus-tut.txt" t)))
+    (if (gnus-group-entry name)
+       (cond ((eq noerror nil)
+              (error "Documentation group already exists"))
+             ((eq noerror t)
+              ;; stay silent
+              )
+             (t
+              (gnus-message 1 "Documentation group already exists")))
+      ;; else:
+      (if (not file)
+         (gnus-message 1 "Couldn't find doc group")
+       (gnus-group-make-group
+        (gnus-group-real-name name)
+        (list 'nndoc "gnus-help"
+              (list 'nndoc-address file)
+              (list 'nndoc-article-type 'mbox))))
+      ))
   (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)))
@@ -2020,13 +2726,14 @@ and NEW-NAME will be prompted for."
          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)
                          ((= char ?d) 'digest)
                          ((= char ?f) 'forward)
                          ((= char ?a) 'mmfd)
+                         ((= char ?g) 'guess)
                          (t (setq err (format "%c unknown. " char))
                             nil))))
       (setq type found)))
@@ -2071,19 +2778,93 @@ If SOLID (the prefix), create a solid group."
                  (nnweb-type ,(intern type))
                  (nnweb-ephemeral-p t))))
     (if solid
-       (gnus-group-make-group group "nnweb" "" `(,(intern type) ,search))
+       (progn
+         (gnus-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)))
+(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"
+  (interactive)
+  (require 'nnrss)
+  (if (not url)
+      (setq url (read-from-minibuffer "URL to Search for RSS: ")))
+  (let ((feedinfo (nnrss-discover-feed url)))
+    (if feedinfo
+       (let ((title (gnus-newsgroup-savable-name
+                     (read-from-minibuffer "Title: "
+                                           (gnus-newsgroup-savable-name
+                                            (or (cdr (assoc 'title
+                                                            feedinfo))
+                                                "")))))
+             (desc  (read-from-minibuffer "Description: "
+                                          (cdr (assoc 'description
+                                                      feedinfo))))
+             (href (cdr (assoc 'href feedinfo)))
+             (encodable (mm-coding-system-p 'utf-8)))
+         (when encodable
+           ;; Unify non-ASCII text.
+           (setq title (mm-decode-coding-string
+                        (mm-encode-coding-string title 'utf-8) 'utf-8)))
+         (gnus-group-make-group (if encodable
+                                    (mm-encode-coding-string title 'utf-8)
+                                  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)
+    (when (gnus-group-entry group)
       (error "Archive group already exists"))
     (gnus-group-make-group
      (gnus-group-real-name group)
@@ -2107,17 +2888,17 @@ 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
-            (concat (file-name-as-directory (directory-file-name dir))
-                    ext)
+            (expand-file-name ext dir)
             '(nndir "")))
       (setq ext (format "<%d>" (setq i (1+ i)))))
     (gnus-group-make-group
      (gnus-group-real-name group)
      (list 'nndir (gnus-group-real-name group) (list 'nndir-directory dir)))))
 
+(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
@@ -2126,7 +2907,7 @@ score file entries for articles to include in the group."
    (list
     (read-string "nnkiboze group name: ")
     (read-string "Source groups (regexp): ")
-    (let ((headers (mapcar (lambda (group) (list group))
+    (let ((headers (mapcar 'list
                           '("subject" "from" "number" "date" "message-id"
                             "references" "chars" "lines" "xref"
                             "followup" "all" "body" "head")))
@@ -2135,15 +2916,20 @@ score file entries for articles to include in the group."
                                          "Match on header: " headers nil t))))
        (setq regexps nil)
        (while (not (equal "" (setq regexp (read-string
-                                           (format "Match on %s (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)
-  (nnheader-temp-write (gnus-score-file-name (concat "nnkiboze:" group))
-    (let (emacs-lisp-mode-hook)
-      (pp scores (current-buffer)))))
+  (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)
+       (gnus-pp scores)))))
 
 (defun gnus-group-add-to-virtual (n vgroup)
   "Add the current group to a virtual group."
@@ -2172,7 +2958,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)
@@ -2195,6 +2981,63 @@ 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)
+  "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))))
+
+(defun gnus-group-nnimap-edit-acl (group)
+  "Edit the Access Control List of current nnimap GROUP."
+  (interactive (list (gnus-group-group-name)))
+  (let ((mailbox (gnus-group-real-name group)) method acl)
+    (unless group
+      (error "No group on current line"))
+    (unless (gnus-get-info group)
+      (error "Killed group; can't be edited"))
+    (unless (eq (car (setq method (gnus-find-method-for-group group))) 'nnimap)
+      (error "%s is not an nnimap group" group))
+    (unless (setq acl (nnimap-acl-get mailbox (cadr method)))
+      (error "Server does not support ACL's"))
+    (gnus-edit-form acl (format "Editing the access control list for `%s'.
+
+   An access control list is a list of (identifier . rights) elements.
+
+   The identifier string specifies the corresponding user.  The
+   identifier \"anyone\" is reserved to refer to the universal identity.
+
+   Rights is a string listing a (possibly empty) set of alphanumeric
+   characters, each character listing a set of operations which is being
+   controlled.  Letters are reserved for ``standard'' rights, listed
+   below.  Digits are reserved for implementation or site defined rights.
+
+   l - lookup (mailbox is visible to LIST/LSUB commands)
+   r - read (SELECT the mailbox, perform CHECK, FETCH, PARTIAL,
+       SEARCH, COPY from mailbox)
+   s - keep seen/unseen information across sessions (STORE \\SEEN flag)
+   w - write (STORE flags other than \\SEEN and \\DELETED)
+   i - insert (perform APPEND, COPY into mailbox)
+   p - post (send mail to submission address for mailbox,
+       not enforced by IMAP4 itself)
+   c - create and delete mailbox (CREATE new sub-mailboxes in any
+       implementation-defined hierarchy, RENAME or DELETE mailbox)
+   d - delete messages (STORE \\DELETED flag, perform EXPUNGE)
+   a - administer (perform SETACL)" group)
+                   `(lambda (form)
+                      (nnimap-acl-edit
+                       ,mailbox ',method ',acl form)))))
+
 ;; Group sorting commands
 ;; Suggested by Joe Hildebrand <hildjj@idaho.fuentez.com>.
 
@@ -2206,6 +3049,7 @@ If REVERSE (the prefix), reverse the sorting order."
   (interactive (list gnus-group-sort-function current-prefix-arg))
   (funcall gnus-group-sort-alist-function
           (gnus-make-sort-function func) reverse)
+  (gnus-group-unmark-all-groups)
   (gnus-group-list-groups)
   (gnus-dribble-touch))
 
@@ -2228,6 +3072,12 @@ If REVERSE, sort in reverse order."
   (interactive "P")
   (gnus-group-sort-groups 'gnus-group-sort-by-alphabet reverse))
 
+(defun gnus-group-sort-groups-by-real-name (&optional reverse)
+  "Sort the group buffer alphabetically by real (unprefixed) group name.
+If REVERSE, sort in reverse order."
+  (interactive "P")
+  (gnus-group-sort-groups 'gnus-group-sort-by-real-name reverse))
+
 (defun gnus-group-sort-groups-by-unread (&optional reverse)
   "Sort the group buffer by number of unread articles.
 If REVERSE, sort in reverse order."
@@ -2253,11 +3103,17 @@ If REVERSE, sort in reverse order."
   (gnus-group-sort-groups 'gnus-group-sort-by-rank reverse))
 
 (defun gnus-group-sort-groups-by-method (&optional reverse)
-  "Sort the group buffer alphabetically by backend name.
+  "Sort the group buffer alphabetically by back end name.
 If REVERSE, sort in reverse order."
   (interactive "P")
   (gnus-group-sort-groups 'gnus-group-sort-by-method reverse))
 
+(defun gnus-group-sort-groups-by-server (&optional reverse)
+  "Sort the group buffer alphabetically by server name.
+If REVERSE, sort in reverse order."
+  (interactive "P")
+  (gnus-group-sort-groups 'gnus-group-sort-by-server reverse))
+
 ;;; Selected group sorting.
 
 (defun gnus-group-sort-selected-groups (n func &optional reverse)
@@ -2266,13 +3122,15 @@ If REVERSE, sort in reverse order."
   (let ((groups (gnus-group-process-prefix n)))
     (funcall gnus-group-sort-selected-function
             groups (gnus-make-sort-function func) reverse)
-    (gnus-group-list-groups)))
+    (gnus-group-unmark-all-groups)
+    (gnus-group-list-groups)
+    (gnus-dribble-touch)))
 
 (defun gnus-group-sort-selected-flat (groups func reverse)
   (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
@@ -2286,46 +3144,59 @@ If REVERSE, sort in reverse order."
     ;; Go through all the infos and replace the old entries
     ;; with the new infos.
     (while infos
-      (setcar entries (pop infos))
+      (setcar (car entries) (pop infos))
       (pop entries))
     ;; Update the hashtable.
     (gnus-make-hashtable-from-newsrc-alist)))
 
-(defun gnus-group-sort-selected-groups-by-alphabet (&optional reverse)
+(defun gnus-group-sort-selected-groups-by-alphabet (&optional n reverse)
   "Sort the group buffer alphabetically by group name.
-If REVERSE, sort in reverse order."
-  (interactive "P")
-  (gnus-group-sort-selected-groups 'gnus-group-sort-by-alphabet reverse))
-
-(defun gnus-group-sort-selected-groups-by-unread (&optional reverse)
+Obeys the process/prefix convention.  If REVERSE (the symbolic prefix),
+sort in reverse order."
+  (interactive (gnus-interactive "P\ny"))
+  (gnus-group-sort-selected-groups n 'gnus-group-sort-by-alphabet reverse))
+
+(defun gnus-group-sort-selected-groups-by-real-name (&optional n reverse)
+  "Sort the group buffer alphabetically by real group name.
+Obeys the process/prefix convention.  If REVERSE (the symbolic prefix),
+sort in reverse order."
+  (interactive (gnus-interactive "P\ny"))
+  (gnus-group-sort-selected-groups n 'gnus-group-sort-by-real-name reverse))
+
+(defun gnus-group-sort-selected-groups-by-unread (&optional n reverse)
   "Sort the group buffer by number of unread articles.
-If REVERSE, sort in reverse order."
-  (interactive "P")
-  (gnus-group-sort-selected-groups 'gnus-group-sort-by-unread reverse))
+Obeys the process/prefix convention.  If REVERSE (the symbolic prefix),
+sort in reverse order."
+  (interactive (gnus-interactive "P\ny"))
+  (gnus-group-sort-selected-groups n 'gnus-group-sort-by-unread reverse))
 
-(defun gnus-group-sort-selected-groups-by-level (&optional reverse)
+(defun gnus-group-sort-selected-groups-by-level (&optional n reverse)
   "Sort the group buffer by group level.
-If REVERSE, sort in reverse order."
-  (interactive "P")
-  (gnus-group-sort-selected-groups 'gnus-group-sort-by-level reverse))
+Obeys the process/prefix convention.  If REVERSE (the symbolic prefix),
+sort in reverse order."
+  (interactive (gnus-interactive "P\ny"))
+  (gnus-group-sort-selected-groups n 'gnus-group-sort-by-level reverse))
 
-(defun gnus-group-sort-selected-groups-by-score (&optional reverse)
+(defun gnus-group-sort-selected-groups-by-score (&optional n reverse)
   "Sort the group buffer by group score.
-If REVERSE, sort in reverse order."
-  (interactive "P")
-  (gnus-group-sort-selected-groups 'gnus-group-sort-by-score reverse))
+Obeys the process/prefix convention.  If REVERSE (the symbolic prefix),
+sort in reverse order."
+  (interactive (gnus-interactive "P\ny"))
+  (gnus-group-sort-selected-groups n 'gnus-group-sort-by-score reverse))
 
-(defun gnus-group-sort-selected-groups-by-rank (&optional reverse)
+(defun gnus-group-sort-selected-groups-by-rank (&optional n reverse)
   "Sort the group buffer by group rank.
-If REVERSE, sort in reverse order."
-  (interactive "P")
-  (gnus-group-sort-selected-groups 'gnus-group-sort-by-rank reverse))
-
-(defun gnus-group-sort-selected-groups-by-method (&optional reverse)
-  "Sort the group buffer alphabetically by backend name.
-If REVERSE, sort in reverse order."
-  (interactive "P")
-  (gnus-group-sort-selected-groups 'gnus-group-sort-by-method reverse))
+Obeys the process/prefix convention.  If REVERSE (the symbolic prefix),
+sort in reverse order."
+  (interactive (gnus-interactive "P\ny"))
+  (gnus-group-sort-selected-groups n 'gnus-group-sort-by-rank reverse))
+
+(defun gnus-group-sort-selected-groups-by-method (&optional n reverse)
+  "Sort the group buffer alphabetically by back end name.
+Obeys the process/prefix convention.  If REVERSE (the symbolic prefix),
+sort in reverse order."
+  (interactive (gnus-interactive "P\ny"))
+  (gnus-group-sort-selected-groups n 'gnus-group-sort-by-method reverse))
 
 ;;; Sorting predicates.
 
@@ -2340,8 +3211,8 @@ If REVERSE, 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 info1))))
     (< (or (and (numberp n1) n1) 0)
        (or (and (numberp n2) n2) 0))))
 
@@ -2350,15 +3221,24 @@ If REVERSE, sort in reverse order."
   (< (gnus-info-level info1) (gnus-info-level info2)))
 
 (defun gnus-group-sort-by-method (info1 info2)
-  "Sort alphabetically by backend name."
-  (string< (symbol-name (car (gnus-find-method-for-group
-                             (gnus-info-group info1) info1)))
-          (symbol-name (car (gnus-find-method-for-group
-                             (gnus-info-group info2) info2)))))
+  "Sort alphabetically by back end name."
+  (string< (car (gnus-find-method-for-group
+                (gnus-info-group info1) info1))
+          (car (gnus-find-method-for-group
+                (gnus-info-group info2) info2))))
+
+(defun gnus-group-sort-by-server (info1 info2)
+  "Sort alphabetically by server name."
+  (string< (gnus-method-to-full-server-name
+           (gnus-find-method-for-group
+            (gnus-info-group info1) info1))
+          (gnus-method-to-full-server-name
+           (gnus-find-method-for-group
+            (gnus-info-group info2) info2))))
 
 (defun gnus-group-sort-by-score (info1 info2)
   "Sort by group score."
-  (< (gnus-info-score info1) (gnus-info-score info2)))
+  (> (gnus-info-score info1) (gnus-info-score info2)))
 
 (defun gnus-group-sort-by-rank (info1 info2)
   "Sort by level and score."
@@ -2371,7 +3251,8 @@ If REVERSE, 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)
@@ -2391,20 +3272,29 @@ If REVERSE, sort in reverse order."
        (when (gnus-group-native-p (gnus-info-group info))
          (gnus-info-clear-data info)))
       (gnus-get-unread-articles)
-      (gnus-dribble-enter "")
+      (gnus-dribble-touch)
       (when (gnus-y-or-n-p
             "Move the cache away to avoid problems in the future? ")
        (call-interactively 'gnus-cache-move-cache)))))
 
 (defun gnus-info-clear-data (info)
   "Clear all marks and read ranges from INFO."
-  (let ((group (gnus-info-group info)))
+  (let ((group (gnus-info-group info))
+       action)
+    (dolist (el (gnus-info-marks info))
+      (push `(,(cdr el) add (,(car el))) action))
+    (push `(,(gnus-info-read info) add (read)) action)
     (gnus-undo-register
       `(progn
+        (gnus-request-set-mark ,group ',action)
         (gnus-info-set-marks ',info ',(gnus-info-marks info) t)
         (gnus-info-set-read ',info ',(gnus-info-read info))
         (when (gnus-group-goto-group ,group)
+          (gnus-get-unread-articles-in-group ',info ',(gnus-active group) t)
           (gnus-group-update-group-line))))
+    (setq action (mapcar (lambda (el) (list (nth 0 el) 'del (nth 2 el)))
+                        action))
+    (gnus-request-set-mark group action)
     (gnus-info-set-read info nil)
     (when (gnus-info-marks info)
       (gnus-info-set-marks info nil))))
@@ -2412,7 +3302,7 @@ If REVERSE, sort in reverse order."
 ;; Group catching up.
 
 (defun gnus-group-catchup-current (&optional n all)
-  "Mark all articles not marked as unread in current newsgroup as read.
+  "Mark all unread articles in the current newsgroup as read.
 If prefix argument N is numeric, the next N newsgroups will be
 caught up.  If ALL is non-nil, marked articles will also be marked as
 read.  Cross references (Xref: header) of articles are ignored.
@@ -2420,7 +3310,8 @@ The number of newsgroups that this function was unable to catch
 up is returned."
   (interactive "P")
   (let ((groups (gnus-group-process-prefix n))
-       (ret 0))
+       (ret 0)
+       group)
     (unless groups (error "No groups selected"))
     (if (not
         (or (not gnus-interactive-catchup) ;Without confirmation?
@@ -2431,24 +3322,23 @@ 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 groups
+      (while (setq group (pop groups))
+       (gnus-group-remove-mark group)
        ;; Virtual groups have to be given special treatment.
-       (let ((method (gnus-find-method-for-group (car groups))))
+       (let ((method (gnus-find-method-for-group group)))
          (when (eq 'nnvirtual (car method))
            (nnvirtual-catchup-group
-            (gnus-group-real-name (car groups)) (nth 1 method) all)))
-       (gnus-group-remove-mark (car groups))
-       (if (>= (gnus-group-group-level) gnus-level-zombie)
+            (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 (car groups))
-               (gnus-group-catchup (car groups) all))
+                 (gnus-group-goto-group group)
+               (gnus-group-catchup group all))
              (gnus-group-update-group-line)
-           (setq ret (1+ ret))))
-       (setq groups (cdr groups)))
+           (setq ret (1+ ret)))))
       (gnus-group-next-unread-group 1)
       ret)))
 
@@ -2463,33 +3353,40 @@ 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))
-        (num (car entry)))
+  (let* ((entry (gnus-group-entry group))
+        (num (car entry))
+        (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.
     (if (not (numberp (car entry)))
        (gnus-message 1 "Can't catch up %s; non-active group" group)
+      (gnus-update-read-articles group nil)
+      (when all
+       ;; Nix out the lists of marks and dormants.
+       (gnus-request-set-mark group (list (list (cdr (assq 'tick marks))
+                                                'del '(tick))
+                                          (list (cdr (assq 'dormant marks))
+                                                'del '(dormant))))
+       (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 (gnus-list-of-unread-articles group))
-       (when all
-         (let ((marks (nth 3 (nth 2 entry))))
-           (gnus-add-marked-articles
-            group 'expire (gnus-uncompress-range (cdr (assq 'tick marks))))
-           (gnus-add-marked-articles
-            group 'expire (gnus-uncompress-range (cdr (assq 'tick marks)))))))
-      (when entry
-       (gnus-update-read-articles group nil)
-       ;; Also nix out the lists of marks and dormants.
-       (when all
-         (gnus-add-marked-articles group 'tick nil nil 'force)
-         (gnus-add-marked-articles group 'dormant nil nil 'force))
-       (let ((gnus-newsgroup-name group))
-         (gnus-run-hooks 'gnus-group-catchup-group-hook))
-       num))))
+        (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)))
 
 (defun gnus-group-expire-articles (&optional n)
-  "Expire all expirable articles in the current newsgroup."
+  "Expire all expirable articles in the current newsgroup.
+Uses the process/prefix convention."
   (interactive "P")
   (let ((groups (gnus-group-process-prefix n))
        group)
@@ -2497,32 +3394,43 @@ or nil if no action could be taken."
       (error "No groups to expire"))
     (while (setq group (pop groups))
       (gnus-group-remove-mark group)
-      (when (gnus-check-backend-function 'request-expire-articles group)
-       (gnus-message 6 "Expiring articles in %s..." group)
-       (let* ((info (gnus-get-info group))
-              (expirable (if (gnus-group-total-expirable-p group)
-                             (cons nil (gnus-list-of-read-articles group))
-                           (assq 'expire (gnus-info-marks info))))
-              (expiry-wait (gnus-group-find-parameter group 'expiry-wait)))
-         (when expirable
-           (setcdr
-            expirable
-            (gnus-compress-sequence
-             (if expiry-wait
-                 ;; We set the expiry variables to the group
-                 ;; parameter.
-                 (let ((nnmail-expiry-wait-function nil)
-                       (nnmail-expiry-wait expiry-wait))
-                   (gnus-request-expire-articles
-                    (gnus-uncompress-sequence (cdr expirable)) group))
-               ;; Just expire using the normal expiry values.
-               (gnus-request-expire-articles
-                (gnus-uncompress-sequence (cdr expirable)) group))))
-           (gnus-close-group group))
-         (gnus-message 6 "Expiring articles in %s...done" group)))
+      (gnus-group-expire-articles-1 group)
       (gnus-dribble-touch)
       (gnus-group-position-point))))
 
+(defun gnus-group-expire-articles-1 (group)
+  (when (gnus-check-backend-function 'request-expire-articles 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))
+                       (assq 'expire (gnus-info-marks info))))
+          (expiry-wait (gnus-group-find-parameter group 'expiry-wait))
+          (nnmail-expiry-target
+           (or (gnus-group-find-parameter group 'expiry-target)
+               nnmail-expiry-target)))
+      (when expirable
+       (gnus-check-group group)
+       (setcdr
+        expirable
+        (gnus-compress-sequence
+         (if expiry-wait
+             ;; We set the expiry variables to the group
+             ;; parameter.
+             (let ((nnmail-expiry-wait-function nil)
+                   (nnmail-expiry-wait expiry-wait))
+               (gnus-request-expire-articles
+                (gnus-uncompress-sequence (cdr expirable)) group))
+           ;; Just expire using the normal expiry values.
+           (gnus-request-expire-articles
+            (gnus-uncompress-sequence (cdr expirable)) group))))
+       (gnus-close-group group))
+      (gnus-message 6 "Expiring articles in %s...done"
+                   (gnus-group-decoded-name group))
+      ;; Return the list of un-expired articles.
+      (cdr expirable))))
+
 (defun gnus-group-expire-all-groups ()
   "Expire all expirable articles in all newsgroups."
   (interactive)
@@ -2539,27 +3447,29 @@ or nil if no action could be taken."
   (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 "Illegal 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)))
+    (error "Invalid level: %d" level))
+  (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)
@@ -2576,26 +3486,22 @@ or nil if no action could be taken."
   "Toggle subscription of the current group.
 If given numerical prefix, toggle the N next groups."
   (interactive "P")
-  (let ((groups (gnus-group-process-prefix n))
-       group)
-    (while groups
-      (setq group (car groups)
-           groups (cdr groups))
-      (gnus-group-remove-mark group)
-      (gnus-group-unsubscribe-group
-       group
-       (cond
-       ((eq do-sub 'unsubscribe)
-        gnus-level-default-unsubscribed)
-       ((eq do-sub 'subscribe)
-        gnus-level-default-subscribed)
-       ((<= (gnus-group-group-level) gnus-level-subscribed)
-        gnus-level-default-unsubscribed)
-       (t
-        gnus-level-default-subscribed))
-       t)
-      (gnus-group-update-group-line))
-    (gnus-group-next-group 1)))
+  (dolist (group (gnus-group-process-prefix n))
+    (gnus-group-remove-mark group)
+    (gnus-group-unsubscribe-group
+     group
+     (cond
+      ((eq do-sub 'unsubscribe)
+       gnus-level-default-unsubscribed)
+      ((eq do-sub 'subscribe)
+       gnus-level-default-subscribed)
+      ((<= (gnus-group-group-level) gnus-level-subscribed)
+       gnus-level-default-unsubscribed)
+      (t
+       gnus-level-default-subscribed))
+     t)
+    (gnus-group-update-group-line))
+  (gnus-group-next-group 1))
 
 (defun gnus-group-unsubscribe-group (group &optional level silent)
   "Toggle subscription to GROUP.
@@ -2607,7 +3513,7 @@ group line."
          (gnus-read-active-file-p)
          nil
          'gnus-group-history)))
-  (let ((newsrc (gnus-gethash group gnus-newsrc-hashtb)))
+  (let ((newsrc (gnus-group-entry group)))
     (cond
      ((string-match "^[ \t]*$" group)
       (error "Empty group name"))
@@ -2631,7 +3537,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)))
@@ -2650,13 +3556,15 @@ N and the number of steps taken is returned."
     (gnus-group-yank-group)
     (gnus-group-position-point)))
 
-(defun gnus-group-kill-all-zombies ()
-  "Kill all zombie newsgroups."
-  (interactive)
-  (setq gnus-killed-list (nconc gnus-zombie-list gnus-killed-list))
-  (setq gnus-zombie-list nil)
-  (gnus-dribble-touch)
-  (gnus-group-list-groups))
+(defun gnus-group-kill-all-zombies (&optional dummy)
+  "Kill all zombie newsgroups.
+The optional DUMMY should always be nil."
+  (interactive (list (not (gnus-yes-or-no-p "Really kill all zombies? "))))
+  (unless dummy
+    (setq gnus-killed-list (nconc gnus-zombie-list gnus-killed-list))
+    (setq gnus-zombie-list nil)
+    (gnus-dribble-touch)
+    (gnus-group-list-groups)))
 
 (defun gnus-group-kill-region (begin end)
   "Kill newsgroups in current region (excluding current point).
@@ -2668,12 +3576,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)))
@@ -2697,7 +3603,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))
@@ -2705,32 +3611,31 @@ of groups killed."
            (push (cons (car entry) (nth 2 entry))
                  gnus-list-of-killed-groups))
          (gnus-group-change-level
-          (if entry entry group) gnus-level-killed (if entry nil level)))
+          (if entry entry group) gnus-level-killed (if entry nil level))
+         (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.
-      (let (entry)
-       (setq groups (nreverse groups))
-       (while groups
-         (gnus-group-remove-mark (setq group (pop groups)))
-         (gnus-delete-line)
-         (push group gnus-killed-list)
-         (setq gnus-newsrc-alist
-               (delq (assoc group gnus-newsrc-alist)
-                     gnus-newsrc-alist))
-         (when gnus-group-change-level-function
-           (funcall gnus-group-change-level-function
-                    group gnus-level-killed 3))
-         (cond
-          ((setq entry (gnus-gethash group gnus-newsrc-hashtb))
-           (push (cons (car entry) (nth 2 entry))
-                 gnus-list-of-killed-groups)
-           (setcdr (cdr entry) (cdddr entry)))
-          ((member group gnus-zombie-list)
-           (setq gnus-zombie-list (delete group gnus-zombie-list))))
-         ;; There may be more than one instance displayed.
-         (while (gnus-group-goto-group group)
-           (gnus-delete-line)))
-       (gnus-make-hashtable-from-newsrc-alist)))
+      (dolist (group (nreverse groups))
+       (gnus-group-remove-mark group)
+       (gnus-delete-line)
+       (push group gnus-killed-list)
+       (setq gnus-newsrc-alist
+             (delq (assoc group gnus-newsrc-alist)
+                   gnus-newsrc-alist))
+       (when gnus-group-change-level-function
+         (funcall gnus-group-change-level-function
+                  group gnus-level-killed 3))
+       (cond
+        ((setq entry (gnus-group-entry group))
+         (push (cons (car entry) (nth 2 entry))
+               gnus-list-of-killed-groups)
+         (setcdr (cdr entry) (cdddr entry)))
+        ((member group gnus-zombie-list)
+         (setq gnus-zombie-list (delete group gnus-zombie-list))))
+       ;; There may be more than one instance displayed.
+       (while (gnus-group-goto-group group)
+         (gnus-delete-line)))
+      (gnus-make-hashtable-from-newsrc-alist))
 
     (gnus-group-position-point)
     (if (< (length out) 2) (car out) (nreverse out))))
@@ -2754,7 +3659,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
@@ -2791,11 +3696,11 @@ yanked) a list of yanked groups is returned."
       (gnus-make-hashtable-from-newsrc-alist)
       (gnus-group-list-groups)))
    (t
-    (error "Can't kill; illegal level: %d" level))))
+    (error "Can't kill; invalid level: %d" level))))
 
 (defun gnus-group-list-all-groups (&optional arg)
   "List all newsgroups with level ARG or lower.
-Default is gnus-level-unsubscribed, which lists all subscribed and most
+Default is `gnus-level-unsubscribed', which lists all subscribed and most
 unsubscribed groups."
   (interactive "P")
   (gnus-group-list-groups (or arg gnus-level-unsubscribed) t))
@@ -2834,7 +3739,8 @@ entail asking the server for the groups."
   (interactive)
   ;; First we make sure that we have really read the active file.
   (unless (gnus-read-active-file-p)
-    (let ((gnus-read-active-file t))
+    (let ((gnus-read-active-file t)
+         (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
@@ -2852,10 +3758,12 @@ entail asking the server for the groups."
        group)
     (erase-buffer)
     (while groups
+      (setq group (pop groups))
       (gnus-add-text-properties
        (point) (prog1 (1+ (point))
                 (insert "       *: "
-                        (setq group (pop groups)) "\n"))
+                        (gnus-group-decoded-name group)
+                        "\n"))
        (list 'gnus-group (gnus-intern-safe group gnus-active-hashtb)
             'gnus-unread t
             'gnus-level (inline (gnus-group-level group)))))
@@ -2874,7 +3782,12 @@ If ARG is a number, it specifies which levels you are interested in
 re-scanning.  If ARG is non-nil and not a number, this will force
 \"hard\" re-reading of the active files from all servers."
   (interactive "P")
-  (let ((gnus-inhibit-demon t))
+  (require 'nnmail)
+  (let ((gnus-inhibit-demon t)
+       ;; Binding this variable will inhibit multiple fetchings
+       ;; of the same mail source.
+       (nnmail-fetched-sources (list t)))
+    (gnus-run-hooks 'gnus-get-top-new-news-hook)
     (gnus-run-hooks 'gnus-get-new-news-hook)
 
     ;; Read any slave files.
@@ -2883,7 +3796,10 @@ re-scanning.  If ARG is non-nil and not a number, this will force
 
     ;; We might read in new NoCeM messages here.
     (when (and gnus-use-nocem
-              (null arg))
+              (or (and (numberp gnus-use-nocem)
+                       (numberp arg)
+                       (>= arg gnus-use-nocem))
+                  (not arg)))
       (gnus-nocem-scan-groups))
     ;; If ARG is not a number, then we read the active file.
     (when (and arg (not (numberp arg)))
@@ -2902,6 +3818,7 @@ re-scanning.  If ARG is non-nil and not a number, this will force
          (gnus-get-unread-articles arg))
       (let ((gnus-read-active-file (if arg nil gnus-read-active-file)))
        (gnus-get-unread-articles arg)))
+    (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)))))
@@ -2909,23 +3826,34 @@ 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))
         (beg (unless n
                (point)))
-        group)
+        group method
+        (gnus-inhibit-demon t)
+        ;; Binding this variable will inhibit multiple fetchings
+        ;; of the same mail source.
+        (nnmail-fetched-sources (list t)))
+    (gnus-run-hooks 'gnus-get-new-news-hook)
     (while (setq group (pop groups))
       (gnus-group-remove-mark group)
       ;; Bypass any previous denials from the server.
-      (gnus-remove-denial (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)
+      (gnus-remove-denial (setq method (gnus-find-method-for-group group)))
+      (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) active))
            (gnus-group-update-group group))
        (if (eq (gnus-server-status (gnus-find-method-for-group group))
                'denied)
@@ -2947,8 +3875,8 @@ to use."
     (gnus-group-group-name)
     (when current-prefix-arg
       (completing-read
-       "Faq dir: " (and (listp gnus-group-faq-directory)
-                       (mapcar (lambda (file) (list file))
+       "FAQ dir: " (and (listp gnus-group-faq-directory)
+                       (mapcar #'list
                                gnus-group-faq-directory))))))
   (unless group
     (error "No group name given"))
@@ -2959,15 +3887,67 @@ to use."
     (while (and (not found)
                (setq dir (pop dirs)))
       (let ((name (gnus-group-real-name group)))
-       (while (string-match "\\." name)
-         (setq name (replace-match "/" t t name)))
-       (setq file (concat (file-name-as-directory dir) name)))
+       (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-group-name)
+            gnus-newsgroup-name)))
+  (unless group
+    (error "No group name given"))
+  (let ((name (gnus-group-real-name group))
+       hierarchy)
+    (when (string-match "\\(^[^\\.]+\\)\\..*" name)
+      (setq hierarchy (match-string 1 name))
+      (if gnus-group-fetch-control-use-browse-url
+         (browse-url (concat "ftp://ftp.isc.org/usenet/control/"
+                             hierarchy "/" name ".gz"))
+       (let ((enable-local-variables nil))
+         (gnus-group-read-ephemeral-group
+          group
+          `(nndoc ,group (nndoc-address
+                          ,(find-file-noselect
+                            (concat "/ftp@ftp.isc.org:/usenet/control/"
+                                    hierarchy "/" name ".gz")))
+                  (nndoc-article-type mbox)) t nil nil))))))
+
 (defun gnus-group-describe-group (force &optional group)
   "Display a description of the current newsgroup."
   (interactive (list current-prefix-arg (gnus-group-group-name)))
@@ -3004,8 +3984,12 @@ to use."
     (mapatoms
      (lambda (group)
        (setq b (point))
-       (insert (format "      *: %-20s %s\n" (symbol-name group)
-                      (symbol-value group)))
+       (let ((charset (gnus-group-name-charset nil (symbol-name group))))
+        (insert (format "      *: %-20s %s\n"
+                        (gnus-group-name-decode
+                         (symbol-name group) charset)
+                        (gnus-group-name-decode
+                         (symbol-value group) charset))))
        (gnus-add-text-properties
        b (1+ b) (list 'gnus-group group
                       'gnus-unread t 'gnus-marked nil
@@ -3026,6 +4010,7 @@ to use."
      (lambda (group)
        (and (symbol-name group)
            (string-match regexp (symbol-name group))
+           (symbol-value group)
            (push (symbol-name group) groups)))
      gnus-active-hashtb)
     ;; Also go through all descriptions that are known to Gnus.
@@ -3033,7 +4018,6 @@ to use."
       (mapatoms
        (lambda (group)
         (and (string-match regexp (symbol-value group))
-             (gnus-active (symbol-name group))
              (push (symbol-name group) groups)))
        gnus-description-hashtb))
     (if (not groups)
@@ -3041,23 +4025,25 @@ to use."
       ;; Print out all the groups.
       (save-excursion
        (pop-to-buffer "*Gnus Help*")
-       (buffer-disable-undo (current-buffer))
+       (buffer-disable-undo)
        (erase-buffer)
        (setq groups (sort groups 'string<))
        (while groups
          ;; Groups may be entered twice into the list of groups.
          (when (not (string= (car groups) prev))
-           (insert (setq prev (car groups)) "\n")
-           (when (and gnus-description-hashtb
-                      (setq des (gnus-gethash (car groups)
-                                              gnus-description-hashtb)))
-             (insert "  " des "\n")))
+           (setq prev (car groups))
+           (let ((charset (gnus-group-name-charset nil prev)))
+             (insert (gnus-group-name-decode prev charset) "\n")
+             (when (and gnus-description-hashtb
+                        (setq des (gnus-gethash (car groups)
+                                                gnus-description-hashtb)))
+               (insert "  " (gnus-group-name-decode des charset) "\n"))))
          (setq groups (cdr groups)))
        (goto-char (point-min))))
     (pop-to-buffer obuf)))
 
 (defun gnus-group-description-apropos (regexp)
-  "List all newsgroups that have names or descriptions that match a regexp."
+  "List all newsgroups that have names or descriptions that match REGEXP."
   (interactive "sGnus description apropos (regexp): ")
   (when (not (or gnus-description-hashtb
                 (gnus-read-all-descriptions-files)))
@@ -3078,8 +4064,8 @@ This command may read the active file."
   (when (and level
             (> (prefix-numeric-value level) gnus-level-killed))
     (gnus-get-killed-groups))
-  (gnus-group-prepare-flat
-   (or level gnus-level-subscribed) all (or lowest 1) regexp)
+  (funcall gnus-group-prepare-function
+   (or level gnus-level-subscribed) (and all t) (or lowest 1) regexp)
   (goto-char (point-min))
   (gnus-group-position-point))
 
@@ -3125,13 +4111,15 @@ group."
 
 (defun gnus-group-find-new-groups (&optional arg)
   "Search for new groups and add them.
-Each new group will be treated with `gnus-subscribe-newsgroup-method.'
-If ARG (the prefix), use the `ask-server' method to query
-the server for new groups."
-  (interactive "P")
-  (gnus-find-new-newsgroups arg)
+Each new group will be treated with `gnus-subscribe-newsgroup-method'.
+With 1 C-u, use the `ask-server' method to query the server for new
+groups.
+With 2 C-u's, use most complete method possible to query the server
+for new groups, and subscribe the new groups as zombies."
+  (interactive "p")
+  (gnus-find-new-newsgroups (or arg 1))
   (gnus-group-list-groups))
-  
+
 (defun gnus-group-edit-global-kill (&optional article group)
   "Edit the global kill file.
 If GROUP, edit that local kill file instead."
@@ -3154,23 +4142,26 @@ 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.
-The hook gnus-suspend-gnus-hook is called before actually suspending."
+The hook `gnus-suspend-gnus-hook' is called before actually suspending."
   (interactive)
   (gnus-run-hooks 'gnus-suspend-gnus-hook)
+  (gnus-offer-save-summaries)
   ;; Kill Gnus buffers except for group mode buffer.
-  (let* ((group-buf (get-buffer gnus-group-buffer))
-        ;; Do this on a separate list in case the user does a ^G before we finish
-        (gnus-buffer-list
-         (delete group-buf (delete gnus-dribble-buffer
-                                   (append gnus-buffer-list nil)))))
-    (while gnus-buffer-list
-      (gnus-kill-buffer (pop gnus-buffer-list)))
+  (let ((group-buf (get-buffer gnus-group-buffer)))
+    (mapcar (lambda (buf)
+             (unless (or (member buf (list group-buf gnus-dribble-buffer))
+                         (with-current-buffer buf
+                           (eq major-mode 'message-mode)))
+               (gnus-kill-buffer buf)))
+           (gnus-buffers))
+    (setq gnus-backlog-articles nil)
     (gnus-kill-gnus-frames)
     (when group-buf
-      (setq gnus-buffer-list (list group-buf))
       (bury-buffer group-buf)
       (delete-windows-on group-buf t))))
 
@@ -3215,6 +4206,12 @@ The hook `gnus-exit-gnus-hook' is called before actually exiting."
                     (file-name-nondirectory gnus-current-startup-file))))
     (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)
+                           (buffer-size)))))
+      (gnus-dribble-enter
+       ";;; Gnus was exited on purpose without saving the .newsrc files."))
     (gnus-dribble-save)
     (gnus-close-backends)
     (gnus-clear-system)
@@ -3235,76 +4232,75 @@ 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 backend: "
+                    "Which back end: "
                     (append gnus-valid-select-methods gnus-server-alist)
                     nil t (cons "nntp" 0) 'gnus-method-history)))
-          ;; We either got a backend name or a virtual server name.
+          ;; 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)))
+                     (mapcar 'list 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)
-  (let* ((entry (gnus-gethash
-                (or method-only-group (gnus-info-group info))
-                gnus-newsrc-hashtb))
-        (part-info info)
-        (info (if method-only-group (nth 2 entry) info))
-        method)
-    (when method-only-group
+  (when (or info part)
+    (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)
+      (when method-only-group
+       (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
+       ;; whether we have to extend the info, and if so, do that.
+       (let ((len (length info))
+             (total (if (eq part 'method) 5 6)))
+         (when (< len total)
+           (setcdr (nthcdr (1- len) info)
+                   (make-list (- total len) nil)))
+         ;; Then we enter the new info.
+         (setcar (nthcdr (1- total) info) part-info)))
       (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
-      ;; whether we have to extend the info, and if so, do that.
-      (let ((len (length info))
-           (total (if (eq part 'method) 5 6)))
-       (when (< len total)
-         (setcdr (nthcdr (1- len) info)
-                 (make-list (- total len) nil)))
-       ;; Then we enter the new info.
-       (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)
-       (setq method (gnus-info-method info))
-       (when (gnus-server-equal method "native")
-         (setq method nil))
+       ;; This is a new group, so we just create it.
        (save-excursion
          (set-buffer gnus-group-buffer)
-         (if method
-             ;; It's a foreign group...
-             (gnus-group-make-group
-              (gnus-group-real-name (gnus-info-group info))
-              (if (stringp method) method
-                (prin1-to-string (car method)))
-              (and (consp method)
-                   (nth 1 (gnus-info-method info))))
-           ;; It's a native group.
-           (gnus-group-make-group (gnus-info-group info))))
-       (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))))
-    ;; Whether it was a new group or not, we now have the entry, so we
-    ;; can do the update.
-    (if entry
-       (progn
-         (setcar (nthcdr 2 entry) info)
-         (when (and (not (eq (car entry) t))
-                    (gnus-active (gnus-info-group info)))
-           (setcar entry (length (gnus-list-of-unread-articles (car info))))))
-      (error "No such group: %s" (gnus-info-group info)))))
+         (setq method (gnus-info-method info))
+         (when (gnus-server-equal method "native")
+           (setq method nil))
+         (save-excursion
+           (set-buffer gnus-group-buffer)
+           (if method
+               ;; It's a foreign group...
+               (gnus-group-make-group
+                (gnus-group-real-name (gnus-info-group info))
+                (if (stringp method) method
+                  (prin1-to-string (car method)))
+                (and (consp method)
+                     (nth 1 (gnus-info-method info))))
+             ;; It's a native group.
+             (gnus-group-make-group (gnus-info-group info))))
+         (gnus-message 6 "Note: New group created")
+         (setq entry
+               (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
+         (progn
+           (setcar (nthcdr 2 entry) info)
+           (when (and (not (eq (car entry) t))
+                      (gnus-active (gnus-info-group info)))
+             (setcar entry (length
+                            (gnus-list-of-unread-articles (car info))))))
+       (error "No such group: %s" (gnus-info-group info))))))
 
 (defun gnus-group-set-method-info (group select-method)
   (gnus-group-set-info select-method group 'method))
@@ -3314,10 +4310,9 @@ and the second element is the address."
 
 (defun gnus-add-marked-articles (group type articles &optional info force)
   ;; Add ARTICLES of TYPE to the info of GROUP.
-  ;; If INFO is non-nil, use that info.         If FORCE is non-nil, don't
+  ;; If INFO is non-nil, use that info.  If FORCE is non-nil, don't
   ;; add, but replace marked articles of TYPE with ARTICLES.
   (let ((info (or info (gnus-get-info group)))
-       (uncompressed '(score bookmark killed))
        marked m)
     (or (not info)
        (and (not (setq marked (nthcdr 3 info)))
@@ -3333,12 +4328,22 @@ and the second element is the address."
        (if force
            (if (null articles)
                (setcar (nthcdr 3 info)
-                       (delq (assq type (car marked)) (car marked)))
+                       (gnus-delete-alist type (car marked)))
              (setcdr m (gnus-compress-sequence articles t)))
          (setcdr m (gnus-compress-sequence
                     (sort (nconc (gnus-uncompress-range (cdr m))
                                  (copy-sequence articles)) '<) t))))))
 
+(defun gnus-add-mark (group mark article)
+  "Mark ARTICLE in GROUP with MARK, whether the group is displayed or not."
+  (let ((buffer (gnus-summary-buffer-name group)))
+    (if (gnus-buffer-live-p buffer)
+       (save-excursion
+         (set-buffer (get-buffer buffer))
+         (gnus-summary-add-mark article mark))
+      (gnus-add-marked-articles group (cdr (assq mark gnus-article-mark-lists))
+                               (list article)))))
+
 ;;;
 ;;; Group timestamps
 ;;;
@@ -3359,8 +4364,8 @@ or `gnus-group-catchup-group-hook'."
 (defun gnus-group-timestamp-delta (group)
   "Return the offset in seconds from the timestamp for GROUP to the current time, as a floating point number."
   (let* ((time (or (gnus-group-timestamp group)
-                 (list 0 0)))
-         (delta (gnus-time-minus (current-time) time)))
+                  (list 0 0)))
+        (delta (subtract-time (current-time) time)))
     (+ (* (nth 0 delta) 65536.0)
        (nth 1 delta))))
 
@@ -3371,6 +4376,168 @@ or `gnus-group-catchup-group-hook'."
        ""
       (gnus-time-iso8601 time))))
 
+(defun gnus-group-list-cached (level &optional lowest)
+  "List all groups with cached articles.
+If the prefix LEVEL is non-nil, it should be a number that says which
+level to cut off listing groups.
+If LOWEST, don't list groups with level lower than LOWEST.
+
+This command may read the active file."
+  (interactive "P")
+  (when level
+    (setq level (prefix-numeric-value level)))
+  (when (or (not level) (>= level gnus-level-zombie))
+    (gnus-cache-open))
+  (funcall gnus-group-prepare-function
+          (or level gnus-level-subscribed)
+          #'(lambda (info)
+              (let ((marks (gnus-info-marks info)))
+                (assq 'cache marks)))
+          lowest
+          #'(lambda (group)
+              (or (gnus-gethash group
+                                gnus-cache-active-hashtb)
+                  ;; Cache active file might use "."
+                  ;; instead of ":".
+                  (gnus-gethash
+                   (mapconcat 'identity
+                              (split-string group ":")
+                              ".")
+                   gnus-cache-active-hashtb))))
+  (goto-char (point-min))
+  (gnus-group-position-point))
+
+(defun gnus-group-list-dormant (level &optional lowest)
+  "List all groups with dormant articles.
+If the prefix LEVEL is non-nil, it should be a number that says which
+level to cut off listing groups.
+If LOWEST, don't list groups with level lower than LOWEST.
+
+This command may read the active file."
+  (interactive "P")
+  (when level
+    (setq level (prefix-numeric-value level)))
+  (when (or (not level) (>= level gnus-level-zombie))
+    (gnus-cache-open))
+  (funcall gnus-group-prepare-function
+          (or level gnus-level-subscribed)
+          #'(lambda (info)
+              (let ((marks (gnus-info-marks info)))
+                (assq 'dormant marks)))
+          lowest
+          'ignore)
+  (goto-char (point-min))
+  (gnus-group-position-point))
+
+(defun gnus-group-listed-groups ()
+  "Return a list of listed groups."
+  (let (point groups)
+    (goto-char (point-min))
+    (while (setq point (text-property-not-all (point) (point-max)
+                                             'gnus-group nil))
+      (goto-char point)
+      (push (symbol-name (get-text-property point 'gnus-group)) groups)
+      (forward-char 1))
+    groups))
+
+(defun gnus-group-list-plus (&optional args)
+  "List groups plus the current selection."
+  (interactive "P")
+  (let ((gnus-group-listed-groups (gnus-group-listed-groups))
+       (gnus-group-list-mode gnus-group-list-mode) ;; Save it.
+       func)
+    (push last-command-event unread-command-events)
+    (if (featurep 'xemacs)
+       (push (make-event 'key-press '(key ?A)) unread-command-events)
+      (push ?A unread-command-events))
+    (let (gnus-pick-mode keys)
+      (setq keys (if (featurep 'xemacs)
+                    (events-to-keys (read-key-sequence nil))
+                  (read-key-sequence nil)))
+      (setq func (lookup-key (current-local-map) keys)))
+    (if (or (not func)
+           (numberp func))
+       (ding)
+      (call-interactively func))))
+
+(defun gnus-group-list-flush (&optional args)
+  "Flush groups from the current selection."
+  (interactive "P")
+  (let ((gnus-group-list-option 'flush))
+    (gnus-group-list-plus args)))
+
+(defun gnus-group-list-limit (&optional args)
+  "List groups limited within the current selection."
+  (interactive "P")
+  (let ((gnus-group-list-option 'limit))
+    (gnus-group-list-plus args)))
+
+(defun gnus-group-mark-article-read (group article)
+  "Mark ARTICLE read."
+  (let ((buffer (gnus-summary-buffer-name group))
+       (mark gnus-read-mark)
+       active n)
+    (if (get-buffer buffer)
+       (with-current-buffer buffer
+         (setq active gnus-newsgroup-active)
+         (gnus-activate-group group)
+         (when gnus-newsgroup-prepared
+           (when (and gnus-newsgroup-auto-expire
+                      (memq mark gnus-auto-expirable-marks))
+             (setq mark gnus-expirable-mark))
+           (setq mark (gnus-request-update-mark
+                       group article mark))
+           (gnus-mark-article-as-read article mark)
+           (setq gnus-newsgroup-active (gnus-active group))
+           (when active
+             (setq n (1+ (cdr active)))
+             (while (<= n (cdr gnus-newsgroup-active))
+               (unless (eq n article)
+                 (push n gnus-newsgroup-unselected))
+               (setq n (1+ n)))
+             (setq gnus-newsgroup-unselected
+                   (nreverse gnus-newsgroup-unselected)))))
+      (gnus-activate-group group)
+      (gnus-group-make-articles-read group (list article))
+      (when (gnus-group-auto-expirable-p group)
+       (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)
 
+;;; arch-tag: 2eb5440f-0bca-4091-814c-e37817536af6
 ;;; gnus-group.el ends here