mml.el: Require url when compiling
[gnus] / lisp / gnus-sum.el
index bb8c719..090cbed 100644 (file)
@@ -1,6 +1,6 @@
 ;;; gnus-sum.el --- summary mode commands for Gnus
 
-;; Copyright (C) 1996-2011 Free Software Foundation, Inc.
+;; Copyright (C) 1996-2014 Free Software Foundation, Inc.
 
 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
 ;; Keywords: news
@@ -24,9 +24,6 @@
 
 ;;; Code:
 
-;; For Emacs <22.2 and XEmacs.
-(eval-and-compile
-  (unless (fboundp 'declare-function) (defmacro declare-function (&rest r))))
 (eval-when-compile
   (require 'cl))
 (eval-when-compile
@@ -122,6 +119,7 @@ If t, fetch all the available old headers."
   "*Use nnir to search an entire server when referring threads. A
 nil value will only search for thread-related articles in the
 current group."
+  :version "24.1"
   :group 'gnus-thread
   :type 'boolean)
 
@@ -364,7 +362,7 @@ newsgroups, set the variable to nil in `gnus-select-group-hook'."
 This variable can either be the symbols `first' (place point on the
 first subject), `unread' (place point on the subject line of the first
 unread article), `best' (place point on the subject line of the
-higest-scored article), `unseen' (place point on the subject line of
+highest-scored article), `unseen' (place point on the subject line of
 the first unseen article), `unseen-or-unread' (place point on the subject
 line of the first unseen article or, if all articles have been seen, on the
 subject line of the first unread article), or a function to be called to
@@ -450,7 +448,8 @@ current article is unread."
   :group 'gnus-summary-maneuvering
   :type 'boolean)
 
-(defcustom gnus-auto-center-summary 2
+(defcustom gnus-auto-center-summary
+  (max (or (bound-and-true-p scroll-margin) 0) 2)
   "*If non-nil, always center the current summary buffer.
 In particular, if `vertical' do only vertical recentering.  If non-nil
 and non-`vertical', do both horizontal and vertical recentering."
@@ -845,7 +844,6 @@ controls how articles are sorted."
                           (function :tag "other"))
                   (boolean :tag "Reverse order"))))
 
-
 (defcustom gnus-thread-sort-functions '(gnus-thread-sort-by-number)
   "*List of functions used for sorting threads in the summary buffer.
 By default, threads are sorted by article number.
@@ -871,7 +869,11 @@ and `gnus-thread-sort-by-total-score' (see
 `gnus-thread-score-function').
 
 When threading is turned off, the variable
-`gnus-article-sort-functions' controls how articles are sorted."
+`gnus-article-sort-functions' controls how articles are sorted.
+
+By default, threads and their subthreads are sorted according to
+the value of this variable.  To use a different sorting order for
+subthreads, customize `gnus-subthread-sort-functions'."
   :group 'gnus-summary-sort
   :type '(repeat
           (gnus-widget-reversible
@@ -888,6 +890,29 @@ When threading is turned off, the variable
                    (function :tag "other"))
            (boolean :tag "Reverse order"))))
 
