*** empty log message ***
[gnus] / lisp / gnus-group.el
index 25f82f1..0af00bd 100644 (file)
@@ -1,7 +1,7 @@
 ;;; gnus-group.el --- group mode commands for Gnus
-;; Copyright (C) 1996 Free Software Foundation, Inc.
+;; Copyright (C) 1996,97,98,99 Free Software Foundation, Inc.
 
-;; Author: Lars Magne Ingebrigtsen <larsi@ifi.uio.no>
+;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
 ;; Keywords: news
 
 ;; This file is part of GNU Emacs.
 
 ;;; Code:
 
-(require 'gnus-load)
+(eval-when-compile (require 'cl))
+
+(require 'gnus)
 (require 'gnus-start)
 (require 'nnmail)
 (require 'gnus-spec)
 (require 'gnus-int)
 (require 'gnus-range)
 (require 'gnus-win)
-
-(defvar gnus-group-archive-directory
-  "/ftp@ftp.hpc.uh.edu:/pub/emacs/ding-list/"
-  "*The address of the (ding) archives.")
-
-(defvar gnus-group-recent-archive-directory
-  "/ftp@ftp.hpc.uh.edu:/pub/emacs/ding-list-recent/"
-  "*The address of the most recent (ding) articles.")
-
-;; Suggested by Andrew Eskilsson <pi92ae@lelle.pt.hk-r.se>.
-(defvar gnus-no-groups-message "No news is horrible news"
-  "*Message displayed by Gnus when no groups are available.")
-
-(defvar gnus-keep-same-level nil
+(require 'gnus-undo)
+(require 'time-date)
+
+(defcustom gnus-group-archive-directory
+  "*ftp@ftp.hpc.uh.edu:/pub/emacs/ding-list/"
+  "*The address of the (ding) archives."
+  :group 'gnus-group-foreign
+  :type 'directory)
+
+(defcustom gnus-group-recent-archive-directory
+  "*ftp@ftp.hpc.uh.edu:/pub/emacs/ding-list-recent/"
+  "*The address of the most recent (ding) articles."
+  :group 'gnus-group-foreign
+  :type 'directory)
+
+(defcustom gnus-no-groups-message "No news is no news"
+  "*Message displayed by Gnus when no groups are available."
+  :group 'gnus-start
+  :type 'string)
+
+(defcustom gnus-keep-same-level nil
   "*Non-nil means that the next newsgroup after the current will be on the same level.
 When you type, for instance, `n' after reading the last article in the
 current newsgroup, you will go to the next newsgroup.  If this variable
@@ -56,43 +65,75 @@ next newsgroup with the same level, or, if no such newsgroup is
 available, the next newsgroup with the lowest possible level higher
 than the current level.
 If this variable is `best', Gnus will make the next newsgroup the one
-with the best level.")
-
-(defvar gnus-group-goto-unread t
-  "*If non-nil, movement commands will go to the next unread and subscribed group.")
-
-(defvar gnus-goto-next-group-when-activating t
-  "*If non-nil, the \\<gnus-group-mode-map>\\[gnus-group-get-new-news-this-group] command will advance point to the next group.")
-
-(defvar gnus-permanently-visible-groups nil
+with the best level."
+  :group 'gnus-group-levels
+  :type '(choice (const nil)
+                (const best)
+                (sexp :tag "other" t)))
+
+(defcustom gnus-group-goto-unread t
+  "*If non-nil, movement commands will go to the next unread and subscribed group."
+  :link '(custom-manual "(gnus)Group Maneuvering")
+  :group 'gnus-group-various
+  :type 'boolean)
+
+(defcustom gnus-goto-next-group-when-activating t
+  "*If non-nil, the \\<gnus-group-mode-map>\\[gnus-group-get-new-news-this-group] command will advance point to the next group."
+  :link '(custom-manual "(gnus)Scanning New Messages")
+  :group 'gnus-group-various
+  :type 'boolean)
+
+(defcustom gnus-permanently-visible-groups nil
   "*Regexp to match groups that should always be listed in the group buffer.
-This means that they will still be listed when there are no unread
-articles in the groups.")
+This means that they will still be listed even when there are no
+unread articles in the groups.
 
-(defvar gnus-list-groups-with-ticked-articles t
+If nil, no groups are permanently visible."
+  :group 'gnus-group-listing
+  :type 'regexp)
+
+(defcustom gnus-list-groups-with-ticked-articles t
   "*If non-nil, list groups that have only ticked articles.
-If nil, only list groups that have unread articles.")
+If nil, only list groups that have unread articles."
+  :group 'gnus-group-listing
+  :type 'boolean)
 
-(defvar gnus-group-default-list-level gnus-level-subscribed
+(defcustom gnus-group-default-list-level gnus-level-subscribed
   "*Default listing level.
-Ignored if `gnus-group-use-permanent-levels' is non-nil.")
+Ignored if `gnus-group-use-permanent-levels' is non-nil."
+  :group 'gnus-group-listing
+  :type 'integer)
 
-(defvar gnus-group-list-inactive-groups t
-  "*If non-nil, inactive groups will be listed.")
+(defcustom gnus-group-list-inactive-groups t
+  "*If non-nil, inactive groups will be listed."
+  :group 'gnus-group-listing
+  :group 'gnus-group-levels
+  :type 'boolean)
 
-(defvar gnus-group-sort-function 'gnus-group-sort-by-alphabet
+(defcustom gnus-group-sort-function 'gnus-group-sort-by-alphabet
   "*Function used for sorting the group buffer.
 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-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-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'.
 
 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.")
-
-(defvar gnus-group-line-format "%M\%S\%p\%P\%5y: %(%g%)%l\n"
+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"
   "*Format of group lines.
 It works along the same lines as a normal formatting string,
 with some simple extensions.
@@ -115,9 +156,11 @@ with some simple extensions.
 %p    Process mark (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.
 %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
@@ -135,30 +178,47 @@ 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.
+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.")
+effect."
+  :group 'gnus-group-visual
+  :type 'string)
 
-(defvar gnus-group-mode-line-format "Gnus: %%b {%M\%:%S}"
+(defcustom gnus-group-mode-line-format "Gnus: %%b {%M\%:%S}"
   "*The format specification for the group mode line.
 It works along the same lines as a normal formatting string,
 with some simple extensions:
 
 %S   The native news server.
 %M   The native select method.
-%:   \":\" if %S isn't \"\".")
-
-(defvar gnus-group-mode-hook nil
-  "*A hook for Gnus group mode.")
-
-(defvar gnus-group-catchup-group-hook nil
-  "*A hook run when catching up a group from the group buffer.")
-
-(defvar gnus-group-update-group-hook nil
-  "*A hook called when updating group lines.")
-
-(defvar gnus-group-prepare-function 'gnus-group-prepare-flat
+%:   \":\" if %S isn't \"\"."
+  :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)
+
+(defcustom gnus-group-menu-hook nil
+  "Hook run after the creation of the group mode menu."
+  :group 'gnus-group-various
+  :type 'hook)
+
+(defcustom gnus-group-catchup-group-hook nil
+  "Hook run when catching up a group from the group buffer."
+  :group 'gnus-group-various
+  :link '(custom-manual "(gnus)Group Data")
+  :type 'hook)
+
+(defcustom gnus-group-update-group-hook nil
+  "Hook called when updating group lines."
+  :group 'gnus-group-visual
+  :type 'hook)
+
+(defcustom gnus-group-prepare-function 'gnus-group-prepare-flat
   "*A function that is called to generate the group buffer.
 The function is called with three arguments: The first is a number;
 all group with a level less or equal to that number should be listed,
@@ -166,33 +226,151 @@ if the second is non-nil, empty groups should also be displayed.  If
 the third is non-nil, it is a number.  No groups with a level lower
 than this number should be displayed.
 
-The only current function implemented is `gnus-group-prepare-flat'.")
-
-(defvar gnus-group-prepare-hook nil
-  "*A hook called after the group buffer has been generated.
-If you want to modify the group buffer, you can use this hook.")
-
-(defvar gnus-suspend-gnus-hook nil
-  "*A hook called when suspending (not exiting) Gnus.")
-
-(defvar gnus-exit-gnus-hook nil
-  "*A hook called when exiting Gnus.")
-
-(defvar gnus-after-exiting-gnus-hook nil
-  "*A hook called after exiting Gnus.")
-
-(defvar gnus-group-update-hook '(gnus-group-highlight-line)
-  "*A hook called when a group line is changed.
+The only current function implemented is `gnus-group-prepare-flat'."
+  :group 'gnus-group-listing
+  :type 'function)
+
+(defcustom gnus-group-prepare-hook nil
+  "Hook called after the group buffer has been generated.
+If you want to modify the group buffer, you can use this hook."
+  :group 'gnus-group-listing
+  :type 'hook)
+
+(defcustom gnus-suspend-gnus-hook nil
+  "Hook called when suspending (not exiting) Gnus."
+  :group 'gnus-exit
+  :type 'hook)
+
+(defcustom gnus-exit-gnus-hook nil
+  "Hook called when exiting Gnus."
+  :group 'gnus-exit
+  :type 'hook)
+
+(defcustom gnus-after-exiting-gnus-hook nil
+  "Hook called after exiting Gnus."
+  :group 'gnus-exit
+  :type 'hook)
+
+(defcustom gnus-group-update-hook '(gnus-group-highlight-line)
+  "Hook called when a group line is changed.
 The hook will not be called if `gnus-visual' is nil.
 
 The default function `gnus-group-highlight-line' will
 highlight the line according to the `gnus-group-highlight'
-variable.")
+variable."
+  :group 'gnus-group-visual
+  :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"
+     "gnus-help"
+     (nndoc "gnus-help"
+           (nndoc-article-type mbox)
+           (eval `(nndoc-address
+                   ,(let ((file (nnheader-find-etc-directory
+                                 "gnus-tut.txt" t)))
+                      (unless file
+                        (error "Couldn't find doc group"))
+                      file))))))
+  "*Alist of useful group-server pairs."
+  :group 'gnus-group-listing
+  :type '(repeat (list (string :tag "Description")
+                      (string :tag "Name")
+                      (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) (eq level 4)) .
+     gnus-group-news-4-empty-face)
+    ((and (not mailp) (eq level 4)) .
+     gnus-group-news-4-face)
+    ((and (= unread 0) (not mailp) (eq level 5)) .
+     gnus-group-news-5-empty-face)
+    ((and (not mailp) (eq level 5)) .
+     gnus-group-news-5-face)
+    ((and (= unread 0) (not mailp) (eq level 6)) .
+     gnus-group-news-6-empty-face)
+    ((and (not mailp) (eq level 6)) .
+     gnus-group-news-6-face)
+    ((and (= unread 0) (not mailp)) .
+     gnus-group-news-low-empty-face)
+    ((and (not mailp)) .
+     gnus-group-news-low-face)
+    ;; Mail.
+    ((and (= unread 0) (eq level 1)) .
+     gnus-group-mail-1-empty-face)
+    ((eq level 1) .
+     gnus-group-mail-1-face)
+    ((and (= unread 0) (eq level 2)) .
+     gnus-group-mail-2-empty-face)
+    ((eq level 2) .
+     gnus-group-mail-2-face)
+    ((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)
+    (t .
+     gnus-group-mail-low-face))
+  "*Controls the highlighting of group buffer lines.
+
+Below is a list of `Form'/`Face' pairs.  When deciding how a a
+particular group line should be displayed, each form is
+evaluated.  The content of the face field after the first true form is
+used.  You can change how those group lines are displayed by
+editing the face field.
+
+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.
+level: The level of the group.
+score: The score of the group.
+ticked: The number of ticked articles."
+  :group 'gnus-group-visual
+  :type '(repeat (cons (sexp :tag "Form") face)))
+
+(defcustom gnus-new-mail-mark ?%
+  "Mark used for groups with new mail."
+  :group 'gnus-group-visual
+  :type 'character)
 
 ;;; Internal variables
 
+(defvar gnus-group-sort-alist-function 'gnus-group-sort-flat
+  "Function for sorting the group buffer.")
+
+(defvar gnus-group-sort-selected-function 'gnus-group-sort-selected-flat
+  "Function for sorting the selected groups in the group buffer.")
+
 (defvar gnus-group-indentation-function nil)
 (defvar gnus-goto-missing-group-function nil)
+(defvar gnus-group-update-group-function nil)
 (defvar gnus-group-goto-next-group-function nil
   "Function to override finding the next group after listing groups.")
 
@@ -203,7 +381,7 @@ variable.")
     (?S gnus-tmp-subscribed ?c)
     (?L gnus-tmp-level ?d)
     (?N (cond ((eq number t) "*" )
-             ((numberp number) 
+             ((numberp number)
               (int-to-string
                (+ number
                   (gnus-range-length (cdr (assq 'dormant gnus-tmp-marked)))
@@ -228,6 +406,8 @@ variable.")
     (?P gnus-group-indentation ?s)
     (?l gnus-tmp-grouplens ?s)
     (?z gnus-tmp-news-method-string ?s)
+    (?m (gnus-group-new-mail gnus-tmp-group) ?c)
+    (?d (gnus-group-timestamp-string gnus-tmp-group) ?s)
     (?u gnus-tmp-user-defined ?s)))
 
 (defvar gnus-group-mode-line-format-alist
@@ -258,11 +438,13 @@ variable.")
     "=" 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
@@ -273,6 +455,7 @@ variable.")
     "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
@@ -282,7 +465,7 @@ variable.")
     "r" gnus-group-read-init-file
     "B" gnus-group-browse-foreign-server
     "b" gnus-group-check-bogus-groups
-    "F" gnus-find-new-newsgroups
+    "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
@@ -300,7 +483,6 @@ variable.")
     "V" gnus-version
     "s" gnus-group-save-newsrc
     "z" gnus-group-suspend
-                                       ;    "Z" gnus-group-clear-dribble
     "q" gnus-group-exit
     "Q" gnus-group-quit
     "?" gnus-group-describe-briefly
@@ -322,13 +504,14 @@ variable.")
     "m" gnus-group-mark-group
     "u" gnus-group-unmark-group
     "w" gnus-group-mark-region
-    "m" gnus-group-mark-buffer
+    "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
@@ -339,7 +522,9 @@ variable.")
     "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)
 
@@ -359,6 +544,15 @@ variable.")
     "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
@@ -375,7 +569,9 @@ variable.")
     "f" gnus-score-flush-cache)
 
   (gnus-define-keys (gnus-group-help-map "H" gnus-group-mode-map)
-    "f" gnus-group-fetch-faq)
+    "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
@@ -387,6 +583,172 @@ variable.")
     "\C-k" gnus-group-kill-level
     "z" gnus-group-kill-all-zombies))
 
+(defun gnus-group-make-menu-bar ()
+  (gnus-turn-off-edit-menu 'group)
+  (unless (boundp 'gnus-group-reading-menu)
+
+    (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)]
+       ["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 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)]
+       ["Toggle subscription" gnus-group-unsubscribe-current-group
+       (gnus-group-group-name)]
+       ["Kill" gnus-group-kill-group (gnus-group-group-name)]
+       ["Yank" gnus-group-yank-group gnus-list-of-killed-groups]
+       ["Describe" gnus-group-describe-group (gnus-group-group-name)]
+       ["Fetch FAQ" gnus-group-fetch-faq (gnus-group-group-name)]
+       ;; 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
+       (gnus-group-group-name)]
+       ["Select quick" gnus-group-quick-select-group (gnus-group-group-name)]
+       ["Customize" gnus-group-customize (gnus-group-group-name)]
+       ("Edit"
+       ["Parameters" gnus-group-edit-group-parameters
+        (gnus-group-group-name)]
+       ["Select method" gnus-group-edit-group-method
+        (gnus-group-group-name)]
+       ["Info" gnus-group-edit-group (gnus-group-group-name)]
+       ["Local kill file" gnus-group-edit-local-kill (gnus-group-group-name)]
+       ["Global kill file" gnus-group-edit-global-kill t])))
+
+    (easy-menu-define
+     gnus-group-group-menu gnus-group-mode-map ""
+     '("Groups"
+       ("Listing"
+       ["List unread subscribed groups" gnus-group-list-groups t]
+       ["List (un)subscribed groups" gnus-group-list-all-groups t]
+       ["List killed groups" gnus-group-list-killed gnus-killed-list]
+       ["List zombie groups" gnus-group-list-zombies gnus-zombie-list]
+       ["List level..." gnus-group-list-level t]
+       ["Describe all groups" gnus-group-describe-all-groups t]
+       ["Group apropos..." gnus-group-apropos t]
+       ["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])
+       ("Sort"
+       ["Default sort" gnus-group-sort-groups t]
+       ["Sort by method" gnus-group-sort-groups-by-method t]
+       ["Sort by rank" gnus-group-sort-groups-by-rank t]
+       ["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 process/prefixed"
+       ["Default sort" gnus-group-sort-selected-groups
+        (or (not (boundp 'gnus-topic-mode)) (not gnus-topic-mode))]
+       ["Sort by method" gnus-group-sort-selected-groups-by-method
+        (or (not (boundp 'gnus-topic-mode)) (not gnus-topic-mode))]
+       ["Sort by rank" gnus-group-sort-selected-groups-by-rank
+        (or (not (boundp 'gnus-topic-mode)) (not gnus-topic-mode))]
+       ["Sort by score" gnus-group-sort-selected-groups-by-score
+        (or (not (boundp 'gnus-topic-mode)) (not gnus-topic-mode))]
+       ["Sort by level" gnus-group-sort-selected-groups-by-level
+        (or (not (boundp 'gnus-topic-mode)) (not gnus-topic-mode))]
+       ["Sort by unread" gnus-group-sort-selected-groups-by-unread
+        (or (not (boundp 'gnus-topic-mode)) (not gnus-topic-mode))]
+       ["Sort by name" gnus-group-sort-selected-groups-by-alphabet
+        (or (not (boundp 'gnus-topic-mode)) (not gnus-topic-mode))])
+       ("Mark"
+       ["Mark group" gnus-group-mark-group
+        (and (gnus-group-group-name)
+             (not (memq (gnus-group-group-name) gnus-group-marked)))]
+       ["Unmark group" gnus-group-unmark-group
+        (and (gnus-group-group-name)
+             (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 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]
+       ["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]
+       ["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
+        (gnus-check-backend-function
+         'request-rename-group (gnus-group-group-name))]
+       ["Delete group" gnus-group-delete-group
+        (gnus-check-backend-function
+         'request-delete-group (gnus-group-group-name))])
+       ("Move"
+       ["Next" gnus-group-next-group t]
+       ["Previous" gnus-group-prev-group t]
+       ["Next unread" gnus-group-next-unread-group t]
+       ["Previous unread" gnus-group-prev-unread-group t]
+       ["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]
+       ["First unread group" gnus-group-first-unread-group t]
+       ["Best unread group" gnus-group-best-unread-group t])
+       ["Delete bogus groups" gnus-group-check-bogus-groups t]
+       ["Find new newsgroups" gnus-group-find-new-groups t]
+       ["Transpose" gnus-group-transpose-groups
+       (gnus-group-group-name)]
+       ["Read a directory as a group..." gnus-group-enter-directory t]))
+
+    (easy-menu-define
+     gnus-group-misc-menu gnus-group-mode-map ""
+     '("Misc"
+       ("SOUP"
+       ["Pack replies" nnsoup-pack-replies (fboundp 'nnsoup-request-group)]
+       ["Send replies" gnus-soup-send-replies
+        (fboundp 'gnus-soup-pack-packet)]
+       ["Pack packet" gnus-soup-pack-packet (fboundp 'gnus-soup-pack-packet)]
+       ["Save areas" gnus-soup-save-areas (fboundp 'gnus-soup-pack-packet)]
+       ["Brew SOUP" gnus-group-brew-soup (fboundp 'gnus-soup-pack-packet)])
+       ["Send a 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]
+       ["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]
+       ["Enter server buffer" gnus-group-enter-server-mode t]
+       ["Expire all expirable articles" gnus-group-expire-all-groups t]
+       ["Generate any kiboze groups" nnkiboze-generate-groups t]
+       ["Gnus version" gnus-version t]
+       ["Save .newsrc files" gnus-group-save-newsrc t]
+       ["Suspend Gnus" gnus-group-suspend t]
+       ["Clear dribble buffer" gnus-group-clear-dribble t]
+       ["Read manual" gnus-info-find-node t]
+       ["Flush score cache" gnus-score-flush-cache t]
+       ["Toggle topics" gnus-topic-mode t]
+       ["Send a bug report" gnus-bug t]
+       ["Exit from Gnus" gnus-group-exit t]
+       ["Exit without saving" gnus-group-quit t]))
+
+    (gnus-run-hooks 'gnus-group-menu-hook)))
+
 (defun gnus-group-mode ()
   "Major mode for reading news.
 
@@ -405,8 +767,7 @@ The following commands are available:
 
 \\{gnus-group-mode-map}"
   (interactive)
-  (when (and menu-bar-mode
-            (gnus-visual-p 'group-menu 'menu))
+  (when (gnus-visual-p 'group-menu 'menu)
     (gnus-group-make-menu-bar))
   (kill-all-local-variables)
   (gnus-simplify-mode-line)
@@ -415,20 +776,24 @@ The following commands are available:
   (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)
+  (gnus-set-default-directory)
   (gnus-update-format-specifications nil 'group 'group-mode)
   (gnus-update-group-mark-positions)
-  (gnus-make-local-hook 'post-command-hook)
-  (gnus-add-hook 'post-command-hook 'gnus-clear-inboxes-moved nil t)
-  (run-hooks 'gnus-group-mode-hook))
+  (when gnus-use-undo
+    (gnus-undo-mode 1))
+  (when gnus-slave
+    (gnus-slave-mode))
+  (gnus-run-hooks 'gnus-group-mode-hook))
 
 (defun gnus-update-group-mark-positions ()
   (save-excursion
-    (let ((gnus-process-mark 128)
+    (let ((gnus-process-mark ?\200)
          (gnus-group-marked '("dummy.group"))
-         (gnus-active-hashtb (make-vector 10 0)))
+         (gnus-active-hashtb (make-vector 10 0))
+         (topic ""))
       (gnus-set-active "dummy.group" '(0 . 0))
       (gnus-set-work-buffer)
       (gnus-group-insert-group-line "dummy.group" 0 nil 0 nil)
@@ -437,9 +802,6 @@ The following commands are available:
            (list (cons 'process (and (search-forward "\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")
@@ -464,9 +826,8 @@ The following commands are available:
     (or level gnus-group-default-list-level gnus-level-subscribed))))
 
 (defun gnus-group-setup-buffer ()
-  (switch-to-buffer 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))))
@@ -475,53 +836,68 @@ The following commands are available:
   "List newsgroups with level LEVEL or lower that have unread articles.
 Default is all subscribed groups.
 If argument UNREAD is non-nil, groups with no unread articles are also
-listed."
-  (interactive (list (if current-prefix-arg
-                        (prefix-numeric-value current-prefix-arg)
-                      (or
-                       (gnus-group-default-level nil t)
-                       gnus-group-default-list-level
-                       gnus-level-subscribed))))
-  (or level
-      (setq level (car gnus-group-list-mode)
-           unread (cdr gnus-group-list-mode)))
+listed.
+
+Also see the `gnus-group-use-permanent-levels' variable."
+  (interactive
+   (list (if current-prefix-arg
+            (prefix-numeric-value current-prefix-arg)
+          (or
+           (gnus-group-default-level nil t)
+           gnus-group-default-list-level
+           gnus-level-subscribed))))
+  (unless level
+    (setq level (car gnus-group-list-mode)
+         unread (cdr gnus-group-list-mode)))
   (setq level (gnus-group-default-level level))
-  (gnus-group-setup-buffer)            ;May call from out of group buffer
-  (gnus-update-format-specifications)
+  (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)))
-       (group (gnus-group-group-name)))
+       (empty (= (point-min) (point-max)))
+       (group (gnus-group-group-name))
+       number)
     (set-buffer gnus-group-buffer)
-    (funcall gnus-group-prepare-function level unread lowest)
-    (if (zerop (buffer-size))
-       (gnus-message 5 gnus-no-groups-message)
-      (goto-char (point-max))
-      (when (or (not gnus-group-goto-next-group-function)
-               (not (funcall gnus-group-goto-next-group-function 
-                             group props)))
-       (if (not group)
-           ;; Go to the first group with unread articles.
-           (gnus-group-search-forward t)
-         ;; Find the right group to put point on.  If the current group
-         ;; has disappeared in the new listing, try to find the next
-         ;; one.        If no next one can be found, just leave point at the
-         ;; first newsgroup in the buffer.
-         (if (not (gnus-goto-char
+    (setq number (funcall gnus-group-prepare-function level unread lowest))
+    (when (or (and (numberp number)
+                  (zerop number))
+             (zerop (buffer-size)))
+      ;; No groups in the buffer.
+      (gnus-message 5 gnus-no-groups-message))
+    ;; We have some groups displayed.
+    (goto-char (point-max))
+    (when (or (not gnus-group-goto-next-group-function)
+             (not (funcall gnus-group-goto-next-group-function
+                           group props)))
+      (cond
+       (empty
+       (goto-char (point-min)))
+       ((not group)
+       ;; Go to the first group with unread articles.
+       (gnus-group-search-forward t))
+       (t
+       ;; Find the right group to put point on.  If the current group
+       ;; has disappeared in the new listing, try to find the next
+       ;; one.  If no next one can be found, just leave point at the
+       ;; first newsgroup in the buffer.
+       (when (not (gnus-goto-char
                    (text-property-any
                     (point-min) (point-max)
-                    'gnus-group (gnus-intern-safe group gnus-active-hashtb))))
-             (let ((newsrc (cdddr (gnus-gethash group gnus-newsrc-hashtb))))
-               (while (and newsrc
-                           (not (gnus-goto-char
-                                 (text-property-any
-                                  (point-min) (point-max) 'gnus-group
-                                  (gnus-intern-safe
-                                   (caar newsrc) gnus-active-hashtb)))))
-                 (setq newsrc (cdr newsrc)))
-               (or newsrc (progn (goto-char (point-max))
-                                 (forward-line -1)))))))
-      ;; Adjust cursor point.
-      (gnus-group-position-point))))
+                    'gnus-group (gnus-intern-safe
+                                 group gnus-active-hashtb))))
+         (let ((newsrc (cdddr (gnus-gethash group gnus-newsrc-hashtb))))
+           (while (and newsrc
+                       (not (gnus-goto-char
+                             (text-property-any
+                              (point-min) (point-max) 'gnus-group
+                              (gnus-intern-safe
+                               (caar newsrc) gnus-active-hashtb)))))
+             (setq newsrc (cdr newsrc)))
+           (unless newsrc
+             (goto-char (point-max))
+             (forward-line -1)))))))
+    ;; Adjust cursor point.
+    (gnus-group-position-point)))
 
 (defun gnus-group-list-level (level &optional all)
   "List groups on LEVEL.
@@ -540,35 +916,35 @@ If REGEXP, only list groups matching REGEXP."
        (lowest (or lowest 1))
        info clevel unread group params)
     (erase-buffer)
-    (if (< lowest gnus-level-zombie)
-       ;; 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))))
+    (when (< lowest gnus-level-zombie)
+      ;; 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))))
                                        ; 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)
@@ -583,7 +959,8 @@ If REGEXP, only list groups matching REGEXP."
 
     (gnus-group-set-mode-line)
     (setq gnus-group-list-mode (cons level all))
-    (run-hooks 'gnus-group-prepare-hook)))
+    (gnus-run-hooks 'gnus-group-prepare-hook)
+    t))
 
 (defun gnus-group-prepare-flat-list-dead (groups level mark regexp)
   ;; List zombies and killed lists somewhat faster, which was
@@ -623,7 +1000,8 @@ If REGEXP, only list groups matching REGEXP."
           (not (gnus-ephemeral-group-p group))
           (gnus-dribble-enter
            (concat "(gnus-group-set-info '"
-                   (prin1-to-string (nth 2 entry)) ")")))
+                   (gnus-prin1-to-string (nth 2 entry))
+                   ")")))
       (setq gnus-group-indentation (gnus-group-group-indentation))
       (gnus-delete-line)
       (gnus-group-insert-group-line-info group)
@@ -633,6 +1011,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))
+       (gnus-group-indentation (gnus-group-group-indentation))
        active info)
     (if entry
        (progn
@@ -647,10 +1026,13 @@ If REGEXP, only list groups matching REGEXP."
        (if (member group gnus-zombie-list) gnus-level-zombie gnus-level-killed)
        nil
        (if (setq active (gnus-active group))
-          (- (1+ (cdr active)) (car active)) 0)
+          (if (zerop (cdr active))
+              0
+            (- (1+ (cdr active)) (car active)))
+        nil)
        nil))))
 
-(defun gnus-group-insert-group-line (gnus-tmp-group gnus-tmp-level 
+(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."
@@ -677,11 +1059,13 @@ If REGEXP, only list groups matching REGEXP."
              (or (gnus-gethash gnus-tmp-group gnus-description-hashtb) "")
            ""))
         (gnus-tmp-moderated
-         (if (member gnus-tmp-group gnus-moderated-list) ?m ? ))
+         (if (and gnus-moderated-hashtb
+                  (gnus-gethash gnus-tmp-group gnus-moderated-hashtb))
+             ?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-server-get-method gnus-tmp-group gnus-tmp-method)) ;
         (gnus-tmp-news-server (or (cadr gnus-tmp-method) ""))
         (gnus-tmp-news-method (or (car gnus-tmp-method) ""))
         (gnus-tmp-news-method-string
@@ -709,25 +1093,63 @@ If REGEXP, only list groups matching REGEXP."
        ;; Insert the text.
        (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)
-                     t)
-       gnus-marked ,gnus-tmp-marked-mark
-       gnus-indentation ,gnus-group-indentation
-       gnus-level ,gnus-tmp-level))
+                 gnus-unread ,(if (numberp number)
+                                  (string-to-int gnus-tmp-number-of-unread)
+                                t)
+                 gnus-marked ,gnus-tmp-marked-mark
+                 gnus-indentation ,gnus-group-indentation
+                 gnus-level ,gnus-tmp-level))
     (when (inline (gnus-visual-p 'group-highlight 'highlight))
       (forward-line -1)
-      (run-hooks 'gnus-group-update-hook)
+      (gnus-run-hooks 'gnus-group-update-hook)
       (forward-line))
     ;; Allow XEmacs to remove front-sticky text properties.
     (gnus-group-remove-excess-properties)))
 
+(defun gnus-group-highlight-line ()
+  "Highlight the current line according to `gnus-group-highlight'."
+  (let* ((list gnus-group-highlight)
+        (p (point))
+        (end (progn (end-of-line) (point)))
+        ;; now find out where the line starts and leave point there.
+        (beg (progn (beginning-of-line) (point)))
+        (group (gnus-group-group-name))
+        (entry (gnus-group-entry group))
+        (unread (if (numberp (car entry)) (car entry) 0))
+        (active (gnus-active group))
+        (total (if active (1+ (- (cdr active) (car active))) 0))
+        (info (nth 2 entry))
+        (method (gnus-server-get-method group (gnus-info-method info)))
+        (marked (gnus-info-marks info))
+        (mailp (memq 'mail (assoc (symbol-name
+                                   (car (or method gnus-select-method)))
+                                  gnus-valid-select-methods)))
+        (level (or (gnus-info-level info) gnus-level-killed))
+        (score (or (gnus-info-score info) 0))
+        (ticked (gnus-range-length (cdr (assq 'tick marked))))
+        (group-age (gnus-group-timestamp-delta group))
+        (inhibit-read-only t))
+    ;; Eval the cars of the lists until we find a match.
+    (while (and list
+               (not (eval (caar list))))
+      (setq list (cdr list)))
+    (let ((face (cdar list)))
+      (unless (eq face (get-text-property beg 'face))
+       (gnus-put-text-property-excluding-characters-with-faces
+        beg end 'face
+        (setq face (if (boundp face) (symbol-value face) face)))
+       (gnus-extent-start-open beg)))
+    (goto-char p)))
+
 (defun gnus-group-update-group (group &optional visible-only)
   "Update all lines where GROUP appear.
 If VISIBLE-ONLY is non-nil, the group won't be displayed if it isn't
 already."
-  (save-excursion
+  ;; Can't use `save-excursion' here, so we do it manually.
+  (let ((buf (current-buffer))
+       mark)
     (set-buffer gnus-group-buffer)
+    (setq mark (point-marker))
     ;; The buffer may be narrowed.
     (save-restriction
       (widen)
@@ -736,10 +1158,12 @@ already."
            found buffer-read-only)
        ;; Enter the current status into the dribble buffer.
        (let ((entry (gnus-gethash group gnus-newsrc-hashtb)))
-         (if (and entry (not (gnus-ephemeral-group-p group)))
-             (gnus-dribble-enter
-              (concat "(gnus-group-set-info '" (prin1-to-string (nth 2 entry))
-                      ")"))))
+         (when (and entry
+                    (not (gnus-ephemeral-group-p group)))
+           (gnus-dribble-enter
+            (concat "(gnus-group-set-info '"
+                    (gnus-prin1-to-string (nth 2 entry))
+                    ")"))))
        ;; Find all group instances.  If topics are in use, each group
        ;; may be listed in more than once.
        (while (setq loc (text-property-any
@@ -751,7 +1175,7 @@ already."
            (gnus-group-insert-group-line-info group)
            (save-excursion
              (forward-line -1)
-             (run-hooks 'gnus-group-update-group-hook)))
+             (gnus-run-hooks 'gnus-group-update-group-hook)))
          (setq loc (1+ loc)))
        (unless (or found visible-only)
          ;; No such line in the buffer, find out where it's supposed to
@@ -773,8 +1197,13 @@ already."
            (gnus-group-insert-group-line-info group)
            (save-excursion
              (forward-line -1)
-             (run-hooks 'gnus-group-update-group-hook))))
-       (gnus-group-set-mode-line)))))
+             (gnus-run-hooks 'gnus-group-update-group-hook))))
+       (when gnus-group-update-group-function
+         (funcall gnus-group-update-group-function group))
+       (gnus-group-set-mode-line)))
+    (goto-char mark)
+    (set-marker mark nil)
+    (set-buffer buf)))
 
 (defun gnus-group-set-mode-line ()
   "Update the mode line in the group buffer."
@@ -783,17 +1212,14 @@ already."
     (save-excursion
       (set-buffer gnus-group-buffer)
       (let* ((gformat (or gnus-group-mode-line-format-spec
-                         (setq gnus-group-mode-line-format-spec
-                               (gnus-parse-format
-                                gnus-group-mode-line-format
-                                gnus-group-mode-line-format-alist))))
+                         (gnus-set-format 'group-mode)))
             (gnus-tmp-news-server (cadr gnus-select-method))
             (gnus-tmp-news-method (car gnus-select-method))
             (gnus-tmp-colon (if (equal gnus-tmp-news-server "") "" ":"))
             (max-len 60)
             gnus-tmp-header            ;Dummy binding for user-defined formats
             ;; Get the resulting string.
-            (modified 
+            (modified
              (and gnus-dribble-buffer
                   (buffer-name gnus-dribble-buffer)
                   (buffer-modified-p gnus-dribble-buffer)
@@ -803,12 +1229,13 @@ already."
             (mode-string (eval gformat)))
        ;; Say whether the dribble buffer has been modified.
        (setq mode-line-modified
-             (if modified "---*- " "----- "))
+             (if modified (car gnus-mode-line-modified)
+               (cdr gnus-mode-line-modified)))
        ;; If the line is too long, we chop it off.
        (when (> (length mode-string) max-len)
          (setq mode-string (substring mode-string 0 (- max-len 4))))
        (prog1
-           (setq mode-line-buffer-identification 
+           (setq mode-line-buffer-identification
                  (gnus-mode-line-buffer-identification
                   (list mode-string)))
          (set-buffer-modified-p modified))))))
@@ -816,7 +1243,8 @@ 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)))
-    (and group (symbol-name group))))
+    (when group
+      (symbol-name group))))
 
 (defun gnus-group-group-level ()
   "Get the level of the newsgroup on the current line."
@@ -833,6 +1261,17 @@ already."
   "Get the number of unread articles of the newsgroup on the current line."
   (get-text-property (gnus-point-at-bol) 'gnus-unread))
 
+(defun gnus-group-new-mail (group)
+  (if (nnmail-new-mail-p (gnus-group-real-name group))
+      gnus-new-mail-mark
+    ? ))
+
+(defun gnus-group-level (group)
+  "Return the estimated level of GROUP."
+  (or (gnus-info-level (gnus-get-info group))
+      (and (member group gnus-zombie-list) gnus-level-zombie)
+      gnus-level-killed))
+
 (defun gnus-group-search-forward (&optional backward all level first-too)
   "Find the next newsgroup with unread articles.
 If BACKWARD is non-nil, find the previous newsgroup instead.
@@ -846,29 +1285,32 @@ If FIRST-TOO, the current line is also eligible as a target."
        pos found lev)
     (if (and backward (progn (beginning-of-line)) (bobp))
        nil
-      (or first-too (forward-line way))
+      (unless first-too
+       (forward-line way))
       (while (and
              (not (eobp))
              (not (setq
                    found
-                   (and (or all
-                            (and
-                             (let ((unread
-                                    (get-text-property (point) 'gnus-unread)))
-                               (and (numberp unread) (> unread 0)))
-                             (setq lev (get-text-property (point)
+                   (and
+                    (get-text-property (point) 'gnus-group)
+                    (or all
+                        (and
+                         (let ((unread
+                                (get-text-property (point) 'gnus-unread)))
+                           (and (numberp unread) (> unread 0)))
+                         (setq lev (get-text-property (point)
+                                                      'gnus-level))
+                         (<= lev gnus-level-subscribed)))
+                    (or (not level)
+                        (and (setq lev (get-text-property (point)
                                                           'gnus-level))
-                             (<= lev gnus-level-subscribed)))
-                        (or (not level)
-                            (and (setq lev (get-text-property (point)
-                                                              'gnus-level))
-                                 (or (= lev level)
-                                     (and (< lev low)
-                                          (< level lev)
-                                          (progn
-                                            (setq low lev)
-                                            (setq pos (point))
-                                            nil))))))))
+                             (or (= lev level)
+                                 (and (< lev low)
+                                      (< level lev)
+                                      (progn
+                                        (setq low lev)
+                                        (setq pos (point))
+                                        nil))))))))
              (zerop (forward-line way)))))
     (if found
        (progn (gnus-group-position-point) t)
@@ -887,19 +1329,20 @@ If FIRST-TOO, the current line is also eligible as a target."
     (while (and (> n 0)
                (not (eobp)))
       (when (setq group (gnus-group-group-name))
-       ;; Update the mark.
+       ;; Go to the mark position.
        (beginning-of-line)
-       (forward-char
-        (or (cdr (assq 'process gnus-group-mark-positions)) 2))
-       (delete-char 1)
-       (if unmark
-           (progn
-             (insert " ")
-             (setq gnus-group-marked (delete group gnus-group-marked)))
-         (insert "#")
-         (setq gnus-group-marked
-               (cons group (delete group gnus-group-marked)))))
-      (or no-advance (gnus-group-next-group 1))
+       (forward-char (or (cdr (assq 'process gnus-group-mark-positions)) 2))
+       (subst-char-in-region
+        (point) (1+ (point)) (char-after)
+        (if unmark
+            (progn
+              (setq gnus-group-marked (delete group gnus-group-marked))
+              ? )
+          (setq gnus-group-marked
+                (cons group (delete group gnus-group-marked)))
+          gnus-process-mark)))
+      (unless no-advance
+       (gnus-group-next-group 1))
       (decf n))
     (gnus-summary-position-point)
     n))
@@ -957,25 +1400,23 @@ Return nil if the group isn't displayed."
 
 (defun gnus-group-set-mark (group)
   "Set the process mark on GROUP."
-  (if (gnus-group-goto-group group) 
+  (if (gnus-group-goto-group group)
       (save-excursion
        (gnus-group-mark-group 1 nil t))
     (setq gnus-group-marked (cons group (delete group gnus-group-marked)))))
 
 (defun gnus-group-universal-argument (arg &optional groups func)
-  "Perform any command on all groups accoring to the process/prefix convention."
+  "Perform any command on all groups according to the process/prefix convention."
   (interactive "P")
-  (let ((groups (or groups (gnus-group-process-prefix arg)))
-       group func)
-    (if (eq (setq func (or func
-                          (key-binding
-                           (read-key-sequence
-                            (substitute-command-keys
-                             "\\<gnus-group-mode-map>\\[gnus-group-universal-argument]")))))
-           'undefined)
-       (gnus-error 1 "Undefined key")
-      (while groups
-       (gnus-group-remove-mark (setq group (pop groups)))
+  (if (eq (setq func (or func
+                        (key-binding
+                         (read-key-sequence
+                          (substitute-command-keys
+                           "\\<gnus-group-mode-map>\\[gnus-group-universal-argument]")))))
+         'undefined)
+      (gnus-error 1 "Undefined key")
+    (gnus-group-iterate arg
+      (lambda (group)
        (command-execute func))))
   (gnus-group-position-point))
 
@@ -991,16 +1432,13 @@ Take into consideration N (the prefix) and the list of marked groups."
          (n (abs n))
          group groups)
       (save-excursion
-       (while (and (> n 0)
-                   (setq group (gnus-group-group-name)))
-         (setq groups (cons group groups))
+       (while (> n 0)
+         (if (setq group (gnus-group-group-name))
+             (push group groups))
          (setq n (1- n))
          (gnus-group-next-group way)))
       (nreverse groups)))
-   ((and (boundp 'transient-mark-mode)
-        transient-mark-mode
-        (boundp 'mark-active)
-        mark-active)
+   ((gnus-region-active-p)
     ;; Work on the region between point and mark.
     (let ((max (max (point) (mark)))
          groups)
@@ -1021,9 +1459,33 @@ Take into consideration N (the prefix) and the list of marked groups."
     (let ((group (gnus-group-group-name)))
       (and group (list group))))))
 
+;;; !!!Surely gnus-group-iterate should be a macro instead?  I can't
+;;; imagine why I went through these contortions...
+(eval-and-compile
+  (let ((function (make-symbol "gnus-group-iterate-function"))
+       (window (make-symbol "gnus-group-iterate-window"))
+       (groups (make-symbol "gnus-group-iterate-groups"))
+       (group (make-symbol "gnus-group-iterate-group")))
+    (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
+and with point over the group in question."
+       (let ((,groups (gnus-group-process-prefix arg))
+             (,window (selected-window))
+             ,group)
+         (while (setq ,group (pop ,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
@@ -1031,11 +1493,16 @@ optional argument NO-ARTICLE is non-nil, no article will be
 auto-selected upon group entry.         If GROUP is non-nil, fetch that
 group."
   (interactive "P")
-  (let ((group (or group (gnus-group-group-name)))
+  (let ((no-display (eq all 0))
+       (group (or group (gnus-group-group-name)))
        number active marked entry)
-    (or group (error "No group on current line"))
-    (setq marked (nth 3 (nth 2 (setq entry (gnus-gethash
-                                           group gnus-newsrc-hashtb)))))
+    (when (eq all 0)
+      (setq all nil))
+    (unless group
+      (error "No group on current line"))
+    (setq marked (gnus-info-marks
+                 (nth 2 (setq entry (gnus-gethash
+                                     group gnus-newsrc-hashtb)))))
     ;; 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
@@ -1045,11 +1512,11 @@ group."
                 (- (1+ (cdr active)) (car active)))))
     (gnus-summary-read-group
      group (or all (and (numberp number)
-                       (zerop (+ number (gnus-range-length 
+                       (zerop (+ number (gnus-range-length
                                          (cdr (assq 'tick marked)))
                                  (gnus-range-length
                                   (cdr (assq 'dormant marked)))))))
-     no-article)))
+     no-article nil no-display nil select-articles)))
 
 (defun gnus-group-select-group (&optional all)
   "Select this newsgroup.
@@ -1061,10 +1528,17 @@ If ALL is a number, fetch this number of articles."
 
 (defun gnus-group-quick-select-group (&optional all)
   "Select the current group \"quickly\".
-This means that no highlighting or scoring will be performed."
+This means that no highlighting or scoring will be performed.
+If ALL (the prefix argument) is 0, don't even generate the summary
+buffer.
+
+This might be useful if you want to toggle threading
+before entering the group."
   (interactive "P")
+  (require 'gnus-score)
   (let (gnus-visual
        gnus-score-find-score-files-function
+       gnus-home-score-file
        gnus-apply-kill-hook
        gnus-summary-expunge-below)
     (gnus-group-read-group all t)))
@@ -1075,36 +1549,93 @@ This means that no highlighting or scoring will be performed."
   (let ((gnus-inhibit-limiting t))
     (gnus-group-read-group all t)))
 
+(defun gnus-group-select-group-ephemerally ()
+  "Select the current group without doing any processing whatsoever.
+You will actually be entered into a group that's a copy of
+the current group; no changes you make while in this group will
+be permanent."
+  (interactive)
+  (require 'gnus-score)
+  (let* (gnus-visual
+        gnus-score-find-score-files-function gnus-apply-kill-hook
+        gnus-summary-expunge-below gnus-show-threads gnus-suppress-duplicates
+        gnus-summary-mode-hook gnus-select-group-hook
+        (group (gnus-group-group-name))
+        (method (gnus-find-method-for-group group)))
+    (gnus-group-read-ephemeral-group
+     (gnus-group-prefixed-name group method) method)))
+
 ;;;###autoload
 (defun gnus-fetch-group (group)
   "Start Gnus if necessary and enter GROUP.
 Returns whether the fetching was successful or not."
   (interactive "sGroup name: ")
-  (or (get-buffer gnus-group-buffer)
-      (gnus))
+  (unless (get-buffer gnus-group-buffer)
+    (gnus-no-server))
   (gnus-group-read-group nil 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)
+
 ;; 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)
+(defun gnus-group-read-ephemeral-group (group method &optional activate
+                                             quit-config request-only
+                                             select-articles)
+  "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.
+
+Return the name of the group is selection was successful."
+  ;; Transform the select method into a unique server.
+  (when (stringp method)
+    (setq method (gnus-server-to-method method)))
+  (setq method
+       `(,(car method) ,(concat (cadr method) "-ephemeral")
+         (,(intern (format "%s-address" (car method))) ,(cadr method))
+         ,@(cddr method)))
   (let ((group (if (gnus-group-foreign-p group) group
                 (gnus-group-prefixed-name group method))))
     (gnus-sethash
      group
-     `(t nil (,group ,gnus-level-default-subscribed nil nil ,method
-                    ((quit-config . ,(if quit-config quit-config
-                                       (cons (current-buffer) 'summary))))))
+     `(-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))))))
      gnus-newsrc-hashtb)
+    (push method gnus-ephemeral-servers)
     (set-buffer gnus-group-buffer)
-    (or (gnus-check-server method)
-       (error "Unable to contact server: %s" (gnus-status-message method)))
-    (if activate (or (gnus-request-group group)
-                    (error "Couldn't request group")))
-    (condition-case ()
-       (gnus-group-read-group t t group)
-      (error nil)
-      (quit nil))))
+    (unless (gnus-check-server method)
+      (error "Unable to contact server: %s" (gnus-status-message method)))
+    (when activate
+      (gnus-activate-group group 'scan)
+      (unless (gnus-request-group group)
+       (error "Couldn't request group: %s"
+              (nnheader-get-report (car method)))))
+    (if request-only
+       group
+      (condition-case ()
+         (when (gnus-group-read-group t t group select-articles)
+           group)
+       ;;(error nil)
+       (quit nil)))))
 
 (defun gnus-group-jump-to-group (group)
   "Jump to newsgroup GROUP."
@@ -1118,59 +1649,50 @@ Returns whether the fetching was successful or not."
   (when (equal group "")
     (error "Empty group name"))
 
-  (when (string-match "[\000-\032]" group)
-    (error "Control characters in group: %s" group))
-
-  (let ((b (text-property-any
-           (point-min) (point-max)
-           'gnus-group (gnus-intern-safe group gnus-active-hashtb))))
-    (unless (gnus-ephemeral-group-p group)
-      (if b
-         ;; Either go to the line in the group buffer...
-         (goto-char b)
-       ;; ... or insert the line.
-       (or
-        t ;; Don't activate group.
-        (gnus-active group)
-        (gnus-activate-group group)
-        (error "%s error: %s" group (gnus-status-message group)))
-
-       (gnus-group-update-group group)
-       (goto-char (text-property-any
-                   (point-min) (point-max)
-                   'gnus-group (gnus-intern-safe group gnus-active-hashtb)))))
-    ;; Adjust cursor point.
-    (gnus-group-position-point)))
+  (unless (gnus-ephemeral-group-p group)
+    ;; Either go to the line in the group buffer...
+    (unless (gnus-group-goto-group group)
+      ;; ... or insert the line.
+      (gnus-group-update-group group)
+      (gnus-group-goto-group group)))
+  ;; Adjust cursor point.
+  (gnus-group-position-point))
 
-(defun gnus-group-goto-group (group)
-  "Goto to newsgroup GROUP."
+(defun gnus-group-goto-group (group &optional far)
+  "Goto to newsgroup GROUP.
+If FAR, it is likely that the group is not on the current line."
   (when group
-    (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
+    (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)
-       (eq (get-text-property (point) 'gnus-group)
-           (gnus-intern-safe group gnus-active-hashtb)))
-      (forward-line -1)
-      (point))
-     ((save-excursion
+       (point))
+       ((save-excursion
+         (forward-line 1)
+         (eq (get-text-property (point) 'gnus-group)
+             (gnus-intern-safe group gnus-active-hashtb)))
        (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)))))))
+       (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))))))))
 
 (defun gnus-group-next-group (n &optional silent)
   "Go to next N'th newsgroup.
@@ -1242,17 +1764,16 @@ If EXCLUDE-GROUP, do not go to that group."
        unread best-point)
     (while (not (eobp))
       (setq unread (get-text-property (point) 'gnus-unread))
-      (if (and (numberp unread) (> unread 0))
-         (progn
-           (if (and (get-text-property (point) 'gnus-level)
-                    (< (get-text-property (point) 'gnus-level) best)
-                    (or (not exclude-group)
-                        (not (equal exclude-group (gnus-group-group-name)))))
-               (progn
-                 (setq best (get-text-property (point) 'gnus-level))
-                 (setq best-point (point))))))
+      (when (and (numberp unread) (> unread 0))
+       (when (and (get-text-property (point) 'gnus-level)
+                  (< (get-text-property (point) 'gnus-level) best)
+                  (or (not exclude-group)
+                      (not (equal exclude-group (gnus-group-group-name)))))
+         (setq best (get-text-property (point) 'gnus-level))
+         (setq best-point (point))))
       (forward-line 1))
-    (if best-point (goto-char best-point))
+    (when best-point
+      (goto-char best-point))
     (gnus-summary-position-point)
     (and best-point (gnus-group-group-name))))
 
@@ -1277,15 +1798,17 @@ If EXCLUDE-GROUP, do not go to that group."
   (interactive)
   (gnus-enter-server-buffer))
 
-(defun gnus-group-make-group (name &optional method address)
+(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
 ADDRESS."
   (interactive
    (list
-    (read-string "Group name: ")
-    (gnus-read-server "From method: ")))
+    (gnus-read-group "Group name: ")
+    (gnus-read-method "From method: ")))
 
+  (when (stringp method)
+    (setq method (or (gnus-server-to-method method) method)))
   (let* ((meth (when (and method
                          (not (gnus-server-equal method gnus-select-method)))
                 (if address (list (intern method) address)
@@ -1304,9 +1827,10 @@ ADDRESS."
      t)
     ;; Make it active.
     (gnus-set-active nname (cons 1 0))
-    (or (gnus-ephemeral-group-p name)
-       (gnus-dribble-enter
-        (concat "(gnus-group-set-info '" (prin1-to-string (cdr info)) ")")))
+    (unless (gnus-ephemeral-group-p name)
+      (gnus-dribble-enter
+       (concat "(gnus-group-set-info '"
+              (gnus-prin1-to-string (cdr info)) ")")))
     ;; Insert the line.
     (gnus-group-insert-group-line-info nname)
     (forward-line -1)
@@ -1319,8 +1843,8 @@ ADDRESS."
                 gnus-valid-select-methods)
       (require backend))
     (gnus-check-server meth)
-    (and (gnus-check-backend-function 'request-create-group nname)
-        (gnus-request-create-group nname))
+    (when (gnus-check-backend-function 'request-create-group nname)
+      (gnus-request-create-group nname nil args))
     t))
 
 (defun gnus-group-delete-group (group &optional force)
@@ -1332,15 +1856,16 @@ doing the deletion."
   (interactive
    (list (gnus-group-group-name)
         current-prefix-arg))
-  (or group (error "No group to rename"))
-  (or (gnus-check-backend-function 'request-delete-group group)
-      (error "This backend does not support group deletion"))
+  (unless group
+    (error "No group to rename"))
+  (unless (gnus-check-backend-function 'request-delete-group group)
+    (error "This backend 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!
+         ()                            ; 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)
@@ -1362,20 +1887,19 @@ and NEW-NAME will be prompted for."
       (unless (gnus-check-backend-function
               'request-rename-group (gnus-group-group-name))
        (error "This backend does not support renaming groups"))
-      (read-string "New group name: " (gnus-group-group-name)))))
+      (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"))
-  (unless group 
+  (unless group
     (error "No group to rename"))
-  (when (string-match "^[ \t]*$" new-name)
-    (error "Not a valid group name"))
-  (when (equal group new-name)
+  (when (equal (gnus-group-real-name group) new-name)
     (error "Can't rename to the same name"))
 
   ;; We find the proper prefixed name.
   (setq new-name
-       (if (equal (gnus-group-real-name new-name) new-name)
+       (if (gnus-group-native-p group)
            ;; Native group.
            new-name
          ;; Foreign group.
@@ -1397,6 +1921,9 @@ 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)))
 
 (defun gnus-group-edit-group (group &optional part)
@@ -1408,6 +1935,8 @@ and NEW-NAME will be prompted for."
       (error "No group on current line"))
     (unless (setq info (gnus-get-info group))
       (error "Killed group; can't be edited"))
+    (ignore-errors
+      (gnus-close-group group))
     (gnus-edit-form
      ;; Find the proper form to edit.
      (cond ((eq part 'method)
@@ -1417,11 +1946,12 @@ and NEW-NAME will be prompted for."
           (t info))
      ;; The proper documentation.
      (format
-      "Editing the %s."
+      "Editing the %s for `%s'."
       (cond
        ((eq part 'method) "select method")
        ((eq part 'params) "group parameters")
-       (t "group info")))
+       (t "group info"))
+      group)
      `(lambda (form)
        (gnus-group-edit-group-done ',part ,group form)))))
 
@@ -1471,23 +2001,26 @@ and NEW-NAME will be prompted for."
     (gnus-group-update-group (or new-group group))
     (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)
+                      gnus-useful-groups)))
+     (list (cadr entry) (caddr entry))))
+  (setq method (gnus-copy-sequence method))
+  (let (entry)
+    (while (setq entry (memq (assq 'eval method) method))
+      (setcar entry (eval (cadar entry)))))
+  (gnus-group-make-group group method))
+
 (defun gnus-group-make-help-group ()
   "Create the Gnus documentation group."
   (interactive)
-  (let ((path load-path)
-       (name (gnus-group-prefixed-name "gnus-help" '(nndoc "gnus-help")))
-       file dir)
-    (and (gnus-gethash name gnus-newsrc-hashtb)
-        (error "Documentation group already exists"))
-    (while path
-      (setq dir (file-name-as-directory (expand-file-name (pop path)))
-           file nil)
-      (when (or (file-exists-p (setq file (concat dir "gnus-tut.txt")))
-               (file-exists-p
-                (setq file (concat (file-name-directory
-                                    (directory-file-name dir))
-                                   "etc/gnus-tut.txt"))))
-       (setq path nil)))
+  (let ((name (gnus-group-prefixed-name "gnus-help" '(nndoc "gnus-help")))
+       (file (nnheader-find-etc-directory "gnus-tut.txt" t)))
+    (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
@@ -1507,7 +2040,7 @@ and NEW-NAME will be prompted for."
          char found)
       (while (not found)
        (message
-        "%sFile type (mbox, babyl, digest, forward, mmfd, guess) [mbdfag]: "
+        "%sFile type (mbox, babyl, digest, forward, mmdf, guess) [mbdfag]: "
         err)
        (setq found (cond ((= (setq char (read-char)) ?m) 'mbox)
                          ((= char ?b) 'babyl)
@@ -1523,10 +2056,47 @@ and NEW-NAME will be prompted for."
                 (file-name-nondirectory file) '(nndoc "")))))
     (gnus-group-make-group
      (gnus-group-real-name name)
-     (list 'nndoc (file-name-nondirectory file)
+     (list 'nndoc file
           (list 'nndoc-address file)
           (list 'nndoc-article-type (or type 'guess))))))
 
+(defvar nnweb-type-definition)
+(defvar gnus-group-web-type-history nil)
+(defvar gnus-group-web-search-history nil)
+(defun gnus-group-make-web-group (&optional solid)
+  "Create an ephemeral nnweb group.
+If SOLID (the prefix), create a solid group."
+  (interactive "P")
+  (require 'nnweb)
+  (let* ((group
+         (if solid (gnus-read-group "Group name: ")
+           (message-unique-id)))
+        (default-type (or (car gnus-group-web-type-history)
+                          (symbol-name (caar nnweb-type-definition))))
+        (type
+         (gnus-string-or
+          (completing-read
+           (format "Search engine type (default %s): " default-type)
+           (mapcar (lambda (elem) (list (symbol-name (car elem))))
+                   nnweb-type-definition)
+           nil t nil 'gnus-group-web-type-history)
+          default-type))
+        (search
+         (read-string
+          "Search string: "
+          (cons (or (car gnus-group-web-search-history) "") 0)
+          'gnus-group-web-search-history))
+        (method
+         `(nnweb ,group (nnweb-search ,search)
+                 (nnweb-type ,(intern type))
+                 (nnweb-ephemeral-p t))))
+    (if solid
+       (gnus-group-make-group group "nnweb" "" `(,(intern type) ,search))
+      (gnus-group-read-ephemeral-group
+       group method t
+       (cons (current-buffer)
+            (if (eq major-mode 'gnus-summary-mode) 'summary 'group))))))
+
 (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."
@@ -1541,7 +2111,7 @@ Given a prefix, create a full group."
           (list 'nndir-directory
                 (if all gnus-group-archive-directory
                   gnus-group-recent-archive-directory))))
-    (gnus-group-add-parameter group (cons 'to-address "ding@ifi.uio.no"))))
+    (gnus-group-add-parameter group (cons 'to-address "ding@gnus.org"))))
 
 (defun gnus-group-make-directory-group (dir)
   "Create an nndir group.
@@ -1550,8 +2120,10 @@ directory will be used as a newsgroup.   The directory should contain
 mail messages or news articles in files that have numeric names."
   (interactive
    (list (read-file-name "Create group from directory: ")))
-  (or (file-exists-p dir) (error "No such directory"))
-  (or (file-directory-p dir) (error "Not a directory"))
+  (unless (file-exists-p dir)
+    (error "No such directory"))
+  (unless (file-directory-p dir)
+    (error "Not a directory"))
   (let ((ext "")
        (i 0)
        group)
@@ -1585,11 +2157,11 @@ score file entries for articles to include in the group."
        (while (not (equal "" (setq regexp (read-string
                                            (format "Match on %s (string): "
                                                    header)))))
-         (setq regexps (cons (list regexp nil nil 'r) regexps)))
-       (setq scores (cons (cons header regexps) scores)))
+         (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))
+  (with-temp-file (gnus-score-file-name (concat "nnkiboze:" group))
     (let (emacs-lisp-mode-hook)
       (pp scores (current-buffer)))))
 
@@ -1599,8 +2171,9 @@ score file entries for articles to include in the group."
    (list current-prefix-arg
         (completing-read "Add to virtual group: " gnus-newsrc-hashtb nil t
                          "nnvirtual:")))
-  (or (eq (car (gnus-find-method-for-group vgroup)) 'nnvirtual)
-      (error "%s is not an nnvirtual group" vgroup))
+  (unless (eq (car (gnus-find-method-for-group vgroup)) 'nnvirtual)
+    (error "%s is not an nnvirtual group" vgroup))
+  (gnus-close-group vgroup)
   (let* ((groups (gnus-group-process-prefix n))
         (method (gnus-info-method (gnus-get-info vgroup))))
     (setcar (cdr method)
@@ -1619,8 +2192,8 @@ 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.
-    (and (gnus-gethash pgroup gnus-newsrc-hashtb)
-        (error "Group %s already exists." pgroup))
+    (when (gnus-gethash pgroup gnus-newsrc-hashtb)
+      (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)
     (gnus-group-update-group pgroup)
@@ -1630,45 +2203,44 @@ score file entries for articles to include in the group."
 (defun gnus-group-enter-directory (dir)
   "Enter an ephemeral nneething group."
   (interactive "DDirectory to read: ")
-  (let* ((method (list 'nneething dir))
+  (let* ((method (list 'nneething dir '(nneething-read-only t)))
         (leaf (gnus-group-prefixed-name
                (file-name-nondirectory (directory-file-name dir))
                method))
         (name (gnus-generate-new-group-name leaf)))
-    (let ((nneething-read-only t))
-      (or (gnus-group-read-ephemeral-group
-          name method t
-          (cons (current-buffer) (if (eq major-mode 'gnus-summary-mode)
-                                     'summary 'group)))
-         (error "Couldn't enter %s" dir)))))
+    (unless (gnus-group-read-ephemeral-group
+            name method t
+            (cons (current-buffer)
+                  (if (eq major-mode 'gnus-summary-mode)
+                      'summary 'group)))
+      (error "Couldn't enter %s" dir))))
 
 ;; Group sorting commands
 ;; Suggested by Joe Hildebrand <hildjj@idaho.fuentez.com>.
 
 (defun gnus-group-sort-groups (func &optional reverse)
   "Sort the group buffer according to FUNC.
-If REVERSE, reverse the sorting order."
-  (interactive (list gnus-group-sort-function
-                    current-prefix-arg))
-  (let ((func (cond 
-              ((not (listp func)) func)
-              ((null func) func)
-              ((= 1 (length func)) (car func))
-              (t `(lambda (t1 t2)
-                    ,(gnus-make-sort-function 
-                      (reverse func)))))))
-    ;; We peel off the dummy group from the alist.
-    (when func
-      (when (equal (car (gnus-info-group gnus-newsrc-alist)) "dummy.group")
-       (pop gnus-newsrc-alist))
-      ;; Do the sorting.
-      (setq gnus-newsrc-alist
-           (sort gnus-newsrc-alist func))
-      (when reverse
-       (setq gnus-newsrc-alist (nreverse gnus-newsrc-alist)))
-      ;; Regenerate the hash table.
-      (gnus-make-hashtable-from-newsrc-alist)
-      (gnus-group-list-groups))))
+When used interactively, the sorting function used will be
+determined by the `gnus-group-sort-function' variable.
+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-list-groups)
+  (gnus-dribble-touch))
+
+(defun gnus-group-sort-flat (func reverse)
+  ;; We peel off the dummy group from the alist.
+  (when func
+    (when (equal (gnus-info-group (car gnus-newsrc-alist)) "dummy.group")
+      (pop gnus-newsrc-alist))
+    ;; Do the sorting.
+    (setq gnus-newsrc-alist
+         (sort gnus-newsrc-alist func))
+    (when reverse
+      (setq gnus-newsrc-alist (nreverse gnus-newsrc-alist)))
+    ;; Regenerate the hash table.
+    (gnus-make-hashtable-from-newsrc-alist)))
 
 (defun gnus-group-sort-groups-by-alphabet (&optional reverse)
   "Sort the group buffer alphabetically by group name.
@@ -1706,10 +2278,92 @@ If REVERSE, sort in reverse order."
   (interactive "P")
   (gnus-group-sort-groups 'gnus-group-sort-by-method reverse))
 
+;;; Selected group sorting.
+
+(defun gnus-group-sort-selected-groups (n func &optional reverse)
+  "Sort the process/prefixed groups."
+  (interactive (list current-prefix-arg gnus-group-sort-function))
+  (let ((groups (gnus-group-process-prefix n)))
+    (funcall gnus-group-sort-selected-function
+            groups (gnus-make-sort-function func) reverse)
+    (gnus-group-list-groups)))
+
+(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))
+           entries))
+    ;; Then sort the infos.
+    (setq infos
+         (sort
+          (mapcar
+           (lambda (entry) (car entry))
+           (setq entries (nreverse entries)))
+          func))
+    (when reverse
+      (setq infos (nreverse infos)))
+    ;; Go through all the infos and replace the old entries
+    ;; with the new infos.
+    (while 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 n reverse)
+  "Sort the group buffer alphabetically by 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-alphabet reverse))
+
+(defun gnus-group-sort-selected-groups-by-unread (&optional n reverse)
+  "Sort the group buffer by number of unread articles.
+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 n reverse)
+  "Sort the group buffer by group level.
+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 n reverse)
+  "Sort the group buffer by group score.
+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 n reverse)
+  "Sort the group buffer by group rank.
+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 backend 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.
+
 (defun gnus-group-sort-by-alphabet (info1 info2)
   "Sort alphabetically."
   (string< (gnus-info-group info1) (gnus-info-group info2)))
 
+(defun gnus-group-sort-by-real-name (info1 info2)
+  "Sort alphabetically on real (unprefixed) names."
+  (string< (gnus-group-real-name (gnus-info-group info1))
+          (gnus-group-real-name (gnus-info-group info2))))
+
 (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)))
@@ -1740,57 +2394,87 @@ If REVERSE, sort in reverse order."
        (and (= level1 level2)
             (> (gnus-info-score info1) (gnus-info-score info2))))))
 
-;; Group catching up.
+;;; Clearing data
 
-(defun gnus-group-clear-data (n)
+(defun gnus-group-clear-data (&optional arg)
   "Clear all marks and read ranges from the current group."
   (interactive "P")
-  (let ((groups (gnus-group-process-prefix n))
-       group info)
-    (while (setq group (pop groups))
-      (setq info (gnus-get-info group))
-      (gnus-info-set-read info nil)
-      (when (gnus-info-marks info)
-       (gnus-info-set-marks info nil))
-      (gnus-get-unread-articles-in-group info (gnus-active group) t)
-      (when (gnus-group-goto-group group)
-       (gnus-group-remove-mark group)
-       (gnus-group-update-group-line)))))
+  (gnus-group-iterate arg
+    (lambda (group)
+      (let (info)
+       (gnus-info-clear-data (setq info (gnus-get-info group)))
+       (gnus-get-unread-articles-in-group info (gnus-active group) t)
+       (when (gnus-group-goto-group group)
+         (gnus-group-update-group-line))))))
+
+(defun gnus-group-clear-data-on-native-groups ()
+  "Clear all marks and read ranges from all native groups."
+  (interactive)
+  (when (gnus-yes-or-no-p "Really clear all data from almost all groups? ")
+    (let ((alist (cdr gnus-newsrc-alist))
+         info)
+      (while (setq info (pop alist))
+       (when (gnus-group-native-p (gnus-info-group info))
+         (gnus-info-clear-data info)))
+      (gnus-get-unread-articles)
+      (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)))
+    (gnus-undo-register
+      `(progn
+        (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-group-update-group-line))))
+    (gnus-info-set-read info nil)
+    (when (gnus-info-marks info)
+      (gnus-info-set-marks info nil))))
+
+;; Group catching up.
 
 (defun gnus-group-catchup-current (&optional n all)
   "Mark all articles not marked as unread in current newsgroup as read.
-If prefix argument N is numeric, the ARG next newsgroups will be
+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.
-The difference between N and actual number of newsgroups that were
-caught up is returned."
+The number of newsgroups that this function was unable to catch
+up is returned."
   (interactive "P")
-  (unless (gnus-group-group-name)
-    (error "No group on the current line"))
-  (if (not (or (not gnus-interactive-catchup) ;Without confirmation?
-              gnus-expert-user
-              (gnus-y-or-n-p
-               (if all
-                   "Do you really want to mark all articles as read? "
-                 "Mark all unread articles as read? "))))
-      n
-    (let ((groups (gnus-group-process-prefix n))
-         (ret 0))
-      (while groups
+  (let ((groups (gnus-group-process-prefix n))
+       (ret 0)
+       group)
+    (unless groups (error "No groups selected"))
+    (if (not
+        (or (not gnus-interactive-catchup) ;Without confirmation?
+            gnus-expert-user
+            (gnus-y-or-n-p
+             (format
+              (if all
+                  "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)
+                (format "these %d groups" (length groups)))))))
+       n
+      (while (setq group (pop groups))
        ;; Virtual groups have to be given special treatment.
-       (let ((method (gnus-find-method-for-group (car groups))))
-         (if (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)
+       (let ((method (gnus-find-method-for-group group)))
+         (when (eq 'nnvirtual (car method))
+           (nnvirtual-catchup-group
+            (gnus-group-real-name group) (nth 1 method) all)))
+       (if (>= (gnus-info-level (gnus-get-info 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)))
 
@@ -1809,7 +2493,7 @@ or nil if no action could be taken."
         (num (car entry)))
     ;; Do the updating only if the newsgroup isn't killed.
     (if (not (numberp (car entry)))
-       (gnus-message 1 "Can't catch up; non-active group")
+       (gnus-message 1 "Can't catch up %s; non-active group" group)
       ;; Do auto-expirable marks if that's required.
       (when (gnus-group-auto-expirable-p group)
        (gnus-add-marked-articles
@@ -1826,7 +2510,8 @@ or nil if no action could be taken."
        (when all
          (gnus-add-marked-articles group 'tick nil nil 'force)
          (gnus-add-marked-articles group 'dormant nil nil 'force))
-       (run-hooks 'gnus-group-catchup-group-hook)
+       (let ((gnus-newsgroup-name group))
+         (gnus-run-hooks 'gnus-group-catchup-group-hook))
        num))))
 
 (defun gnus-group-expire-articles (&optional n)
@@ -1844,14 +2529,14 @@ or nil if no action could be taken."
               (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-get-parameter group 'expiry-wait)))
+              (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 groupp
-                 ;; parameter. 
+                 ;; We set the expiry variables to the group
+                 ;; parameter.
                  (let ((nnmail-expiry-wait-function nil)
                        (nnmail-expiry-wait expiry-wait))
                    (gnus-request-expire-articles
@@ -1861,6 +2546,7 @@ or nil if no action could be taken."
                 (gnus-uncompress-sequence (cdr expirable)) group))))
            (gnus-close-group group))
          (gnus-message 6 "Expiring articles in %s...done" group)))
+      (gnus-dribble-touch)
       (gnus-group-position-point))))
 
 (defun gnus-group-expire-all-groups ()
@@ -1882,14 +2568,14 @@ or nil if no action could be taken."
     (string-to-int
      (let ((s (read-string
               (format "Level (default %s): "
-                      (or (gnus-group-group-level) 
+                      (or (gnus-group-group-level)
                           gnus-level-default-subscribed)))))
        (if (string-match "^\\s-*$" s)
-          (int-to-string (or (gnus-group-group-level) 
+          (int-to-string (or (gnus-group-group-level)
                              gnus-level-default-subscribed))
         s)))))
-  (or (and (>= level 1) (<= level gnus-level-killed))
-      (error "Illegal level: %d" level))
+  (unless (and (>= level 1) (<= level gnus-level-killed))
+    (error "Invalid level: %d" level))
   (let ((groups (gnus-group-process-prefix n))
        group)
     (while (setq group (pop groups))
@@ -1902,7 +2588,17 @@ or nil if no action could be taken."
       (gnus-group-update-group-line)))
   (gnus-group-position-point))
 
-(defun gnus-group-unsubscribe-current-group (&optional n)
+(defun gnus-group-unsubscribe (&optional n)
+  "Unsubscribe the current group."
+  (interactive "P")
+  (gnus-group-unsubscribe-current-group n 'unsubscribe))
+
+(defun gnus-group-subscribe (&optional n)
+  "Subscribe the current group."
+  (interactive "P")
+  (gnus-group-unsubscribe-current-group n 'subscribe))
+
+(defun gnus-group-unsubscribe-current-group (&optional n do-sub)
   "Toggle subscription of the current group.
 If given numerical prefix, toggle the N next groups."
   (interactive "P")
@@ -1913,9 +2609,17 @@ If given numerical prefix, toggle the N next groups."
            groups (cdr groups))
       (gnus-group-remove-mark group)
       (gnus-group-unsubscribe-group
-       group (if (<= (gnus-group-group-level) gnus-level-subscribed)
-                gnus-level-default-unsubscribed
-              gnus-level-default-subscribed) t)
+       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)))
 
@@ -1927,16 +2631,16 @@ group line."
    (list (completing-read
          "Group: " gnus-active-hashtb nil
          (gnus-read-active-file-p)
-         nil 
+         nil
          'gnus-group-history)))
   (let ((newsrc (gnus-gethash group gnus-newsrc-hashtb)))
     (cond
-     ((string-match "^[ \t]$" group)
+     ((string-match "^[ \t]*$" group)
       (error "Empty group name"))
      (newsrc
       ;; Toggle subscription flag.
       (gnus-group-change-level
-       newsrc (if level level (if (<= (nth 1 (nth 2 newsrc))
+       newsrc (if level level (if (<= (gnus-info-level (nth 2 newsrc))
                                      gnus-level-subscribed)
                                  (1+ gnus-level-subscribed)
                                gnus-level-default-subscribed)))
@@ -1952,8 +2656,8 @@ group line."
        (or (and (member group gnus-zombie-list)
                gnus-level-zombie)
           gnus-level-killed)
-       (and (gnus-group-group-name)
-           (gnus-gethash (gnus-group-group-name) gnus-newsrc-hashtb)))
+       (when (gnus-group-group-name)
+        (gnus-gethash (gnus-group-group-name) gnus-newsrc-hashtb)))
       (unless silent
        (gnus-group-update-group group)))
      (t (error "No such newsgroup: %s" group)))
@@ -1964,8 +2668,8 @@ group line."
 If given a negative prefix, move down instead. The difference between
 N and the number of steps taken is returned."
   (interactive "p")
-  (or (gnus-group-group-name)
-      (error "No group on current line"))
+  (unless (gnus-group-group-name)
+    (error "No group on current line"))
   (gnus-group-kill-group 1)
   (prog1
       (forward-line (- n))
@@ -1977,6 +2681,7 @@ N and the number of steps taken is returned."
   (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-region (begin end)
@@ -2019,6 +2724,10 @@ of groups killed."
          (gnus-delete-line)
          (when (and (not discard)
                     (setq entry (gnus-gethash group gnus-newsrc-hashtb)))
+           (gnus-undo-register
+             `(progn
+                (gnus-group-goto-group ,(gnus-group-group-name))
+                (gnus-group-yank-group)))
            (push (cons (car entry) (nth 2 entry))
                  gnus-list-of-killed-groups))
          (gnus-group-change-level
@@ -2035,31 +2744,34 @@ of groups killed."
                (delq (assoc group gnus-newsrc-alist)
                      gnus-newsrc-alist))
          (when gnus-group-change-level-function
-           (funcall gnus-group-change-level-function group 9 3))
+           (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)))))
+           (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))))
 
 (defun gnus-group-yank-group (&optional arg)
-  "Yank the last newsgroups killed with \\[gnus-group-kill-group],
-inserting it before the current newsgroup.  The numeric ARG specifies
-how many newsgroups are to be yanked.  The name of the newsgroup yanked
-is returned, or (if several groups are yanked) a list of yanked groups
-is returned."
+  "Yank the last newsgroups killed with \\[gnus-group-kill-group], inserting it before the current newsgroup.
+The numeric ARG specifies how many newsgroups are to be yanked.  The
+name of the newsgroup yanked is returned, or (if several groups are
+yanked) a list of yanked groups is returned."
   (interactive "p")
   (setq arg (or arg 1))
   (let (info group prev out)
     (while (>= (decf arg) 0)
-      (if (not (setq info (pop gnus-list-of-killed-groups)))
-         (error "No more newsgroups to yank"))
+      (when (not (setq info (pop gnus-list-of-killed-groups)))
+       (error "No more newsgroups to yank"))
       (push (setq group (nth 1 info)) out)
       ;; Find which newsgroup to insert this one before - search
       ;; backward until something suitable is found.  If there are no
@@ -2070,7 +2782,10 @@ is returned."
        info (gnus-info-level (cdr info)) gnus-level-killed
        (and prev (gnus-gethash prev gnus-newsrc-hashtb))
        t)
-      (gnus-group-insert-group-line-info group))
+      (gnus-group-insert-group-line-info group)
+      (gnus-undo-register
+       `(when (gnus-group-goto-group ,group)
+          (gnus-group-kill-group 1))))
     (forward-line -1)
     (gnus-group-position-point)
     (if (< (length out) 2) (car out) (nreverse out))))
@@ -2102,7 +2817,7 @@ 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.
@@ -2155,19 +2870,26 @@ entail asking the server for the groups."
             (lambda (sym)
               (and (boundp sym)
                    (symbol-value sym)
-                   (setq list (cons (symbol-name sym) list))))
+                   (push (symbol-name sym) list)))
             gnus-active-hashtb)
            list)
          'string<))
-       (buffer-read-only nil))
+       (buffer-read-only nil)
+       group)
     (erase-buffer)
     (while groups
-      (gnus-group-insert-group-line-info (pop groups)))
+      (gnus-add-text-properties
+       (point) (prog1 (1+ (point))
+                (insert "       *: "
+                        (setq group (pop groups)) "\n"))
+       (list 'gnus-group (gnus-intern-safe group gnus-active-hashtb)
+            'gnus-unread t
+            'gnus-level (inline (gnus-group-level group)))))
     (goto-char (point-min))))
 
 (defun gnus-activate-all-groups (level)
   "Activate absolutely all groups."
-  (interactive (list 7))
+  (interactive (list gnus-level-unsubscribed))
   (let ((gnus-activate-level level)
        (gnus-activate-foreign-newsgroups level))
     (gnus-group-get-new-news)))
@@ -2178,76 +2900,100 @@ 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")
-  (run-hooks 'gnus-get-new-news-hook)
-  ;; We might read in new NoCeM messages here.
-  (when (and gnus-use-nocem 
-            (null arg))
-    (gnus-nocem-scan-groups))
-  ;; If ARG is not a number, then we read the active file.
-  (when (and arg (not (numberp arg)))
-    (let ((gnus-read-active-file t))
-      (gnus-read-active-file))
-    (setq arg nil))
-
-  (setq arg (gnus-group-default-level arg t))
-  (if (and gnus-read-active-file (not arg))
-      (progn
-       (gnus-read-active-file)
-       (gnus-get-unread-articles arg))
-    (let ((gnus-read-active-file (if arg nil gnus-read-active-file)))
-      (gnus-get-unread-articles arg)))
-  (run-hooks 'gnus-after-getting-new-news-hook)
-  (gnus-group-list-groups))
-
-(defun gnus-group-get-new-news-this-group (&optional n)
+  (let ((gnus-inhibit-demon t))
+    (gnus-run-hooks 'gnus-get-new-news-hook)
+
+    ;; Read any slave files.
+    (unless gnus-slave
+      (gnus-master-read-slave-newsrc))
+
+    ;; We might read in new NoCeM messages here.
+    (when (and gnus-use-nocem
+              (null arg))
+      (gnus-nocem-scan-groups))
+    ;; If ARG is not a number, then we read the active file.
+    (when (and arg (not (numberp arg)))
+      (let ((gnus-read-active-file t))
+       (gnus-read-active-file))
+      (setq arg nil)
+
+      ;; If the user wants it, we scan for new groups.
+      (when (eq gnus-check-new-newsgroups 'always)
+       (gnus-find-new-newsgroups)))
+
+    (setq arg (gnus-group-default-level arg t))
+    (if (and gnus-read-active-file (not arg))
+       (progn
+         (gnus-read-active-file)
+         (gnus-get-unread-articles arg))
+      (let ((gnus-read-active-file (if arg nil gnus-read-active-file)))
+       (gnus-get-unread-articles arg)))
+    (gnus-run-hooks 'gnus-after-getting-new-news-hook)
+    (gnus-group-list-groups (and (numberp arg)
+                                (max (car gnus-group-list-mode) arg)))))
+
+(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."
   (interactive "P")
   (let* ((groups (gnus-group-process-prefix n))
         (ret (if (numberp n) (- n (length groups)) 0))
-        (beg (unless n (point)))
-        group)
+        (beg (unless n
+               (point)))
+        group method)
     (while (setq group (pop groups))
       (gnus-group-remove-mark group)
-      (if (gnus-activate-group group 'scan)
+      ;; Bypass any previous denials from the server.
+      (gnus-remove-denial (setq method (gnus-find-method-for-group group)))
+      (if (gnus-activate-group group (if dont-scan nil 'scan))
          (progn
            (gnus-get-unread-articles-in-group
             (gnus-get-info group) (gnus-active group) t)
            (unless (gnus-virtual-group-p group)
              (gnus-close-group group))
+           (when gnus-agent
+             (gnus-agent-save-group-info
+              method (gnus-group-real-name group) (gnus-active group)))
            (gnus-group-update-group group))
        (if (eq (gnus-server-status (gnus-find-method-for-group group))
                'denied)
-           (gnus-error "Server denied access")
+           (gnus-error "Server denied access")
          (gnus-error 3 "%s error: %s" group (gnus-status-message group)))))
-    (when beg (goto-char beg))
+    (when beg
+      (goto-char beg))
     (when gnus-goto-next-group-when-activating
       (gnus-group-next-unread-group 1 t))
     (gnus-summary-position-point)
     ret))
 
 (defun gnus-group-fetch-faq (group &optional faq-dir)
-  "Fetch the FAQ for the current group."
+  "Fetch the FAQ for the current group.
+If given a prefix argument, prompt for the FAQ dir
+to use."
   (interactive
    (list
-    (and (gnus-group-group-name)
-        (gnus-group-real-name (gnus-group-group-name)))
-    (cond (current-prefix-arg
-          (completing-read
-           "Faq dir: " (and (listp gnus-group-faq-directory)
-                            (mapcar (lambda (file) (list file))
-                                    gnus-group-faq-directory)))))))
-  (or faq-dir
-      (setq faq-dir (if (listp gnus-group-faq-directory)
-                       (car gnus-group-faq-directory)
-                     gnus-group-faq-directory)))
-  (or group (error "No group name given"))
-  (let ((file (concat (file-name-as-directory faq-dir)
-                     (gnus-group-real-name group))))
-    (if (not (file-exists-p file))
-       (error "No such file: %s" file)
-      (find-file file))))
+    (gnus-group-group-name)
+    (when current-prefix-arg
+      (completing-read
+       "Faq dir: " (and (listp gnus-group-faq-directory)
+                       (mapcar (lambda (file) (list file))
+                               gnus-group-faq-directory))))))
+  (unless group
+    (error "No group name given"))
+  (let ((dirs (or faq-dir gnus-group-faq-directory))
+       dir found file)
+    (unless (listp dirs)
+      (setq dirs (list dirs)))
+    (while (and (not found)
+               (setq dir (pop dirs)))
+      (let ((name (gnus-group-real-name group)))
+       (setq file (concat (file-name-as-directory dir) name)))
+      (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-describe-group (force &optional group)
   "Display a description of the current newsgroup."
@@ -2258,25 +3004,27 @@ If N is negative, this group and the N-1 previous groups will be checked."
     (when (and force
               gnus-description-hashtb)
       (gnus-sethash mname nil gnus-description-hashtb))
-    (or group (error "No group name given"))
-    (and (or (and gnus-description-hashtb
-                 ;; We check whether this group's method has been
-                 ;; queried for a description file.
-                 (gnus-gethash mname gnus-description-hashtb))
-            (setq desc (gnus-group-get-description group))
-            (gnus-read-descriptions-file method))
-        (gnus-message 1
-         (or desc (gnus-gethash group gnus-description-hashtb)
-             "No description available")))))
+    (unless group
+      (error "No group name given"))
+    (when (or (and gnus-description-hashtb
+                  ;; We check whether this group's method has been
+                  ;; queried for a description file.
+                  (gnus-gethash mname gnus-description-hashtb))
+             (setq desc (gnus-group-get-description group))
+             (gnus-read-descriptions-file method))
+      (gnus-message 1
+                   (or desc (gnus-gethash group gnus-description-hashtb)
+                       "No description available")))))
 
 ;; Suggested by Per Abrahamsen <amanda@iesd.auc.dk>.
 (defun gnus-group-describe-all-groups (&optional force)
   "Pop up a buffer with descriptions of all newsgroups."
   (interactive "P")
-  (and force (setq gnus-description-hashtb nil))
-  (if (not (or gnus-description-hashtb
-              (gnus-read-all-descriptions-files)))
-      (error "Couldn't request descriptions file"))
+  (when force
+    (setq gnus-description-hashtb nil))
+  (when (not (or gnus-description-hashtb
+                (gnus-read-all-descriptions-files)))
+    (error "Couldn't request descriptions file"))
   (let ((buffer-read-only nil)
        b)
     (erase-buffer)
@@ -2305,33 +3053,32 @@ If N is negative, this group and the N-1 previous groups will be checked."
      (lambda (group)
        (and (symbol-name group)
            (string-match regexp (symbol-name group))
-           (setq groups (cons (symbol-name group) groups))))
+           (symbol-value group)
+           (push (symbol-name group) groups)))
      gnus-active-hashtb)
     ;; Also go through all descriptions that are known to Gnus.
     (when search-description
       (mapatoms
        (lambda (group)
         (and (string-match regexp (symbol-value group))
-             (gnus-active (symbol-name group))
-             (setq groups (cons (symbol-name group) groups))))
+             (push (symbol-name group) groups)))
        gnus-description-hashtb))
     (if (not groups)
        (gnus-message 3 "No groups matched \"%s\"." regexp)
       ;; 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.
-         (if (not (string= (car groups) prev))
-             (progn
-               (insert (setq prev (car groups)) "\n")
-               (if (and gnus-description-hashtb
-                        (setq des (gnus-gethash (car groups)
-                                                gnus-description-hashtb)))
-                   (insert "  " des "\n"))))
+         (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 groups (cdr groups)))
        (goto-char (point-min))))
     (pop-to-buffer obuf)))
@@ -2339,9 +3086,9 @@ If N is negative, this group and the N-1 previous groups will be checked."
 (defun gnus-group-description-apropos (regexp)
   "List all newsgroups that have names or descriptions that match a regexp."
   (interactive "sGnus description apropos (regexp): ")
-  (if (not (or gnus-description-hashtb
-              (gnus-read-all-descriptions-files)))
-      (error "Couldn't request descriptions file"))
+  (when (not (or gnus-description-hashtb
+                (gnus-read-all-descriptions-files)))
+    (error "Couldn't request descriptions file"))
   (gnus-group-apropos regexp t))
 
 ;; Suggested by Per Abrahamsen <amanda@iesd.auc.dk>.
@@ -2358,8 +3105,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)
+  (gnus-group-prepare-flat
+   (or level gnus-level-subscribed) all (or lowest 1) regexp)
   (goto-char (point-min))
   (gnus-group-position-point))
 
@@ -2369,6 +3116,8 @@ 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."
   (interactive "P\nsList newsgroups matching: ")
+  (when level
+    (setq level (prefix-numeric-value level)))
   (gnus-group-list-matching (or level gnus-level-killed) regexp t lowest))
 
 ;; Suggested by Jack Vinson <vinson@unagi.cis.upenn.edu>.
@@ -2382,16 +3131,16 @@ If FORCE, force saving whether it is necessary or not."
   "Force Gnus to read the .newsrc file."
   (interactive "P")
   (when (gnus-yes-or-no-p
-        (format "Are you sure you want to read %s? "
-                gnus-current-startup-file))
+        (format "Are you sure you want to restart Gnus? "))
     (gnus-save-newsrc-file)
-    (gnus-setup-news 'force)
-    (gnus-group-list-groups arg)))
+    (gnus-clear-system)
+    (gnus)))
 
 (defun gnus-group-read-init-file ()
   "Read the Gnus elisp init file."
   (interactive)
-  (gnus-read-init-file))
+  (gnus-read-init-file)
+  (gnus-message 5 "Read %s" gnus-init-file))
 
 (defun gnus-group-check-bogus-groups (&optional silent)
   "Check bogus newsgroups.
@@ -2401,6 +3150,17 @@ group."
   (gnus-check-bogus-newsgroups (and (not silent) (not gnus-expert-user)))
   (gnus-group-list-groups))
 
+(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.'
+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."
@@ -2428,18 +3188,15 @@ If GROUP, edit that local kill file instead."
 In fact, cleanup buffers except for group mode buffer.
 The hook gnus-suspend-gnus-hook is called before actually suspending."
   (interactive)
-  (run-hooks 'gnus-suspend-gnus-hook)
+  (gnus-run-hooks 'gnus-suspend-gnus-hook)
   ;; 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 (member buf (list group-buf gnus-dribble-buffer))
+               (kill-buffer buf)))
+           (gnus-buffers))
     (gnus-kill-gnus-frames)
     (when group-buf
-      (setq gnus-buffer-list (list group-buf))
       (bury-buffer group-buf)
       (delete-windows-on group-buf t))))
 
@@ -2453,12 +3210,12 @@ The hook gnus-suspend-gnus-hook is called before actually suspending."
   "Quit reading news after updating .newsrc.eld and .newsrc.
 The hook `gnus-exit-gnus-hook' is called before actually exiting."
   (interactive)
-  (when 
+  (when
       (or noninteractive               ;For gnus-batch-kill
          (not gnus-interactive-exit)   ;Without confirmation
          gnus-expert-user
          (gnus-y-or-n-p "Are you sure you want to quit reading news? "))
-    (run-hooks 'gnus-exit-gnus-hook)
+    (gnus-run-hooks 'gnus-exit-gnus-hook)
     ;; Offer to save data from non-quitted summary buffers.
     (gnus-offer-save-summaries)
     ;; Save the newsrc file(s).
@@ -2468,7 +3225,7 @@ The hook `gnus-exit-gnus-hook' is called before actually exiting."
     ;; Reset everything.
     (gnus-clear-system)
     ;; Allow the user to do things after cleaning up.
-    (run-hooks 'gnus-after-exiting-gnus-hook)))
+    (gnus-run-hooks 'gnus-after-exiting-gnus-hook)))
 
 (defun gnus-group-quit ()
   "Quit reading news without updating .newsrc.eld or .newsrc.
@@ -2482,14 +3239,14 @@ The hook `gnus-exit-gnus-hook' is called before actually exiting."
            (gnus-yes-or-no-p
             (format "Quit reading news without saving %s? "
                     (file-name-nondirectory gnus-current-startup-file))))
-    (run-hooks 'gnus-exit-gnus-hook)
+    (gnus-run-hooks 'gnus-exit-gnus-hook)
     (gnus-configure-windows 'group t)
     (gnus-dribble-save)
     (gnus-close-backends)
     (gnus-clear-system)
     (gnus-kill-buffer gnus-group-buffer)
     ;; Allow the user to do things after cleaning up.
-    (run-hooks 'gnus-after-exiting-gnus-hook)))
+    (gnus-run-hooks 'gnus-after-exiting-gnus-hook)))
 
 (defun gnus-group-describe-briefly ()
   "Give a one line description of the group mode commands."
@@ -2516,8 +3273,8 @@ and the second element is the address."
                      "Address: "
                      (mapcar (lambda (server) (list server))
                              gnus-secondary-servers)))
-            ;; We got a server name, so we find the method.
-            (gnus-server-to-method how)))))
+            ;; We got a server name.
+            how))))
   (gnus-browse-foreign-server method))
 
 (defun gnus-group-set-info (info &optional method-only-group part)
@@ -2583,71 +3340,61 @@ 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)))
             (or (null articles)
-                (setcdr (nthcdr 2 info)
-                        (list (list (cons type (gnus-compress-sequence
-                                                articles t)))))))
+                (setcdr (nthcdr 2 info)
+                        (list (list (cons type (gnus-compress-sequence
+                                                articles t)))))))
        (and (not (setq m (assq type (car marked))))
             (or (null articles)
-                (setcar marked
-                        (cons (cons type (gnus-compress-sequence articles t) )
-                              (car marked)))))
+                (setcar marked
+                        (cons (cons type (gnus-compress-sequence articles t) )
+                              (car marked)))))
        (if force
            (if (null articles)
-               (setcar (nthcdr 3 info)
-                       (delq (assq type (car marked)) (car marked)))
-             (setcdr m (gnus-compress-sequence articles t)))
+               (setcar (nthcdr 3 info)
+                       (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-update-read-articles (group unread)
-  "Update the list of read and ticked articles in GROUP using the
-UNREAD and TICKED lists.
-Note: UNSELECTED has to be sorted over `<'.
-Returns whether the updating was successful."
-  (let* ((active (or gnus-newsgroup-active (gnus-active group)))
-        (entry (gnus-gethash group gnus-newsrc-hashtb))
-        (info (nth 2 entry))
-        (prev 1)
-        (unread (sort (copy-sequence unread) '<))
-        read)
-    (if (or (not info) (not active))
-       ;; There is no info on this group if it was, in fact,
-       ;; killed.  Gnus stores no information on killed groups, so
-       ;; there's nothing to be done.
-       ;; One could store the information somewhere temporarily,
-       ;; perhaps...  Hmmm...
-       ()
-      ;; Remove any negative articles numbers.
-      (while (and unread (< (car unread) 0))
-       (setq unread (cdr unread)))
-      ;; Remove any expired article numbers
-      (while (and unread (< (car unread) (car active)))
-       (setq unread (cdr unread)))
-      ;; Compute the ranges of read articles by looking at the list of
-      ;; unread articles.
-      (while unread
-       (if (/= (car unread) prev)
-           (setq read (cons (if (= prev (1- (car unread))) prev
-                              (cons prev (1- (car unread)))) read)))
-       (setq prev (1+ (car unread)))
-       (setq unread (cdr unread)))
-      (when (<= prev (cdr active))
-       (setq read (cons (cons prev (cdr active)) read)))
-      ;; Enter this list into the group info.
-      (gnus-info-set-read
-       info (if (> (length read) 1) (nreverse read) read))
-      ;; Set the number of unread articles in gnus-newsrc-hashtb.
-      (gnus-get-unread-articles-in-group info (gnus-active group))
-      t)))
+;;;
+;;; Group timestamps
+;;;
+
+(defun gnus-group-set-timestamp ()
+  "Change the timestamp of the current group to the current time.
+This function can be used in hooks like `gnus-select-group-hook'
+or `gnus-group-catchup-group-hook'."
+  (when gnus-newsgroup-name
+    (let ((time (current-time)))
+      (setcdr (cdr time) nil)
+      (gnus-group-set-parameter gnus-newsgroup-name 'timestamp time))))
+
+(defsubst gnus-group-timestamp (group)
+  "Return the timestamp for GROUP."
+  (gnus-group-get-parameter group 'timestamp t))
+
+(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 (subtract-time (current-time) time)))
+    (+ (* (nth 0 delta) 65536.0)
+       (nth 1 delta))))
+
+(defun gnus-group-timestamp-string (group)
+  "Return a string of the timestamp for GROUP."
+  (let ((time (gnus-group-timestamp group)))
+    (if (not time)
+       ""
+      (gnus-time-iso8601 time))))
 
 (provide 'gnus-group)