(gnus-group-highlight-line): Typo fix: beg, not start.
[gnus] / lisp / gnus-group.el
index c8733d1..c17b3a2 100644 (file)
@@ -1,27 +1,25 @@
 ;;; gnus-group.el --- group mode commands for Gnus
 
 ;; Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
-;;   2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+;;   2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
 
 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
 ;; Keywords: news
 
 ;; This file is part of GNU Emacs.
 
-;; GNU Emacs is free software; you can redistribute it and/or modify
+;; GNU Emacs is free software: you can redistribute it and/or modify
 ;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 3, or (at your option)
-;; any later version.
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
 
 ;; GNU Emacs is distributed in the hope that it will be useful,
 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.         See the
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 ;; GNU General Public License for more details.
 
 ;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs; see the file COPYING.  If not, write to the
-;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
-;; Boston, MA 02110-1301, USA.
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
 
 ;;; Commentary:
 
@@ -112,6 +110,18 @@ If nil, no groups are permanently visible."
   :group 'gnus-group-listing
   :type '(choice regexp (const nil)))
 
+(defcustom gnus-safe-html-newsgroups "\\`nnrss[+:]"
+  "Groups in which links in html articles are considered all safe.
+The value may be a regexp matching those groups, a list of group names,
+or nil.  This overrides `mm-w3m-safe-url-regexp' (which see).  This is
+effective only when emacs-w3m renders html articles, i.e., in the case
+`mm-text-html-renderer' is set to `w3m'."
+  :version "23.2"
+  :group 'gnus-group-various
+  :type '(choice regexp
+                (repeat :tag "List of group names" (string :tag "Group"))
+                (const nil)))
+
 (defcustom gnus-list-groups-with-ticked-articles t
   "*If non-nil, list groups that have only ticked articles.
 If nil, only list groups that have unread articles."
@@ -159,7 +169,7 @@ list."
                         (function-item gnus-group-sort-by-rank)
                         (function :tag "other" nil))))
 
