(gnus-summary-mode-map, gnus-summary-make-menu-bar): Add key bindings
[gnus] / lisp / gnus-sum.el
index d9fa84f..edec081 100644 (file)
@@ -1,17 +1,17 @@
 ;;; gnus-sum.el --- summary mode commands for Gnus
 
 ;; Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
-;;   2005, 2006, 2007 Free Software Foundation, Inc.
+;;   2005, 2006, 2007, 2008 Free Software Foundation, Inc.
 
 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
 ;; Keywords: news
 
 ;; This file is part of GNU Emacs.
 
-;; GNU Emacs is free software; you can redistribute it and/or modify
+;; GNU Emacs is free software: you can redistribute it and/or modify
 ;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 2, or (at your option)
-;; any later version.
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
 
 ;; GNU Emacs is distributed in the hope that it will be useful,
 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
 ;; 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:
 
 ;;; Code:
 
+;; For Emacs < 22.2.
+(eval-and-compile
+  (unless (fboundp 'declare-function) (defmacro declare-function (&rest r))))
 (eval-when-compile
-  (require 'cl)
-  (defvar tool-bar-mode))
+  (require 'cl))
+
+(defvar tool-bar-mode)
+(defvar gnus-tmp-header)
 
 (require 'gnus)
 (require 'gnus-group)
@@ -67,7 +70,7 @@ it will be killed sometime later."
 See `gnus-group-goto-unread'."
   :link '(custom-manual "(gnus)Group Maneuvering")
   :group 'gnus-summary-exit
-  :version "23.0" ;; No Gnus
+  :version "23.1" ;; No Gnus
   :type 'boolean)
 
 (defcustom gnus-fetch-old-headers nil
@@ -86,7 +89,9 @@ old headers will be fetched, but none will be displayed.
 The server has to support NOV for any of this to work.
 
 This feature can seriously impact performance it ignores all
-locally cached header entries."
+locally cached header entries.  Setting it to t for groups for a
+server that doesn't expire articles (such as news.gmane.org),
+leads to very slow summary generation."
   :group 'gnus-thread
   :type '(choice (const :tag "off" nil)
                 (const :tag "on" t)
@@ -393,7 +398,7 @@ Valid values include:
 
 If it has any other value or there is no next (unread) article, the
 article selected before entering to the ephemeral group will appear."
-  :version "23.0" ;; No Gnus
+  :version "23.1" ;; No Gnus
   :group 'gnus-summary-maneuvering
   :type '(choice :format "%{%t%}:\n %[Value Menu%] %v"
                 (const next) (const next-unread)
@@ -1080,6 +1085,7 @@ uncached:     Non-nil if the article is uncached."
   :group 'gnus-summary-visual
   :type '(repeat (cons (sexp :tag "Form" nil)
                       face)))
+(put 'gnus-summary-highlight 'risky-local-variable t)
 
 (defcustom gnus-alter-header-function nil
   "Function called to allow alteration of article header structures.
@@ -1138,6 +1144,17 @@ default charset will be used instead."
   :type '(repeat symbol)
   :group 'gnus-charset)
 
+(defcustom gnus-newsgroup-maximum-articles nil
+  "The maximum number of articles a newsgroup.
+If this is a number, old articles in a newsgroup exceeding this number
+are silently ignored.  If it is nil, no article is ignored.  Note that
+setting this variable to a number might prevent you from reading very
+old articles."
+  :group 'gnus-group-select
+  :version "22.2"
+  :type '(choice (const :tag "No limit" nil)
+                integer))
+
 (gnus-define-group-parameter
  ignored-charsets
  :type list
@@ -1191,6 +1208,12 @@ For example: ((1 . cn-gb-2312) (2 . big5))."
   :type 'boolean
   :group 'gnus-summary-marks)
 
+(defcustom gnus-propagate-marks t
+  "If non-nil, do not propagate marks to the backends."
+  :version "23.1" ;; No Gnus
+  :type 'boolean
+  :group 'gnus-summary-marks)
+
 (defcustom gnus-alter-articles-to-read-function nil
   "Function to be called to alter the list of articles to be selected."
   :type '(choice (const nil) function)
@@ -1219,8 +1242,19 @@ that were fetched.  Say, for nnultimate groups."
   :group 'gnus-summary
   :type '(choice boolean regexp))
 
+(defcustom gnus-summary-pipe-output-default-command nil
+  "Command (and optional arguments) used to pipe article to subprocess.
+This will be used as the default command if it is non-nil.  The value
+will be updated if you modify it when executing the command
+`gnus-summary-pipe-output' or the function `gnus-summary-save-in-pipe'."
+  :version "23.1" ;; No Gnus
+  :group 'gnus-summary
+  :type '(radio (const :tag "None" nil) (string :tag "Command")))
+
 (defcustom gnus-summary-muttprint-program "muttprint"
-  "Command (and optional arguments) used to run Muttprint."
+  "Command (and optional arguments) used to run Muttprint.
+The value will be updated if you modify it when executing the command
+`gnus-summary-muttprint'."
   :version "22.1"
   :group 'gnus-summary
   :type 'string)
@@ -1355,6 +1389,12 @@ the type of the variable (string, integer, character, etc).")
     (?E gnus-newsgroup-expunged-tally ?d)
     (?s (gnus-current-score-file-nondirectory) ?s)))
 
+;; This is here rather than in gnus-art for compilation reasons.
+(defvar gnus-article-mode-line-format-alist
+  (nconc '((?w (gnus-article-wash-status) ?s)
+          (?m (gnus-article-mime-part-status) ?s))
+        gnus-summary-mode-line-format-alist))
+
 (defvar gnus-last-search-regexp nil
   "Default regexp for article search command.")
 
@@ -1546,20 +1586,10 @@ For example:
         \"^From:\\\\|^Newsgroups:\\\\|^Subject:\\\\|^Date:\\\\|^To:\")))
 ")
 
-;; Byte-compiler warning.
 (eval-when-compile
   ;; Bind features so that require will believe that gnus-sum has
   ;; already been loaded (avoids infinite recursion)
   (let ((features (cons 'gnus-sum features)))
-    ;; Several of the declarations in gnus-sum are needed to load the
-    ;; following files. Right now, these definitions have been
-    ;; compiled but not defined (evaluated).  We could either do a
-    ;; eval-and-compile about all of the declarations or evaluate the
-    ;; source file.
-    (if (boundp 'gnus-newsgroup-variables)
-        nil
-      (load "gnus-sum.el" t t t))
-    (require 'gnus)
     (require 'gnus-art)))
 
 ;; MIME stuff.
@@ -1586,19 +1616,15 @@ For example:
               (eq gnus-newsgroup-name
                   (car gnus-decode-encoded-word-methods-cache)))
     (setq gnus-decode-encoded-word-methods-cache (list gnus-newsgroup-name))
-    (mapcar (lambda (x)
-             (if (symbolp x)
-                 (nconc gnus-decode-encoded-word-methods-cache (list x))
-               (if (and gnus-newsgroup-name
-                        (string-match (car x) gnus-newsgroup-name))
-                   (nconc gnus-decode-encoded-word-methods-cache
-                          (list (cdr x))))))
-           gnus-decode-encoded-word-methods))
-  (let ((xlist gnus-decode-encoded-word-methods-cache))
-    (pop xlist)
-    (while xlist
-      (setq string (funcall (pop xlist) string))))
-  string)
+    (dolist (method gnus-decode-encoded-word-methods)
+      (if (symbolp method)
+         (nconc gnus-decode-encoded-word-methods-cache (list method))
+       (if (and gnus-newsgroup-name
+                (string-match (car method) gnus-newsgroup-name))
+           (nconc gnus-decode-encoded-word-methods-cache
+                  (list (cdr method)))))))
+  (dolist (method (cdr gnus-decode-encoded-word-methods-cache) string)
+    (setq string (funcall method string))))
 
 ;; Subject simplification.
 
@@ -1670,8 +1696,8 @@ matter is removed.  Additional things can be deleted by setting
       (setq modified-tick (buffer-modified-tick))
       (cond
        ((listp gnus-simplify-subject-fuzzy-regexp)
-       (mapcar 'gnus-simplify-buffer-fuzzy-step
-               gnus-simplify-subject-fuzzy-regexp))
+       (mapc 'gnus-simplify-buffer-fuzzy-step
+             gnus-simplify-subject-fuzzy-regexp))
        (gnus-simplify-subject-fuzzy-regexp
        (gnus-simplify-buffer-fuzzy-step gnus-simplify-subject-fuzzy-regexp)))
       (gnus-simplify-buffer-fuzzy-step "^ *\\[[-+?*!][-+?*!]\\] *")
@@ -1799,12 +1825,14 @@ increase the score of each group you read."
   "?" gnus-summary-mark-as-dormant
   "\C-c\M-\C-s" gnus-summary-limit-include-expunged
   "\C-c\C-s\C-n" gnus-summary-sort-by-number
+  "\C-c\C-s\C-m\C-n" gnus-summary-sort-by-most-recent-number
   "\C-c\C-s\C-l" gnus-summary-sort-by-lines
   "\C-c\C-s\C-c" gnus-summary-sort-by-chars
   "\C-c\C-s\C-a" gnus-summary-sort-by-author
   "\C-c\C-s\C-t" gnus-summary-sort-by-recipient
   "\C-c\C-s\C-s" gnus-summary-sort-by-subject
   "\C-c\C-s\C-d" gnus-summary-sort-by-date
+  "\C-c\C-s\C-m\C-d" gnus-summary-sort-by-most-recent-date
   "\C-c\C-s\C-i" gnus-summary-sort-by-score
   "\C-c\C-s\C-o" gnus-summary-sort-by-original
   "\C-c\C-s\C-r" gnus-summary-sort-by-random
@@ -1939,6 +1967,7 @@ increase the score of each group you read."
 
 (gnus-define-keys (gnus-summary-thread-map "T" gnus-summary-mode-map)
   "k" gnus-summary-kill-thread
+  "E" gnus-summary-expire-thread
   "l" gnus-summary-lower-thread
   "i" gnus-summary-raise-thread
   "T" gnus-summary-toggle-threads
@@ -1997,6 +2026,7 @@ increase the score of each group you read."
   "g" gnus-summary-show-article
   "s" gnus-summary-isearch-article
   "P" gnus-summary-print-article
+  "S" gnus-sticky-article
   "M" gnus-mailing-list-insinuate
   "t" gnus-article-babel)
 
@@ -2183,6 +2213,7 @@ increase the score of each group you read."
   "O" gnus-uu-decode-save
   "b" gnus-uu-decode-binhex
   "B" gnus-uu-decode-binhex
+  "Y" gnus-uu-decode-yenc
   "p" gnus-uu-decode-postscript
   "P" gnus-uu-decode-postscript-and-save)
 
@@ -2250,7 +2281,7 @@ increase the score of each group you read."
          ["Set mark below..." gnus-score-set-mark-below t]
          ["Set expunge below..." gnus-score-set-expunge-below t]
          ["Edit current score file" gnus-score-edit-current-scores t]
-         ["Edit score file" gnus-score-edit-file t]
+         ["Edit score file..." gnus-score-edit-file t]
          ["Trace score" gnus-score-find-trace t]
          ["Find words" gnus-score-find-favourite-words t]
          ["Rescore buffer" gnus-summary-rescore t]
@@ -2444,6 +2475,7 @@ gnus-summary-show-article-from-menu-as-charset-%s" cs))))
              ["Remove article" gnus-cache-remove-article t])
             ["Translate" gnus-article-babel t]
             ["Select article buffer" gnus-summary-select-article-buffer t]
+            ["Make article buffer sticky" gnus-sticky-article t]
             ["Enter digest buffer" gnus-summary-enter-digest-group t]
             ["Isearch article..." gnus-summary-isearch-article t]
             ["Beginning of the article" gnus-summary-beginning-of-article t]
@@ -2484,6 +2516,7 @@ gnus-summary-show-article-from-menu-as-charset-%s" cs))))
        ["Go up thread" gnus-summary-up-thread t]
        ["Top of thread" gnus-summary-top-thread t]
        ["Mark thread as read" gnus-summary-kill-thread t]
+       ["Mark thread as expired" gnus-summary-expire-thread t]
        ["Lower thread score" gnus-summary-lower-thread t]
        ["Raise thread score" gnus-summary-raise-thread t]
        ["Rethread current" gnus-summary-rethread-current t]))
@@ -2529,6 +2562,29 @@ gnus-summary-show-article-from-menu-as-charset-%s" cs))))
        ["Followup via news" gnus-summary-followup-to-mail t]
        ["Followup via news and yank"
         gnus-summary-followup-to-mail-with-original t]
+       ["Strip signature on reply"
+        (lambda ()
+          (interactive)
+          (if (not (memq message-cite-function
+                         '(message-cite-original-without-signature
+                           message-cite-original)))
+              ;; Stupid workaround for XEmacs not honoring :visible.
+              (message "Can't toggle this value of `message-cite-function'")
+            (setq message-cite-function
+                  (if (eq message-cite-function
+                          'message-cite-original-without-signature)
+                      'message-cite-original
+                    'message-cite-original-without-signature))))
+        ;; XEmacs barfs on :visible.
+        ,@(if (featurep 'xemacs) nil
+            '(:visible (memq message-cite-function
+                             '(message-cite-original-without-signature
+                               message-cite-original))))
+        :style toggle
+        :selected (eq message-cite-function
+                      'message-cite-original-without-signature)
+        ,@(if (featurep 'xemacs) nil
+            '(:help "Strip signature from cited article when replying."))]
        ;;("Draft"
        ;;["Send" gnus-summary-send-draft t]
        ;;["Send bounced" gnus-resend-bounced-mail t])
@@ -2568,6 +2624,17 @@ gnus-summary-show-article-from-menu-as-charset-%s" cs))))
         ["Set expirable mark" gnus-summary-mark-as-expirable t]
         ["Set bookmark" gnus-summary-set-bookmark t]
         ["Remove bookmark" gnus-summary-remove-bookmark t])
+       ("Registry Mark"
+        ["Important"     gnus-registry-set-article-Important-mark    t]
+        ["Not Important" gnus-registry-remove-article-Important-mark t]
+        ["Work"          gnus-registry-set-article-Work-mark         t]
+        ["Not Work"      gnus-registry-remove-article-Work-mark      t]
+        ["Later"         gnus-registry-set-article-Later-mark        t]
+        ["Not Later"     gnus-registry-remove-article-Later-mark     t]
+        ["Personal"      gnus-registry-set-article-Personal-mark     t]
+        ["Not Personal"  gnus-registry-remove-article-Personal-mark  t]
+        ["To Do"         gnus-registry-set-article-To-Do-mark        t]
+        ["Not To Do"     gnus-registry-remove-article-To-Do-mark     t])
        ("Limit to"
         ["Marks..." gnus-summary-limit-to-marks t]
         ["Subject..." gnus-summary-limit-to-subject t]
@@ -2638,10 +2705,12 @@ gnus-summary-show-article-from-menu-as-charset-%s" cs))))
         ["Pop article off history" gnus-summary-pop-article t])
        ("Sort"
         ["Sort by number" gnus-summary-sort-by-number t]
+        ["Sort by most recent number" gnus-summary-sort-by-most-recent-number t]
         ["Sort by author" gnus-summary-sort-by-author t]
         ["Sort by recipient" gnus-summary-sort-by-recipient t]
         ["Sort by subject" gnus-summary-sort-by-subject t]
         ["Sort by date" gnus-summary-sort-by-date t]
+        ["Sort by most recent date" gnus-summary-sort-by-most-recent-date t]
         ["Sort by score" gnus-summary-sort-by-score t]
         ["Sort by lines" gnus-summary-sort-by-lines t]
         ["Sort by characters" gnus-summary-sort-by-chars t]
@@ -2732,7 +2801,7 @@ Pre-defined symbols include `gnus-summary-tool-bar-gnome' and
                 (const :tag "Retro look"  gnus-summary-tool-bar-retro)
                 (repeat :tag "User defined list" gmm-tool-bar-item)
                 (symbol))
-  :version "23.0" ;; No Gnus
+  :version "23.1" ;; No Gnus
   :initialize 'custom-initialize-default
   :set 'gnus-summary-tool-bar-update
   :group 'gnus-summary)
@@ -2783,7 +2852,7 @@ Pre-defined symbols include `gnus-summary-tool-bar-gnome' and
 
 See `gmm-tool-bar-from-list' for the format of the list."
   :type '(repeat gmm-tool-bar-item)
-  :version "23.0" ;; No Gnus
+  :version "23.1" ;; No Gnus
   :initialize 'custom-initialize-default
   :set 'gnus-summary-tool-bar-update
   :group 'gnus-summary)
@@ -2818,7 +2887,7 @@ See `gmm-tool-bar-from-list' for the format of the list."
 
 See `gmm-tool-bar-from-list' for the format of the list."
   :type '(repeat gmm-tool-bar-item)
-  :version "23.0" ;; No Gnus
+  :version "23.1" ;; No Gnus
   :initialize 'custom-initialize-default
   :set 'gnus-summary-tool-bar-update
   :group 'gnus-summary)
@@ -2829,12 +2898,13 @@ These items are not displayed in the Gnus summary mode tool bar.
 
 See `gmm-tool-bar-from-list' for the format of the list."
   :type 'gmm-tool-bar-zap-list
-  :version "23.0" ;; No Gnus
+  :version "23.1" ;; No Gnus
   :initialize 'custom-initialize-default
   :set 'gnus-summary-tool-bar-update
   :group 'gnus-summary)
 
 (defvar image-load-path)
+(defvar tool-bar-map)
 
 (defun gnus-summary-make-tool-bar (&optional force)
   "Make a summary mode tool bar from `gnus-summary-tool-bar'.
@@ -2944,6 +3014,9 @@ When FORCE, rebuild the tool bar."
            (setq headers (cdr headers)))
          (list (nreverse outh))))))))
 
+
+(declare-function turn-on-gnus-mailing-list-mode "gnus-ml" ())
+
 \f
 
 (defun gnus-summary-mode (&optional group)
@@ -3336,7 +3409,7 @@ marks of articles."
 (defun gnus-restore-hidden-threads-configuration (config)
   "Restore hidden threads configuration from CONFIG."
   (save-excursion
-    (let (point buffer-read-only)
+    (let (point (inhibit-read-only t))
       (while (setq point (pop config))
        (when (and (< point (point-max))
                   (goto-char point)
@@ -3362,13 +3435,13 @@ display only a single character."
        (i 32))
     ;; Nix out all the control chars...
     (while (>= (setq i (1- i)) 0)
-      (aset table i [??]))
+      (gnus-put-display-table i [??] table))
    ;; ... but not newline and cr, of course.  (cr is necessary for the
     ;; selective display).
-    (aset table ?\n nil)
-    (aset table ?\r nil)
+    (gnus-put-display-table ?\n nil table)
+    (gnus-put-display-table ?\r nil table)
     ;; We keep TAB as well.
-    (aset table ?\t nil)
+    (gnus-put-display-table ?\t nil table)
     ;; We nix out any glyphs 127 through 255, or 127 through 159 in
     ;; Emacs 23 (unicode), that are not set already.
     (let ((i (if (ignore-errors (= (make-char 'latin-iso8859-1 160) 160))
@@ -3376,8 +3449,8 @@ display only a single character."
               256)))
       (while (>= (setq i (1- i)) 127)
        ;; Only modify if the entry is nil.
-       (unless (aref table i)
-         (aset table i [??]))))
+       (unless (gnus-get-display-table i table)
+         (gnus-put-display-table i [??] table))))
     (setq buffer-display-table table)))
 
 (defun gnus-summary-set-article-display-arrow (pos)
@@ -3659,7 +3732,7 @@ buffer that was in action when the last article was fetched."
         (gnus-tmp-subject (mail-header-subject gnus-tmp-header))
         (gnus-tmp-opening-bracket (if gnus-tmp-dummy ?\< ?\[))
         (gnus-tmp-closing-bracket (if gnus-tmp-dummy ?\> ?\]))
-        (buffer-read-only nil))
+        (inhibit-read-only t))
     (when (string= gnus-tmp-name "")
       (setq gnus-tmp-name gnus-tmp-from))
     (unless (numberp gnus-tmp-lines)
@@ -3965,7 +4038,7 @@ If NO-DISPLAY, don't generate a summary buffer."
 (defun gnus-summary-prepare ()
   "Generate the summary buffer."
   (interactive)
-  (let ((buffer-read-only nil))
+  (let ((inhibit-read-only t))
     (erase-buffer)
     (setq gnus-newsgroup-data nil
          gnus-newsgroup-data-reverse nil)
@@ -4013,7 +4086,7 @@ If NO-DISPLAY, don't generate a summary buffer."
   "Query where the respool algorithm would put this article."
   (interactive)
   (gnus-summary-select-article)
-  (message (gnus-general-simplify-subject (gnus-summary-article-subject))))
+  (message "%s" (gnus-general-simplify-subject (gnus-summary-article-subject))))
 
 (defun gnus-gather-threads-by-subject (threads)
   "Gather threads by looking at Subject headers."
@@ -4274,21 +4347,19 @@ Returns HEADER if it was entered in the DEPENDENCIES.  Returns nil otherwise."
          (erase-buffer)))
       (kill-buffer (current-buffer)))
     ;; Sort over trustworthiness.
-    (mapcar
-     (lambda (relation)
-       (when (gnus-dependencies-add-header
-             (make-full-mail-header
-              gnus-reffed-article-number
-              (nth 3 relation) "" (or (nth 4 relation) "")
-              (nth 1 relation)
-              (or (nth 2 relation) "") 0 0 "")
-             gnus-newsgroup-dependencies nil)
-        (push gnus-reffed-article-number gnus-newsgroup-limit)
-        (push gnus-reffed-article-number gnus-newsgroup-sparse)
-        (push (cons gnus-reffed-article-number gnus-sparse-mark)
-              gnus-newsgroup-reads)
-        (decf gnus-reffed-article-number)))
-     (sort relations 'car-less-than-car))
+    (dolist (relation (sort relations 'car-less-than-car))
+      (when (gnus-dependencies-add-header
+            (make-full-mail-header
+             gnus-reffed-article-number
+             (nth 3 relation) "" (or (nth 4 relation) "")
+             (nth 1 relation)
+             (or (nth 2 relation) "") 0 0 "")
+            gnus-newsgroup-dependencies nil)
+       (push gnus-reffed-article-number gnus-newsgroup-limit)
+       (push gnus-reffed-article-number gnus-newsgroup-sparse)
+       (push (cons gnus-reffed-article-number gnus-sparse-mark)
+             gnus-newsgroup-reads)
+       (decf gnus-reffed-article-number)))
     (gnus-message 7 "Making sparse threads...done")))
 
 (defun gnus-build-old-threads ()
@@ -4375,8 +4446,7 @@ the id of the parent article (if any)."
   (let ((deps gnus-newsgroup-dependencies)
        found header)
     (prog1
-       (save-excursion
-         (set-buffer nntp-server-buffer)
+       (with-current-buffer nntp-server-buffer
          (let ((case-fold-search nil))
            (goto-char (point-min))
            (while (and (not found)
@@ -4411,8 +4481,7 @@ the id of the parent article (if any)."
        (mail-parse-charset gnus-newsgroup-charset)
        (dependencies gnus-newsgroup-dependencies)
        header article)
-    (save-excursion
-      (set-buffer nntp-server-buffer)
+    (with-current-buffer nntp-server-buffer
       (let ((case-fold-search nil))
        (goto-char (point-min))
        (while (not (eobp))
@@ -4444,7 +4513,7 @@ the id of the parent article (if any)."
     (gnus-summary-goto-subject article)
     (let* ((datal (gnus-data-find-list article))
           (data (car datal))
-          (buffer-read-only nil)
+          (inhibit-read-only t)
           (level (gnus-summary-thread-level)))
       (gnus-delete-line)
       (let ((inserted (- (point)
@@ -4495,7 +4564,7 @@ the id of the parent article (if any)."
                           (not (equal "" references)))
                  references))
               "none")))
-        (buffer-read-only nil)
+        (inhibit-read-only t)
         (old (car thread)))
     (when thread
       (unless iheader
@@ -4511,7 +4580,7 @@ the id of the parent article (if any)."
 (defun gnus-rebuild-thread (id &optional line)
   "Rebuild the thread containing ID.
 If LINE, insert the rebuilt thread starting on line LINE."
-  (let ((buffer-read-only nil)
+  (let ((inhibit-read-only t)
        old-pos current thread data)
     (if (not gnus-show-threads)
        (setq thread (list (car (gnus-id-to-thread id))))
@@ -4776,11 +4845,11 @@ using some other form will lead to serious barfage."
    (gnus-thread-header h1) (gnus-thread-header h2)))
 
 (defsubst gnus-article-sort-by-random (h1 h2)
-  "Sort articles by article number."
+  "Sort articles randomly."
   (zerop (random 2)))
 
 (defun gnus-thread-sort-by-random (h1 h2)
-  "Sort threads by root article number."
+  "Sort threads randomly."
   (gnus-article-sort-by-random
    (gnus-thread-header h1) (gnus-thread-header h2)))
 
@@ -4806,7 +4875,7 @@ using some other form will lead to serious barfage."
 
 (defsubst gnus-article-sort-by-author (h1 h2)
   "Sort articles by root author."
-  (string-lessp
+  (gnus-string<
    (let ((extract (funcall
                   gnus-extract-address-components
                   (mail-header-from h1))))
@@ -4823,7 +4892,7 @@ using some other form will lead to serious barfage."
 
 (defsubst gnus-article-sort-by-recipient (h1 h2)
   "Sort articles by recipient."
-  (string-lessp
+  (gnus-string<
    (let ((extract (funcall
                   gnus-extract-address-components
                   (or (cdr (assq 'To (mail-header-extra h1))) ""))))
@@ -4840,7 +4909,7 @@ using some other form will lead to serious barfage."
 
 (defsubst gnus-article-sort-by-subject (h1 h2)
   "Sort articles by root subject."
-  (string-lessp
+  (gnus-string<
    (downcase (gnus-simplify-subject-re (mail-header-subject h1)))
    (downcase (gnus-simplify-subject-re (mail-header-subject h2)))))
 
@@ -4944,7 +5013,6 @@ Unscored articles will be counted as having a score of zero."
 (defvar gnus-tmp-root-expunged nil)
 (defvar gnus-tmp-dummy-line nil)
 
-(eval-when-compile (defvar gnus-tmp-header))
 (defun gnus-extra-header (type &optional header)
   "Return the extra header of TYPE."
   (or (cdr (assq type (mail-header-extra (or header gnus-tmp-header))))
@@ -4997,6 +5065,16 @@ If nil, use subject instead."
   :type 'string
   :group 'gnus-thread)
 
+(defcustom gnus-summary-display-while-building nil
+  "If non-nil, show and update the summary buffer as it's being built.
+If the value is t, update the buffer after every line is inserted.  If
+the value is an integer (N), update the display every N lines."
+  :version "22.1"
+  :group 'gnus-thread
+  :type '(choice (const :tag "off" nil)
+                number
+                (const :tag "frequently" t)))
+
 (defun gnus-summary-prepare-threads (threads)
   "Prepare summary buffer from THREADS and indentation LEVEL.
 THREADS is either a list of `(PARENT [(CHILD1 [(GRANDCHILD ...]...) ...])'
@@ -5397,26 +5475,30 @@ If SELECT-ARTICLES, only select those articles from GROUP."
              t
            gnus-summary-ignore-duplicates))
         (info (nth 2 entry))
-        articles fetched-articles cached)
+        charset articles fetched-articles cached)
 
     (unless (gnus-check-server
             (set (make-local-variable 'gnus-current-select-method)
                  (gnus-find-method-for-group group)))
       (error "Couldn't open server"))
+    (setq charset (gnus-group-name-charset gnus-current-select-method group))
 
     (or (and entry (not (eq (car entry) t))) ; Either it's active...
        (gnus-activate-group group)     ; Or we can activate it...
        (progn                          ; Or we bug out.
          (when (equal major-mode 'gnus-summary-mode)
            (gnus-kill-buffer (current-buffer)))
-         (error "Couldn't activate group %s: %s"
-                (gnus-group-decoded-name group) (gnus-status-message group))))
+         (error
+          "Couldn't activate group %s: %s"
+          (mm-decode-coding-string group charset)
+          (mm-decode-coding-string (gnus-status-message group) charset))))
 
     (unless (gnus-request-group group t)
-      (when (equal major-mode 'gnus-summary-mode)
-       (gnus-kill-buffer (current-buffer)))
-      (error "Couldn't request group %s: %s"
-            (gnus-group-decoded-name group) (gnus-status-message group)))
+       (when (equal major-mode 'gnus-summary-mode)
+         (gnus-kill-buffer (current-buffer)))
+       (error "Couldn't request group %s: %s"
+              (mm-decode-coding-string group charset)
+              (mm-decode-coding-string (gnus-status-message group) charset)))
 
     (when gnus-agent
       (gnus-agent-possibly-alter-active group (gnus-active group) info)
@@ -5560,6 +5642,8 @@ If SELECT-ARTICLES, only select those articles from GROUP."
          (gnus-inverse-list-range-intersection
           gnus-newsgroup-articles gnus-newsgroup-seen))))
 
+(declare-function gnus-get-predicate "gnus-agent" (predicate))
+
 (defun gnus-summary-display-make-predicate (display)
   (require 'gnus-agent)
   (when (= (length display) 1)
@@ -5578,8 +5662,7 @@ If SELECT-ARTICLES, only select those articles from GROUP."
     (gnus-get-predicate display)))
 
 ;; Uses the dynamically bound `number' variable.
-(eval-when-compile
-  (defvar number))
+(defvar number)
 (defun gnus-article-marked-p (type &optional article)
   (let ((article (or article number)))
     (cond
@@ -5636,7 +5719,15 @@ If SELECT-ARTICLES, only select those articles from GROUP."
              ;; articles in the group, or (if that's nil), the
              ;; articles in the cache.
              (or
-              (gnus-uncompress-range (gnus-active group))
+              (if gnus-newsgroup-maximum-articles
+                  (let ((active (gnus-active group)))
+                    (gnus-uncompress-range
+                     (cons (max (car active)
+                                (- (cdr active)
+                                   gnus-newsgroup-maximum-articles
+                                   -1))
+                           (cdr active))))
+                (gnus-uncompress-range (gnus-active group)))
               (gnus-cache-articles-in-group group))
            ;; Select only the "normal" subset of articles.
            (gnus-sorted-nunion
@@ -5903,11 +5994,10 @@ If WHERE is `summary', the summary mode line format will be used."
             (symbol-value
              (intern (format "gnus-%s-mode-line-format-spec" where))))
     (let (mode-string)
-      (save-excursion
-       ;; We evaluate this in the summary buffer since these
-       ;; variables are buffer-local to that buffer.
-       (set-buffer gnus-summary-buffer)
-       ;; We bind all these variables that are used in the `eval' form
+      ;; We evaluate this in the summary buffer since these
+      ;; variables are buffer-local to that buffer.
+      (with-current-buffer gnus-summary-buffer
+        ;; We bind all these variables that are used in the `eval' form
        ;; below.
        (let* ((mformat (symbol-value
                         (intern
@@ -6112,12 +6202,11 @@ The resulting hash table is returned, or nil if no Xrefs were found."
        headers id end ref number
        (mail-parse-charset gnus-newsgroup-charset)
        (mail-parse-ignored-charsets
-        (save-excursion (condition-case nil
-                            (set-buffer gnus-summary-buffer)
-                          (error))
-                        gnus-newsgroup-ignored-charsets)))
-    (save-excursion
-      (set-buffer nntp-server-buffer)
+        (save-current-buffer (condition-case nil
+                                  (set-buffer gnus-summary-buffer)
+                                (error))
+                              gnus-newsgroup-ignored-charsets)))
+    (with-current-buffer nntp-server-buffer
       ;; Translate all TAB characters into SPACE characters.
       (subst-char-in-region (point-min) (point-max) ?\t ?  t)
       (subst-char-in-region (point-min) (point-max) ?\r ?  t)
@@ -6283,8 +6372,7 @@ Return a list of headers that match SEQUENCE (see
               (t
                nil)))
        number headers header)
-    (save-excursion
-      (set-buffer nntp-server-buffer)
+    (with-current-buffer nntp-server-buffer
       (subst-char-in-region (point-min) (point-max) ?\r ?  t)
       ;; Allow the user to mangle the headers before parsing them.
       (gnus-run-hooks 'gnus-parse-headers-hook)
@@ -6408,8 +6496,7 @@ the subject line on."
   "Return a list of articles to be worked upon.
 The prefix argument, the list of process marked articles, and the
 current article will be taken into consideration."
-  (save-excursion
-    (set-buffer gnus-summary-buffer)
+  (with-current-buffer gnus-summary-buffer
     (cond
      (n
       ;; A numerical prefix has been given.
@@ -6493,8 +6580,7 @@ executed with point over the summary line of the articles."
 (defun gnus-summary-search-group (&optional backward use-level)
   "Search for next unread newsgroup.
 If optional argument BACKWARD is non-nil, search backward instead."
-  (save-excursion
-    (set-buffer gnus-group-buffer)
+  (with-current-buffer gnus-group-buffer
     (when (gnus-group-search-forward
           backward nil (if use-level (gnus-group-group-level) nil))
       (gnus-group-group-name))))
@@ -6680,7 +6766,7 @@ displayed, no centering will be performed."
        (gnus-group-jump-to-group newsgroup))
     (save-excursion
       ;; Take care of tree window mode.
-      (if (get-buffer-window gnus-group-buffer)
+      (if (get-buffer-window gnus-group-buffer 0)
          (pop-to-buffer gnus-group-buffer)
        (set-buffer gnus-group-buffer))
       (gnus-group-jump-to-group newsgroup))))
@@ -6693,23 +6779,27 @@ displayed, no centering will be performed."
         (active (or (gnus-active group) (gnus-activate-group group)))
         (last (or (cdr active)
                   (error "Group %s couldn't be activated " group)))
+        (bottom (if gnus-newsgroup-maximum-articles
+                    (max (car active)
+                         (- last gnus-newsgroup-maximum-articles -1))
+                  (car active)))
         first nlast unread)
     ;; If none are read, then all are unread.
     (if (not read)
-       (setq first (car active))
+       (setq first bottom)
       ;; If the range of read articles is a single range, then the
       ;; first unread article is the article after the last read
       ;; article.  Sounds logical, doesn't it?
       (if (and (not (listp (cdr read)))
-              (or (< (car read) (car active))
+              (or (< (car read) bottom)
                   (progn (setq read (list read))
                          nil)))
-         (setq first (max (car active) (1+ (cdr read))))
+         (setq first (max bottom (1+ (cdr read))))
        ;; `read' is a list of ranges.
        (when (/= (setq nlast (or (and (numberp (car read)) (car read))
                                  (caar read)))
                  1)
-         (setq first (car active)))
+         (setq first bottom))
        (while read
          (when first
            (while (< first nlast)
@@ -6734,7 +6824,14 @@ displayed, no centering will be performed."
         (gnus-list-range-difference
          (gnus-list-range-difference
           (gnus-sorted-complement
-           (gnus-uncompress-range active)
+           (gnus-uncompress-range
+            (if gnus-newsgroup-maximum-articles
+                (cons (max (car active)
+                           (- (cdr active)
+                              gnus-newsgroup-maximum-articles
+                              -1))
+                      (cdr active))
+              active))
            (gnus-list-of-unread-articles group))
           (cdr (assq 'dormant marked)))
          (cdr (assq 'tick marked))))))
@@ -6746,23 +6843,27 @@ displayed, no centering will be performed."
   (let* ((read (gnus-info-read (gnus-get-info group)))
         (active (or (gnus-active group) (gnus-activate-group group)))
         (last (cdr active))
+        (bottom (if gnus-newsgroup-maximum-articles
+                    (max (car active)
+                         (- last gnus-newsgroup-maximum-articles -1))
+                  (car active)))
         first nlast unread)
     ;; If none are read, then all are unread.
     (if (not read)
-       (setq first (car active))
+       (setq first bottom)
       ;; If the range of read articles is a single range, then the
       ;; first unread article is the article after the last read
       ;; article.  Sounds logical, doesn't it?
       (if (and (not (listp (cdr read)))
-              (or (< (car read) (car active))
+              (or (< (car read) bottom)
                   (progn (setq read (list read))
                          nil)))
-         (setq first (max (car active) (1+ (cdr read))))
+         (setq first (max bottom (1+ (cdr read))))
        ;; `read' is a list of ranges.
        (when (/= (setq nlast (or (and (numberp (car read)) (car read))
                                  (caar read)))
                  1)
-         (setq first (car active)))
+         (setq first bottom))
        (while read
          (when first
             (push (cons first nlast) unread))
@@ -6908,14 +7009,15 @@ If FORCE (the prefix), also save the .newsrc file(s)."
       (gnus-save-newsrc-file)
     (gnus-dribble-save)))
 
+(declare-function gnus-cache-write-active "gnus-cache" (&optional force))
+
 (defun gnus-summary-exit (&optional temporary leave-hidden)
   "Exit reading current newsgroup, and then return to group selection mode.
 `gnus-exit-group-hook' is called with no arguments if that value is non-nil."
   (interactive)
   (gnus-set-global-variables)
   (when (gnus-buffer-live-p gnus-article-buffer)
-    (save-excursion
-      (set-buffer gnus-article-buffer)
+    (with-current-buffer gnus-article-buffer
       (mm-destroy-parts gnus-article-mime-handles)
       ;; Set it to nil for safety reason.
       (setq gnus-article-mime-handle-alist nil)
@@ -6937,9 +7039,13 @@ If FORCE (the prefix), also save the .newsrc file(s)."
     (gnus-run-hooks 'gnus-summary-prepare-exit-hook)
     ;; If we have several article buffers, we kill them at exit.
     (unless gnus-single-article-buffer
-      (gnus-kill-buffer gnus-article-buffer)
-      (gnus-kill-buffer gnus-original-article-buffer)
-      (setq gnus-article-current nil))
+      (when (gnus-buffer-live-p gnus-article-buffer)
+       (with-current-buffer gnus-article-buffer
+         ;; Don't kill sticky article buffers
+         (unless (eq major-mode 'gnus-sticky-article-mode)
+           (gnus-kill-buffer gnus-article-buffer)
+           (setq gnus-article-current nil))))
+      (gnus-kill-buffer gnus-original-article-buffer))
     (when gnus-use-cache
       (gnus-cache-possibly-remove-articles)
       (gnus-cache-save-buffers))
@@ -6971,11 +7077,6 @@ If FORCE (the prefix), also save the .newsrc file(s)."
     (setq group-point (point))
     (if temporary
        nil                             ;Nothing to do.
-      ;; If we have several article buffers, we kill them at exit.
-      (unless gnus-single-article-buffer
-       (gnus-kill-buffer gnus-article-buffer)
-       (gnus-kill-buffer gnus-original-article-buffer)
-       (setq gnus-article-current nil))
       (set-buffer buf)
       (if (not gnus-kill-summary-on-exit)
          (progn
@@ -7022,8 +7123,7 @@ If FORCE (the prefix), also save the .newsrc file(s)."
       (gnus-async-halt-prefetch)
       (run-hooks 'gnus-summary-prepare-exit-hook)
       (when (gnus-buffer-live-p gnus-article-buffer)
-       (save-excursion
-         (set-buffer gnus-article-buffer)
+       (with-current-buffer gnus-article-buffer
          (mm-destroy-parts gnus-article-mime-handles)
          ;; Set it to nil for safety reason.
          (setq gnus-article-mime-handle-alist nil)
@@ -7067,7 +7167,7 @@ The state which existed when entering the ephemeral is reset."
     (cond ((eq major-mode 'gnus-summary-mode)
           (gnus-set-global-variables))
          ((eq major-mode 'gnus-article-mode)
-          (save-excursion
+          (save-current-buffer
             ;; The `gnus-summary-buffer' variable may point
             ;; to the old summary buffer when using a single
             ;; article buffer.
@@ -7162,14 +7262,12 @@ The state which existed when entering the ephemeral is reset."
      (gnus-kill-summary-on-exit
       (when (and gnus-use-trees
                 (gnus-buffer-exists-p buffer))
-       (save-excursion
-         (set-buffer buffer)
+       (with-current-buffer buffer
          (gnus-tree-close gnus-newsgroup-name)))
       (gnus-kill-buffer buffer))
      ;; Deaden the buffer.
      ((gnus-buffer-exists-p buffer)
-      (save-excursion
-       (set-buffer buffer)
+      (with-current-buffer buffer
        (gnus-deaden-summary))))))
 
 (defun gnus-summary-wake-up-the-dead (&rest args)
@@ -7369,7 +7467,7 @@ If optional argument UNREAD is non-nil, only unread article is selected."
   (gnus-summary-position-point))
 
 (defun gnus-summary-goto-subject (article &optional force silent)
-  "Go the subject line of ARTICLE.
+  "Go to the subject line of ARTICLE.
 If FORCE, also allow jumping to articles not currently shown."
   (interactive "nArticle number: ")
   (unless (numberp article)
@@ -7408,15 +7506,15 @@ Given a prefix, will force an `article' buffer configuration."
 
 (defun gnus-summary-display-article (article &optional all-header)
   "Display ARTICLE in article buffer."
-  (when (gnus-buffer-live-p gnus-article-buffer)
-    (with-current-buffer gnus-article-buffer
-      (mm-enable-multibyte)))
+  (unless (and (gnus-buffer-live-p gnus-article-buffer)
+              (with-current-buffer gnus-article-buffer
+                (eq major-mode 'gnus-article-mode)))
+    (gnus-article-setup-buffer))
   (gnus-set-global-variables)
-  (when (gnus-buffer-live-p gnus-article-buffer)
-    (with-current-buffer gnus-article-buffer
-      (setq gnus-article-charset gnus-newsgroup-charset)
-      (setq gnus-article-ignored-charsets gnus-newsgroup-ignored-charsets)
-      (mm-enable-multibyte)))
+  (with-current-buffer gnus-article-buffer
+    (setq gnus-article-charset gnus-newsgroup-charset)
+    (setq gnus-article-ignored-charsets gnus-newsgroup-ignored-charsets)
+    (mm-enable-multibyte))
   (if (null article)
       nil
     (prog1
@@ -7450,8 +7548,7 @@ be displayed."
     (and (not pseudo)
         (gnus-summary-article-pseudo-p article)
         (error "This is a pseudo-article"))
-    (save-excursion
-      (set-buffer gnus-summary-buffer)
+    (with-current-buffer gnus-summary-buffer
       (if (or (and gnus-single-article-buffer
                   (or (null gnus-current-article)
                       (null gnus-article-current)
@@ -7560,8 +7657,7 @@ If BACKWARD, the previous article is selected instead of the next."
                      (?\C-p (gnus-group-prev-unread-group 1))))
        (cursor-in-echo-area t)
        keve key group ended prompt)
-    (save-excursion
-      (set-buffer gnus-group-buffer)
+    (with-current-buffer gnus-group-buffer
       (goto-char start)
       (setq group
            (if (eq gnus-keep-same-level 'best)
@@ -7611,7 +7707,7 @@ If BACKWARD, the previous article is selected instead of the next."
        (gnus-summary-article-subject))))
 
 (defun gnus-summary-prev-article (&optional unread subject)
-  "Select the article after the current one.
+  "Select the article before the current one.
 If UNREAD is non-nil, only unread articles are selected."
   (interactive "P")
   (gnus-summary-next-article unread subject t))
@@ -8518,18 +8614,17 @@ If ALL, mark even excluded ticked and dormants as read."
 This entails weeding out unwanted dormants, low-scored articles,
 fetch-old-headers verbiage, and so on."
   ;; Most groups have nothing to remove.
-  (if (or gnus-inhibit-limiting
-         (and (null gnus-newsgroup-dormant)
-              (eq gnus-newsgroup-display 'gnus-not-ignore)
-              (not (eq gnus-fetch-old-headers 'some))
-              (not (numberp gnus-fetch-old-headers))
-              (not (eq gnus-fetch-old-headers 'invisible))
-              (null gnus-summary-expunge-below)
-              (not (eq gnus-build-sparse-threads 'some))
-              (not (eq gnus-build-sparse-threads 'more))
-              (null gnus-thread-expunge-below)
-              (not gnus-use-nocem)))
-      ()                               ; Do nothing.
+  (unless (or gnus-inhibit-limiting
+             (and (null gnus-newsgroup-dormant)
+                  (eq gnus-newsgroup-display 'gnus-not-ignore)
+                  (not (eq gnus-fetch-old-headers 'some))
+                  (not (numberp gnus-fetch-old-headers))
+                  (not (eq gnus-fetch-old-headers 'invisible))
+                  (null gnus-summary-expunge-below)
+                  (not (eq gnus-build-sparse-threads 'some))
+                  (not (eq gnus-build-sparse-threads 'more))
+                  (null gnus-thread-expunge-below)
+                  (not gnus-use-nocem)))
     (push gnus-newsgroup-limit gnus-newsgroup-limits)
     (setq gnus-newsgroup-limit nil)
     (mapatoms
@@ -8665,8 +8760,7 @@ The difference between N and the number of articles fetched is returned."
          ;; References header, since this is slightly more
          ;; reliable than the References field we got from the
          ;; server.
-         (save-excursion
-           (set-buffer gnus-original-article-buffer)
+         (with-current-buffer gnus-original-article-buffer
            (nnheader-narrow-to-headers)
            (unless (setq ref (message-fetch-field "references"))
              (when (setq ref (message-fetch-field "in-reply-to"))
@@ -8841,8 +8935,7 @@ to guess what the document format is."
           (case-fold-search t)
           (buf (current-buffer))
           dig to-address)
-      (save-excursion
-       (set-buffer gnus-original-article-buffer)
+      (with-current-buffer gnus-original-article-buffer
        ;; Have the digest group inherit the main mail address of
        ;; the parent article.
        (when (setq to-address (or (gnus-fetch-field "reply-to")
@@ -8912,7 +9005,7 @@ Obeys the standard process/prefix convention."
                                     (nndoc-article-type guess))
                       t nil t))
                (progn
-           ;; Make all postings to this group go to the parent group.
+                  ;; Make all postings to this group go to the parent group.
                  (nconc (gnus-info-params (gnus-get-info egroup))
                         params)
                  (push egroup groups))
@@ -9067,6 +9160,7 @@ Optional argument BACKWARD means do search for backward.
 This search includes all articles in the current group that Gnus has
 fetched headers for, whether they are displayed or not."
   (let ((articles nil)
+       ;; Can't eta-reduce because it's a macro.
        (func `(lambda (h) (,(intern (concat "mail-header-" header)) h)))
        (case-fold-search t))
     (dolist (header gnus-newsgroup-headers)
@@ -9171,8 +9265,6 @@ article.  If BACKWARD (the prefix) is non-nil, search backward instead."
     (goto-char (point-max))
     (recenter -3)
     (when gnus-break-pages
-      (when (re-search-backward page-delimiter nil t)
-       (narrow-to-region (match-end 0) (point-max)))
       (gnus-narrow-to-page))))
 
 (defun gnus-summary-print-truncate-and-quote (string &optional len)
@@ -9238,7 +9330,7 @@ to save in."
                          (mail-header-date gnus-current-headers) ")"))))
            (gnus-run-hooks 'gnus-ps-print-hook)
            (save-excursion
-             (if window-system
+             (if ps-print-color-p
                  (ps-spool-buffer-with-faces)
                (ps-spool-buffer)))))
       (kill-buffer buffer))))
@@ -9265,8 +9357,7 @@ strokes are `C-u g'."
       (gnus-summary-select-article nil 'force)
       (let ((deps gnus-newsgroup-dependencies)
            head header lines)
-       (save-excursion
-         (set-buffer gnus-original-article-buffer)
+       (with-current-buffer gnus-original-article-buffer
          (save-restriction
            (message-narrow-to-head)
            (setq head (buffer-string))
@@ -9306,8 +9397,7 @@ strokes are `C-u g'."
          gnus-break-pages)
       ;; Destroy any MIME parts.
       (when (gnus-buffer-live-p gnus-article-buffer)
-       (save-excursion
-         (set-buffer gnus-article-buffer)
+       (with-current-buffer gnus-article-buffer
          (mm-destroy-parts gnus-article-mime-handles)
          ;; Set it to nil for safety reason.
          (setq gnus-article-mime-handle-alist nil)
@@ -9344,7 +9434,7 @@ If ARG is a negative number, hide the unwanted header lines."
     (with-current-buffer gnus-article-buffer
       (widen)
       (article-narrow-to-head)
-      (let* ((buffer-read-only nil)
+      (let* ((inhibit-read-only t)
             (inhibit-point-motion-hooks t)
             (hidden (if (numberp arg)
                         (>= arg 0)
@@ -9371,7 +9461,7 @@ If ARG is a negative number, hide the unwanted header lines."
        (if gnus-break-pages
            (gnus-narrow-to-page)
          (when (gnus-visual-p 'page-marker)
-           (let ((buffer-read-only nil))
+           (let ((inhibit-read-only t))
              (gnus-remove-text-with-property 'gnus-prev)
              (gnus-remove-text-with-property 'gnus-next))))
        (gnus-set-mode-line 'article)))))
@@ -9392,7 +9482,7 @@ prefix specifies how many places to rotate each letter forward."
       (save-restriction
        (widen)
        (let ((start (window-start))
-             buffer-read-only)
+             (inhibit-read-only t))
          (if (equal arg '(4))
              (message-caesar-buffer-body nil t)
            (message-caesar-buffer-body arg))
@@ -9400,6 +9490,8 @@ prefix specifies how many places to rotate each letter forward."
   ;; Create buttons and stuff...
   (gnus-treat-article nil))
 
+(declare-function idna-to-unicode "ext:idna" (str))
+
 (defun gnus-summary-idna-message (&optional arg)
   "Decode IDNA encoded domain names in the current articles.
 IDNA encoded domain names looks like `xn--bar'.  If a string
@@ -9436,7 +9528,7 @@ installed for this command to work."
        (save-restriction
          (widen)
          (let ((pos (window-start))
-               buffer-read-only)
+               (inhibit-read-only t))
            (goto-char (point-min))
            (when (message-goto-body)
              (gnus-narrow-to-body))
@@ -9454,7 +9546,7 @@ installed for this command to work."
   (gnus-eval-in-buffer-window gnus-article-buffer
     (widen)
     (when (gnus-visual-p 'page-marker)
-      (let ((buffer-read-only nil))
+      (let ((inhibit-read-only t))
        (gnus-remove-text-with-property 'gnus-prev)
        (gnus-remove-text-with-property 'gnus-next))
       (setq gnus-page-broken nil))))
@@ -9502,7 +9594,8 @@ ACTION can be either `move' (the default), `crosspost' or `copy'."
                 (crosspost "Crosspost" "Crossposting")))
        (copy-buf (save-excursion
                    (nnheader-set-temp-buffer " *copy article*")))
-       art-group to-method new-xref article to-groups articles-to-update-marks)
+       art-group to-method new-xref article to-groups
+       articles-to-update-marks encoded)
     (unless (assq action names)
       (error "Unknown action %s" action))
     ;; Read the newsgroup name.
@@ -9520,15 +9613,27 @@ ACTION can be either `move' (the default), `crosspost' or `copy'."
                (gnus-article-prepare-hook nil)
                (gnus-mark-article-hook nil))
            (gnus-summary-select-article nil nil nil (car articles))))
-      (setq to-newsgroup
-           (gnus-read-move-group-name
-            (cadr (assq action names))
-            (symbol-value (intern (format "gnus-current-%s-group" action)))
-            articles prefix))
-      (set (intern (format "gnus-current-%s-group" action)) to-newsgroup))
-    (setq to-method (or select-method
-                       (gnus-server-to-method
-                        (gnus-group-method to-newsgroup))))
+      (setq to-newsgroup (gnus-read-move-group-name
+                         (cadr (assq action names))
+                         (symbol-value
+                          (intern (format "gnus-current-%s-group" action)))
+                         articles prefix)
+           encoded to-newsgroup
+           to-method (gnus-server-to-method (gnus-group-method to-newsgroup)))
+      (set (intern (format "gnus-current-%s-group" action))
+          (mm-decode-coding-string
+           to-newsgroup
+           (gnus-group-name-charset to-method to-newsgroup))))
+    (unless to-method
+      (setq to-method (or select-method
+                         (gnus-server-to-method
+                          (gnus-group-method to-newsgroup)))))
+    (setq to-newsgroup
+         (or encoded
+             (and to-newsgroup
+                  (mm-encode-coding-string
+                   to-newsgroup
+                   (gnus-group-name-charset to-method to-newsgroup)))))
     ;; Check the method we are to move this article to...
     (unless (gnus-check-backend-function
             'request-accept-article (car to-method))
@@ -9537,7 +9642,9 @@ ACTION can be either `move' (the default), `crosspost' or `copy'."
       (error "Can't open server %s" (car to-method)))
     (gnus-message 6 "%s to %s: %s..."
                  (caddr (assq action names))
-                 (or (car select-method) to-newsgroup) articles)
+                 (or (car select-method)
+                     (gnus-group-decoded-name to-newsgroup))
+                 articles)
     (while articles
       (setq article (pop articles))
       (setq
@@ -9549,8 +9656,8 @@ ACTION can be either `move' (the default), `crosspost' or `copy'."
         (gnus-dup-unsuppress-article article)
         (let* ((from-method (gnus-find-method-for-group
                              gnus-newsgroup-name))
-               (to-method (gnus-find-method-for-group
-                           to-newsgroup))
+               (to-method (or select-method
+                              (gnus-find-method-for-group to-newsgroup)))
                (move-is-internal (gnus-method-equal from-method to-method)))
           (gnus-request-move-article
            article                     ; Article to move
@@ -9564,8 +9671,7 @@ ACTION can be either `move' (the default), `crosspost' or `copy'."
            move-is-internal)))         ; is this move internal?
        ;; Copy the article.
        ((eq action 'copy)
-        (save-excursion
-          (set-buffer copy-buf)
+        (with-current-buffer copy-buf
           (when (gnus-request-article-this-buffer article gnus-newsgroup-name)
             (save-restriction
               (nnheader-narrow-to-headers)
@@ -9588,8 +9694,7 @@ ACTION can be either `move' (the default), `crosspost' or `copy'."
                             (delete "Xref:" (delete new-xref xref))
                             " ")
                  " " new-xref))
-          (save-excursion
-            (set-buffer copy-buf)
+          (with-current-buffer copy-buf
             ;; First put the article in the destination group.
             (gnus-request-article-this-buffer article gnus-newsgroup-name)
             (when (consp (setq art-group
@@ -9693,8 +9798,7 @@ ACTION can be either `move' (the default), `crosspost' or `copy'."
          ;; Update the Xref header in this article to point to
          ;; the new crossposted article we have just created.
          (when (eq action 'crosspost)
-           (save-excursion
-             (set-buffer copy-buf)
+           (with-current-buffer copy-buf
              (gnus-request-article-this-buffer article gnus-newsgroup-name)
              (nnheader-replace-header "Xref" new-xref)
              (gnus-request-replace-article
@@ -9719,8 +9823,7 @@ ACTION can be either `move' (the default), `crosspost' or `copy'."
 
     (apply 'gnus-summary-remove-process-mark articles-to-update-marks)
     ;; Re-activate all groups that have been moved to.
-    (save-excursion
-      (set-buffer gnus-group-buffer)
+    (with-current-buffer gnus-group-buffer
       (let ((gnus-group-marked to-groups))
        (gnus-group-get-new-news-this-group nil t)))
 
@@ -9750,16 +9853,6 @@ If nil, use to the current newsgroup method."
   :type 'symbol
   :group 'gnus-summary-mail)
 
-(defcustom gnus-summary-display-while-building nil
-  "If non-nil, show and update the summary buffer as it's being built.
-If the value is t, update the buffer after every line is inserted.  If
-the value is an integer (N), update the display every N lines."
-  :version "22.1"
-  :group 'gnus-thread
-  :type '(choice (const :tag "off" nil)
-                number
-                (const :tag "frequently" t)))
-
 (defun gnus-summary-respool-article (&optional n method)
   "Respool the current article.
 The article will be squeezed through the mail spooling process again,
@@ -9815,8 +9908,7 @@ latter case, they will be copied into the relevant groups."
     (or (file-readable-p file)
        (not (file-regular-p file))
        (error "Can't read %s" file))
-    (save-excursion
-      (set-buffer (gnus-get-buffer-create " *import file*"))
+    (with-current-buffer (gnus-get-buffer-create " *import file*")
       (erase-buffer)
       (nnheader-insert-file-contents file)
       (goto-char (point-min))
@@ -9854,8 +9946,7 @@ latter case, they will be copied into the relevant groups."
        group-art)
     (unless (gnus-check-backend-function 'request-accept-article group)
       (error "%s does not support article importing" group))
-    (save-excursion
-      (set-buffer (gnus-get-buffer-create " *import file*"))
+    (with-current-buffer (gnus-get-buffer-create " *import file*")
       (erase-buffer)
       (goto-char (point-min))
       ;; This doesn't look like an article, so we fudge some headers.
@@ -10038,8 +10129,7 @@ groups."
                                             "nndraft:queue")))
       (error "Can't edit the raw article in group %s"
             gnus-newsgroup-name))
-    (save-excursion
-      (set-buffer gnus-summary-buffer)
+    (with-current-buffer gnus-summary-buffer
       (let ((mail-parse-charset gnus-newsgroup-charset)
            (mail-parse-ignored-charsets gnus-newsgroup-ignored-charsets))
        (gnus-set-global-variables)
@@ -10069,7 +10159,16 @@ groups."
                  (message-options message-options)
                  (message-options-set-recipient)
                  (mail-parse-ignored-charsets
-                  ',gnus-newsgroup-ignored-charsets))
+                  ',gnus-newsgroup-ignored-charsets)
+                 (rfc2047-header-encoding-alist
+                  ',(let ((charset (gnus-group-name-charset
+                                    (gnus-find-method-for-group
+                                     gnus-newsgroup-name)
+                                    gnus-newsgroup-name)))
+                      (append (list (cons "Newsgroups" charset)
+                                    (cons "Followup-To" charset)
+                                    (cons "Xref" charset))
+                              rfc2047-header-encoding-alist))))
              ,(if (not raw) '(progn
                                (mml-to-mime)
                                (mml-destroy-buffers)
@@ -10137,8 +10236,7 @@ groups."
                    (let ((nntp-server-buffer (current-buffer)))
                      (setq header (car (gnus-get-newsgroup-headers
                                         nil t))))
-                   (save-excursion
-                     (set-buffer gnus-summary-buffer)
+                   (with-current-buffer gnus-summary-buffer
                      (gnus-data-set-header
                       (gnus-data-find (cdr gnus-article-current))
                       header)
@@ -10156,8 +10254,7 @@ groups."
               (cdr gnus-article-current))))
        ;; Prettify the article buffer again.
        (unless no-highlight
-         (save-excursion
-           (set-buffer gnus-article-buffer)
+         (with-current-buffer gnus-article-buffer
            ;;;!!! Fix this -- article should be rehighlighted.
            ;;;(gnus-run-hooks 'gnus-article-display-hook)
            (set-buffer gnus-original-article-buffer)
@@ -10187,8 +10284,7 @@ groups."
   (interactive)
   (let (gnus-mark-article-hook)
     (gnus-summary-select-article)
-    (save-excursion
-      (set-buffer gnus-original-article-buffer)
+    (with-current-buffer gnus-original-article-buffer
       (let ((groups (nnmail-article-group 'identity trace)))
        (unless silent
          (if groups
@@ -10349,7 +10445,7 @@ ARTICLE can also be a list of articles."
       (unless (numberp article)
        (error "%s is not a number" article))
       (push article gnus-newsgroup-replied)
-      (let ((buffer-read-only nil))
+      (let ((inhibit-read-only t))
        (when (gnus-summary-goto-subject article nil t)
          (gnus-summary-update-secondary-mark article))))))
 
@@ -10359,7 +10455,7 @@ ARTICLE can also be a list of articles."
   (let ((articles (if (listp article) article (list article))))
     (dolist (article articles)
       (push article gnus-newsgroup-forwarded)
-      (let ((buffer-read-only nil))
+      (let ((inhibit-read-only t))
        (when (gnus-summary-goto-subject article nil t)
          (gnus-summary-update-secondary-mark article))))))
 
@@ -10578,7 +10674,7 @@ If NO-EXPIRE, auto-expiry will be inhibited."
                (= mark gnus-dormant-mark) (= mark gnus-unread-mark))))
 
        (when (gnus-summary-goto-subject article nil t)
-         (let ((buffer-read-only nil))
+         (let ((inhibit-read-only t))
            (gnus-summary-show-thread)
            ;; Fix the mark.
            (gnus-summary-update-mark mark 'unread)
@@ -10622,7 +10718,7 @@ If NO-EXPIRE, auto-expiry will be inhibited."
 
 (defun gnus-summary-update-mark (mark type)
   (let ((forward (cdr (assq type gnus-summary-mark-positions)))
-       (buffer-read-only nil))
+       (inhibit-read-only t))
     (re-search-backward "[\n\r]" (point-at-bol) 'move-to-limit)
     (when forward
       (when (looking-at "\r")
@@ -10757,12 +10853,12 @@ The difference between N and the number of marks cleared is returned."
   (gnus-summary-mark-forward (- n) gnus-unread-mark))
 
 (defun gnus-summary-mark-unread-as-read ()
-  "Intended to be used by `gnus-summary-mark-article-hook'."
+  "Intended to be used by `gnus-mark-article-hook'."
   (when (memq gnus-current-article gnus-newsgroup-unreads)
     (gnus-summary-mark-article gnus-current-article gnus-read-mark)))
 
 (defun gnus-summary-mark-read-and-unread-as-read (&optional new-mark)
-  "Intended to be used by `gnus-summary-mark-article-hook'."
+  "Intended to be used by `gnus-mark-article-hook'."
   (let ((mark (gnus-summary-article-mark)))
     (when (or (gnus-unread-mark-p mark)
              (gnus-read-mark-p mark))
@@ -10770,7 +10866,7 @@ The difference between N and the number of marks cleared is returned."
                                 (or new-mark gnus-read-mark)))))
 
 (defun gnus-summary-mark-current-read-and-unread-as-read (&optional new-mark)
-  "Intended to be used by `gnus-summary-mark-article-hook'."
+  "Intended to be used by `gnus-mark-article-hook'."
   (let ((mark (gnus-summary-article-mark)))
     (when (or (gnus-unread-mark-p mark)
              (gnus-read-mark-p mark))
@@ -10778,7 +10874,7 @@ The difference between N and the number of marks cleared is returned."
                                 (or new-mark gnus-read-mark)))))
 
 (defun gnus-summary-mark-unread-as-ticked ()
-  "Intended to be used by `gnus-summary-mark-article-hook'."
+  "Intended to be used by `gnus-mark-article-hook'."
   (when (memq gnus-current-article gnus-newsgroup-unreads)
     (gnus-summary-mark-article gnus-current-article gnus-ticked-mark)))
 
@@ -10807,8 +10903,7 @@ even ticked and dormant ones."
   (setq score (if score
                  (prefix-numeric-value score)
                (or gnus-summary-default-score 0)))
-  (save-excursion
-    (set-buffer gnus-summary-buffer)
+  (with-current-buffer gnus-summary-buffer
     (goto-char (point-min))
     (while
        (progn
@@ -10837,8 +10932,7 @@ even ticked and dormant ones."
   (setq score (if score
                  (prefix-numeric-value score)
                (or gnus-summary-default-score 0)))
-  (save-excursion
-    (set-buffer gnus-summary-buffer)
+  (with-current-buffer gnus-summary-buffer
     (goto-char (point-min))
     (while (and (progn
                  (when (> (gnus-summary-article-score) score)
@@ -10851,7 +10945,7 @@ even ticked and dormant ones."
 (defun gnus-summary-limit-include-expunged (&optional no-error)
   "Display all the hidden articles that were expunged for low scores."
   (interactive)
-  (let ((buffer-read-only nil))
+  (let ((inhibit-read-only t))
     (let ((scored gnus-newsgroup-scored)
          headers h)
       (while scored
@@ -10866,9 +10960,8 @@ even ticked and dormant ones."
        (goto-char (point-min))
        (push gnus-newsgroup-limit gnus-newsgroup-limits)
        (setq gnus-newsgroup-limit (copy-sequence gnus-newsgroup-limit))
-       (mapcar (lambda (x) (push (mail-header-number x)
-                                 gnus-newsgroup-limit))
-               headers)
+       (dolist (x headers)
+         (push (mail-header-number x) gnus-newsgroup-limit))
        (gnus-summary-prepare-unthreaded (nreverse headers))
        (goto-char (point-min))
        (gnus-summary-position-point)
@@ -11228,8 +11321,7 @@ Returns nil if no threads were there to be hidden."
        (start (point))
        (article (gnus-summary-article-number)))
     (goto-char start)
-    ;; Go forward until either the buffer ends or the subthread
-    ;; ends.
+    ;; Go forward until either the buffer ends or the subthread ends.
     (when (and (not (eobp))
               (or (zerop (gnus-summary-next-thread 1 t))
                   (goto-char (point-max))))
@@ -11331,14 +11423,21 @@ taken."
   (while (gnus-summary-go-up-thread))
   (gnus-summary-article-number))
 
+(defun gnus-summary-expire-thread ()
+  "Mark articles under current thread as expired."
+  (interactive)
+  (gnus-summary-kill-thread 0))
+
 (defun gnus-summary-kill-thread (&optional unmark)
   "Mark articles under current thread as read.
 If the prefix argument is positive, remove any kinds of marks.
+If the prefix argument is zero, mark thread as expired.
 If the prefix argument is negative, tick articles instead."
   (interactive "P")
   (when unmark
     (setq unmark (prefix-numeric-value unmark)))
-  (let ((articles (gnus-summary-articles-in-thread)))
+  (let ((articles (gnus-summary-articles-in-thread))
+       (hide (or (null unmark) (= unmark 0))))
     (save-excursion
       ;; Expand the thread.
       (gnus-summary-show-thread)
@@ -11349,15 +11448,17 @@ If the prefix argument is negative, tick articles instead."
               (gnus-summary-mark-article-as-read gnus-killed-mark))
              ((> unmark 0)
               (gnus-summary-mark-article-as-unread gnus-unread-mark))
+             ((= unmark 0)
+              (gnus-summary-mark-article-as-unread gnus-expirable-mark))
              (t
               (gnus-summary-mark-article-as-unread gnus-ticked-mark)))
        (setq articles (cdr articles))))
-    ;; Hide killed subtrees.
-    (and (null unmark)
+    ;; Hide killed subtrees when hide is true.
+    (and hide
         gnus-thread-hide-killed
         (gnus-summary-hide-thread))
-    ;; If marked as read, go to next unread subject.
-    (when (null unmark)
+    ;; If hide is t, go to next unread subject.
+    (when hide
       ;; Go to next unread subject.
       (gnus-summary-next-subject 1 t)))
   (gnus-set-mode-line 'summary))
@@ -11370,6 +11471,12 @@ Argument REVERSE means reverse order."
   (interactive "P")
   (gnus-summary-sort 'number reverse))
 
+(defun gnus-summary-sort-by-most-recent-number (&optional reverse)
+  "Sort the summary buffer by most recent article number.
+Argument REVERSE means reverse order."
+  (interactive "P")
+  (gnus-summary-sort 'most-recent-number reverse))
+
 (defun gnus-summary-sort-by-random (&optional reverse)
   "Randomize the order in the summary buffer.
 Argument REVERSE means to randomize in reverse order."
@@ -11403,6 +11510,12 @@ Argument REVERSE means reverse order."
   (interactive "P")
   (gnus-summary-sort 'date reverse))
 
+(defun gnus-summary-sort-by-most-recent-date (&optional reverse)
+  "Sort the summary buffer by most recent date.
+Argument REVERSE means reverse order."
+  (interactive "P")
+  (gnus-summary-sort 'most-recent-date reverse))
+
 (defun gnus-summary-sort-by-score (&optional reverse)
   "Sort the summary buffer by score.
 Argument REVERSE means reverse order."
@@ -11425,7 +11538,7 @@ Argument REVERSE means reverse order."
   "Sort the summary buffer using the default sorting method.
 Argument REVERSE means reverse order."
   (interactive "P")
-  (let* ((buffer-read-only)
+  (let* ((inhibit-read-only t)
         (gnus-summary-prepare-hook nil))
     ;; We do the sorting by regenerating the threads.
     (gnus-summary-prepare)
@@ -11448,7 +11561,7 @@ Argument REVERSE means reverse order."
              article
            `(lambda (t1 t2)
               (,article t2 t1))))
-        (buffer-read-only)
+        (inhibit-read-only t)
         (gnus-summary-prepare-hook nil))
     ;; We do the sorting by regenerating the threads.
     (gnus-summary-prepare)
@@ -11497,10 +11610,9 @@ will not be marked as saved."
                                              gnus-display-mime-function))
                (gnus-article-prepare-hook (when decode
                                             gnus-article-prepare-hook)))
-           (gnus-summary-select-article t nil nil article)
+           (gnus-summary-select-article t t nil article)
            (gnus-summary-goto-subject article)))
-       (save-excursion
-         (set-buffer save-buffer)
+       (with-current-buffer save-buffer
          (erase-buffer)
          (insert-buffer-substring (if decode
                                       gnus-article-buffer
@@ -11517,22 +11629,69 @@ will not be marked as saved."
     (gnus-set-mode-line 'summary)
     n))
 
-(defun gnus-summary-pipe-output (&optional arg headers)
+(defun gnus-summary-pipe-output (&optional n sym)
   "Pipe the current article to a subprocess.
 If N is a positive number, pipe the N next articles.
 If N is a negative number, pipe the N previous articles.
 If N is nil and any articles have been marked with the process mark,
 pipe those articles instead.
-If HEADERS (the symbolic prefix), include the headers, too."
+The default command to which articles are piped is specified by the
+variable `gnus-summary-pipe-output-default-command'; if it is nil, you
+will be prompted for the command.
+
+The properties `:decode' and `:headers' that are put to the function
+symbol `gnus-summary-save-in-pipe' control whether this function
+decodes articles and what headers to keep (see the doc string for the
+`gnus-default-article-saver' variable).  If SYM (the symbolic prefix)
+is neither omitted nor the symbol `r', force including all headers
+regardless of the `:headers' property.  If it is the symbol `r',
+articles that are not decoded and include all headers will be piped
+no matter what the properties `:decode' and `:headers' are."
   (interactive (gnus-interactive "P\ny"))
   (require 'gnus-art)
-  (let ((gnus-default-article-saver 'gnus-summary-save-in-pipe)
-       (gnus-save-all-headers (or headers gnus-save-all-headers)))
-    (gnus-summary-save-article arg t))
-  (let ((buffer (get-buffer "*Shell Command Output*")))
-    (when (and buffer
-              (not (zerop (buffer-size buffer))))
-      (gnus-configure-windows 'pipe))))
+  (let* ((articles (gnus-summary-work-articles n))
+        (result-buffer "*Shell Command Output*")
+        (all-headers (not (memq sym '(nil r))))
+        (gnus-save-all-headers (or all-headers gnus-save-all-headers))
+        (raw (eq sym 'r))
+        (headers (get 'gnus-summary-save-in-pipe :headers))
+        command result)
+    (unless (numberp (car articles))
+      (error "No article to pipe"))
+    (setq command (gnus-read-shell-command
+                  (concat "Shell command on "
+                          (if (cdr articles)
+                              (format "these %d articles" (length articles))
+                            "this article")
+                          ": ")
+                  gnus-summary-pipe-output-default-command))
+    (when (string-equal command "")
+      (error "A command is required"))
+    (when all-headers
+      (put 'gnus-summary-save-in-pipe :headers nil))
+    (unwind-protect
+       (while articles
+         (gnus-summary-goto-subject (pop articles))
+         (save-window-excursion (gnus-summary-save-in-pipe command raw))
+         (when (and (get-buffer result-buffer)
+                    (not (zerop (buffer-size (get-buffer result-buffer)))))
+           (setq result (concat result (with-current-buffer result-buffer
+                                         (buffer-string))))))
+      (put 'gnus-summary-save-in-pipe :headers headers))
+    (unless (zerop (length result))
+      (if (with-current-buffer (get-buffer-create result-buffer)
+           (erase-buffer)
+           (insert result)
+           (prog1
+               (and (= (count-lines (point-min) (point)) 1)
+                    (progn
+                      (end-of-line 0)
+                      (<= (current-column)
+                          (window-width (minibuffer-window)))))
+             (goto-char (point-min))))
+         (message "%s" (substring result 0 -1))
+       (message nil)
+       (gnus-configure-windows 'pipe)))))
 
 (defun gnus-summary-save-article-mail (&optional arg)
   "Append the current article to a Unix mail box file.
@@ -11620,7 +11779,7 @@ save those articles instead."
       (save-restriction
        (widen)
        (let ((start (window-start))
-             buffer-read-only)
+             (inhibit-read-only t))
          (message-pipe-buffer-body program)
          (set-window-start (get-buffer-window (current-buffer)) start))))))
 
@@ -11628,8 +11787,7 @@ save those articles instead."
   "Return a value based on the split METHODS."
   (let (split-name method result match)
     (when methods
-      (save-excursion
-       (set-buffer gnus-original-article-buffer)
+      (with-current-buffer gnus-original-article-buffer
        (save-restriction
          (nnheader-narrow-to-headers)
          (while (and methods (not split-name))
@@ -11678,47 +11836,52 @@ save those articles instead."
                      (format "these %d articles" (length articles))
                    "this article")))
         (to-newsgroup
-         (cond
-          ((null split-name)
-           (gnus-completing-read-with-default
-            default prom
-            gnus-active-hashtb
-            'gnus-valid-move-group-p
-            nil prefix
-            'gnus-group-history))
-          ((= 1 (length split-name))
-           (gnus-completing-read-with-default
-            (car split-name) prom
-            gnus-active-hashtb
-            'gnus-valid-move-group-p
-            nil nil
-            'gnus-group-history))
-          (t
-           (gnus-completing-read-with-default
-            nil prom
-            (mapcar 'list (nreverse split-name))
-            nil nil nil
-            'gnus-group-history))))
-        (to-method (gnus-server-to-method (gnus-group-method to-newsgroup))))
+         (let (active group)
+           (when (or (null split-name) (= 1 (length split-name)))
+             (setq active (gnus-make-hashtable (length gnus-active-hashtb)))
+             (mapatoms (lambda (symbol)
+                         (setq group (symbol-name symbol))
+                         (when (string-match "[^\000-\177]" group)
+                           (setq group (gnus-group-decoded-name group)))
+                         (set (intern group active) group))
+                       gnus-active-hashtb))
+           (cond
+            ((null split-name)
+             (gnus-completing-read-with-default
+              default prom active 'gnus-valid-move-group-p nil prefix
+              'gnus-group-history))
+            ((= 1 (length split-name))
+             (gnus-completing-read-with-default
+              (car split-name) prom active 'gnus-valid-move-group-p nil nil
+              'gnus-group-history))
+            (t
+             (gnus-completing-read-with-default
+              nil prom (mapcar 'list (nreverse split-name)) nil nil nil
+              'gnus-group-history)))))
+        (to-method (gnus-server-to-method (gnus-group-method to-newsgroup)))
+        encoded)
     (when to-newsgroup
       (if (or (string= to-newsgroup "")
              (string= to-newsgroup prefix))
          (setq to-newsgroup default))
       (unless to-newsgroup
        (error "No group name entered"))
-      (or (gnus-active to-newsgroup)
-         (gnus-activate-group to-newsgroup nil nil to-method)
+      (setq encoded (mm-encode-coding-string
+                    to-newsgroup
+                    (gnus-group-name-charset to-method to-newsgroup)))
+      (or (gnus-active encoded)
+         (gnus-activate-group encoded nil nil to-method)
          (if (gnus-y-or-n-p (format "No such group: %s.  Create it? "
                                     to-newsgroup))
-             (or (and (gnus-request-create-group to-newsgroup to-method)
-                      (gnus-activate-group
-                       to-newsgroup nil nil to-method)
-                      (gnus-subscribe-group to-newsgroup))
+             (or (and (gnus-request-create-group encoded to-method)
+                      (gnus-activate-group encoded nil nil to-method)
+                      (gnus-subscribe-group encoded))
                  (error "Couldn't create group %s" to-newsgroup)))
-         (error "No such group: %s" to-newsgroup)))
-    to-newsgroup))
+         (error "No such group: %s" to-newsgroup))
+      encoded)))
 
 (defvar gnus-summary-save-parts-counter)
+(declare-function mm-uu-dissect "mm-uu" (&optional noheader mime-type))
 
 (defun gnus-summary-save-parts (type dir n &optional reverse)
   "Save parts matching TYPE to DIR.
@@ -11735,10 +11898,13 @@ If REVERSE, save parts that do not match TYPE."
         current-prefix-arg))
   (gnus-summary-iterate n
     (let ((gnus-display-mime-function nil)
+         gnus-article-prepare-hook
+         gnus-article-decode-hook
+         gnus-display-mime-function
+         gnus-break-pages
          (gnus-inhibit-treatment t))
       (gnus-summary-select-article))
-    (save-excursion
-      (set-buffer gnus-article-buffer)
+    (with-current-buffer gnus-article-buffer
       (let ((handles (or gnus-article-mime-handles
                         (mm-dissect-buffer nil gnus-article-loose-mime)
                         (and gnus-article-emulate-mime
@@ -11776,7 +11942,7 @@ If REVERSE, save parts that do not match TYPE."
 ;; Summary extract commands
 
 (defun gnus-summary-insert-pseudos (pslist &optional not-view)
-  (let ((buffer-read-only nil)
+  (let ((inhibit-read-only t)
        (article (gnus-summary-article-number))
        after-article b e)
     (unless (gnus-summary-goto-subject article)
@@ -11904,8 +12070,7 @@ If REVERSE, save parts that do not match TYPE."
        ;; We have found the header.
        header
       ;; We have to really fetch the header to this article.
-      (save-excursion
-       (set-buffer nntp-server-buffer)
+      (with-current-buffer nntp-server-buffer
        (when (setq where (gnus-request-head id group))
          (nnheader-fold-continuation-lines)
          (goto-char (point-max))
@@ -11933,8 +12098,7 @@ If REVERSE, save parts that do not match TYPE."
              ;; a different group (or server), we fudge some bogus
              ;; article numbers for this article.
              (mail-header-set-number header gnus-reffed-article-number))
-           (save-excursion
-             (set-buffer gnus-summary-buffer)
+           (with-current-buffer gnus-summary-buffer
              (decf gnus-reffed-article-number)
              (gnus-remove-header (mail-header-number header))
              (push header gnus-newsgroup-headers)
@@ -12071,7 +12235,8 @@ UNREAD is a sorted list."
        (save-excursion
          (let (setmarkundo)
            ;; Propagate the read marks to the backend.
-           (when (gnus-check-backend-function 'request-set-mark group)
+           (when (and gnus-propagate-marks
+                      (gnus-check-backend-function 'request-set-mark group))
              (let ((del (gnus-remove-from-range (gnus-info-read info) read))
                    (add (gnus-remove-from-range read (gnus-info-read info))))
                (when (or add del)
@@ -12280,12 +12445,24 @@ returned."
     (when gnus-suppress-duplicates
       (gnus-dup-suppress-articles))
 
-    ;; We might want to build some more threads first.
-    (when (and gnus-fetch-old-headers
-              (eq gnus-headers-retrieved-by 'nov))
-      (if (eq gnus-fetch-old-headers 'invisible)
-         (gnus-build-all-threads)
-       (gnus-build-old-threads)))
+    (if (and gnus-fetch-old-headers
+            (eq gnus-headers-retrieved-by 'nov))
+       ;; We might want to build some more threads first.
+       (if (eq gnus-fetch-old-headers 'invisible)
+           (gnus-build-all-threads)
+         (gnus-build-old-threads))
+      ;; Mark the inserted articles that are unread as unread.
+      (setq gnus-newsgroup-unreads
+           (gnus-sorted-nunion
+            gnus-newsgroup-unreads
+            (gnus-sorted-nintersection
+             (gnus-list-of-unread-articles gnus-newsgroup-name)
+             articles)))
+      ;; Mark the inserted articles as selected so that the information
+      ;; of the marks having been changed by a user may be updated when
+      ;; exiting this group.  See `gnus-summary-update-info'.
+      (dolist (art articles)
+       (setq gnus-newsgroup-unselected (delq art gnus-newsgroup-unselected))))
     ;; Let the Gnus agent mark articles as read.
     (when gnus-agent
       (gnus-agent-get-undownloaded-list))