+(defcustom gnus-subthread-sort-functions 'gnus-thread-sort-functions
+  "*List of functions used for sorting subthreads in the summary buffer.
+By default, subthreads are sorted the same as threads, i.e.,
+according to the value of `gnus-thread-sort-functions'."
+  :version "24.4"
+  :group 'gnus-summary-sort
+  :type '(choice
+         (const :tag "Sort subthreads like threads" gnus-thread-sort-functions)
+         (repeat
+          (gnus-widget-reversible
+           (choice (function-item gnus-thread-sort-by-number)
+                   (function-item gnus-thread-sort-by-author)
+                   (function-item gnus-thread-sort-by-recipient)
+                   (function-item gnus-thread-sort-by-subject)
+                   (function-item gnus-thread-sort-by-date)
+                   (function-item gnus-thread-sort-by-score)
+                   (function-item gnus-thread-sort-by-most-recent-number)
+                   (function-item gnus-thread-sort-by-most-recent-date)
+                   (function-item gnus-thread-sort-by-random)
+                   (function-item gnus-thread-sort-by-total-score)
+                   (function :tag "other"))
+           (boolean :tag "Reverse order")))))
+
 (defcustom gnus-thread-score-function '+
   "*Function used for calculating the total score of a thread.
 
@@ -1113,7 +1138,6 @@ score:        The article's score.
 default:      The default article score.
 default-high: The default score for high scored articles.
 default-low:  The default score for low scored articles.
-below:        The score below which articles are automatically marked as read.
 mark:         The article's mark.
 uncached:     Non-nil if the article is uncached."
   :group 'gnus-summary-visual
@@ -1165,7 +1189,7 @@ using `gnus-ignored-from-addresses'."
 
 (defcustom gnus-summary-newsgroup-prefix "=> "
   "*String prefixed to the Newsgroup field in the summary
-line when using `gnus-ignored-from-addresses'."
+line when using the option `gnus-ignored-from-addresses'."
   :version "22.1"
   :group 'gnus-summary
   :type 'string)
@@ -1242,13 +1266,6 @@ For example: ((1 . cn-gb-2312) (2 . big5))."
   :type 'boolean
   :group 'gnus-summary-marks)
 
-(defcustom gnus-propagate-marks nil
-  "If non-nil, Gnus will store and retrieve marks from the backends.
-This means that marks will be stored both in .newsrc.eld and in
-the backend, and will slow operation down somewhat."
-  :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)
@@ -1371,15 +1388,12 @@ the normal Gnus MIME machinery."
     (?c (or (mail-header-chars gnus-tmp-header) 0) ?d)
     (?k (gnus-summary-line-message-size gnus-tmp-header) ?s)
     (?L gnus-tmp-lines ?s)
-    (?Z (or ,(gnus-macroexpand-all
-             '(nnir-article-rsv (mail-header-number gnus-tmp-header)))
+    (?Z (or (nnir-article-rsv (mail-header-number gnus-tmp-header))
            0) ?d)
-    (?G (or ,(gnus-macroexpand-all
-             '(nnir-article-group (mail-header-number gnus-tmp-header)))
+    (?G (or (nnir-article-group (mail-header-number gnus-tmp-header))
            "") ?s)
-    (?g (or ,(gnus-macroexpand-all
-             '(gnus-group-short-name
-               (nnir-article-group (mail-header-number gnus-tmp-header))))
+    (?g (or (gnus-group-short-name
+            (nnir-article-group (mail-header-number gnus-tmp-header)))
            "") ?s)
     (?O gnus-tmp-downloaded ?c)
     (?I gnus-tmp-indentation ?s)
@@ -1499,9 +1513,6 @@ the type of the variable (string, integer, character, etc).")
 (defvar gnus-newsgroup-forwarded nil
   "List of articles that have been forwarded in the current newsgroup.")
 
-(defvar gnus-newsgroup-recent nil
-  "List of articles that have are recent in the current newsgroup.")
-
 (defvar gnus-newsgroup-expirable nil
   "Sorted list of articles in the current newsgroup that can be expired.")
 
@@ -1535,6 +1546,9 @@ This list will always be a subset of gnus-newsgroup-undownloaded.")
 (defvar gnus-newsgroup-seen nil
   "Range of seen articles in the current newsgroup.")
 
+(defvar gnus-newsgroup-unexist nil
+  "Range of unexisting articles in the current newsgroup.")
+
 (defvar gnus-newsgroup-articles nil
   "List of articles in the current newsgroup.")
 
@@ -1578,11 +1592,11 @@ This list will always be a subset of gnus-newsgroup-undownloaded.")
     gnus-newsgroup-saved
     gnus-newsgroup-replied
     gnus-newsgroup-forwarded
-    gnus-newsgroup-recent
     gnus-newsgroup-expirable
     gnus-newsgroup-killed
     gnus-newsgroup-unseen
     gnus-newsgroup-seen
+    gnus-newsgroup-unexist
     gnus-newsgroup-cached
     gnus-newsgroup-downloadable
     gnus-newsgroup-undownloaded
@@ -1661,7 +1675,7 @@ For example:
 
 This variable is a list of FUNCTION or (REGEXP . FUNCTION).  If item
 is FUNCTION, FUNCTION will be apply to all newsgroups.  If item is a
-\(REGEXP . FUNCTION), FUNCTION will be only apply to thes newsgroups
+\(REGEXP . FUNCTION), FUNCTION will be applied only to the newsgroups
 whose names match REGEXP.
 
 For example:
@@ -1831,9 +1845,9 @@ increase the score of each group you read."
 
 (gnus-define-keys gnus-summary-mode-map
   " " gnus-summary-next-page
+  [?\S-\ ] gnus-summary-prev-page
   "\177" gnus-summary-prev-page
   [delete] gnus-summary-prev-page
-  [backspace] gnus-summary-prev-page
   "\r" gnus-summary-scroll-up
   "\M-\r" gnus-summary-scroll-down
   "n" gnus-summary-next-unread-article
@@ -1923,7 +1937,8 @@ increase the score of each group you read."
   "a" gnus-summary-post-news
   "x" gnus-summary-limit-to-unread
   "s" gnus-summary-isearch-article
-  [tab] gnus-summary-widget-forward
+  "\t" gnus-summary-widget-forward
+  [backtab] gnus-summary-widget-backward
   "t" gnus-summary-toggle-header
   "g" gnus-summary-show-article
   "l" gnus-summary-goto-last-article
@@ -2069,6 +2084,7 @@ increase the score of each group you read."
 (gnus-define-keys (gnus-summary-article-map "A" gnus-summary-mode-map)
   " " gnus-summary-next-page
   "n" gnus-summary-next-page
+  [?\S-\ ] gnus-summary-prev-page
   "\177" gnus-summary-prev-page
   [delete] gnus-summary-prev-page
   "p" gnus-summary-prev-page
@@ -2087,7 +2103,8 @@ increase the score of each group you read."
   "W" gnus-warp-to-article
   "g" gnus-summary-show-article
   "s" gnus-summary-isearch-article
-  [tab] gnus-summary-widget-forward
+  "\t" gnus-summary-widget-forward
+  [backtab] gnus-summary-widget-backward
   "P" gnus-summary-print-article
   "S" gnus-sticky-article
   "M" gnus-mailing-list-insinuate
@@ -2168,6 +2185,7 @@ increase the score of each group you read."
 (gnus-define-keys (gnus-summary-wash-mime-map "M" gnus-summary-wash-map)
   "w" gnus-article-decode-mime-words
   "c" gnus-article-decode-charset
+  "h" gnus-mime-buttonize-attachments-in-header
   "v" gnus-mime-view-all-parts
   "b" gnus-article-view-part)
 
@@ -2202,7 +2220,6 @@ increase the score of each group you read."
   "\M-\C-e" gnus-summary-expire-articles-now
   "\177" gnus-summary-delete-article
   [delete] gnus-summary-delete-article
-  [backspace] gnus-summary-delete-article
   "m" gnus-summary-move-article
   "r" gnus-summary-respool-article
   "w" gnus-summary-edit-article
@@ -2338,7 +2355,8 @@ increase the score of each group you read."
           ["Mark above" gnus-summary-mark-above t]
           ["Tick above" gnus-summary-tick-above t]
           ["Clear above" gnus-summary-clear-above t])
-         ["Current score" gnus-summary-current-score t]
+         ["Current article score" gnus-summary-current-score t]
+         ["Current thread score" (gnus-summary-current-score 'total) t]
          ["Set score" gnus-summary-set-score t]
          ["Switch current score file..." gnus-score-change-score-file t]
          ["Set mark below..." gnus-score-set-mark-below t]
@@ -2374,6 +2392,8 @@ increase the score of each group you read."
              ["QP" gnus-article-de-quoted-unreadable t]
              ["Base64" gnus-article-de-base64-unreadable t]
              ["View MIME buttons" gnus-summary-display-buttonized t]
+             ["View MIME buttons in header"
+              gnus-mime-buttonize-attachments-in-header t]
              ["View all" gnus-mime-view-all-parts t]
              ["Verify and Decrypt" gnus-summary-force-verify-and-decrypt t]
              ["Encrypt body" gnus-article-encrypt-body
@@ -2533,7 +2553,7 @@ gnus-summary-show-article-from-menu-as-charset-%s" cs))))
              ["Unshar and save" gnus-uu-decode-unshar-and-save t]
              ["Save" gnus-uu-decode-save t]
              ["Binhex" gnus-uu-decode-binhex t]
-             ["Postscript" gnus-uu-decode-postscript t]
+             ["PostScript" gnus-uu-decode-postscript t]
              ["All MIME parts" gnus-summary-save-parts t])
             ("Cache"
              ["Enter article" gnus-cache-enter-article t]
@@ -2839,7 +2859,7 @@ Setter function for custom variables."
                                   'gnus-summary-tool-bar-retro)
   "Specifies the Gnus summary tool bar.
 
-It can be either a list or a symbol refering to a list.  See
+It can be either a list or a symbol referring to a list.  See
 `gmm-tool-bar-from-list' for the format of the list.  The
 default key map is `gnus-summary-mode-map'.
 
@@ -2973,16 +2993,10 @@ When FORCE, rebuild the tool bar."
                                        'gnus-summary-mode-map)))
       (when map
        ;; Need to set `gnus-summary-tool-bar-map' because `gnus-article-mode'
-       ;; uses it's value.
+       ;; uses its value.
        (setq gnus-summary-tool-bar-map map))))
   (set (make-local-variable 'tool-bar-map) gnus-summary-tool-bar-map))
 
-(defun gnus-score-set-default (var value)
-  "A version of set that updates the GNU Emacs menu-bar."
-  (set var value)
-  ;; It is the message that forces the active status to be updated.
-  (message ""))
-
 (defun gnus-make-score-map (type)
   "Make a summary score map of type TYPE."
   (if t
@@ -3066,6 +3080,7 @@ When FORCE, rebuild the tool bar."
 (declare-function turn-on-gnus-mailing-list-mode "gnus-ml" ())
 (defvar bookmark-make-record-function)
 \f
+(defvar bidi-paragraph-direction)
 
 (defun gnus-summary-mode (&optional group)
   "Major mode for reading articles.
@@ -3087,6 +3102,7 @@ buffer; read the info pages for more information (`\\[gnus-info-find-node]').
 The following commands are available:
 
 \\{gnus-summary-mode-map}"
+  ;; FIXME: Use define-derived-mode.
   (interactive)
   (kill-all-local-variables)
   (let ((gnus-summary-local-variables gnus-newsgroup-variables))
@@ -3105,6 +3121,9 @@ The following commands are available:
   (setq buffer-read-only t             ;Disable modification
        show-trailing-whitespace nil)
   (setq truncate-lines t)
+  ;; Force paragraph direction to be left-to-right.  Don't make it
+  ;; bound globally in old Emacsen and XEmacsen.
+  (set (make-local-variable 'bidi-paragraph-direction) 'left-to-right)
   (add-to-invisibility-spec '(gnus-sum . t))
   (gnus-summary-set-display-table)
   (gnus-set-default-directory)
@@ -3264,13 +3283,6 @@ The following commands are available:
   "Say whether this article is a sparse article or not."
   `(memq ,article gnus-newsgroup-ancient))
 
-(defun gnus-article-parent-p (number)
-  "Say whether this article is a parent or not."
-  (let ((data (gnus-data-find-list number)))
-    (and (cdr data)              ; There has to be an article after...
-        (< (gnus-data-level (car data)) ; And it has to have a higher level.
-           (gnus-data-level (nth 1 data))))))
-
 (defun gnus-article-children (number)
   "Return a list of all children to NUMBER."
   (let* ((data (gnus-data-find-list number))
@@ -3292,14 +3304,6 @@ The following commands are available:
   "Say whether this article is intangible or not."
   '(get-text-property (point) 'gnus-intangible))
 
-(defun gnus-article-read-p (article)
-  "Say whether ARTICLE is read or not."
-  (not (or (memq article gnus-newsgroup-marked)
-          (memq article gnus-newsgroup-spam-marked)
-          (memq article gnus-newsgroup-unreads)
-          (memq article gnus-newsgroup-unselected)
-          (memq article gnus-newsgroup-dormant))))
-
 ;; Some summary mode macros.
 
 (defmacro gnus-summary-article-number ()
@@ -3507,7 +3511,8 @@ display only a single character."
                                               (current-buffer))))))
 
 (defun gnus-summary-setup-buffer (group)
-  "Initialize summary buffer."
+  "Initialize summary buffer.
+If the setup was successful, non-nil is returned."
   (let ((buffer (gnus-summary-buffer-name group))
        (dead-name (concat "*Dead Summary "
                           (gnus-group-decoded-name group) "*")))
@@ -3519,8 +3524,8 @@ display only a single character."
          (set-buffer buffer)
          (setq gnus-summary-buffer (current-buffer))
          (not gnus-newsgroup-prepared))
-      ;; Fix by Sudish Joseph <joseph@cis.ohio-state.edu>
-      (setq gnus-summary-buffer (set-buffer (gnus-get-buffer-create buffer)))
+      (set-buffer (gnus-get-buffer-create buffer))
+      (setq gnus-summary-buffer (current-buffer))
       (gnus-summary-mode group)
       (when (gnus-group-quit-config group)
        (set (make-local-variable 'gnus-single-article-buffer) nil))
@@ -3536,7 +3541,7 @@ display only a single character."
   "Set the global equivalents of the buffer-local variables.
 They are set to the latest values they had.  These reflect the summary
 buffer that was in action when the last article was fetched."
-  (when (eq major-mode 'gnus-summary-mode)
+  (when (derived-mode-p 'gnus-summary-mode)
     (setq gnus-summary-buffer (current-buffer))
     (let ((name gnus-newsgroup-name)
          (marked gnus-newsgroup-marked)
@@ -3559,7 +3564,7 @@ buffer that was in action when the last article was fetched."
            (push (eval (car locals)) vlist))
          (setq locals (cdr locals)))
        (setq vlist (nreverse vlist)))
-      (with-current-buffer gnus-group-buffer
+      (with-temp-buffer
        (setq gnus-newsgroup-name name
              gnus-newsgroup-marked marked
              gnus-newsgroup-spam-marked spam
@@ -3578,11 +3583,7 @@ buffer that was in action when the last article was fetched."
            (if (consp (car locals))
                (set (caar locals) (pop vlist))
              (set (car locals) (pop vlist)))
-           (setq locals (cdr locals))))
-       ;; The article buffer also has local variables.
-       (when (gnus-buffer-live-p gnus-article-buffer)
-         (set-buffer gnus-article-buffer)
-         (setq gnus-summary-buffer summary))))))
+           (setq locals (cdr locals))))))))
 
 (defun gnus-summary-article-unread-p (article)
   "Say whether ARTICLE is unread or not."
@@ -3684,8 +3685,8 @@ buffer that was in action when the last article was fetched."
 (defun gnus-summary-from-or-to-or-newsgroups (header gnus-tmp-from)
   (let ((mail-parse-charset gnus-newsgroup-charset)
        (ignored-from-addresses (gnus-ignored-from-addresses))
-       ; Is it really necessary to do this next part for each summary line?
-       ; Luckily, doesn't seem to slow things down much.
+       ;; Is it really necessary to do this next part for each summary line?
+       ;; Luckily, doesn't seem to slow things down much.
        (mail-parse-ignored-charsets
         (with-current-buffer gnus-summary-buffer
           gnus-newsgroup-ignored-charsets)))
@@ -3706,13 +3707,13 @@ buffer that was in action when the last article was fetched."
                     (cdr (assq 'Newsgroups extra-headers))
                     (and
                      (memq 'Newsgroups gnus-extra-headers)
-                      (eq (car (gnus-find-method-for-group
-                                gnus-newsgroup-name)) 'nntp)
+                     (eq (car (gnus-find-method-for-group
+                               gnus-newsgroup-name)) 'nntp)
                      (gnus-group-real-name gnus-newsgroup-name))))
              (concat gnus-summary-newsgroup-prefix newsgroups)))))
      (gnus-string-mark-left-to-right
       (inline
-       (gnus-summary-extract-address-component gnus-tmp-from))))))
+       (gnus-summary-extract-address-component gnus-tmp-from))))))
 
 (defun gnus-summary-insert-line (gnus-tmp-header
                                 gnus-tmp-level gnus-tmp-current
@@ -3743,8 +3744,6 @@ buffer that was in action when the last article was fetched."
                 gnus-forwarded-mark)
                ((memq gnus-tmp-current gnus-newsgroup-saved)
                 gnus-saved-mark)
-               ((memq gnus-tmp-number gnus-newsgroup-recent)
-                gnus-recent-mark)
                ((memq gnus-tmp-number gnus-newsgroup-unseen)
                 gnus-unseen-mark)
                (t gnus-no-mark)))
@@ -3937,7 +3936,11 @@ Input should look like this: \"Sun, 14 Oct 2001 13:34:39 +0200\"."
   "Start reading news in newsgroup GROUP.
 If SHOW-ALL is non-nil, already read articles are also listed.
 If NO-ARTICLE is non-nil, no article is selected initially.
-If NO-DISPLAY, don't generate a summary buffer."
+If NO-DISPLAY, don't generate the summary buffer contents.
+If KILL-BUFFER, it should be a buffer that's killed once the new
+summary buffer has been generated.
+If BACKWARD, move point to the previous group in the group buffer
+If SELECT-ARTICLES, only select those articles from GROUP."
   (let (result)
     (while (and group
                (null (setq result
@@ -3967,7 +3970,7 @@ If NO-DISPLAY, don't generate a summary buffer."
   ;;  (when (and (not (gnus-group-native-p group))
   ;;        (not (gnus-gethash group gnus-newsrc-hashtb)))
   ;;    (error "Dead non-native groups can't be entered"))
-  (gnus-message 5 "Retrieving newsgroup: %s..."
+  (gnus-message 7 "Retrieving newsgroup: %s..."
                (gnus-group-decoded-name group))
   (let* ((new-group (gnus-summary-setup-buffer group))
         (quit-config (gnus-group-quit-config group))
@@ -3986,7 +3989,7 @@ If NO-DISPLAY, don't generate a summary buffer."
       t)
      ;; We couldn't select this group.
      ((null did-select)
-      (when (and (eq major-mode 'gnus-summary-mode)
+      (when (and (derived-mode-p 'gnus-summary-mode)
                 (not (equal (current-buffer) kill-buffer)))
        (kill-buffer (current-buffer))
        (if (not quit-config)
@@ -4005,7 +4008,7 @@ If NO-DISPLAY, don't generate a summary buffer."
      ;; The user did a `C-g' while prompting for number of articles,
      ;; so we exit this group.
      ((eq did-select 'quit)
-      (and (eq major-mode 'gnus-summary-mode)
+      (and (derived-mode-p 'gnus-summary-mode)
           (not (equal (current-buffer) kill-buffer))
           (kill-buffer (current-buffer)))
       (when kill-buffer
@@ -4021,6 +4024,8 @@ If NO-DISPLAY, don't generate a summary buffer."
      ;; The group was successfully selected.
      (t
       (gnus-set-global-variables)
+      (when (boundp 'spam-install-hooks)
+       (spam-initialize))
       ;; Save the active value in effect when the group was entered.
       (setq gnus-newsgroup-active
            (gnus-copy-sequence
@@ -4048,7 +4053,7 @@ If NO-DISPLAY, don't generate a summary buffer."
       (unless no-display
        (gnus-summary-prepare))
       (when gnus-use-trees
-       (gnus-tree-open group)
+       (gnus-tree-open)
        (setq gnus-summary-highlight-line-function
              'gnus-tree-highlight-article))
       ;; If the summary buffer is empty, but there are some low-scored
@@ -4087,9 +4092,10 @@ If NO-DISPLAY, don't generate a summary buffer."
                 gnus-auto-select-first)
            (progn
              (let ((art (gnus-summary-article-number)))
-               (unless (and (not gnus-plugged)
-                            (or (memq art gnus-newsgroup-undownloaded)
-                                (memq art gnus-newsgroup-downloadable)))
+               (when (and art
+                          gnus-plugged
+                          (not (memq art gnus-newsgroup-undownloaded))
+                          (not (memq art gnus-newsgroup-downloadable)))
                  (gnus-summary-goto-article art))))
          ;; Don't select any articles.
          (gnus-summary-position-point)
@@ -4263,7 +4269,7 @@ If NO-DISPLAY, don't generate a summary buffer."
     result))
 
 (defun gnus-sort-gathered-threads (threads)
-  "Sort subtreads inside each gathered thread by `gnus-sort-gathered-threads-function'."
+  "Sort subthreads inside each gathered thread by `gnus-sort-gathered-threads-function'."
   (let ((result threads))
     (while threads
       (when (stringp (caar threads))
@@ -4733,7 +4739,7 @@ If LINE, insert the rebuilt thread starting on line LINE."
       (car headers))))
 
 (defun gnus-parent-headers (in-headers &optional generation)
-  "Return the headers of the GENERATIONeth parent of HEADERS."
+  "Return the headers of the GENERATIONth parent of HEADERS."
   (unless generation
     (setq generation 1))
   (let ((parent t)
@@ -4868,10 +4874,25 @@ If LINE, insert the rebuilt thread starting on line LINE."
            (gnus-delete-line)))))))
 
 (defun gnus-sort-threads-recursive (threads func)
+  ;; Responsible for sorting the root articles of threads.
+  (let ((subthread-sort-func (if (eq gnus-subthread-sort-functions
+                                    'gnus-thread-sort-functions)
+                                func
+                              (gnus-make-sort-function
+                               gnus-subthread-sort-functions))))
+    (sort (mapcar (lambda (thread)
+                   (cons (car thread)
+                         (and (cdr thread)
+                              (gnus-sort-subthreads-recursive
+                               (cdr thread) subthread-sort-func))))
+                 threads) func)))
+
+(defun gnus-sort-subthreads-recursive (threads func)
+  ;; Responsible for sorting subthreads.
   (sort (mapcar (lambda (thread)
                  (cons (car thread)
                        (and (cdr thread)
-                            (gnus-sort-threads-recursive (cdr thread) func))))
+                            (gnus-sort-subthreads-recursive (cdr thread) func))))
                threads) func))
 
 (defun gnus-sort-threads-loop (threads func)
@@ -4897,9 +4918,9 @@ If LINE, insert the rebuilt thread starting on line LINE."
     (gnus-message 8 "Sorting threads...")
     (prog1
        (condition-case nil
-           (let ((max-lisp-eval-depth (max max-lisp-eval-depth 5000)))
-             (gnus-sort-threads-recursive
-              threads (gnus-make-sort-function gnus-thread-sort-functions)))
+           (let ((max-lisp-eval-depth (max max-lisp-eval-depth 5000))
+                 (sort-func (gnus-make-sort-function gnus-thread-sort-functions)))
+             (gnus-sort-threads-recursive threads sort-func))
          ;; Even after binding max-lisp-eval-depth, the recursive
          ;; sorter might fail for very long threads.  In that case,
          ;; try using a (less well-tested) non-recursive sorter.
@@ -5395,8 +5416,6 @@ or a straight list of headers."
                    gnus-forwarded-mark)
                   ((memq number gnus-newsgroup-saved)
                    gnus-saved-mark)
-                  ((memq number gnus-newsgroup-recent)
-                   gnus-recent-mark)
                   ((memq number gnus-newsgroup-unseen)
                    gnus-unseen-mark)
                   (t gnus-no-mark))
@@ -5520,6 +5539,8 @@ or a straight list of headers."
         (cdr (assq number gnus-newsgroup-scored))
         (memq number gnus-newsgroup-processable))))))
 
+(declare-function gnus-parameter-list-identifier "gnus-art" (name) t)
+
 (defun gnus-group-get-list-identifiers (group)
   "Get list identifier regexp for GROUP."
   (or (gnus-parameter-list-identifier group)
@@ -5551,7 +5572,7 @@ or a straight list of headers."
 (defun gnus-fetch-headers (articles &optional limit force-new dependencies)
   "Fetch headers of ARTICLES."
   (let ((name (gnus-group-decoded-name gnus-newsgroup-name)))
-    (gnus-message 5 "Fetching headers for %s..." name)
+    (gnus-message 7 "Fetching headers for %s..." name)
     (prog1
        (if (eq 'nov
                (setq gnus-headers-retrieved-by
@@ -5568,7 +5589,7 @@ or a straight list of headers."
            (gnus-get-newsgroup-headers-xover
             articles force-new dependencies gnus-newsgroup-name t)
          (gnus-get-newsgroup-headers dependencies force-new))
-      (gnus-message 5 "Fetching headers for %s...done" name))))
+      (gnus-message 7 "Fetching headers for %s...done" name))))
 
 (defun gnus-select-newsgroup (group &optional read-all select-articles)
   "Select newsgroup GROUP.
@@ -5592,7 +5613,7 @@ If SELECT-ARTICLES, only select those articles from 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)
+         (when (derived-mode-p 'gnus-summary-mode)
            (gnus-kill-buffer (current-buffer)))
          (error
           "Couldn't activate group %s: %s"
@@ -5600,7 +5621,7 @@ If SELECT-ARTICLES, only select those articles from GROUP."
           (mm-decode-coding-string (gnus-status-message group) charset))))
 
     (unless (gnus-request-group group t)
-      (when (equal major-mode 'gnus-summary-mode)
+      (when (derived-mode-p 'gnus-summary-mode)
        (gnus-kill-buffer (current-buffer)))
       (error "Couldn't request group %s: %s"
             (mm-decode-coding-string group charset)
@@ -5684,7 +5705,9 @@ If SELECT-ARTICLES, only select those articles from GROUP."
       ;; Init the dependencies hash table.
       (setq gnus-newsgroup-dependencies
            (gnus-make-hashtable (length articles)))
-      (gnus-set-global-variables)
+      (if (gnus-buffer-live-p gnus-group-buffer)
+         (gnus-set-global-variables)
+       (set-default 'gnus-newsgroup-name gnus-newsgroup-name))
       ;; Retrieve the headers and read them in.
 
       (setq gnus-newsgroup-headers (gnus-fetch-headers articles))
@@ -5807,14 +5830,13 @@ If SELECT-ARTICLES, only select those articles from GROUP."
       (memq article gnus-newsgroup-forwarded))
      ((eq type 'seen)
       (not (memq article gnus-newsgroup-unseen)))
-     ((eq type 'recent)
-      (memq article gnus-newsgroup-recent))
      (t t))))
 
 (defun gnus-articles-to-read (group &optional read-all)
   "Find out what articles the user wants to read."
   (let* ((only-read-p t)
         (articles
+         (gnus-list-range-difference
          ;; Select all articles if `read-all' is non-nil, or if there
          ;; are no unread articles.
          (if (or read-all
@@ -5841,7 +5863,8 @@ If SELECT-ARTICLES, only select those articles from GROUP."
            (setq only-read-p nil)
            (gnus-sorted-nunion
             (gnus-sorted-union gnus-newsgroup-dormant gnus-newsgroup-marked)
-            gnus-newsgroup-unreads)))
+            gnus-newsgroup-unreads))
+         (cdr (assq 'unexist (gnus-info-marks (gnus-get-info group))))))
         (scored-list (gnus-killed-articles gnus-newsgroup-killed articles))
         (scored (length scored-list))
         (number (length articles))
@@ -5869,15 +5892,15 @@ If SELECT-ARTICLES, only select those articles from GROUP."
                          (read-string
                           (if only-read-p
                               (format
-                             "How many articles from %s (available %d, default %d): "
-                             (gnus-group-decoded-name
-                              (gnus-group-real-name gnus-newsgroup-name))
-                             number default)
-                            (format
-                               "How many articles from %s (%d available): "
+                               "How many articles from %s (available %d, default %d): "
                                (gnus-group-decoded-name
                                 (gnus-group-real-name gnus-newsgroup-name))
-                               default))
+                               number default)
+                            (format
+                             "How many articles from %s (%d default): "
+                             (gnus-group-decoded-name
+                              (gnus-group-real-name gnus-newsgroup-name))
+                             default))
                           nil
                           nil
                           (number-to-string default))))
@@ -5930,17 +5953,6 @@ If SELECT-ARTICLES, only select those articles from GROUP."
       (setq articles (cdr articles)))
     out))
 
-(defun gnus-uncompress-marks (marks)
-  "Uncompress the mark ranges in MARKS."
-  (let ((uncompressed '(score bookmark))
-       out)
-    (while marks
-      (if (memq (caar marks) uncompressed)
-         (push (car marks) out)
-       (push (cons (caar marks) (gnus-uncompress-range (cdar marks))) out))
-      (setq marks (cdr marks)))
-    out))
-
 (defun gnus-article-mark-to-type (mark)
   "Return the type of MARK."
   (or (cadr (assq mark gnus-article-special-mark-lists))
@@ -5968,7 +5980,6 @@ If SELECT-ARTICLES, only select those articles from GROUP."
       (setq mark (car marks)
            mark-type (gnus-article-mark-to-type mark)
            var (intern (format "gnus-newsgroup-%s" (car (rassq mark types)))))
-
       ;; We set the variable according to the type of the marks list,
       ;; and then adjust the marks to a subset of the active articles.
       (cond
@@ -6023,7 +6034,9 @@ If SELECT-ARTICLES, only select those articles from GROUP."
                          (and (numberp (car articles))
                               (> min (car articles)))))
            (pop articles))
-         (set var articles))))))))
+         (set var articles))
+        ((eq mark 'unexist)
+         (set var (cdr marks)))))))))
 
 (defun gnus-update-missing-marks (missing)
   "Go through the list of MISSING articles and remove them from the mark lists."
@@ -6099,7 +6112,8 @@ If SELECT-ARTICLES, only select those articles from GROUP."
                         (gnus-active gnus-newsgroup-name) del))
              (push (list del 'del (list (cdr type))) delta-marks))))
 
-       (when list
+       (when (or list
+                 (eq (cdr type) 'unexist))
          (push (cons (cdr type) list) newmarked)))
 
       (when delta-marks
@@ -6287,13 +6301,18 @@ The resulting hash table is returned, or nil if no Xrefs were found."
         (entry (gnus-group-entry group))
         (info (nth 2 entry))
         (active (gnus-active group))
+        (set-marks
+         (gnus-method-option-p
+          (gnus-find-method-for-group group)
+          'server-marks))
         range)
     (if (not entry)
        ;; Group that Gnus doesn't know exists, but still allow the
        ;; backend to set marks.
-       (gnus-request-set-mark
-        group (list (list (gnus-compress-sequence (sort articles #'<))
-                          'add '(read))))
+       (when set-marks
+         (gnus-request-set-mark
+          group (list (list (gnus-compress-sequence (sort articles #'<))
+                            'add '(read)))))
       ;; Normal, subscribed groups.
       (setq range (gnus-compute-read-articles group articles))
       (with-current-buffer gnus-group-buffer
@@ -6302,11 +6321,14 @@ The resulting hash table is returned, or nil if no Xrefs were found."
             (gnus-info-set-marks ',info ',(gnus-info-marks info) t)
             (gnus-info-set-read ',info ',(gnus-info-read info))
             (gnus-get-unread-articles-in-group ',info (gnus-active ,group))
-            (gnus-request-set-mark ,group (list (list ',range 'del '(read))))
+            (when ,set-marks
+              (gnus-request-set-mark
+               ,group (list (list ',range 'del '(read)))))
             (gnus-group-update-group ,group t))))
       ;; Add the read articles to the range.
       (gnus-info-set-read info range)
-      (gnus-request-set-mark group (list (list range 'add '(read))))
+      (when set-marks
+       (gnus-request-set-mark group (list (list range 'add '(read)))))
       ;; Then we have to re-compute how many unread
       ;; articles there are in this group.
       (when active
@@ -6619,9 +6641,9 @@ too, instead of trying to fetch new headers."
       ;; article if ID is a number -- so that the next `P' or `N'
       ;; command will fetch the previous (or next) article even
       ;; if the one we tried to fetch this time has been canceled.
-      (when (> number gnus-newsgroup-end)
+      (unless (and gnus-newsgroup-end (< number gnus-newsgroup-end))
        (setq gnus-newsgroup-end number))
-      (when (< number gnus-newsgroup-begin)
+      (unless (and gnus-newsgroup-begin (> number gnus-newsgroup-begin))
        (setq gnus-newsgroup-begin number))
       (setq gnus-newsgroup-unselected
            (delq number gnus-newsgroup-unselected)))
@@ -7097,7 +7119,7 @@ With ARG, turn line truncation on if ARG is positive."
 (defun gnus-summary-find-for-reselect ()
   "Return the number of an article to stay on across a reselect.
 The current article is considered, then following articles, then previous
-articles.  An article is sought which is not cancelled and isn't a temporary
+articles.  An article is sought which is not canceled and isn't a temporary
 insertion from another group.  If there's no such then return a dummy 0."
   (let (found)
     (dolist (rev '(nil t))
@@ -7196,6 +7218,7 @@ If FORCE (the prefix), also save the .newsrc file(s)."
     (gnus-dribble-save)))
 
 (declare-function gnus-cache-write-active "gnus-cache" (&optional force))
+(declare-function gnus-article-stop-animations "gnus-art" ())
 
 (defun gnus-summary-exit (&optional temporary leave-hidden)
   "Exit reading current newsgroup, and then return to group selection mode.
@@ -7236,7 +7259,7 @@ If FORCE (the prefix), also save the .newsrc file(s)."
     (when gnus-suppress-duplicates
       (gnus-dup-enter-articles))
     (when gnus-use-trees
-      (gnus-tree-close group))
+      (gnus-tree-close))
     (when gnus-use-cache
       (gnus-cache-write-active))
     ;; Remove entries for this group.
@@ -7247,7 +7270,8 @@ If FORCE (the prefix), also save the .newsrc file(s)."
       (gnus-summary-update-info))
     (gnus-close-group group)
     ;; Make sure where we were, and go to next newsgroup.
-    (set-buffer gnus-group-buffer)
+    (when (buffer-live-p (get-buffer gnus-group-buffer))
+      (set-buffer gnus-group-buffer))
     (unless quit-config
       (gnus-group-jump-to-group group))
     (gnus-run-hooks 'gnus-summary-exit-hook)
@@ -7258,6 +7282,7 @@ If FORCE (the prefix), also save the .newsrc file(s)."
                (not (string= group (gnus-group-group-name))))
       (gnus-group-next-unread-group 1))
     (setq group-point (point))
+    (gnus-article-stop-animations)
     (if temporary
        nil                             ;Nothing to do.
       (set-buffer buf)
@@ -7272,7 +7297,8 @@ If FORCE (the prefix), also save the .newsrc file(s)."
          (gnus-kill-buffer buf)))
 
       (setq gnus-current-select-method gnus-select-method)
-      (set-buffer gnus-group-buffer)
+      (when (gnus-buffer-live-p gnus-group-buffer)
+       (set-buffer gnus-group-buffer))
       (if quit-config
          (gnus-handle-ephemeral-exit quit-config)
        (goto-char group-point)
@@ -7297,6 +7323,8 @@ If FORCE (the prefix), also save the .newsrc file(s)."
       (unless quit-config
        (setq gnus-newsgroup-name nil)))))
 
+(declare-function gnus-stop-downloads "gnus-art" ())
+
 (defalias 'gnus-summary-quit 'gnus-summary-exit-no-update)
 (defun gnus-summary-exit-no-update (&optional no-questions)
   "Quit reading current newsgroup without updating read article info."
@@ -7313,6 +7341,7 @@ If FORCE (the prefix), also save the .newsrc file(s)."
       (when (gnus-buffer-live-p gnus-article-buffer)
        (with-current-buffer gnus-article-buffer
          (gnus-article-stop-animations)
+         (gnus-stop-downloads)
          (mm-destroy-parts gnus-article-mime-handles)
          ;; Set it to nil for safety reason.
          (setq gnus-article-mime-handle-alist nil)
@@ -7323,15 +7352,17 @@ If FORCE (the prefix), also save the .newsrc file(s)."
        (gnus-kill-buffer gnus-original-article-buffer)
        (setq gnus-article-current nil))
       ;; Return to the group buffer.
-      (gnus-configure-windows 'group 'force)
       (if (not gnus-kill-summary-on-exit)
-         (gnus-deaden-summary)
+         (progn
+           (gnus-deaden-summary)
+           (gnus-configure-windows 'group 'force))
+       (gnus-configure-windows 'group 'force)
        (gnus-close-group group)
        (gnus-kill-buffer gnus-summary-buffer))
       (unless gnus-single-article-buffer
        (setq gnus-article-current nil))
       (when gnus-use-trees
-       (gnus-tree-close group))
+       (gnus-tree-close))
       (gnus-async-prefetch-remove-group group)
       (when (get-buffer gnus-article-buffer)
        (bury-buffer gnus-article-buffer))
@@ -7341,21 +7372,23 @@ If FORCE (the prefix), also save the .newsrc file(s)."
        (gnus-group-update-group group nil t))
       (when (equal (gnus-group-group-name) group)
        (gnus-group-next-unread-group 1))
+      (gnus-article-stop-animations)
       (when quit-config
        (gnus-handle-ephemeral-exit quit-config)))))
 
 (defun gnus-handle-ephemeral-exit (quit-config)
   "Handle movement when leaving an ephemeral group.
 The state which existed when entering the ephemeral is reset."
-  (if (not (buffer-name (car quit-config)))
-      (gnus-configure-windows 'group 'force)
+  (if (not (buffer-live-p (car quit-config)))
+      (when (gnus-buffer-live-p gnus-group-buffer)
+       (gnus-configure-windows 'group 'force))
     (set-buffer (car quit-config))
     (unless (eq (cdr quit-config) 'group)
       (setq gnus-current-select-method
            (gnus-find-method-for-group gnus-newsgroup-name)))
-    (cond ((eq major-mode 'gnus-summary-mode)
+    (cond ((derived-mode-p 'gnus-summary-mode)
           (gnus-set-global-variables))
-         ((eq major-mode 'gnus-article-mode)
+         ((derived-mode-p 'gnus-article-mode)
           (save-current-buffer
             ;; The `gnus-summary-buffer' variable may point
             ;; to the old summary buffer when using a single
@@ -7370,7 +7403,7 @@ The state which existed when entering the ephemeral is reset."
            (gnus-configure-windows 'pick 'force)
          (gnus-configure-windows (cdr quit-config) 'force))
       (gnus-configure-windows (cdr quit-config) 'force))
-    (when (eq major-mode 'gnus-summary-mode)
+    (when (derived-mode-p 'gnus-summary-mode)
       (if (memq gnus-auto-select-on-ephemeral-exit '(next-noselect
                                                     next-unread-noselect))
          (when (zerop (cond ((eq gnus-auto-select-on-ephemeral-exit
@@ -7440,7 +7473,7 @@ The state which existed when entering the ephemeral is reset."
       (when (and gnus-use-trees
                 (gnus-buffer-exists-p buffer))
        (with-current-buffer buffer
-         (gnus-tree-close gnus-newsgroup-name)))
+         (gnus-tree-close)))
       (gnus-kill-buffer buffer))
      ;; Deaden the buffer.
      ((gnus-buffer-exists-p buffer)
@@ -7669,7 +7702,7 @@ Given a prefix, will force an `article' buffer configuration."
   "Display ARTICLE in article buffer."
   (unless (and (gnus-buffer-live-p gnus-article-buffer)
               (with-current-buffer gnus-article-buffer
-                (eq major-mode 'gnus-article-mode)))
+                (derived-mode-p 'gnus-article-mode)))
     (gnus-article-setup-buffer))
   (gnus-set-global-variables)
   (with-current-buffer gnus-article-buffer
@@ -7701,7 +7734,7 @@ non-nil, the article will be re-fetched even if it already present in
 the article buffer.  If PSEUDO is non-nil, pseudo-articles will also
 be displayed."
   ;; Make sure we are in the summary buffer to work around bbdb bug.
-  (unless (eq major-mode 'gnus-summary-mode)
+  (unless (derived-mode-p 'gnus-summary-mode)
     (set-buffer gnus-summary-buffer))
   (let ((article (or article (gnus-summary-article-number)))
        (all-headers (not (not all-headers))) ;Must be t or nil.
@@ -7746,10 +7779,6 @@ be displayed."
                                            gnus-buttonized-mime-types)))
     (gnus-summary-select-article nil 'force)))
 
-(defun gnus-summary-set-current-mark (&optional current-mark)
-  "Obsolete function."
-  nil)
-
 (defun gnus-summary-next-article (&optional unread subject backward push)
   "Select the next article.
 If UNREAD, only unread articles are selected.
@@ -7757,7 +7786,7 @@ If SUBJECT, only articles with SUBJECT are selected.
 If BACKWARD, the previous article is selected instead of the next."
   (interactive "P")
   ;; Make sure we are in the summary buffer.
-  (unless (eq major-mode 'gnus-summary-mode)
+  (unless (derived-mode-p 'gnus-summary-mode)
     (set-buffer gnus-summary-buffer))
   (cond
    ;; Is there such an article?
@@ -7889,6 +7918,8 @@ If UNREAD is non-nil, only unread articles are selected."
    (and gnus-auto-select-same
        (gnus-summary-article-subject))))
 
+(declare-function gnus-article-only-boring-p "gnus-art" ())
+
 (defun gnus-summary-next-page (&optional lines circular stop)
   "Show next page of the selected article.
 If at the end of the current article, select the next article.
@@ -7902,7 +7933,6 @@ If STOP is non-nil, just stop when reaching the end of the message.
 
 Also see the variable `gnus-article-skip-boring'."
   (interactive "P")
-  (setq gnus-summary-buffer (current-buffer))
   (gnus-set-global-variables)
   (let ((article (gnus-summary-article-number))
        (article-window (get-buffer-window gnus-article-buffer t))
@@ -8223,9 +8253,17 @@ If NOT-MATCHING, excluding articles that have subjects that match a regexp."
   "Limit the summary buffer to articles that have authors that match a regexp.
 If NOT-MATCHING, excluding articles that have authors that match a regexp."
   (interactive
-   (list (read-string (if current-prefix-arg
-                         "Exclude author (regexp): "
-                       "Limit to author (regexp): "))
+   (list (let* ((header (gnus-summary-article-header))
+               (default (and header (car (mail-header-parse-address
+                                          (mail-header-from header))))))
+          (read-string (concat (if current-prefix-arg
+                                   "Exclude author (regexp"
+                                 "Limit to author (regexp")
+                               (if default
+                                   (concat ", default \"" default "\"): ")
+                                 "): "))
+                       nil nil
+                       default))
         current-prefix-arg))
   (gnus-summary-limit-to-subject from "from" not-matching))
 
@@ -8449,6 +8487,8 @@ If REVERSE (the prefix), limit to articles that don't match."
   (interactive "sMatch headers (regexp): \nP")
   (gnus-summary-limit-to-bodies match reverse t))
 
+(declare-function article-goto-body "gnus-art" ())
+
 (defun gnus-summary-limit-to-bodies (match &optional reverse headersp)
   "Limit the summary buffer to articles that have bodies that match MATCH.
 If REVERSE (the prefix), limit to articles that don't match."
@@ -9042,7 +9082,43 @@ non-numeric or nil fetch the number specified by the
               'gnus-article-sort-by-number)))
       (setq gnus-newsgroup-articles
            (gnus-sorted-nunion gnus-newsgroup-articles (nreverse article-ids)))
-      (gnus-summary-limit-include-thread id))))
+      (gnus-summary-limit-include-thread id)))
+  (gnus-summary-show-thread))
+
+(defun gnus-summary-open-group-with-article (message-id)
+  "Open a group containing the article with the given MESSAGE-ID."
+  (interactive "sMessage-ID: ")
+  (require 'nndoc)
+  (with-temp-buffer
+    ;; Prepare a dummy article
+    (erase-buffer)
+    (insert "From nobody Tue Sep 13 22:05:34 2011\n\n")
+
+    ;; Prepare pretty modelines for summary and article buffers
+    (let ((gnus-summary-mode-line-format "Found %G")
+          (gnus-article-mode-line-format
+           ;; Group names just get in the way here, especially the
+           ;; abbreviated ones
+           (if (string-match "%[gG]" gnus-article-mode-line-format)
+              (concat (substring gnus-article-mode-line-format
+                                 0 (match-beginning 0))
+                      (substring gnus-article-mode-line-format (match-end 0)))
+            gnus-article-mode-line-format)))
+
+      ;; Build an ephemeral group containing the dummy article (hidden)
+      (gnus-group-read-ephemeral-group
+       message-id
+       `(nndoc ,message-id
+              (nndoc-address ,(current-buffer))
+              (nndoc-article-type mbox))
+       :activate
+       (cons (current-buffer) gnus-current-window-configuration)
+       (not :request-only)
+       '(-1)                           ; :select-articles
+       (not :parameters)
+       0))                             ; :number
+    ;; Fetch the desired article
+    (gnus-summary-refer-article message-id)))
 
 (defun gnus-summary-refer-article (message-id)
   "Fetch an article specified by MESSAGE-ID."
@@ -9156,7 +9232,7 @@ To control what happens when you exit the group, see the
                           (list (cons 'save-article-group ogroup))))
           (case-fold-search t)
           (buf (current-buffer))
-          dig to-address)
+          dig to-address charset)
       (with-current-buffer gnus-original-article-buffer
        ;; Have the digest group inherit the main mail address of
        ;; the parent article.
@@ -9164,21 +9240,38 @@ To control what happens when you exit the group, see the
                                   (gnus-fetch-field "from")))
          (setq params
                (append
+                params
                 (list (cons 'to-address
                             (funcall gnus-decode-encoded-address-function
                                      to-address))))))
        (setq dig (nnheader-set-temp-buffer " *gnus digest buffer*"))
        (insert-buffer-substring gnus-original-article-buffer)
-       ;; Remove lines that may lead nndoc to misinterpret the
-       ;; document type.
        (narrow-to-region
         (goto-char (point-min))
         (or (search-forward "\n\n" nil t) (point)))
+       ;; Remove lines that may lead nndoc to misinterpret the
+       ;; document type.
        (goto-char (point-min))
        (delete-matching-lines "^Path:\\|^From ")
+       ;; Parse charset, and decode content transfer encoding.
+       (setq charset (mail-content-type-get
+                      (mail-header-parse-content-type
+                       (or (gnus-fetch-field "content-type") ""))
+                      'charset))
+       (let ((encoding (gnus-fetch-field "content-transfer-encoding")))
+         (when encoding
+           (message-remove-header "content-transfer-encoding")
+           (goto-char (point-max))
+           (widen)
+           (narrow-to-region (point) (point-max))
+           (mm-decode-content-transfer-encoding
+            (intern (downcase (mail-header-strip encoding))))))
        (widen))
       (unwind-protect
-         (if (let ((gnus-newsgroup-ephemeral-charset gnus-newsgroup-charset)
+         (if (let ((gnus-newsgroup-ephemeral-charset
+                    (if charset
+                        (intern (downcase (gnus-strip-whitespace charset)))
+                      gnus-newsgroup-charset))
                    (gnus-newsgroup-ephemeral-ignored-charsets
                     gnus-newsgroup-ignored-charsets))
                (gnus-group-read-ephemeral-group
@@ -9256,6 +9349,17 @@ With optional ARG, move across that many fields."
   (select-window (gnus-get-buffer-window gnus-article-buffer))
   (widget-forward arg))
 
+(defun gnus-summary-widget-backward (arg)
+  "Move point to the previous field or button in the article.
+With optional ARG, move across that many fields."
+  (interactive "p")
+  (gnus-summary-select-article)
+  (gnus-configure-windows 'article)
+  (select-window (gnus-get-buffer-window gnus-article-buffer))
+  (unless (widget-at (point))
+    (goto-char (point-max)))
+  (widget-backward arg))
+
 (defun gnus-summary-isearch-article (&optional regexp-p)
   "Do incremental search forward on the current article.
 If REGEXP-P (the prefix) is non-nil, do regexp isearch."
@@ -9551,6 +9655,8 @@ to save in."
          (ps-spool-buffer-with-faces)
        (ps-spool-buffer)))))
 
+(declare-function gnus-flush-original-article-buffer "gnus-art" ())
+
 (defun gnus-summary-show-complete-article ()
   "Show a complete version of the current article.
 This is only useful if you're looking at a partial version of the
@@ -9622,7 +9728,12 @@ C-u g', show the raw article."
          (gnus-summary-update-secondary-mark (cdr gnus-article-current))))))
    ((not arg)
     ;; Select the article the normal way.
-    (gnus-summary-select-article nil 'force))
+    (if (eq mm-text-html-renderer 'shr)
+       (progn
+         (require 'shr)
+         (let ((shr-ignore-cache t))
+           (gnus-summary-select-article nil 'force)))
+      (gnus-summary-select-article nil 'force)))
    ((equal arg '(16))
     ;; C-u C-u g
     (let ((gnus-inhibit-article-treatments t))
@@ -9642,6 +9753,7 @@ C-u g', show the raw article."
       (when (gnus-buffer-live-p gnus-article-buffer)
        (with-current-buffer gnus-article-buffer
          (gnus-article-stop-animations)
+         (gnus-stop-downloads)
          (mm-destroy-parts gnus-article-mime-handles)
          ;; Set it to nil for safety reason.
          (setq gnus-article-mime-handle-alist nil)
@@ -9668,6 +9780,12 @@ If ARG is a negative number, turn header display off."
               t)))
   (gnus-summary-show-article))
 
+(declare-function article-narrow-to-head "gnus-art" ())
+(declare-function gnus-article-hidden-text-p "gnus-art" (type))
+(declare-function gnus-delete-wash-type "gnus-art" (type))
+(declare-function gnus-mime-buttonize-attachments-in-header
+                 "gnus-art" (&optional interactive))
+
 (defun gnus-summary-toggle-header (&optional arg)
   "Show the headers if they are hidden, or hide them if they are shown.
 If ARG is a positive number, show the entire header.
@@ -9698,7 +9816,10 @@ If ARG is a negative number, hide the unwanted header lines."
                  (gnus-treat-hide-boring-headers nil))
              (gnus-delete-wash-type 'headers)
              (gnus-treat-article 'head))
-         (gnus-treat-article 'head))
+         (gnus-treat-article 'head)
+         ;; Add attachment buttons to the header.
+         (when gnus-mime-display-attachment-buttons-in-header
+           (gnus-mime-buttonize-attachments-in-header)))
        (widen)
        (if window
            (set-window-start window (goto-char (point-min))))
@@ -9777,7 +9898,7 @@ installed for this command to work."
            (when (message-goto-body)
              (gnus-narrow-to-body))
            (goto-char (point-min))
-           (while (search-forward "·" (point-max) t)
+           (while (search-forward "·" (point-max) t)
              (replace-match "."))
            (unmorse-region (point-min) (point-max))
            (widen)
@@ -10059,7 +10180,10 @@ ACTION can be either `move' (the default), `crosspost' or `copy'."
                  (gnus-add-marked-articles
                   to-group 'expire (list to-article) info))
 
-               (when to-marks
+               (when (and to-marks
+                          (gnus-method-option-p
+                           (gnus-find-method-for-group to-group)
+                           'server-marks))
                  (gnus-request-set-mark
                   to-group (list (list (list to-article) 'add to-marks)))))
 
@@ -10110,17 +10234,20 @@ ACTION can be either `move' (the default), `crosspost' or `copy'."
 
 (defun gnus-summary-push-marks-to-backend (article)
   (let ((set nil)
+       (del nil)
        (marks gnus-article-mark-lists))
     (unless (memq article gnus-newsgroup-unreads)
       (push 'read set))
     (while marks
-      (when (and (eq (gnus-article-mark-to-type (cdar marks)) 'list)
-                (memq article (symbol-value
-                               (intern (format "gnus-newsgroup-%s"
-                                               (caar marks))))))
-       (push (cdar marks) set))
+      (if (and (eq (gnus-article-mark-to-type (cdar marks)) 'list)
+              (memq article (symbol-value
+                             (intern (format "gnus-newsgroup-%s"
+                                             (caar marks))))))
+         (push (cdar marks) set)
+       (push (cdar marks) del))
       (pop marks))
-    (gnus-request-set-mark gnus-newsgroup-name `(((,article) set ,set)))))
+    (gnus-request-set-mark gnus-newsgroup-name `(((,article) set ,set)
+                                                ((,article) del ,del)))))
 
 (defun gnus-summary-copy-article (&optional n to-newsgroup select-method)
   "Copy the current article to some other group.
@@ -10273,16 +10400,19 @@ This will be the case if the article has both been mailed and posted."
              'request-expire-articles gnus-newsgroup-name))
     ;; This backend supports expiry.
     (let* ((total (gnus-group-total-expirable-p gnus-newsgroup-name))
-          (expirable (if total
-                         (progn
-                           ;; We need to update the info for
-                           ;; this group for `gnus-list-of-read-articles'
-                           ;; to give us the right answer.
-                           (gnus-run-hooks 'gnus-exit-group-hook)
-                           (gnus-summary-update-info)
-                           (gnus-list-of-read-articles gnus-newsgroup-name))
-                       (setq gnus-newsgroup-expirable
-                             (sort gnus-newsgroup-expirable '<))))
+          (expirable
+           (gnus-list-range-difference
+            (if total
+                (progn
+                  ;; We need to update the info for
+                  ;; this group for `gnus-list-of-read-articles'
+                  ;; to give us the right answer.
+                  (gnus-run-hooks 'gnus-exit-group-hook)
+                  (gnus-summary-update-info)
+                  (gnus-list-of-read-articles gnus-newsgroup-name))
+              (setq gnus-newsgroup-expirable
+                    (sort gnus-newsgroup-expirable '<)))
+            gnus-newsgroup-unexist))
           (expiry-wait (if now 'immediate
                          (gnus-group-find-parameter
                           gnus-newsgroup-name 'expiry-wait)))
@@ -10571,13 +10701,31 @@ groups."
 
 ;;; Respooling
 
+(defvar nnimap-split-fancy)
+(defvar nnimap-split-methods)
+
 (defun gnus-summary-respool-query (&optional silent trace)
   "Query where the respool algorithm would put this article."
   (interactive)
   (let (gnus-mark-article-hook)
     (gnus-summary-select-article)
     (with-current-buffer gnus-original-article-buffer
-      (let ((groups (nnmail-article-group 'identity trace)))
+      (let ((groups
+            (if (eq (car (gnus-find-method-for-group gnus-newsgroup-name))
+                    'nnimap)
+                ;; nnimap has its own splitting variables.
+                (let ((nnmail-split-methods
+                       (cond
+                        ((eq nnimap-split-methods 'default)
+                         nnmail-split-methods)
+                        (nnimap-split-methods
+                         nnimap-split-methods)
+                        (nnimap-split-fancy
+                         'nnmail-split-fancy)))
+                      (nnmail-split-fancy (or nnimap-split-fancy
+                                              nnmail-split-fancy)))
+                  (nnmail-article-group 'identity trace))
+              (nnmail-article-group 'identity trace))))
        (unless silent
          (if groups
              (message "This message would go to %s"
@@ -10891,6 +11039,7 @@ If NO-EXPIRE, auto-expiry will be inhibited."
        (setq gnus-newsgroup-dormant (delq article gnus-newsgroup-dormant))
        (setq gnus-newsgroup-expirable (delq article gnus-newsgroup-expirable))
        (setq gnus-newsgroup-reads (delq article gnus-newsgroup-reads))
+       (setq gnus-newsgroup-unreads (delq article gnus-newsgroup-unreads))
        (cond ((= mark gnus-ticked-mark)
               (setq gnus-newsgroup-marked
                     (gnus-add-to-sorted-list gnus-newsgroup-marked
@@ -10985,8 +11134,6 @@ If NO-EXPIRE, auto-expiry will be inhibited."
          gnus-forwarded-mark)
         ((memq article gnus-newsgroup-saved)
          gnus-saved-mark)
-        ((memq article gnus-newsgroup-recent)
-         gnus-recent-mark)
         ((memq article gnus-newsgroup-unseen)
          gnus-unseen-mark)
         (t gnus-no-mark))
@@ -11565,6 +11712,7 @@ Returns nil if no thread was there to be shown."
         (beg (progn (beginning-of-line) (if (bobp) (point) (1- (point)))))
         (eoi (when end
                (if (fboundp 'next-single-char-property-change)
+                   ;; Note: XEmacs version of n-s-c-p-c may return nil
                    (or (next-single-char-property-change end 'invisible)
                        (point-max))
                  (while (progn
@@ -11630,10 +11778,10 @@ If PREDICATE is supplied, threads that satisfy this predicate
 will not be hidden.
 Returns nil if no threads were there to be hidden."
   (interactive)
+  (beginning-of-line)
   (let ((start (point))
        (starteol (line-end-position))
        (article (gnus-summary-article-number)))
-    (goto-char start)
     ;; Go forward until either the buffer ends or the subthread ends.
     (when (and (not (eobp))
               (or (zerop (gnus-summary-next-thread 1 t))
@@ -11923,7 +12071,7 @@ will not be marked as saved."
          ;; This is a pseudo-article.
          (if (assq 'name header)
              (gnus-copy-file (cdr (assq 'name header)))
-           (gnus-message 1 "Article %d is unsaveable" article))
+           (gnus-message 1 "Article %d is unsavable" article))
        ;; This is a real article.
        (save-window-excursion
          (gnus-summary-select-article decode decode nil article)
@@ -11945,6 +12093,8 @@ will not be marked as saved."
     (gnus-set-mode-line 'summary)
     n))
 
+(declare-function gnus-summary-save-in-pipe "gnus-art" (&optional command raw))
+
 (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.
@@ -12394,6 +12544,15 @@ If REVERSE, save parts that do not match TYPE."
                (not (setq header (car (gnus-get-newsgroup-headers nil t)))))
            ()                          ; Malformed head.
          (unless (gnus-summary-article-sparse-p (mail-header-number header))
+            (when (and (bound-and-true-p gnus-registry-enabled)
+                       (not (gnus-ephemeral-group-p (car where))))
+              (gnus-registry-handle-action
+               (mail-header-id header) nil
+               (gnus-group-prefixed-name
+               (car where)
+               (or gnus-override-method (gnus-find-method-for-group group)))
+               (mail-header-subject header)
+               (mail-header-from header)))
            (when (and (stringp id)
                       (or
                        (not (string= (gnus-group-real-name group)
@@ -12497,7 +12656,7 @@ If REVERSE, save parts that do not match TYPE."
                         (memq article gnus-newsgroup-undownloaded)
                         (not (memq article gnus-newsgroup-cached)))))
     (let ((face (funcall (gnus-summary-highlight-line-0))))
-      (unless (eq face (get-text-property beg 'face))
+      (unless (eq face (gnus-get-text-property-excluding-characters-with-faces beg 'face))
        (gnus-put-text-property-excluding-characters-with-faces
         beg (point-at-eol) 'face
         (setq face (if (boundp face) (symbol-value face) face)))
@@ -12541,10 +12700,9 @@ UNREAD is a sorted list."
        (save-excursion
          (let (setmarkundo)
            ;; Propagate the read marks to the backend.
-           (when (and (or gnus-propagate-marks
-                          (gnus-method-option-p
-                           (gnus-find-method-for-group group)
-                           'server-marks))
+           (when (and (gnus-method-option-p
+                       (gnus-find-method-for-group group)
+                       'server-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))))
@@ -12584,7 +12742,7 @@ UNREAD is a sorted list."
                 (string-match "Summary" buffer)
                 (with-current-buffer buffer
                   ;; We check that this is, indeed, a summary buffer.
-                  (and (eq major-mode 'gnus-summary-mode)
+                  (and (derived-mode-p 'gnus-summary-mode)
                        ;; Also make sure this isn't bogus.
                        gnus-newsgroup-prepared
                        ;; Also make sure that this isn't a
@@ -12594,12 +12752,16 @@ UNREAD is a sorted list."
     ;; Go through all these summary buffers and offer to save them.
     (when buffers
       (save-excursion
-       (map-y-or-n-p
-        "Update summary buffer %s? "
-        (lambda (buf)
-          (switch-to-buffer buf)
-          (gnus-summary-exit))
-        buffers)))))
+       (if (eq gnus-interactive-exit 'quiet)
+           (dolist (buffer buffers)
+             (switch-to-buffer buffer)
+             (gnus-summary-exit))
+         (map-y-or-n-p
+          "Update summary buffer %s? "
+          (lambda (buf)
+            (switch-to-buffer buf)
+            (gnus-summary-exit))
+          buffers))))))
 
 (defun gnus-summary-setup-default-charset ()
   "Setup newsgroup default charset."
@@ -12715,7 +12877,7 @@ returned."
 
 (defun gnus-summary-generic-mark (n mark move unread)
   "Mark N articles with MARK."
-  (unless (eq major-mode 'gnus-summary-mode)
+  (unless (derived-mode-p 'gnus-summary-mode)
     (error "This command can only be used in the summary buffer"))
   (gnus-summary-show-thread)
   (let ((nummove
@@ -12748,7 +12910,7 @@ returned."
     (setq gnus-newsgroup-headers
          (gnus-merge 'list
                      gnus-newsgroup-headers
-                     (gnus-fetch-headers articles)
+                     (gnus-fetch-headers articles nil t)
                      'gnus-article-sort-by-number))
     (setq gnus-newsgroup-articles
          (gnus-sorted-nunion gnus-newsgroup-articles articles))
@@ -12801,7 +12963,9 @@ If ALL is a number, fetch this number of articles."
              ;; Some nntp servers lie about their active range.  When
              ;; this happens, the active range can be in the millions.
              ;; Use a compressed range to avoid creating a huge list.
-             (gnus-range-difference (list gnus-newsgroup-active) old))
+             (gnus-range-difference
+              (gnus-range-difference (list gnus-newsgroup-active) old)
+              gnus-newsgroup-unexist))
        (setq len (gnus-range-length older))
        (cond
         ((null older) nil)
@@ -12835,9 +12999,9 @@ If ALL is a number, fetch this number of articles."
                        (gnus-group-decoded-name gnus-newsgroup-name)
                        (if initial "max" "default")
                        len)
-                      (if initial
-                          (cons (number-to-string initial)
-                                0)))))
+                      nil nil
+                      (and initial
+                           (number-to-string initial)))))
                (unless (string-match "^[ \t]*$" input)
                  (setq all (string-to-number input))
                  (if (< all len)
@@ -12885,6 +13049,7 @@ If ALL is a number, fetch this number of articles."
   (gnus-summary-position-point))
 
 ;;; Bookmark support for Gnus.
+(declare-function gnus-article-show-summary "gnus-art" ())
 (declare-function bookmark-make-record-default
                   "bookmark" (&optional no-file no-context posn))
 (declare-function bookmark-prop-get "bookmark" (bookmark prop))
@@ -12951,7 +13116,7 @@ BOOKMARK is a bookmark name or a bookmark record."
 (run-hooks 'gnus-sum-load-hook)
 
 ;; Local Variables:
-;; coding: iso-8859-1
+;; coding: utf-8
 ;; End:
 
 ;;; gnus-sum.el ends here