-(defcustom gnus-group-line-format "%M\%S\%p\%P\%5y:%B%(%g%)%O\n"
+(defcustom gnus-group-line-format "%M\%S\%p\%P\%5y:%B%(%g%)\n"
   "*Format of group lines.
 It works along the same lines as a normal formatting string,
 with some simple extensions.
@@ -282,13 +292,8 @@ If you want to modify the group buffer, you can use this hook."
   :group 'gnus-exit
   :type 'hook)
 
-(defcustom gnus-group-update-hook '(gnus-group-highlight-line)
-  "Hook called when a group line is changed.
-The hook will not be called if `gnus-visual' is nil.
-
-The default function `gnus-group-highlight-line' will
-highlight the line according to the `gnus-group-highlight'
-variable."
+(defcustom gnus-group-update-hook nil
+  "Hook called when a group line is changed."
   :group 'gnus-group-visual
   :type 'hook)
 
@@ -418,7 +423,6 @@ group: The name of the group.
 unread: The number of unread articles in the group.
 method: The select method used.
 mailp: Whether it's a mail group or not.
-newsp: Whether it's a news group or not
 level: The level of the group.
 score: The score of the group.
 ticked: The number of ticked articles."
@@ -499,7 +503,10 @@ simple manner.")
                   (gnus-range-length (cdr (assq 'tick gnus-tmp-marked))))))
              (t number)) ?s)
     (?R gnus-tmp-number-of-read ?s)
-    (?U (gnus-number-of-unseen-articles-in-group gnus-tmp-group) ?d)
+    (?U (if (gnus-active gnus-tmp-group)
+           (gnus-number-of-unseen-articles-in-group gnus-tmp-group)
+         "*")
+       ?s)
     (?t gnus-tmp-number-total ?d)
     (?y gnus-tmp-number-of-unread ?s)
     (?I (gnus-range-length (cdr (assq 'dormant gnus-tmp-marked))) ?d)
@@ -650,7 +657,6 @@ simple manner.")
   "h" gnus-group-make-help-group
   "u" gnus-group-make-useful-group
   "a" gnus-group-make-archive-group
-  "k" gnus-group-make-kiboze-group
   "l" gnus-group-nnimap-edit-acl
   "m" gnus-group-make-group
   "E" gnus-group-edit-group
@@ -666,17 +672,10 @@ simple manner.")
   "R" gnus-group-make-rss-group
   "c" gnus-group-customize
   "z" gnus-group-compact-group
-  "x" gnus-group-nnimap-expunge
+  "x" gnus-group-expunge-group
   "\177" gnus-group-delete-group
   [delete] gnus-group-delete-group)
 
-(gnus-define-keys (gnus-group-soup-map "s" gnus-group-group-map)
-  "b" gnus-group-brew-soup
-  "w" gnus-soup-save-areas
-  "s" gnus-soup-send-replies
-  "p" gnus-soup-pack-packet
-  "r" nnsoup-pack-replies)
-
 (gnus-define-keys (gnus-group-sort-map "S" gnus-group-group-map)
   "s" gnus-group-sort-groups
   "a" gnus-group-sort-groups-by-alphabet
@@ -928,7 +927,6 @@ simple manner.")
        ["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]
        ["Make an ephemeral group..." gnus-group-read-ephemeral-group t]
@@ -962,13 +960,6 @@ simple manner.")
     (easy-menu-define
      gnus-group-misc-menu gnus-group-mode-map ""
      `("Gnus"
-       ("SOUP"
-       ["Pack replies" nnsoup-pack-replies (fboundp 'nnsoup-request-group)]
-       ["Send replies" gnus-soup-send-replies
-        (fboundp 'gnus-soup-pack-packet)]
-       ["Pack packet" gnus-soup-pack-packet (fboundp 'gnus-soup-pack-packet)]
-       ["Save areas" gnus-soup-save-areas (fboundp 'gnus-soup-pack-packet)]
-       ["Brew SOUP" gnus-group-brew-soup (fboundp 'gnus-soup-pack-packet)])
        ["Send a mail" gnus-group-mail t]
        ["Send a message (mail or news)" gnus-group-post-news t]
        ["Create a local message" gnus-group-news t]
@@ -986,7 +977,6 @@ simple manner.")
        ["Browse foreign server..." gnus-group-browse-foreign-server t]
        ["Enter server buffer" gnus-group-enter-server-mode t]
        ["Expire all expirable articles" gnus-group-expire-all-groups t]
-       ["Generate any kiboze groups" nnkiboze-generate-groups t]
        ["Gnus version" gnus-version t]
        ["Save .newsrc files" gnus-group-save-newsrc t]
        ["Suspend Gnus" gnus-group-suspend t]
@@ -1110,6 +1100,7 @@ See `gmm-tool-bar-from-list' for the format of the list."
   :group 'gnus-group)
 
 (defvar image-load-path)
+(defvar tool-bar-map)
 
 (defun gnus-group-make-tool-bar (&optional force)
   "Make a group mode tool bar from `gnus-group-tool-bar'.
@@ -1188,8 +1179,8 @@ The following commands are available:
       (goto-char (point-min))
       (setq gnus-group-mark-positions
            (list (cons 'process (and (search-forward
-                                      (mm-string-as-multibyte "\200") nil t)
-                                     (- (point) 2))))))))
+                                      (mm-string-to-multibyte "\200") nil t)
+                                     (- (point) (point-min) 1))))))))
 
 (defun gnus-mouse-pick-group (e)
   "Enter the group under the mouse pointer."
@@ -1279,7 +1270,7 @@ Also see the `gnus-group-use-permanent-levels' variable."
                   (zerop number))
              (zerop (buffer-size)))
       ;; No groups in the buffer.
-      (gnus-message 5 gnus-no-groups-message))
+      (gnus-message 5 "%s" gnus-no-groups-message))
     ;; We have some groups displayed.
     (goto-char (point-max))
     (when (or (not gnus-group-goto-next-group-function)
@@ -1361,7 +1352,8 @@ if it is a string, only list groups matching REGEXP."
          (setq not-in-list (delete group not-in-list)))
        (when (gnus-group-prepare-logic
               group
-              (and unread              ; This group might be unchecked
+              (and (or unread          ; This group might be unchecked
+                       predicate)      ; Check if this group should be listed
                    (or (not (stringp regexp))
                        (string-match regexp group))
                    (<= (setq clevel (gnus-info-level info)) level)
@@ -1375,7 +1367,7 @@ if it is a string, only list groups matching REGEXP."
                       (if (eq unread t) ; Unactivated?
                           gnus-group-list-inactive-groups
                                        ; We list unactivated
-                        (> unread 0))
+                        (and (numberp unread) (> unread 0)))
                                        ; We list groups with unread articles
                       (and gnus-list-groups-with-ticked-articles
                            (cdr (assq 'tick (gnus-info-marks info))))
@@ -1580,7 +1572,7 @@ if it is a string, only list groups matching REGEXP."
              ?m ? ))
         (gnus-tmp-moderated-string
          (if (eq gnus-tmp-moderated ?m) "(m)" ""))
-        (gnus-tmp-group-icon "==&&==")
+         (gnus-tmp-group-icon (gnus-group-get-icon gnus-tmp-qualified-group))
         (gnus-tmp-news-server (or (cadr gnus-tmp-method) ""))
         (gnus-tmp-news-method (or (car gnus-tmp-method) ""))
         (gnus-tmp-news-method-string
@@ -1627,138 +1619,150 @@ if it is a string, only list groups matching REGEXP."
                              'gnus-tool-bar-update))
     (forward-line -1)
     (when (inline (gnus-visual-p 'group-highlight 'highlight))
-      (gnus-run-hooks 'gnus-group-update-hook))
+      (gnus-group-highlight-line gnus-tmp-qualified-group beg end))
+    (gnus-run-hooks 'gnus-group-update-hook)
     (forward-line)
     ;; Allow XEmacs to remove front-sticky text properties.
     (gnus-group-remove-excess-properties)))
 
-(defun gnus-group-highlight-line ()
-  "Highlight the current line according to `gnus-group-highlight'."
-  (let* ((list gnus-group-highlight)
-        (p (point))
-        (end (point-at-eol))
-        ;; now find out where the line starts and leave point there.
-        (beg (progn (beginning-of-line) (point)))
-        (group (gnus-group-group-name))
-        (entry (gnus-group-entry group))
-        (unread (if (numberp (car entry)) (car entry) 0))
-        (active (gnus-active group))
-        (total (if active (1+ (- (cdr active) (car active))) 0))
-        (info (nth 2 entry))
-        (method (inline (gnus-server-get-method group (gnus-info-method info))))
-        (marked (gnus-info-marks info))
-        (mailp (apply 'append
-                      (mapcar
-                       (lambda (x)
-                         (memq x (assoc (symbol-name
-                                         (car (or method gnus-select-method)))
-                                        gnus-valid-select-methods)))
-                       '(mail post-mail))))
-        (level (or (gnus-info-level info) gnus-level-killed))
-        (score (or (gnus-info-score info) 0))
-        (ticked (gnus-range-length (cdr (assq 'tick marked))))
-        (group-age (gnus-group-timestamp-delta group))
-        (inhibit-read-only t))
-    ;; FIXME: http://thread.gmane.org/gmane.emacs.gnus.general/65451/focus=65465
-    ;; ======================================================================
-    ;; From: Richard Stallman
-    ;; Subject: Re: Rewriting gnus-group-highlight-line (was: [...])
-    ;; Cc: ding@gnus.org
-    ;; Date: Sat, 27 Oct 2007 19:41:20 -0400
-    ;; Message-ID: <E1IlvHM-0006TS-7t@fencepost.gnu.org>
-    ;;
-    ;; [...]
-    ;; The kludge is that the alist elements contain expressions that refer
-    ;; to local variables with short names.  Perhaps write your own tiny
-    ;; evaluator that handles just `and', `or', and numeric comparisons
-    ;; and just a few specific variables.
-    ;; ======================================================================
-    ;;
-    ;; Similar for other evaluated variables.  Grep for risky-local-variable
-    ;; to find them!  -- rsteib
-    ;;
-    ;; Eval the cars of the lists until we find a match.
-    (while (and list
-               (not (eval (caar list))))
-      (setq list (cdr list)))
-    (let ((face (cdar list)))
-      (unless (eq face (get-text-property beg 'face))
-       (gnus-put-text-property-excluding-characters-with-faces
-        beg end 'face
-        (setq face (if (boundp face) (symbol-value face) face)))
-       (gnus-extent-start-open beg)))
-    (goto-char p)))
+(defun gnus-group-update-eval-form (group list)
+  "Eval `car' of each element of LIST, and return the first that return t.
+Some value are bound so the form can use them."
+  (when list
+    (let* ((entry (gnus-group-entry group))
+           (unread (if (numberp (car entry)) (car entry) 0))
+           (active (gnus-active group))
+           (total (if active (1+ (- (cdr active) (car active))) 0))
+           (info (nth 2 entry))
+           (method (inline (gnus-server-get-method group (gnus-info-method info))))
+           (marked (gnus-info-marks info))
+           (mailp (apply 'append
+                         (mapcar
+                          (lambda (x)
+                            (memq x (assoc (symbol-name
+                                            (car (or method gnus-select-method)))
+                                           gnus-valid-select-methods)))
+                          '(mail post-mail))))
+           (level (or (gnus-info-level info) gnus-level-killed))
+           (score (or (gnus-info-score info) 0))
+           (ticked (gnus-range-length (cdr (assq 'tick marked))))
+           (group-age (gnus-group-timestamp-delta group)))
+      ;; FIXME: http://thread.gmane.org/gmane.emacs.gnus.general/65451/focus=65465
+      ;; ======================================================================
+      ;; From: Richard Stallman
+      ;; Subject: Re: Rewriting gnus-group-highlight-line (was: [...])
+      ;; Cc: ding@gnus.org
+      ;; Date: Sat, 27 Oct 2007 19:41:20 -0400
+      ;; Message-ID: <E1IlvHM-0006TS-7t@fencepost.gnu.org>
+      ;;
+      ;; [...]
+      ;; The kludge is that the alist elements contain expressions that refer
+      ;; to local variables with short names.  Perhaps write your own tiny
+      ;; evaluator that handles just `and', `or', and numeric comparisons
+      ;; and just a few specific variables.
+      ;; ======================================================================
+      ;;
+      ;; Similar for other evaluated variables.  Grep for risky-local-variable
+      ;; to find them!  -- rsteib
+      ;;
+      ;; Eval the cars of the lists until we find a match.
+      (while (and list
+                  (not (eval (caar list))))
+        (setq list (cdr list)))
+      list)))
+
+(defun gnus-group-highlight-line (group beg end)
+  "Highlight the current line according to `gnus-group-highlight'.
+GROUP is current group, and the line to highlight starts at START
+and ends at END."
+  (let ((face (cdar (gnus-group-update-eval-form
+                      group
+                      gnus-group-highlight))))
+    (unless (eq face (get-text-property beg 'face))
+      (let ((inhibit-read-only t))
+        (gnus-put-text-property-excluding-characters-with-faces
+         beg end 'face
+         (if (boundp face) (symbol-value face) face)))
+      (gnus-extent-start-open beg))))
+
+(defun gnus-group-get-icon (group)
+  "Return an icon for GROUP according to `gnus-group-icon-list'."
+  (if gnus-group-icon-list
+      (let ((image-path
+             (cdar (gnus-group-update-eval-form group gnus-group-icon-list))))
+        (if image-path
+            (propertize " "
+                        'display
+                        (append
+                         (gnus-create-image (expand-file-name image-path))
+                         '(:ascent center)))
+          " "))
+    " "))
 
 (defun gnus-group-update-group (group &optional visible-only)
   "Update all lines where GROUP appear.
 If VISIBLE-ONLY is non-nil, the group won't be displayed if it isn't
 already."
-  ;; Can't use `save-excursion' here, so we do it manually.
-  (let ((buf (current-buffer))
-       mark)
-    (set-buffer gnus-group-buffer)
-    (setq mark (point-marker))
-    ;; The buffer may be narrowed.
-    (save-restriction
-      (widen)
-      (let ((ident (gnus-intern-safe group gnus-active-hashtb))
-           (loc (point-min))
-           found buffer-read-only)
-       ;; Enter the current status into the dribble buffer.
-       (let ((entry (gnus-group-entry group)))
-         (when (and entry
-                    (not (gnus-ephemeral-group-p group)))
-           (gnus-dribble-enter
-            (concat "(gnus-group-set-info '"
-                    (gnus-prin1-to-string (nth 2 entry))
-                    ")"))))
-       ;; Find all group instances.  If topics are in use, each group
-       ;; may be listed in more than once.
-       (while (setq loc (text-property-any
-                         loc (point-max) 'gnus-group ident))
-         (setq found t)
-         (goto-char loc)
-         (let ((gnus-group-indentation (gnus-group-group-indentation)))
-           (gnus-delete-line)
-           (gnus-group-insert-group-line-info group)
-           (save-excursion
-             (forward-line -1)
-             (gnus-run-hooks 'gnus-group-update-group-hook)))
-         (setq loc (1+ loc)))
-       (unless (or found visible-only)
-         ;; No such line in the buffer, find out where it's supposed to
-         ;; go, and insert it there (or at the end of the buffer).
-         (if gnus-goto-missing-group-function
-             (funcall gnus-goto-missing-group-function group)
-           (let ((entry (cddr (gnus-group-entry group))))
-             (while (and entry (car entry)
-                         (not
-                          (gnus-goto-char
-                           (text-property-any
-                            (point-min) (point-max)
-                            'gnus-group (gnus-intern-safe
-                                         (caar entry) gnus-active-hashtb)))))
-               (setq entry (cdr entry)))
-             (or entry (goto-char (point-max)))))
-         ;; Finally insert the line.
-         (let ((gnus-group-indentation (gnus-group-group-indentation)))
-           (gnus-group-insert-group-line-info group)
-           (save-excursion
-             (forward-line -1)
-             (gnus-run-hooks 'gnus-group-update-group-hook))))
-       (when gnus-group-update-group-function
-         (funcall gnus-group-update-group-function group))
-       (gnus-group-set-mode-line)))
-    (goto-char mark)
-    (set-marker mark nil)
-    (set-buffer buf)))
+  (with-current-buffer gnus-group-buffer
+    (save-excursion
+      ;; The buffer may be narrowed.
+      (save-restriction
+        (widen)
+        (let ((ident (gnus-intern-safe group gnus-active-hashtb))
+              (loc (point-min))
+              found buffer-read-only)
+          ;; Enter the current status into the dribble buffer.
+          (let ((entry (gnus-group-entry group)))
+            (when (and entry
+                       (not (gnus-ephemeral-group-p group)))
+              (gnus-dribble-enter
+               (concat "(gnus-group-set-info '"
+                       (gnus-prin1-to-string (nth 2 entry))
+                       ")"))))
+          ;; Find all group instances.  If topics are in use, each group
+          ;; may be listed in more than once.
+          (while (setq loc (text-property-any
+                            loc (point-max) 'gnus-group ident))
+            (setq found t)
+            (goto-char loc)
+            (let ((gnus-group-indentation (gnus-group-group-indentation)))
+              (gnus-delete-line)
+              (gnus-group-insert-group-line-info group)
+              (save-excursion
+                (forward-line -1)
+                (gnus-run-hooks 'gnus-group-update-group-hook)))
+            (setq loc (1+ loc)))
+          (unless (or found visible-only)
+            ;; No such line in the buffer, find out where it's supposed to
+            ;; go, and insert it there (or at the end of the buffer).
+            (if gnus-goto-missing-group-function
+                (funcall gnus-goto-missing-group-function group)
+              (let ((entry (cddr (gnus-group-entry group))))
+                (while (and entry (car entry)
+                            (not
+                             (gnus-goto-char
+                              (text-property-any
+                               (point-min) (point-max)
+                               'gnus-group (gnus-intern-safe
+                                            (caar entry)
+                                            gnus-active-hashtb)))))
+                  (setq entry (cdr entry)))
+                (or entry (goto-char (point-max)))))
+            ;; Finally insert the line.
+            (let ((gnus-group-indentation (gnus-group-group-indentation)))
+              (gnus-group-insert-group-line-info group)
+              (save-excursion
+                (forward-line -1)
+                (gnus-run-hooks 'gnus-group-update-group-hook))))
+          (when gnus-group-update-group-function
+            (funcall gnus-group-update-group-function group))
+          (gnus-group-set-mode-line))))))
 
 (defun gnus-group-set-mode-line ()
   "Update the mode line in the group buffer."
   (when (memq 'group gnus-updated-mode-lines)
     ;; Yes, we want to keep this mode line updated.
-    (save-excursion
-      (set-buffer gnus-group-buffer)
+    (with-current-buffer gnus-group-buffer
       (let* ((gformat (or gnus-group-mode-line-format-spec
                          (gnus-set-format 'group-mode)))
             (gnus-tmp-news-server (cadr gnus-select-method))
@@ -1771,8 +1775,7 @@ already."
              (and gnus-dribble-buffer
                   (buffer-name gnus-dribble-buffer)
                   (buffer-modified-p gnus-dribble-buffer)
-                  (save-excursion
-                    (set-buffer gnus-dribble-buffer)
+                  (with-current-buffer gnus-dribble-buffer
                     (not (zerop (buffer-size))))))
             (mode-string (eval gformat)))
        ;; Say whether the dribble buffer has been modified.
@@ -2190,7 +2193,10 @@ be permanent."
 The arguments are the same as `completing-read' except that COLLECTION
 and HIST default to `gnus-active-hashtb' and `gnus-group-history'
 respectively if they are omitted."
-  (let (group)
+  (let ((completion-styles (and (boundp 'completion-styles)
+                               completion-styles))
+       group)
+    (push 'substring completion-styles)
     (mapatoms (lambda (symbol)
                (setq group (symbol-name symbol))
                (set (intern (if (string-match "[^\000-\177]" group)
@@ -2325,7 +2331,7 @@ Return the name of the group if selection was successful."
         (message "Quit reading the ephemeral group")
         nil)))))
 
-(defcustom gnus-group-gmane-group-download-format
+(defcustom gnus-gmane-group-download-format
   "http://download.gmane.org/%s/%s/%s"
   "URL for downloading mbox files.
 It must contain three \"%s\".  They correspond to the group, the
@@ -2338,11 +2344,11 @@ minimal and maximal article numbers, respectively."
 ;; FIXME:
 ;; - Add documentation, menu, key bindings, ...
 
-(defun gnus-group-read-ephemeral-gmane-group (group start &optional range)
+(defun gnus-read-ephemeral-gmane-group (group start &optional range)
   "Read articles from Gmane group GROUP as an ephemeral group.
 START is the first article.  RANGE specifies how many articles
 are fetched.  The articles are downloaded via HTTP using the URL
-specified by `gnus-group-gmane-group-download-format'."
+specified by `gnus-gmane-group-download-format'."
   ;; See <http://gmane.org/export.php> for more information.
   (interactive
    (list
@@ -2352,12 +2358,12 @@ specified by `gnus-group-gmane-group-download-format'."
   (unless range (setq range 500))
   (when (< range 1)
     (error "Invalid range: %s" range))
-  (let ((tmpfile (make-temp-file
+  (let ((tmpfile (mm-make-temp-file
                  (format "%s.start-%s.range-%s." group start range)))
        (gnus-thread-sort-functions '(gnus-thread-sort-by-number)))
     (with-temp-file tmpfile
       (url-insert-file-contents
-       (format gnus-group-gmane-group-download-format
+       (format gnus-gmane-group-download-format
               group start (+ start range)))
       (write-region (point-min) (point-max) tmpfile)
       (gnus-group-read-ephemeral-group
@@ -2366,7 +2372,7 @@ specified by `gnus-group-gmane-group-download-format'."
               (nndoc-article-type mbox))))
     (delete-file tmpfile)))
 
-(defun gnus-group-read-ephemeral-gmane-group-url (url)
+(defun gnus-read-ephemeral-gmane-group-url (url)
   "Create an ephemeral Gmane group from URL.
 
 Valid input formats include:
@@ -2378,7 +2384,7 @@ Valid input formats include:
   ;;   be customizable?
   ;; - The URLs should be added to `gnus-button-alist'.  Probably we should
   ;;   prompt the user to decide: "View via `browse-url' or in Gnus? "
-  ;;   (`gnus-group-read-ephemeral-gmane-group-url')
+  ;;   (`gnus-read-ephemeral-gmane-group-url')
   (interactive
    (list (gnus-group-completing-read "Gmane URL: ")))
   (let (group start range)
@@ -2400,7 +2406,7 @@ Valid input formats include:
           "^http://\\(?:thread\\|article\\|permalink\\)\.gmane\.org/\\([^/]+\\)/\\([0-9]+\\)"
           url)
          (string-match
-          ;; Don't advertize these in the doc string yet:
+          ;; Don't advertise these in the doc string yet:
           "^\\(?:nntp\\|news\\)://news\.gmane\.org/\\([^/]+\\)/\\([0-9]+\\)"
           url)
          (string-match
@@ -2411,7 +2417,53 @@ Valid input formats include:
            start (string-to-number (match-string 2 url))))
      (t
       (error "Can't parse URL %s" url)))
-    (gnus-group-read-ephemeral-gmane-group group start range)))
+    (gnus-read-ephemeral-gmane-group group start range)))
+
+(defcustom gnus-bug-group-download-format-alist
+  '((emacs . "http://debbugs.gnu.org/%s;mbox=yes")
+    (debian
+     . "http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=%s&mbox=yes"))
+  "Alist of symbols for bug trackers and the corresponding URL format string.
+The URL format string must contain a single \"%s\", specifying
+the bug number, and browsing the URL must return mbox output."
+  :group 'gnus-group-foreign
+  :version "23.2" ;; No Gnus
+  :type '(repeat (cons (symbol) (string :tag "URL format string"))))
+
+(defun gnus-read-ephemeral-bug-group (number mbox-url)
+  "Browse bug NUMBER as ephemeral group."
+  (interactive (list (read-string "Enter bug number: "
+                                 (thing-at-point 'word) nil)
+                    ;; FIXME: Add completing-read from
+                    ;; `gnus-emacs-bug-group-download-format' ...
+                    (cdr (assoc 'emacs gnus-bug-group-download-format-alist))))
+  (when (stringp number)
+    (setq number (string-to-number number)))
+  (let ((tmpfile (mm-make-temp-file "gnus-temp-group-")))
+    (with-temp-file tmpfile
+      (url-insert-file-contents (format mbox-url number))
+      (write-region (point-min) (point-max) tmpfile)
+      (gnus-group-read-ephemeral-group
+       "gnus-read-ephemeral-bug"
+       `(nndoc ,tmpfile
+              (nndoc-article-type mbox))))
+    (delete-file tmpfile)))
+
+(defun gnus-read-ephemeral-debian-bug-group (number)
+  "Browse Debian bug NUMBER as ephemeral group."
+  (interactive (list (read-string "Enter bug number: "
+                                 (thing-at-point 'word) nil)))
+  (gnus-read-ephemeral-bug-group
+   number
+   (cdr (assoc 'debian gnus-bug-group-download-format-alist))))
+
+(defun gnus-read-ephemeral-emacs-bug-group (number)
+  "Browse Emacs bug NUMBER as ephemeral group."
+  (interactive (list (read-string "Enter bug number: "
+                                 (thing-at-point 'word) nil)))
+  (gnus-read-ephemeral-bug-group
+   number
+   (cdr (assoc 'emacs gnus-bug-group-download-format-alist))))
 
 (defun gnus-group-jump-to-group (group &optional prompt)
   "Jump to newsgroup GROUP.
@@ -2879,7 +2931,10 @@ and NEW-NAME will be prompted for."
    (let ((entry (assoc (completing-read "Create group: " gnus-useful-groups
                                        nil t)
                       gnus-useful-groups)))
-     (list (cadr entry) (caddr entry))))
+     (list (cadr entry)
+          ;; Don't use `caddr' here since macros within the `interactive'
+          ;; form won't be expanded.
+          (car (cddr entry)))))
   (setq method (gnus-copy-sequence method))
   (let (entry)
     (while (setq entry (memq (assq 'eval method) method))
@@ -3006,12 +3061,21 @@ If there is, use Gnus to create an nnrss group"
        (let* ((title (gnus-newsgroup-savable-name
                       (read-from-minibuffer "Title: "
                                             (gnus-newsgroup-savable-name
-                                             (or (cdr (assoc 'title
-                                                             feedinfo))
-                                                 "")))))
+                                             (mapconcat
+                                              'identity
+                                              (split-string
+                                               (or (cdr (assoc 'title
+                                                               feedinfo))
+                                                   ""))
+                                              " ")))))
               (desc  (read-from-minibuffer "Description: "
-                                           (cdr (assoc 'description
-                                                       feedinfo))))
+                                           (mapconcat
+                                            'identity
+                                            (split-string
+                                             (or (cdr (assoc 'description
+                                                             feedinfo))
+                                                 ""))
+                                            " ")))
               (href (cdr (assoc 'href feedinfo)))
               (coding (gnus-group-name-charset '(nnrss "") title)))
          (when coding
@@ -3024,42 +3088,6 @@ If there is, use Gnus to create an nnrss group"
          (nnrss-save-server-data nil))
       (error "No feeds found for %s" url))))
 
-(defvar nnwarchive-type-definition)
-(defvar gnus-group-warchive-type-history nil)
-(defvar gnus-group-warchive-login-history nil)
-(defvar gnus-group-warchive-address-history nil)
-
-(defun gnus-group-make-warchive-group ()
-  "Create a nnwarchive group."
-  (interactive)
-  (require 'nnwarchive)
-  (let* ((group (gnus-read-group "Group name: "))
-        (default-type (or (car gnus-group-warchive-type-history)
-                          (symbol-name (caar nnwarchive-type-definition))))
-        (type
-         (gnus-string-or
-          (completing-read
-           (format "Warchive type (default %s): " default-type)
-           (mapcar (lambda (elem) (list (symbol-name (car elem))))
-                   nnwarchive-type-definition)
-           nil t nil 'gnus-group-warchive-type-history)
-          default-type))
-        (address (read-string "Warchive address: "
-                              nil 'gnus-group-warchive-address-history))
-        (default-login (or (car gnus-group-warchive-login-history)
-                           user-mail-address))
-        (login
-         (gnus-string-or
-          (read-string
-           (format "Warchive login (default %s): " user-mail-address)
-           default-login 'gnus-group-warchive-login-history)
-          user-mail-address))
-        (method
-         `(nnwarchive ,address
-                      (nnwarchive-type ,(intern type))
-                      (nnwarchive-login ,login))))
-    (gnus-group-make-group group method)))
-
 (defun gnus-group-make-archive-group (&optional all)
   "Create the (ding) Gnus archive group of the most recent articles.
 Given a prefix, create a full group."
@@ -3100,41 +3128,6 @@ mail messages or news articles in files that have numeric names."
      (gnus-group-real-name group)
      (list 'nndir (gnus-group-real-name group) (list 'nndir-directory dir)))))
 
-(defvar nnkiboze-score-file)
-(declare-function nnkiboze-score-file "nnkiboze" (group))
-
-(defun gnus-group-make-kiboze-group (group address scores)
-  "Create an nnkiboze group.
-The user will be prompted for a name, a regexp to match groups, and
-score file entries for articles to include in the group."
-  (interactive
-   (list
-    (read-string "nnkiboze group name: ")
-    (read-string "Source groups (regexp): ")
-    (let ((headers (mapcar 'list
-                          '("subject" "from" "number" "date" "message-id"
-                            "references" "chars" "lines" "xref"
-                            "followup" "all" "body" "head")))
-         scores header regexp regexps)
-      (while (not (equal "" (setq header (completing-read
-                                         "Match on header: " headers nil t))))
-       (setq regexps nil)
-       (while (not (equal "" (setq regexp (read-string
-                                           (format "Match on %s (regexp): "
-                                                   header)))))
-         (push (list regexp nil nil 'r) regexps))
-       (push (cons header regexps) scores))
-      scores)))
-  (gnus-group-make-group group "nnkiboze" address)
-  (let* ((nnkiboze-current-group group)
-        (score-file (car (nnkiboze-score-file "")))
-        (score-dir (file-name-directory score-file)))
-    (unless (file-exists-p score-dir)
-      (make-directory score-dir))
-    (with-temp-file score-file
-      (let (emacs-lisp-mode-hook)
-       (gnus-pp scores)))))
-
 (defun gnus-group-add-to-virtual (n vgroup)
   "Add the current group to a virtual group."
   (interactive
@@ -3185,22 +3178,17 @@ score file entries for articles to include in the group."
                       'summary 'group)))
       (error "Couldn't enter %s" dir))))
 
-(eval-and-compile
-  (autoload 'nnimap-expunge "nnimap")
-  (autoload 'nnimap-acl-get "nnimap")
-  (autoload 'nnimap-acl-edit "nnimap"))
-
-(defun gnus-group-nnimap-expunge (group)
+(defun gnus-group-expunge-group (group)
   "Expunge deleted articles in current nnimap GROUP."
   (interactive (list (gnus-group-group-name)))
-  (let ((mailbox (gnus-group-real-name group)) method)
-    (unless group
-      (error "No group on current line"))
-    (unless (gnus-get-info group)
-      (error "Killed group; can't be edited"))
-    (unless (eq 'nnimap (car (setq method (gnus-find-method-for-group group))))
-      (error "%s is not an nnimap group" group))
-    (nnimap-expunge mailbox (cadr method))))
+  (let ((method (gnus-find-method-for-group group)))
+    (if (not (gnus-check-backend-function
+             'request-expunge-group (car method)))
+       (error "%s does not support expunging" (car method))
+      (gnus-request-expunge-group group method))))
+
+(autoload 'nnimap-acl-get "nnimap")
+(autoload 'nnimap-acl-edit "nnimap")
 
 (defun gnus-group-nnimap-edit-acl (group)
   "Edit the Access Control List of current nnimap GROUP."
@@ -4005,23 +3993,13 @@ re-scanning.  If ARG is non-nil and not a number, this will force
                        (>= arg gnus-use-nocem))
                   (not arg)))
       (gnus-nocem-scan-groups))
-    ;; If ARG is not a number, then we read the active file.
-    (when (and arg (not (numberp arg)))
-      (let ((gnus-read-active-file t))
-       (gnus-read-active-file))
-      (setq arg nil)
-
-      ;; If the user wants it, we scan for new groups.
-      (when (eq gnus-check-new-newsgroups 'always)
-       (gnus-find-new-newsgroups)))
-
-    (setq arg (gnus-group-default-level arg t))
-    (if (and gnus-read-active-file (not arg))
-       (progn
-         (gnus-read-active-file)
-         (gnus-get-unread-articles arg))
-      (let ((gnus-read-active-file (if arg nil gnus-read-active-file)))
-       (gnus-get-unread-articles arg)))
+
+    (gnus-get-unread-articles arg)
+
+    ;; If the user wants it, we scan for new groups.
+    (when (eq gnus-check-new-newsgroups 'always)
+      (gnus-find-new-newsgroups))
+
     (gnus-check-reasonable-setup)
     (gnus-run-hooks 'gnus-after-getting-new-news-hook)
     (gnus-group-list-groups (and (numberp arg)
@@ -4169,7 +4147,7 @@ If given a prefix argument, prompt for a group."
                   (gnus-gethash mname gnus-description-hashtb))
              (setq desc (gnus-group-get-description group))
              (gnus-read-descriptions-file method))
-      (gnus-message 1
+      (gnus-message 1 "%s"
                    (or desc (gnus-gethash group gnus-description-hashtb)
                        "No description available")))))
 
@@ -4330,11 +4308,9 @@ If GROUP, edit that local kill file instead."
   (interactive "P")
   (setq gnus-current-kill-article article)
   (gnus-kill-file-edit-file group)
-  (gnus-message
-   6
-   (substitute-command-keys
-    (format "Editing a %s kill file (Type \\[gnus-kill-file-exit] to exit)"
-           (if group "local" "global")))))
+  (gnus-message 6 "Editing a %s kill file (Type %s to exit)"
+               (if group "local" "global")
+               (substitute-command-keys "\\[gnus-kill-file-exit]")))
 
 (defun gnus-group-edit-local-kill (article group)
   "Edit a local kill file."
@@ -4411,8 +4387,7 @@ The hook `gnus-exit-gnus-hook' is called before actually exiting."
     (gnus-run-hooks 'gnus-exit-gnus-hook)
     (gnus-configure-windows 'group t)
     (when (and (gnus-buffer-live-p gnus-dribble-buffer)
-              (not (zerop (save-excursion
-                           (set-buffer gnus-dribble-buffer)
+              (not (zerop (with-current-buffer gnus-dribble-buffer
                            (buffer-size)))))
       (gnus-dribble-enter
        ";;; Gnus was exited on purpose without saving the .newsrc files."))
@@ -4426,7 +4401,7 @@ The hook `gnus-exit-gnus-hook' is called before actually exiting."
 (defun gnus-group-describe-briefly ()
   "Give a one line description of the group mode commands."
   (interactive)
-  (gnus-message 7 (substitute-command-keys "\\<gnus-group-mode-map>\\[gnus-group-read-group]:Select  \\[gnus-group-next-unread-group]:Forward  \\[gnus-group-prev-unread-group]:Backward  \\[gnus-group-exit]:Exit  \\[gnus-info-find-node]:Run Info  \\[gnus-group-describe-briefly]:This help")))
+  (gnus-message 7 "%s" (substitute-command-keys "\\<gnus-group-mode-map>\\[gnus-group-read-group]:Select  \\[gnus-group-next-unread-group]:Forward  \\[gnus-group-prev-unread-group]:Backward  \\[gnus-group-exit]:Exit  \\[gnus-info-find-node]:Run Info  \\[gnus-group-describe-briefly]:This help")))
 
 (defun gnus-group-browse-foreign-server (method)
   "Browse a foreign news server.
@@ -4473,13 +4448,11 @@ and the second element is the address."
          (setcar (nthcdr (1- total) info) part-info)))
       (unless entry
        ;; This is a new group, so we just create it.
-       (save-excursion
-         (set-buffer gnus-group-buffer)
+       (with-current-buffer gnus-group-buffer
          (setq method (gnus-info-method info))
          (when (gnus-server-equal method "native")
            (setq method nil))
-         (save-excursion
-           (set-buffer gnus-group-buffer)
+         (with-current-buffer gnus-group-buffer
            (if method
                ;; It's a foreign group...
                (gnus-group-make-group
@@ -4543,8 +4516,7 @@ and the second element is the address."
   "Mark ARTICLE in GROUP with MARK, whether the group is displayed or not."
   (let ((buffer (gnus-summary-buffer-name group)))
     (if (gnus-buffer-live-p buffer)
-       (save-excursion
-         (set-buffer (get-buffer buffer))
+       (with-current-buffer (get-buffer buffer)
          (gnus-summary-add-mark article mark))
       (gnus-add-marked-articles group (cdr (assq mark gnus-article-mark-lists))
                                (list article)))))
@@ -4744,5 +4716,4 @@ Compacting group %s... (this may take a long time)"
 
 (provide 'gnus-group)
 
-;;; arch-tag: 2eb5440f-0bca-4091-814c-e37817536af6
 ;;; gnus-group.el ends here