(gnus-summary-save-in-pipe): Add optional argument `raw'.
[gnus] / lisp / gnus-art.el
index 674988f..16e65a6 100644 (file)
@@ -1,17 +1,17 @@
 ;;; gnus-art.el --- article mode commands for Gnus
 
 ;; Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
 ;;; gnus-art.el --- article 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.
 
 
 ;; 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
 ;; 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 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
 ;; 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:
 
 
 ;;; Commentary:
 
 ;;; Code:
 
+;; For Emacs < 22.2.
+(eval-and-compile
+  (unless (fboundp 'declare-function) (defmacro declare-function (&rest r))))
 (eval-when-compile
 (eval-when-compile
-  (require 'cl)
-  (defvar tool-bar-map)
-  (defvar w3m-minor-mode-map))
+  (require 'cl))
+(defvar tool-bar-map)
+(defvar w3m-minor-mode-map)
 
 (require 'gnus)
 ;; Avoid the "Recursive load suspected" error in Emacs 21.1.
 
 (require 'gnus)
 ;; Avoid the "Recursive load suspected" error in Emacs 21.1.
@@ -175,12 +176,15 @@ If `gnus-visible-headers' is non-nil, this variable will be ignored."
   "*All headers that do not match this regexp will be hidden.
 This variable can also be a list of regexp of headers to remain visible.
 If this variable is non-nil, `gnus-ignored-headers' will be ignored."
   "*All headers that do not match this regexp will be hidden.
 This variable can also be a list of regexp of headers to remain visible.
 If this variable is non-nil, `gnus-ignored-headers' will be ignored."
-  :type '(repeat :value-to-internal (lambda (widget value)
-                                     (custom-split-regexp-maybe value))
-                :match (lambda (widget value)
-                         (or (stringp value)
-                             (widget-editable-list-match widget value)))
-                regexp)
+  :type '(choice
+         (repeat :value-to-internal (lambda (widget value)
+                                      (custom-split-regexp-maybe value))
+                 :match (lambda (widget value)
+                          (or (stringp value)
+                              (widget-editable-list-match widget value)))
+                 regexp)
+         (const :tag "Use gnus-ignored-headers" nil)
+         regexp)
   :group 'gnus-article-hiding)
 
 (defcustom gnus-sorted-header-list
   :group 'gnus-article-hiding)
 
 (defcustom gnus-sorted-header-list
@@ -548,13 +552,15 @@ Gnus provides the following functions:
 * gnus-summary-save-in-vm (use VM's folder format)
 * gnus-summary-write-to-file (article format -- overwrite)
 * gnus-summary-write-body-to-file (article body -- overwrite)
 * gnus-summary-save-in-vm (use VM's folder format)
 * gnus-summary-write-to-file (article format -- overwrite)
 * gnus-summary-write-body-to-file (article body -- overwrite)
+* gnus-summary-save-in-pipe (article format)
 
 The symbol of each function may have the following properties:
 
 * :decode
 The value non-nil means save decoded articles.  This is meaningful
 only with `gnus-summary-save-in-file', `gnus-summary-save-body-in-file',
 
 The symbol of each function may have the following properties:
 
 * :decode
 The value non-nil means save decoded articles.  This is meaningful
 only with `gnus-summary-save-in-file', `gnus-summary-save-body-in-file',
-`gnus-summary-write-to-file', and `gnus-summary-write-body-to-file'.
+`gnus-summary-write-to-file', `gnus-summary-write-body-to-file', and
+`gnus-summary-save-in-pipe'.
 
 * :function
 The value specifies an alternative function which appends, not
 
 * :function
 The value specifies an alternative function which appends, not
@@ -577,6 +583,7 @@ headers should be saved."
                (function-item gnus-summary-save-in-vm)
                (function-item gnus-summary-write-to-file)
                (function-item gnus-summary-write-body-to-file)
                (function-item gnus-summary-save-in-vm)
                (function-item gnus-summary-write-to-file)
                (function-item gnus-summary-write-body-to-file)
+               (function-item gnus-summary-save-in-pipe)
                (function)))
 
 (defcustom gnus-article-save-coding-system
                (function)))
 
 (defcustom gnus-article-save-coding-system
@@ -715,7 +722,7 @@ The following additional specs are available:
 (defcustom gnus-copy-article-ignored-headers nil
   "List of headers to be removed when copying an article.
 Each element is a regular expression."
 (defcustom gnus-copy-article-ignored-headers nil
   "List of headers to be removed when copying an article.
 Each element is a regular expression."
-  :version "23.0" ;; No Gnus
+  :version "23.1" ;; No Gnus
   :type '(repeat regexp)
   :group 'gnus-article-various)
 
   :type '(repeat regexp)
   :group 'gnus-article-various)
 
@@ -882,7 +889,7 @@ See the manual for the valid properties for various image types.
 Currently, `pbm' is used for X-Face images and `png' is used for Face
 images in Emacs.  Only the `:face' property is effective on the `xface'
 image type in XEmacs if it is built with the libcompface library."
 Currently, `pbm' is used for X-Face images and `png' is used for Face
 images in Emacs.  Only the `:face' property is effective on the `xface'
 image type in XEmacs if it is built with the libcompface library."
-  :version "23.0" ;; No Gnus
+  :version "23.1" ;; No Gnus
   :group 'gnus-article-headers
   :type '(repeat (cons :format "%v" (symbol :tag "Image type") plist)))
 
   :group 'gnus-article-headers
   :type '(repeat (cons :format "%v" (symbol :tag "Image type") plist)))
 
@@ -1054,7 +1061,7 @@ used."
 When 0, point will be placed on the same part as before.  When
 positive (negative), move point forward (backwards) this many
 parts.  When nil, redisplay article."
 When 0, point will be placed on the same part as before.  When
 positive (negative), move point forward (backwards) this many
 parts.  When nil, redisplay article."
-  :version "23.0" ;; No Gnus
+  :version "23.1" ;; No Gnus
   :group 'gnus-article-mime
   :type '(choice (const nil :tag "Redisplay article.")
                 (const 1 :tag "Next part.")
   :group 'gnus-article-mime
   :type '(choice (const nil :tag "Redisplay article.")
                 (const 1 :tag "Next part.")
@@ -1116,10 +1123,7 @@ predicate.  See Info node `(gnus)Customizing Articles'."
   :type gnus-article-treat-head-custom)
 (put 'gnus-treat-buttonize-head 'highlight t)
 
   :type gnus-article-treat-head-custom)
 (put 'gnus-treat-buttonize-head 'highlight t)
 
-(defcustom gnus-treat-emphasize
-  (and (or window-system
-          (featurep 'xemacs))
-       50000)
+(defcustom gnus-treat-emphasize 50000
   "Emphasize text.
 Valid values are nil, t, `head', `first', `last', an integer or a
 predicate.  See Info node `(gnus)Customizing Articles'."
   "Emphasize text.
 Valid values are nil, t, `head', `first', `last', an integer or a
 predicate.  See Info node `(gnus)Customizing Articles'."
@@ -1362,7 +1366,7 @@ If it is a regexp, only long headers matching this regexp are unfolded.
 If it is t, all long headers are unfolded.
 
 This variable has no effect if `gnus-treat-unfold-headers' is nil."
 If it is t, all long headers are unfolded.
 
 This variable has no effect if `gnus-treat-unfold-headers' is nil."
-  :version "23.0" ;; No Gnus
+  :version "23.1" ;; No Gnus
   :group 'gnus-article-treat
   :type '(choice (const nil)
                 (const :tag "all" t)
   :group 'gnus-article-treat
   :type '(choice (const nil)
                 (const :tag "all" t)
@@ -1518,11 +1522,12 @@ node `(gnus)Picons' for details."
 (put 'gnus-treat-newsgroups-picon 'highlight t)
 
 (defcustom gnus-treat-body-boundary
 (put 'gnus-treat-newsgroups-picon 'highlight t)
 
 (defcustom gnus-treat-body-boundary
-  (if (and (eq window-system 'x)
-          (or gnus-treat-newsgroups-picon
-              gnus-treat-mail-picon
-              gnus-treat-from-picon))
-      'head nil)
+  (if (or gnus-treat-newsgroups-picon
+         gnus-treat-mail-picon
+         gnus-treat-from-picon)
+      ;; If there's much decoration, the user might prefer a boundery.
+      'head
+    nil)
   "Draw a boundary at the end of the headers.
 Valid values are nil and `head'.
 See Info node `(gnus)Customizing Articles' for details."
   "Draw a boundary at the end of the headers.
 Valid values are nil and `head'.
 See Info node `(gnus)Customizing Articles' for details."
@@ -1699,11 +1704,6 @@ Initialized from `text-mode-syntax-table.")
 
 (defvar gnus-save-article-buffer nil)
 
 
 (defvar gnus-save-article-buffer nil)
 
-(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-number-of-articles-to-be-saved nil)
 
 (defvar gnus-inhibit-hiding nil)
 (defvar gnus-number-of-articles-to-be-saved nil)
 
 (defvar gnus-inhibit-hiding nil)
@@ -1713,8 +1713,7 @@ Initialized from `text-mode-syntax-table.")
 ;;; Macros for dealing with the article buffer.
 
 (defmacro gnus-with-article-headers (&rest forms)
 ;;; Macros for dealing with the article buffer.
 
 (defmacro gnus-with-article-headers (&rest forms)
-  `(save-excursion
-     (set-buffer gnus-article-buffer)
+  `(with-current-buffer gnus-article-buffer
      (save-restriction
        (let ((inhibit-read-only t)
             (inhibit-point-motion-hooks t)
      (save-restriction
        (let ((inhibit-read-only t)
             (inhibit-point-motion-hooks t)
@@ -1726,8 +1725,7 @@ Initialized from `text-mode-syntax-table.")
 (put 'gnus-with-article-headers 'edebug-form-spec '(body))
 
 (defmacro gnus-with-article-buffer (&rest forms)
 (put 'gnus-with-article-headers 'edebug-form-spec '(body))
 
 (defmacro gnus-with-article-buffer (&rest forms)
-  `(save-excursion
-     (set-buffer gnus-article-buffer)
+  `(with-current-buffer gnus-article-buffer
      (let ((inhibit-read-only t))
        ,@forms)))
 
      (let ((inhibit-read-only t))
        ,@forms)))
 
@@ -2224,11 +2222,11 @@ unfolded."
        (mail-header-fold-field)
        (goto-char (point-max))))))
 
        (mail-header-fold-field)
        (goto-char (point-max))))))
 
-(defcustom gnus-article-truncate-lines default-truncate-lines
+(defcustom gnus-article-truncate-lines (default-value 'truncate-lines)
   "Value of `truncate-lines' in Gnus Article buffer.
 Valid values are nil, t, `head', `first', `last', an integer or a
 predicate.  See Info node `(gnus)Customizing Articles'."
   "Value of `truncate-lines' in Gnus Article buffer.
 Valid values are nil, t, `head', `first', `last', an integer or a
 predicate.  See Info node `(gnus)Customizing Articles'."
-  :version "23.0" ;; No Gnus
+  :version "23.1" ;; No Gnus
   :group 'gnus-article
   ;; :link '(custom-manual "(gnus)Customizing Articles")
   :type 'boolean)
   :group 'gnus-article
   ;; :link '(custom-manual "(gnus)Customizing Articles")
   :type 'boolean)
@@ -2237,7 +2235,7 @@ predicate.  See Info node `(gnus)Customizing Articles'."
   "Toggle whether to fold or truncate long lines in article the buffer.
 If ARG is non-nil and not a number, toggle
 `gnus-article-truncate-lines' too.  If ARG is a number, truncate
   "Toggle whether to fold or truncate long lines in article the buffer.
 If ARG is non-nil and not a number, toggle
 `gnus-article-truncate-lines' too.  If ARG is a number, truncate
-long lines iff arg is positive."
+long lines if and only if arg is positive."
   (interactive "P")
   (cond
    ((and (numberp arg) (> arg 0))
   (interactive "P")
   (cond
    ((and (numberp arg) (> arg 0))
@@ -2334,12 +2332,11 @@ long lines iff arg is positive."
         (forward-line 1)
         (point))))))
 
         (forward-line 1)
         (point))))))
 
-(eval-when-compile
-  (defvar gnus-face-properties-alist))
+(defvar gnus-face-properties-alist)
 
 
-(defun article-display-face ()
+(defun article-display-face (&optional force)
   "Display any Face headers in the header."
   "Display any Face headers in the header."
-  (interactive)
+  (interactive (list 'force))
   (let ((wash-face-p buffer-read-only))
     (gnus-with-article-headers
       ;; When displaying parts, this function can be called several times on
   (let ((wash-face-p buffer-read-only))
     (gnus-with-article-headers
       ;; When displaying parts, this function can be called several times on
@@ -2349,7 +2346,8 @@ long lines iff arg is positive."
       ;; read-only.
       (if (and wash-face-p (memq 'face gnus-article-wash-types))
          (gnus-delete-images 'face)
       ;; read-only.
       (if (and wash-face-p (memq 'face gnus-article-wash-types))
          (gnus-delete-images 'face)
-       (let (face faces from)
+       (let ((from (message-fetch-field "from"))
+             face faces)
          (save-current-buffer
            (when (and wash-face-p
                       (gnus-buffer-live-p gnus-original-article-buffer)
          (save-current-buffer
            (when (and wash-face-p
                       (gnus-buffer-live-p gnus-original-article-buffer)
@@ -2357,16 +2355,22 @@ long lines iff arg is positive."
              (set-buffer gnus-original-article-buffer))
            (save-restriction
              (mail-narrow-to-head)
              (set-buffer gnus-original-article-buffer))
            (save-restriction
              (mail-narrow-to-head)
-             (while (gnus-article-goto-header "Face")
-               (push (mail-header-field-value) faces))))
+             (when (or force
+                       ;; Check whether this face is censored.
+                       (not (and gnus-article-x-face-too-ugly
+                                 (or from
+                                     (setq from (message-fetch-field "from")))
+                                 (string-match gnus-article-x-face-too-ugly
+                                               from))))
+               (while (gnus-article-goto-header "Face")
+                 (push (mail-header-field-value) faces)))))
          (when faces
            (goto-char (point-min))
          (when faces
            (goto-char (point-min))
-           (let ((from (gnus-article-goto-header "from"))
-                 png image)
-             (unless from
+           (let (png image)
+             (unless (setq from (gnus-article-goto-header "from"))
                (insert "From:")
                (setq from (point))
                (insert "From:")
                (setq from (point))
-               (insert "[no `from' set]\n"))
+               (insert " [no `from' set]\n"))
              (while faces
                (when (setq png (gnus-convert-face-to-png (pop faces)))
                  (setq image
              (while faces
                (when (setq png (gnus-convert-face-to-png (pop faces)))
                  (setq image
@@ -2391,7 +2395,8 @@ long lines iff arg is positive."
          ;; instead.
          (gnus-delete-images 'xface)
        ;; Display X-Faces.
          ;; instead.
          (gnus-delete-images 'xface)
        ;; Display X-Faces.
-       (let (x-faces from face)
+       (let ((from (message-fetch-field "from"))
+             x-faces face)
          (save-current-buffer
            (when (and wash-face-p
                       (gnus-buffer-live-p gnus-original-article-buffer)
          (save-current-buffer
            (when (and wash-face-p
                       (gnus-buffer-live-p gnus-original-article-buffer)
@@ -2402,43 +2407,41 @@ long lines iff arg is positive."
              (set-buffer gnus-original-article-buffer))
            (save-restriction
              (mail-narrow-to-head)
              (set-buffer gnus-original-article-buffer))
            (save-restriction
              (mail-narrow-to-head)
-             (while (gnus-article-goto-header "X-Face")
-               (push (mail-header-field-value) x-faces))
-             (setq from (message-fetch-field "from"))))
-         ;; Sending multiple EOFs to xv doesn't work, so we only do a
-         ;; single external face.
-         (when (stringp gnus-article-x-face-command)
-           (setq x-faces (list (car x-faces))))
-         (when (and x-faces
-                    gnus-article-x-face-command
-                    (or force
-                        ;; Check whether this face is censored.
-                        (not gnus-article-x-face-too-ugly)
-                        (and from
-                             (not (string-match gnus-article-x-face-too-ugly
-                                                from)))))
-           (while (setq face (pop x-faces))
-             ;; We display the face.
-             (cond ((stringp gnus-article-x-face-command)
-                    ;; The command is a string, so we interpret the command
-                    ;; as a, well, command, and fork it off.
-                    (let ((process-connection-type nil))
-                      (gnus-set-process-query-on-exit-flag
-                       (start-process
-                        "article-x-face" nil shell-file-name
-                        shell-command-switch gnus-article-x-face-command)
-                       nil)
-                      (with-temp-buffer
-                        (insert face)
-                        (process-send-region "article-x-face"
-                                             (point-min) (point-max)))
-                      (process-send-eof "article-x-face")))
-                   ((functionp gnus-article-x-face-command)
-                    ;; The command is a lisp function, so we call it.
-                    (funcall gnus-article-x-face-command face))
-                   (t
-                    (error "%s is not a function"
-                           gnus-article-x-face-command))))))))))
+             (and gnus-article-x-face-command
+                  (or force
+                      ;; Check whether this face is censored.
+                      (not (and gnus-article-x-face-too-ugly
+                                (or from
+                                    (setq from (message-fetch-field "from")))
+                                (string-match gnus-article-x-face-too-ugly
+                                              from))))
+                  (while (gnus-article-goto-header "X-Face")
+                    (push (mail-header-field-value) x-faces)))))
+         (when x-faces
+           ;; We display the face.
+           (cond ((functionp gnus-article-x-face-command)
+                  ;; The command is a lisp function, so we call it.
+                  (mapc gnus-article-x-face-command x-faces))
+                 ((stringp gnus-article-x-face-command)
+                  ;; The command is a string, so we interpret the command
+                  ;; as a, well, command, and fork it off.
+                  (let ((process-connection-type nil))
+                    (gnus-set-process-query-on-exit-flag
+                     (start-process
+                      "article-x-face" nil shell-file-name
+                      shell-command-switch gnus-article-x-face-command)
+                     nil)
+                    ;; Sending multiple EOFs to xv doesn't work,
+                    ;; so we only do a single external face.
+                    (with-temp-buffer
+                      (insert (car x-faces))
+                      (process-send-region "article-x-face"
+                                           (point-min) (point-max)))
+                    (process-send-eof "article-x-face")))
+                 (t
+                  (error "`%s' set to `%s' is not a function"
+                         gnus-article-x-face-command
+                         'gnus-article-x-face-command)))))))))
 
 (defun article-decode-mime-words ()
   "Decode all MIME-encoded words in the article."
 
 (defun article-decode-mime-words ()
   "Decode all MIME-encoded words in the article."
@@ -2525,44 +2528,31 @@ If PROMPT (the prefix), prompt for a coding system to use."
       (goto-char (setq end start)))))
 
 (defun article-decode-group-name ()
       (goto-char (setq end start)))))
 
 (defun article-decode-group-name ()
-  "Decode group names in `Newsgroups:'."
+  "Decode group names in Newsgroups, Followup-To and Xref headers."
   (let ((inhibit-point-motion-hooks t)
        (inhibit-read-only t)
   (let ((inhibit-point-motion-hooks t)
        (inhibit-read-only t)
-       (method (gnus-find-method-for-group gnus-newsgroup-name)))
+       (method (gnus-find-method-for-group gnus-newsgroup-name))
+       regexp)
     (when (and (or gnus-group-name-charset-method-alist
                   gnus-group-name-charset-group-alist)
               (gnus-buffer-live-p gnus-original-article-buffer))
       (save-restriction
        (article-narrow-to-head)
     (when (and (or gnus-group-name-charset-method-alist
                   gnus-group-name-charset-group-alist)
               (gnus-buffer-live-p gnus-original-article-buffer))
       (save-restriction
        (article-narrow-to-head)
-       (with-current-buffer gnus-original-article-buffer
-         (goto-char (point-min)))
-       (while (re-search-forward
-               "^Newsgroups:\\(\\(.\\|\n[\t ]\\)*\\)\n[^\t ]" nil t)
-         (replace-match (save-match-data
-                          (gnus-decode-newsgroups
-                           ;; XXX how to use data in article buffer?
-                           (with-current-buffer gnus-original-article-buffer
-                             (re-search-forward
-                              "^Newsgroups:\\(\\(.\\|\n[\t ]\\)*\\)\n[^\t ]"
-                              nil t)
-                             (match-string 1))
-                           gnus-newsgroup-name method))
-                        t t nil 1))
-       (goto-char (point-min))
-       (with-current-buffer gnus-original-article-buffer
-         (goto-char (point-min)))
-       (while (re-search-forward
-               "^Followup-To:\\(\\(.\\|\n[\t ]\\)*\\)\n[^\t ]" nil t)
-         (replace-match (save-match-data
-                          (gnus-decode-newsgroups
-                           ;; XXX how to use data in article buffer?
-                           (with-current-buffer gnus-original-article-buffer
-                             (re-search-forward
-                              "^Followup-To:\\(\\(.\\|\n[\t ]\\)*\\)\n[^\t ]"
-                              nil t)
-                             (match-string 1))
-                           gnus-newsgroup-name method))
-                        t t nil 1))))))
+       (dolist (header '("Newsgroups" "Followup-To" "Xref"))
+         (with-current-buffer gnus-original-article-buffer
+           (goto-char (point-min)))
+         (setq regexp (concat "^" header
+                              ":\\([^\n]*\\(?:\n[\t ]+[^\n]+\\)*\\)\n"))
+         (while (re-search-forward regexp nil t)
+           (replace-match (save-match-data
+                            (gnus-decode-newsgroups
+                             ;; XXX how to use data in article buffer?
+                             (with-current-buffer gnus-original-article-buffer
+                               (re-search-forward regexp nil t)
+                               (match-string 1))
+                             gnus-newsgroup-name method))
+                          t t nil 1))
+         (goto-char (point-min)))))))
 
 (autoload 'idna-to-unicode "idna")
 
 
 (autoload 'idna-to-unicode "idna")
 
@@ -2715,6 +2705,9 @@ charset defined in `gnus-summary-show-article-charset-alist' is used."
             (t
              (apply (car func) (cdr func))))))))))
 
             (t
              (apply (car func) (cdr func))))))))))
 
+;; External.
+(declare-function w3-region "ext:w3-display" (st nd))
+
 (defun gnus-article-wash-html-with-w3 ()
   "Wash the current buffer with w3."
   (mm-setup-w3)
 (defun gnus-article-wash-html-with-w3 ()
   "Wash the current buffer with w3."
   (mm-setup-w3)
@@ -2726,6 +2719,9 @@ charset defined in `gnus-summary-show-article-charset-alist' is used."
        (w3-region (point-min) (point-max))
       (error))))
 
        (w3-region (point-min) (point-max))
       (error))))
 
+;; External.
+(declare-function w3m-region "ext:w3m" (start end &optional url charset))
+
 (defun gnus-article-wash-html-with-w3m ()
   "Wash the current buffer with emacs-w3m."
   (mm-setup-w3m)
 (defun gnus-article-wash-html-with-w3m ()
   "Wash the current buffer with emacs-w3m."
   (mm-setup-w3m)
@@ -2741,7 +2737,7 @@ charset defined in `gnus-summary-show-article-charset-alist' is used."
           ;; Put the mark meaning this part was rendered by emacs-w3m.
           'mm-inline-text-html-with-w3m t))))
 
           ;; Put the mark meaning this part was rendered by emacs-w3m.
           'mm-inline-text-html-with-w3m t))))
 
-(eval-when-compile (defvar charset)) ;; Bound by `article-wash-html'.
+(defvar charset) ;; Bound by `article-wash-html'.
 
 (defun gnus-article-wash-html-with-w3m-standalone ()
   "Wash the current buffer with w3m."
 
 (defun gnus-article-wash-html-with-w3m-standalone ()
   "Wash the current buffer with w3m."
@@ -2769,7 +2765,7 @@ exit from the summary buffer.  If it is the symbol `file', query
 on each file, if it is `ask' ask once when exiting from the
 summary buffer."
   :group 'gnus-article
 on each file, if it is `ask' ask once when exiting from the
 summary buffer."
   :group 'gnus-article
-  :version "23.0" ;; No Gnus
+  :version "23.1" ;; No Gnus
   :type '(choice (const :tag "Don't delete" nil)
                 (const :tag "Don't ask" t)
                 (const :tag "Ask" ask)
   :type '(choice (const :tag "Don't delete" nil)
                 (const :tag "Don't ask" t)
                 (const :tag "Ask" ask)
@@ -2783,9 +2779,9 @@ summary buffer."
             (or how
                 (setq how gnus-article-browse-delete-temp)))
     (when (and (eq how 'ask)
             (or how
                 (setq how gnus-article-browse-delete-temp)))
     (when (and (eq how 'ask)
-              (y-or-n-p (format
-                         "Delete all %s temporary HTML file(s)? "
-                         (length gnus-article-browse-html-temp-list)))
+              (gnus-y-or-n-p (format
+                              "Delete all %s temporary HTML file(s)? "
+                              (length gnus-article-browse-html-temp-list)))
               (setq how t)))
     (dolist (file gnus-article-browse-html-temp-list)
       (when (and (file-exists-p file)
               (setq how t)))
     (dolist (file gnus-article-browse-html-temp-list)
       (when (and (file-exists-p file)
@@ -2799,62 +2795,223 @@ summary buffer."
     (setq gnus-article-browse-html-temp-list nil))
   gnus-article-browse-html-temp-list)
 
     (setq gnus-article-browse-html-temp-list nil))
   gnus-article-browse-html-temp-list)
 
-(defun gnus-article-browse-html-parts (list)
+(defun gnus-article-browse-html-parts (list &optional header)
   "View all \"text/html\" parts from LIST.
   "View all \"text/html\" parts from LIST.
-Recurse into multiparts."
+Recurse into multiparts.  The optional HEADER that should be a decoded
+message header will be added to the bodies of the \"text/html\" parts."
   ;; Internal function used by `gnus-article-browse-html-article'.
   ;; Internal function used by `gnus-article-browse-html-article'.
-  (let ((showed))
+  (let (type file charset tmp-file showed)
     ;; Find and show the html-parts.
     (dolist (handle list)
       ;; If HTML, show it:
     ;; Find and show the html-parts.
     (dolist (handle list)
       ;; If HTML, show it:
-      (when (listp handle)
-       (cond ((and (bufferp (car handle))
-                   (string-match "text/html" (car (mm-handle-type handle))))
-              (let ((tmp-file (mm-make-temp-file
-                               ;; Do we need to care for 8.3 filenames?
-                               "mm-" nil ".html")))
-                (mm-save-part-to-file handle tmp-file)
-                (add-to-list 'gnus-article-browse-html-temp-list tmp-file)
-                (add-hook 'gnus-summary-prepare-exit-hook
-                          'gnus-article-browse-delete-temp-files)
-                (add-hook 'gnus-exit-gnus-hook
-                          (lambda  ()
-                            (gnus-article-browse-delete-temp-files t)))
-                ;; FIXME: Warn if there's an <img> tag?
-                (browse-url-of-file tmp-file)
-                (setq showed t)))
-             ;; If multipart, recurse
-             ((and (stringp (car handle))
-                   (string-match "^multipart/" (car handle))
-                   (setq showed
-                         (or showed
-                             (gnus-article-browse-html-parts handle))))))))
+      (cond ((not (listp handle)))
+           ((or (equal (car (setq type (mm-handle-type handle))) "text/html")
+                (and (equal (car type) "message/external-body")
+                     (or header
+                         (setq file (or (mail-content-type-get type 'name)
+                                        (mail-content-type-get
+                                         (mm-handle-disposition handle)
+                                         'filename))))
+                     (or (mm-handle-cache handle)
+                         (condition-case code
+                             (progn (mm-extern-cache-contents handle) t)
+                           (error
+                            (gnus-message 3 "%s" (error-message-string code))
+                            (when (>= gnus-verbose 3) (sit-for 2))
+                            nil)))
+                     (progn
+                       (setq handle (mm-handle-cache handle)
+                             type (mm-handle-type handle))
+                       (equal (car type) "text/html"))))
+            (when (or (setq charset (mail-content-type-get type 'charset))
+                      header
+                      (not file))
+              (setq tmp-file (mm-make-temp-file
+                              ;; Do we need to care for 8.3 filenames?
+                              "mm-" nil ".html")))
+            ;; Add a meta html tag to specify charset and a header.
+            (cond
+             (header
+              (let (title eheader body hcharset coding)
+                (with-temp-buffer
+                  (mm-enable-multibyte)
+                  (setq case-fold-search t)
+                  (insert header "\n")
+                  (setq title (message-fetch-field "subject"))
+                  (goto-char (point-min))
+                  (while (re-search-forward "\\(<\\)\\|\\(>\\)\\|&" nil t)
+                    (replace-match (cond ((match-beginning 1) "&lt;")
+                                         ((match-beginning 2) "&gt;")
+                                         (t "&amp;"))))
+                  (goto-char (point-min))
+                  (insert "<pre>\n")
+                  (goto-char (point-max))
+                  (insert "</pre>\n<hr>\n")
+                  ;; We have to examine charset one by one since
+                  ;; charset specified in parts might be different.
+                  (if (eq charset 'gnus-decoded)
+                      (setq charset 'utf-8
+                            eheader (mm-encode-coding-string (buffer-string)
+                                                             charset)
+                            title (when title
+                                    (mm-encode-coding-string title charset))
+                            body (mm-encode-coding-string (mm-get-part handle)
+                                                          charset))
+                    (setq hcharset (mm-find-mime-charset-region (point-min)
+                                                                (point-max)))
+                    (cond ((= (length hcharset) 1)
+                           (setq hcharset (car hcharset)
+                                 coding (mm-charset-to-coding-system
+                                         hcharset)))
+                          ((> (length hcharset) 1)
+                           (setq hcharset 'utf-8
+                                 coding hcharset)))
+                    (if coding
+                        (if charset
+                            (progn
+                              (setq body
+                                    (mm-charset-to-coding-system charset))
+                              (if (eq coding body)
+                                  (setq eheader (mm-encode-coding-string
+                                                 (buffer-string) coding)
+                                        title (when title
+                                                (mm-encode-coding-string
+                                                 title coding))
+                                        body (mm-get-part handle))
+                                (setq charset 'utf-8
+                                      eheader (mm-encode-coding-string
+                                               (buffer-string) charset)
+                                      title (when title
+                                              (mm-encode-coding-string
+                                               title charset))
+                                      body (mm-encode-coding-string
+                                            (mm-decode-coding-string
+                                             (mm-get-part handle) body)
+                                            charset))))
+                          (setq charset hcharset
+                                eheader (mm-encode-coding-string
+                                         (buffer-string) coding)
+                                title (when title
+                                        (mm-encode-coding-string
+                                         title coding))
+                                body (mm-get-part handle)))
+                      (setq eheader (mm-string-as-unibyte (buffer-string))
+                            body (mm-get-part handle))))
+                  (erase-buffer)
+                  (mm-disable-multibyte)
+                  (insert body)
+                  (when charset
+                    (mm-add-meta-html-tag handle charset))
+                  (when title
+                    (goto-char (point-min))
+                    (unless (search-forward "<title>" nil t)
+                      (re-search-forward "<head>\\s-*" nil t)
+                      (insert "<title>" title "</title>\n")))
+                  (goto-char (point-min))
+                  (or (re-search-forward
+                       "<body\\(?:\\s-+[^>]+\\|\\s-*\\)>\\s-*" nil t)
+                      (re-search-forward
+                       "</head\\(?:\\s-+[^>]+\\|\\s-*\\)>\\s-*" nil t))
+                  (insert eheader)
+                  (mm-write-region (point-min) (point-max)
+                                   tmp-file nil nil nil 'binary t))))
+             (charset
+              (mm-with-unibyte-buffer
+                (insert (if (eq charset 'gnus-decoded)
+                            (mm-encode-coding-string
+                             (mm-get-part handle)
+                             (setq charset 'utf-8))
+                          (mm-get-part handle)))
+                (if (or (mm-add-meta-html-tag handle charset)
+                        (not file))
+                    (mm-write-region (point-min) (point-max)
+                                     tmp-file nil nil nil 'binary t)
+                  (setq tmp-file nil))))
+             (tmp-file
+              (mm-save-part-to-file handle tmp-file)))
+            (when tmp-file
+              (add-to-list 'gnus-article-browse-html-temp-list tmp-file))
+            (add-hook 'gnus-summary-prepare-exit-hook
+                      'gnus-article-browse-delete-temp-files)
+            (add-hook 'gnus-exit-gnus-hook
+                      (lambda  ()
+                        (gnus-article-browse-delete-temp-files t)))
+            ;; FIXME: Warn if there's an <img> tag?
+            (browse-url-of-file (or tmp-file (expand-file-name file)))
+            (setq showed t))
+           ;; If multipart, recurse
+           ((equal (mm-handle-media-supertype handle) "multipart")
+            (when (gnus-article-browse-html-parts handle header)
+              (setq showed t)))
+           ((equal (mm-handle-media-type handle) "message/rfc822")
+            (mm-with-multibyte-buffer
+              (mm-insert-part handle)
+              (setq handle (mm-dissect-buffer t t))
+              (when (and (bufferp (car handle))
+                         (stringp (car (mm-handle-type handle))))
+                (setq handle (list handle)))
+              (when header
+                (article-decode-encoded-words)
+                (let ((gnus-visible-headers
+                       (or (get 'gnus-visible-headers 'standard-value)
+                           gnus-visible-headers)))
+                  (article-hide-headers))
+                (goto-char (point-min))
+                (search-forward "\n\n" nil 'move)
+                (skip-chars-backward "\t\n ")
+                (setq header (buffer-substring (point-min) (point)))))
+            (when (prog1
+                      (gnus-article-browse-html-parts handle header)
+                    (mm-destroy-parts handle))
+              (setq showed t)))))
     showed))
 
     showed))
 
-;; FIXME: Documentation in texi/gnus.texi missing.
-(defun gnus-article-browse-html-article ()
+(defun gnus-article-browse-html-article (&optional arg)
   "View \"text/html\" parts of the current article with a WWW browser.
   "View \"text/html\" parts of the current article with a WWW browser.
+The message header is added to the beginning of every html part unless
+the prefix argument ARG is given.
 
 Warning: Spammers use links to images in HTML articles to verify
 whether you have read the message.  As
 
 Warning: Spammers use links to images in HTML articles to verify
 whether you have read the message.  As
-`gnus-article-browse-html-article' passes the unmodified HTML
-content to the browser without eliminating these \"web bugs\" you
-should only use it for mails from trusted senders."
+`gnus-article-browse-html-article' passes the HTML content to the
+browser without eliminating these \"web bugs\" you should only
+use it for mails from trusted senders.
+
+If you always want to display HTML parts in the browser, set
+`mm-text-html-renderer' to nil."
   ;; Cf. `mm-w3m-safe-url-regexp'
   ;; Cf. `mm-w3m-safe-url-regexp'
-  (interactive)
-  (save-window-excursion
-    ;; Open raw article and select the buffer
-    (gnus-summary-show-article t)
-    (gnus-summary-select-article-buffer)
-    (let ((parts (mm-dissect-buffer t t)))
+  (interactive "P")
+  (if arg
+      (gnus-summary-show-article)
+    (let ((gnus-visible-headers (or (get 'gnus-visible-headers 'standard-value)
+                                   gnus-visible-headers))
+         ;; As we insert a <hr>, there's no need for the body boundary.
+         (gnus-treat-body-boundary nil))
+      (gnus-summary-show-article)))
+  (with-current-buffer gnus-article-buffer
+    (let ((header (unless arg
+                   (save-restriction
+                     (widen)
+                     (buffer-substring-no-properties
+                      (goto-char (point-min))
+                      (if (search-forward "\n\n" nil t)
+                          (match-beginning 0)
+                        (goto-char (point-max))
+                        (skip-chars-backward "\t\n ")
+                        (point))))))
+         parts)
+      (set-buffer gnus-original-article-buffer)
+      (setq parts (mm-dissect-buffer t t))
       ;; If singlepart, enforce a list.
       (when (and (bufferp (car parts))
                 (stringp (car (mm-handle-type parts))))
        (setq parts (list parts)))
       ;; Process the list
       ;; If singlepart, enforce a list.
       (when (and (bufferp (car parts))
                 (stringp (car (mm-handle-type parts))))
        (setq parts (list parts)))
       ;; Process the list
-      (unless (gnus-article-browse-html-parts parts)
+      (unless (gnus-article-browse-html-parts parts header)
        (gnus-error 3 "Mail doesn't contain a \"text/html\" part!"))
        (gnus-error 3 "Mail doesn't contain a \"text/html\" part!"))
-      (gnus-summary-show-article))))
+      (mm-destroy-parts parts)
+      (unless arg
+       (gnus-summary-show-article)))))
 
 (defun article-hide-list-identifiers ()
   "Remove list identifies from the Subject header.
 
 (defun article-hide-list-identifiers ()
   "Remove list identifies from the Subject header.
@@ -3544,9 +3701,8 @@ This format is defined by the `gnus-article-time-format' variable."
                                 gnus-newsgroup-name 'highlight-words t)))
             gnus-emphasis-alist)))))
 
                                 gnus-newsgroup-name 'highlight-words t)))
             gnus-emphasis-alist)))))
 
-(eval-when-compile
-  (defvar gnus-summary-article-menu)
-  (defvar gnus-summary-post-menu))
+(defvar gnus-summary-article-menu)
+(defvar gnus-summary-post-menu)
 
 ;;; Saving functions.
 
 
 ;;; Saving functions.
 
@@ -3559,9 +3715,9 @@ This format is defined by the `gnus-article-time-format' variable."
     (let ((gnus-visible-headers
           (or (symbol-value (get gnus-default-article-saver :headers))
               gnus-saved-headers gnus-visible-headers))
     (let ((gnus-visible-headers
           (or (symbol-value (get gnus-default-article-saver :headers))
               gnus-saved-headers gnus-visible-headers))
-         (gnus-article-buffer save-buffer))
-      (save-excursion
-       (set-buffer save-buffer)
+         ;; Ignore group parameter.  See `article-hide-headers'.
+         (gnus-summary-buffer nil))
+      (with-current-buffer save-buffer
        (article-hide-headers 1 t))))
   (save-window-excursion
     (if (not gnus-default-article-saver)
        (article-hide-headers 1 t))))
   (save-window-excursion
     (if (not gnus-default-article-saver)
@@ -3784,39 +3940,96 @@ The directory to save in defaults to `gnus-article-save-directory'."
                  gnus-current-headers nil 'gnus-newsgroup-last-directory))
   (gnus-summary-save-body-in-file filename t))
 
                  gnus-current-headers nil 'gnus-newsgroup-last-directory))
   (gnus-summary-save-body-in-file filename t))
 
-(defun gnus-summary-save-in-pipe (&optional command)
-  "Pipe this article to subprocess."
-  (setq command
-       (cond ((and (eq command 'default)
-                   gnus-last-shell-command)
-              gnus-last-shell-command)
-             ((stringp command)
-              command)
-             (t (read-string
-                 (format
-                  "Shell command on %s: "
-                  (if (and gnus-number-of-articles-to-be-saved
-                           (> gnus-number-of-articles-to-be-saved 1))
-                      (format "these %d articles"
-                              gnus-number-of-articles-to-be-saved)
-                    "this article"))
-                 gnus-last-shell-command))))
-  (when (string-equal command "")
-    (if gnus-last-shell-command
-       (setq command gnus-last-shell-command)
-      (error "A command is required")))
-  (gnus-eval-in-buffer-window gnus-article-buffer
-    (save-restriction
-      (widen)
-      (shell-command-on-region (point-min) (point-max) command nil)))
-  (setq gnus-last-shell-command command))
+(put 'gnus-summary-save-in-pipe :decode t)
+(put 'gnus-summary-save-in-pipe :headers 'gnus-saved-headers)
+(defun gnus-summary-save-in-pipe (&optional command raw)
+  "Pipe this article to subprocess COMMAND.
+Valid values for COMMAND include:
+  a string
+    The executable command name and possibly arguments.
+  nil
+    You will be prompted for the command in the minibuffer.
+  the symbol `default'
+    It will be replaced with the command which the variable
+    `gnus-summary-pipe-output-default-command' holds or the command
+    last used for saving.
+Non-nil value for RAW overrides `:decode' and `:headers' properties
+and the raw article including all headers will be piped."
+  (let ((save-buffer gnus-save-article-buffer)
+       (default (or gnus-summary-pipe-output-default-command
+                    gnus-last-shell-command)))
+    ;; `gnus-save-article-buffer' should be a buffer containing the article
+    ;; contents if this function is called by way of the command
+    ;; `gnus-summary-pipe-output'.  OTOH, that the buffer does not exist
+    ;; means this function is called independently.
+    (unless (gnus-buffer-live-p save-buffer)
+      (let ((article (gnus-summary-article-number))
+           (decode (unless raw
+                     (get 'gnus-summary-save-in-pipe :decode))))
+       (if article
+           (if (vectorp (gnus-summary-article-header article))
+               (save-window-excursion
+                 (let ((gnus-display-mime-function
+                        (when decode
+                          gnus-display-mime-function))
+                       (gnus-article-prepare-hook
+                        (when decode
+                          gnus-article-prepare-hook)))
+                   (gnus-summary-select-article t t nil article)
+                   (gnus-summary-goto-subject article))
+                 (insert-buffer-substring
+                  (prog1
+                      (if decode
+                          gnus-article-buffer
+                        gnus-original-article-buffer)
+                    (setq save-buffer
+                          (nnheader-set-temp-buffer " *Gnus Save*"))))
+                 ;; Remove unwanted headers.
+                 (when (and (not raw)
+                            (or (get 'gnus-summary-save-in-pipe :headers)
+                                (not gnus-save-all-headers)))
+                   (let ((gnus-visible-headers
+                          (or (symbol-value (get 'gnus-summary-save-in-pipe
+                                                 :headers))
+                              gnus-saved-headers gnus-visible-headers))
+                         (gnus-summary-buffer nil))
+                     (article-hide-headers 1 t))))
+             (error "%d is not a real article" article))
+         (error "No article to pipe"))))
+    (unless (stringp command)
+      (setq command
+           (if (and (eq command 'default) default)
+               default
+             (gnus-read-shell-command
+              (format
+               "Shell command on %s: "
+               (if (and gnus-number-of-articles-to-be-saved
+                        (> gnus-number-of-articles-to-be-saved 1))
+                   (format "these %d articles"
+                           gnus-number-of-articles-to-be-saved)
+                 "this article"))
+              default))))
+    (when (string-equal command "")
+      (if default
+         (setq command default)
+       (error "A command is required")))
+    (gnus-eval-in-buffer-window save-buffer
+      (save-restriction
+       (widen)
+       (shell-command-on-region (point-min) (point-max) command nil)))
+    (gnus-kill-buffer save-buffer))
+  (setq gnus-summary-pipe-output-default-command command))
 
 (defun gnus-summary-pipe-to-muttprint (&optional command)
   "Pipe this article to muttprint."
 
 (defun gnus-summary-pipe-to-muttprint (&optional command)
   "Pipe this article to muttprint."
-  (setq command (read-string
-                "Print using command: " gnus-summary-muttprint-program
-                nil gnus-summary-muttprint-program))
-  (gnus-summary-save-in-pipe command))
+  (unless (stringp command)
+    (setq command (read-string
+                  "Print using command: " gnus-summary-muttprint-program
+                  nil gnus-summary-muttprint-program)))
+  (let ((gnus-summary-pipe-output-default-command
+        gnus-summary-pipe-output-default-command))
+    (gnus-summary-save-in-pipe command))
+  (setq gnus-summary-muttprint-program command))
 
 ;;; Article file names when saving.
 
 
 ;;; Article file names when saving.
 
@@ -3886,6 +4099,7 @@ If variable `gnus-use-long-file-name' is non-nil, it is
 
 (defun article-verify-x-pgp-sig ()
   "Verify X-PGP-Sig."
 
 (defun article-verify-x-pgp-sig ()
   "Verify X-PGP-Sig."
+  ;; <ftp://ftp.isc.org/pub/pgpcontrol/FORMAT>
   (interactive)
   (if (gnus-buffer-live-p gnus-original-article-buffer)
       (let ((sig (with-current-buffer gnus-original-article-buffer
   (interactive)
   (if (gnus-buffer-live-p gnus-original-article-buffer)
       (let ((sig (with-current-buffer gnus-original-article-buffer
@@ -3965,7 +4179,7 @@ If variable `gnus-use-long-file-name' is non-nil, it is
       (canlock-verify gnus-original-article-buffer)))
 
 (eval-and-compile
       (canlock-verify gnus-original-article-buffer)))
 
 (eval-and-compile
-  (mapcar
+  (mapc
    (lambda (func)
      (let (afunc gfunc)
        (if (consp func)
    (lambda (func)
      (let (afunc gfunc)
        (if (consp func)
@@ -3978,8 +4192,7 @@ If variable `gnus-use-long-file-name' is non-nil, it is
           `(lambda (&optional interactive &rest args)
              ,(documentation afunc t)
              (interactive (list t))
           `(lambda (&optional interactive &rest args)
              ,(documentation afunc t)
              (interactive (list t))
-             (save-excursion
-               (set-buffer gnus-article-buffer)
+             (with-current-buffer gnus-article-buffer
                (if interactive
                    (call-interactively ',afunc)
                  (apply ',afunc args))))))))
                (if interactive
                    (call-interactively ',afunc)
                  (apply ',afunc args))))))))
@@ -4026,7 +4239,7 @@ If variable `gnus-use-long-file-name' is non-nil, it is
      article-emphasize
      article-treat-dumbquotes
      article-normalize-headers
      article-emphasize
      article-treat-dumbquotes
      article-normalize-headers
-;;     (article-show-all . gnus-article-show-all-headers)
+     ;;(article-show-all . gnus-article-show-all-headers)
      )))
 \f
 ;;;
      )))
 \f
 ;;;
@@ -4056,6 +4269,7 @@ If variable `gnus-use-long-file-name' is non-nil, it is
   "F" gnus-article-followup-with-original
   "\C-hk" gnus-article-describe-key
   "\C-hc" gnus-article-describe-key-briefly
   "F" gnus-article-followup-with-original
   "\C-hk" gnus-article-describe-key
   "\C-hc" gnus-article-describe-key-briefly
+  "\C-hb" gnus-article-describe-bindings
 
   "\C-d" gnus-article-read-summary-keys
   "\M-*" gnus-article-read-summary-keys
 
   "\C-d" gnus-article-read-summary-keys
   "\M-*" gnus-article-read-summary-keys
@@ -4066,6 +4280,13 @@ If variable `gnus-use-long-file-name' is non-nil, it is
 (substitute-key-definition
  'undefined 'gnus-article-read-summary-keys gnus-article-mode-map)
 
 (substitute-key-definition
  'undefined 'gnus-article-read-summary-keys gnus-article-mode-map)
 
+(gnus-define-keys (gnus-article-send-map "S" gnus-article-mode-map)
+  "W" gnus-article-wide-reply-with-original)
+(if (featurep 'xemacs)
+    (set-keymap-default-binding gnus-article-send-map
+                               'gnus-article-read-summary-send-keys)
+  (define-key gnus-article-send-map [t] 'gnus-article-read-summary-send-keys))
+
 (defun gnus-article-make-menu-bar ()
   (unless (boundp 'gnus-article-commands-menu)
     (gnus-summary-make-menu-bar))
 (defun gnus-article-make-menu-bar ()
   (unless (boundp 'gnus-article-commands-menu)
     (gnus-summary-make-menu-bar))
@@ -4178,8 +4399,7 @@ Internal variable.")
        (gnus-set-global-variables)))
     (gnus-article-setup-highlight-words)
     ;; Init original article buffer.
        (gnus-set-global-variables)))
     (gnus-article-setup-highlight-words)
     ;; Init original article buffer.
-    (save-excursion
-      (set-buffer (gnus-get-buffer-create gnus-original-article-buffer))
+    (with-current-buffer (gnus-get-buffer-create gnus-original-article-buffer)
       (mm-enable-multibyte)
       (setq major-mode 'gnus-original-article-mode)
       (make-local-variable 'gnus-original-article))
       (mm-enable-multibyte)
       (setq major-mode 'gnus-original-article-mode)
       (make-local-variable 'gnus-original-article))
@@ -4194,8 +4414,7 @@ Internal variable.")
                         nil)
                     (error "Action aborted"))
                 t)))
                         nil)
                     (error "Action aborted"))
                 t)))
-       (save-excursion
-         (set-buffer name)
+       (with-current-buffer name
          (set (make-local-variable 'gnus-article-edit-mode) nil)
          (when gnus-article-mime-handles
            (mm-destroy-parts gnus-article-mime-handles)
          (set (make-local-variable 'gnus-article-edit-mode) nil)
          (when gnus-article-mime-handles
            (mm-destroy-parts gnus-article-mime-handles)
@@ -4209,8 +4428,7 @@ Internal variable.")
          (unless (eq major-mode 'gnus-article-mode)
            (gnus-article-mode))
          (current-buffer))
          (unless (eq major-mode 'gnus-article-mode)
            (gnus-article-mode))
          (current-buffer))
-      (save-excursion
-       (set-buffer (gnus-get-buffer-create name))
+      (with-current-buffer (gnus-get-buffer-create name)
        (gnus-article-mode)
        (make-local-variable 'gnus-summary-buffer)
        (setq gnus-summary-buffer
        (gnus-article-mode)
        (make-local-variable 'gnus-summary-buffer)
        (setq gnus-summary-buffer
@@ -4225,8 +4443,7 @@ Internal variable.")
     (when article-window
       (set-window-start
        article-window
     (when article-window
       (set-window-start
        article-window
-       (save-excursion
-        (set-buffer gnus-article-buffer)
+       (with-current-buffer gnus-article-buffer
         (goto-char (point-min))
         (if (not line)
             (point-min)
         (goto-char (point-min))
         (if (not line)
             (point-min)
@@ -4280,8 +4497,7 @@ If ALL-HEADERS is non-nil, no headers are hidden."
          (if (or (eq result 'pseudo)
                  (eq result 'nneething))
              (progn
          (if (or (eq result 'pseudo)
                  (eq result 'nneething))
              (progn
-               (save-excursion
-                 (set-buffer summary-buffer)
+               (with-current-buffer summary-buffer
                  (push article gnus-newsgroup-history)
                  (setq gnus-last-article gnus-current-article
                        gnus-current-article 0
                  (push article gnus-newsgroup-history)
                  (setq gnus-last-article gnus-current-article
                        gnus-current-article 0
@@ -4301,8 +4517,7 @@ If ALL-HEADERS is non-nil, no headers are hidden."
                       (not (eq article gnus-current-article)))
              ;; Seems like a new article has been selected.
              ;; `gnus-current-article' must be an article number.
                       (not (eq article gnus-current-article)))
              ;; Seems like a new article has been selected.
              ;; `gnus-current-article' must be an article number.
-             (save-excursion
-               (set-buffer summary-buffer)
+             (with-current-buffer summary-buffer
                (push article gnus-newsgroup-history)
                (setq gnus-last-article gnus-current-article
                      gnus-current-article article
                (push article gnus-newsgroup-history)
                (setq gnus-last-article gnus-current-article
                      gnus-current-article article
@@ -4364,6 +4579,90 @@ If ALL-HEADERS is non-nil, no headers are hidden."
       (funcall gnus-display-mime-function))
     (gnus-run-hooks 'gnus-article-prepare-hook)))
 
       (funcall gnus-display-mime-function))
     (gnus-run-hooks 'gnus-article-prepare-hook)))
 
+;;;
+;;; Gnus Sticky Article Mode
+;;;
+
+(define-derived-mode gnus-sticky-article-mode gnus-article-mode "StickyArticle"
+  "Mode for sticky articles."
+  ;; Release bindings that won't work.
+  (substitute-key-definition 'gnus-article-read-summary-keys 'undefined
+                            gnus-sticky-article-mode-map)
+  (substitute-key-definition 'gnus-article-refer-article 'undefined
+                            gnus-sticky-article-mode-map)
+  (dolist (k '("e" "h" "s" "F" "R"))
+    (define-key gnus-sticky-article-mode-map k nil))
+  (define-key gnus-sticky-article-mode-map "k" 'gnus-kill-sticky-article-buffer)
+  (define-key gnus-sticky-article-mode-map "q" 'bury-buffer)
+  (define-key gnus-sticky-article-mode-map "\C-hc" 'describe-key-briefly)
+  (define-key gnus-sticky-article-mode-map "\C-hk" 'describe-key))
+
+(defun gnus-sticky-article (arg)
+  "Make the current article sticky.
+If a prefix ARG is given, ask for a name for this sticky article buffer."
+  (interactive "P")
+  (gnus-summary-show-thread)
+  (gnus-summary-select-article nil nil 'pseudo)
+  (let (new-art-buf-name)
+    (gnus-eval-in-buffer-window gnus-article-buffer
+      (setq new-art-buf-name
+           (concat
+            "*Sticky Article: "
+            (if arg
+                (read-from-minibuffer "Sticky article buffer name: ")
+              (gnus-with-article-headers
+                (gnus-article-goto-header "subject")
+                (setq new-art-buf-name
+                      (buffer-substring-no-properties
+                       (line-beginning-position) (line-end-position)))
+                (goto-char (point-min))
+                (gnus-article-goto-header "from")
+                (setq new-art-buf-name
+                      (concat
+                       new-art-buf-name ", "
+                       (buffer-substring-no-properties
+                        (line-beginning-position) (line-end-position))))
+                (goto-char (point-min))
+                (gnus-article-goto-header "date")
+                (setq new-art-buf-name
+                      (concat
+                       new-art-buf-name ", "
+                       (buffer-substring-no-properties
+                        (line-beginning-position) (line-end-position))))))
+            "*"))
+      (if (and (gnus-buffer-live-p new-art-buf-name)
+              (with-current-buffer new-art-buf-name
+                (eq major-mode 'gnus-sticky-article-mode)))
+         (switch-to-buffer new-art-buf-name)
+       (setq new-art-buf-name (rename-buffer new-art-buf-name t)))
+      (gnus-sticky-article-mode))
+    (setq gnus-article-buffer new-art-buf-name))
+  (gnus-summary-recenter)
+  (gnus-summary-position-point))
+
+(defun gnus-kill-sticky-article-buffer (&optional buffer)
+  "Kill the given sticky article BUFFER.
+If none is given, assume the current buffer and kill it if it has
+`gnus-sticky-article-mode'."
+  (interactive)
+  (unless buffer
+    (setq buffer (current-buffer)))
+  (with-current-buffer buffer
+    (when (eq major-mode 'gnus-sticky-article-mode)
+      (gnus-kill-buffer buffer))))
+
+(defun gnus-kill-sticky-article-buffers (arg)
+  "Kill all sticky article buffers.
+If a prefix ARG is given, ask for confirmation."
+  (interactive "P")
+  (dolist (buf (gnus-buffers))
+    (with-current-buffer buf
+      (when (eq major-mode 'gnus-sticky-article-mode)
+       (if (not arg)
+           (gnus-kill-buffer buf)
+         (when (yes-or-no-p (concat "Kill buffer " (buffer-name buf) "? "))
+           (gnus-kill-buffer buf)))))))
+
 ;;;
 ;;; Gnus MIME viewing functions
 ;;;
 ;;;
 ;;; Gnus MIME viewing functions
 ;;;
@@ -4534,17 +4833,20 @@ and `gnus-mime-delete-part', and not provided at run-time normally."
     (gnus-summary-show-article)
     (when (and current-id (integerp gnus-auto-select-part))
       (gnus-article-jump-to-part
     (gnus-summary-show-article)
     (when (and current-id (integerp gnus-auto-select-part))
       (gnus-article-jump-to-part
-       (+ current-id gnus-auto-select-part)))))
+       (if (text-property-any (point-min) (point-max)
+                             'gnus-part (+ current-id gnus-auto-select-part))
+          (+ current-id gnus-auto-select-part)
+        (with-current-buffer gnus-article-buffer
+          (length gnus-article-mime-handle-alist)))))))
 
 (defun gnus-mime-replace-part (file)
   "Replace MIME part under point with an external body."
   ;; Useful if file has already been saved to disk
   (interactive
    (list
 
 (defun gnus-mime-replace-part (file)
   "Replace MIME part under point with an external body."
   ;; Useful if file has already been saved to disk
   (interactive
    (list
-    (mm-with-multibyte
-      (read-file-name "Replace MIME part with file: "
-                     (or mm-default-directory default-directory)
-                     nil nil))))
+    (read-file-name "Replace MIME part with file: "
+                    (or mm-default-directory default-directory)
+                    nil nil)))
   (gnus-mime-save-part-and-strip file))
 
 (defun gnus-mime-save-part-and-strip (&optional file)
   (gnus-mime-save-part-and-strip file))
 
 (defun gnus-mime-save-part-and-strip (&optional file)
@@ -4606,8 +4908,9 @@ Deleting parts may malfunction or destroy the article; continue? "))
           (handles gnus-article-mime-handles)
           (none "(none)")
           (description
           (handles gnus-article-mime-handles)
           (none "(none)")
           (description
-           (mail-decode-encoded-word-string (or (mm-handle-description data)
-                                                none)))
+           (let ((desc (mm-handle-description data)))
+             (when desc
+               (mail-decode-encoded-word-string desc))))
           (filename
            (or (mail-content-type-get (mm-handle-disposition data) 'filename)
                none))
           (filename
            (or (mail-content-type-get (mm-handle-disposition data) 'filename)
                none))
@@ -4625,7 +4928,8 @@ Deleting parts may malfunction or destroy the article; continue? "))
            "| Type:           " type "\n"
            "| Filename:       " filename "\n"
            "| Size (encoded): " bsize " Byte\n"
            "| Type:           " type "\n"
            "| Filename:       " filename "\n"
            "| Size (encoded): " bsize " Byte\n"
-           "| Description:    " description "\n"
+           (when description
+             (concat    "| Description:    " description "\n"))
            "`----\n"))
          (setcdr data
                  (cdr (mm-make-handle
            "`----\n"))
          (setcdr data
                  (cdr (mm-make-handle
@@ -4671,7 +4975,11 @@ Deleting parts may malfunction or destroy the article; continue? "))
                ;; Content-Disposition: attachment; filename=...
                (cdr (assq 'filename (cdr (mm-handle-disposition handle))))))
         (def-type (and name (mm-default-file-encoding name))))
                ;; Content-Disposition: attachment; filename=...
                (cdr (assq 'filename (cdr (mm-handle-disposition handle))))))
         (def-type (and name (mm-default-file-encoding name))))
-    (and def-type (cons def-type 0))))
+    (or (and def-type (cons def-type 0))
+       (and handle
+            (equal (mm-handle-media-supertype handle) "text")
+            '("text/plain" . 0))
+       '("application/octet-stream" . 0))))
 
 (defun gnus-mime-view-part-as-type (&optional mime-type pred)
   "Choose a MIME media type, and view the part as such.
 
 (defun gnus-mime-view-part-as-type (&optional mime-type pred)
   "Choose a MIME media type, and view the part as such.
@@ -4705,6 +5013,8 @@ available media-types."
                            (mm-handle-id handle)))
       (setq gnus-article-mime-handles
            (mm-merge-handles gnus-article-mime-handles handle))
                            (mm-handle-id handle)))
       (setq gnus-article-mime-handles
            (mm-merge-handles gnus-article-mime-handles handle))
+      (when (mm-handle-displayed-p handle)
+       (mm-remove-part handle))
       (gnus-mm-display-part handle))))
 
 (defun gnus-mime-copy-part (&optional handle arg)
       (gnus-mm-display-part handle))))
 
 (defun gnus-mime-copy-part (&optional handle arg)
@@ -4872,12 +5182,15 @@ specified charset."
        (gnus-newsgroup-ignored-charsets 'gnus-all)
        gnus-newsgroup-charset form preferred parts)
     (when handle
        (gnus-newsgroup-ignored-charsets 'gnus-all)
        gnus-newsgroup-charset form preferred parts)
     (when handle
-      (if (mm-handle-undisplayer handle)
-         (mm-remove-part handle))
-      (when fun
-       (setq gnus-newsgroup-charset
-             (or (cdr (assq arg gnus-summary-show-article-charset-alist))
-                 (mm-read-coding-system "Charset: ")))
+      (when (prog1
+               (and fun
+                    (setq gnus-newsgroup-charset
+                          (or (cdr (assq
+                                    arg
+                                    gnus-summary-show-article-charset-alist))
+                              (mm-read-coding-system "Charset: "))))
+             (if (mm-handle-undisplayer handle)
+                 (mm-remove-part handle)))
        (gnus-mime-strip-charset-parameters handle)
        (when (and (consp (setq form (cdr-safe fun)))
                   (setq form (ignore-errors
        (gnus-mime-strip-charset-parameters handle)
        (when (and (consp (setq form (cdr-safe fun)))
                   (setq form (ignore-errors
@@ -4965,6 +5278,25 @@ If INTERACTIVE, call FUNCTION interactivly."
       (unless (with-current-buffer gnus-summary-buffer
                (eq gnus-current-article (gnus-summary-article-number)))
        (error "You should select the right article first"))
       (unless (with-current-buffer gnus-summary-buffer
                (eq gnus-current-article (gnus-summary-article-number)))
        (error "You should select the right article first"))
+      (if n
+         (setq n (prefix-numeric-value n))
+       (let ((pt (point)))
+         (setq n (or (get-text-property pt 'gnus-part)
+                     (and (not (bobp))
+                          (get-text-property (1- pt) 'gnus-part))
+                     (get-text-property (prog2
+                                            (forward-line 1)
+                                            (point)
+                                          (goto-char pt))
+                                        'gnus-part)
+                     (get-text-property
+                      (or (and (setq pt (previous-single-property-change
+                                         pt 'gnus-part))
+                               (1- pt))
+                          (next-single-property-change (point) 'gnus-part)
+                          (point))
+                      'gnus-part)
+                     1))))
       ;; Check whether the specified part exists.
       (when (> n (length gnus-article-mime-handle-alist))
        (error "No such part")))
       ;; Check whether the specified part exists.
       (when (> n (length gnus-article-mime-handle-alist))
        (error "No such part")))
@@ -5015,62 +5347,62 @@ If INTERACTIVE, call FUNCTION interactivly."
 
 (defun gnus-article-pipe-part (n)
   "Pipe MIME part N, which is the numerical prefix."
 
 (defun gnus-article-pipe-part (n)
   "Pipe MIME part N, which is the numerical prefix."
-  (interactive "p")
+  (interactive "P")
   (gnus-article-part-wrapper n 'mm-pipe-part))
 
 (defun gnus-article-save-part (n)
   "Save MIME part N, which is the numerical prefix."
   (gnus-article-part-wrapper n 'mm-pipe-part))
 
 (defun gnus-article-save-part (n)
   "Save MIME part N, which is the numerical prefix."
-  (interactive "p")
+  (interactive "P")
   (gnus-article-part-wrapper n 'mm-save-part))
 
 (defun gnus-article-interactively-view-part (n)
   "View MIME part N interactively, which is the numerical prefix."
   (gnus-article-part-wrapper n 'mm-save-part))
 
 (defun gnus-article-interactively-view-part (n)
   "View MIME part N interactively, which is the numerical prefix."
-  (interactive "p")
+  (interactive "P")
   (gnus-article-part-wrapper n 'mm-interactively-view-part))
 
 (defun gnus-article-copy-part (n)
   "Copy MIME part N, which is the numerical prefix."
   (gnus-article-part-wrapper n 'mm-interactively-view-part))
 
 (defun gnus-article-copy-part (n)
   "Copy MIME part N, which is the numerical prefix."
-  (interactive "p")
+  (interactive "P")
   (gnus-article-part-wrapper n 'gnus-mime-copy-part))
 
 (defun gnus-article-view-part-as-charset (n)
   "View MIME part N using a specified charset.
 N is the numerical prefix."
   (gnus-article-part-wrapper n 'gnus-mime-copy-part))
 
 (defun gnus-article-view-part-as-charset (n)
   "View MIME part N using a specified charset.
 N is the numerical prefix."
-  (interactive "p")
+  (interactive "P")
   (gnus-article-part-wrapper n 'gnus-mime-view-part-as-charset))
 
 (defun gnus-article-view-part-externally (n)
   "View MIME part N externally, which is the numerical prefix."
   (gnus-article-part-wrapper n 'gnus-mime-view-part-as-charset))
 
 (defun gnus-article-view-part-externally (n)
   "View MIME part N externally, which is the numerical prefix."
-  (interactive "p")
+  (interactive "P")
   (gnus-article-part-wrapper n 'gnus-mime-view-part-externally))
 
 (defun gnus-article-inline-part (n)
   "Inline MIME part N, which is the numerical prefix."
   (gnus-article-part-wrapper n 'gnus-mime-view-part-externally))
 
 (defun gnus-article-inline-part (n)
   "Inline MIME part N, which is the numerical prefix."
-  (interactive "p")
+  (interactive "P")
   (gnus-article-part-wrapper n 'gnus-mime-inline-part))
 
 (defun gnus-article-save-part-and-strip (n)
   "Save MIME part N and replace it with an external body.
 N is the numerical prefix."
   (gnus-article-part-wrapper n 'gnus-mime-inline-part))
 
 (defun gnus-article-save-part-and-strip (n)
   "Save MIME part N and replace it with an external body.
 N is the numerical prefix."
-  (interactive "p")
+  (interactive "P")
   (gnus-article-part-wrapper n 'gnus-mime-save-part-and-strip t))
 
 (defun gnus-article-replace-part (n)
   "Replace MIME part N with an external body.
 N is the numerical prefix."
   (gnus-article-part-wrapper n 'gnus-mime-save-part-and-strip t))
 
 (defun gnus-article-replace-part (n)
   "Replace MIME part N with an external body.
 N is the numerical prefix."
-  (interactive "p")
+  (interactive "P")
   (gnus-article-part-wrapper n 'gnus-mime-replace-part t t))
 
 (defun gnus-article-delete-part (n)
   "Delete MIME part N and add some information about the removed part.
 N is the numerical prefix."
   (gnus-article-part-wrapper n 'gnus-mime-replace-part t t))
 
 (defun gnus-article-delete-part (n)
   "Delete MIME part N and add some information about the removed part.
 N is the numerical prefix."
-  (interactive "p")
+  (interactive "P")
   (gnus-article-part-wrapper n 'gnus-mime-delete-part t))
 
 (defun gnus-article-view-part-as-type (n)
   "Choose a MIME media type, and view part N as such.
 N is the numerical prefix."
   (gnus-article-part-wrapper n 'gnus-mime-delete-part t))
 
 (defun gnus-article-view-part-as-type (n)
   "Choose a MIME media type, and view part N as such.
 N is the numerical prefix."
-  (interactive "p")
+  (interactive "P")
   (gnus-article-part-wrapper n 'gnus-mime-view-part-as-type t))
 
 (defun gnus-article-mime-match-handle-first (condition)
   (gnus-article-part-wrapper n 'gnus-mime-view-part-as-type t))
 
 (defun gnus-article-mime-match-handle-first (condition)
@@ -5170,9 +5502,7 @@ N is the numerical prefix."
             (mail-content-type-get (mm-handle-type handle) 'url)
             ""))
        (gnus-tmp-type (mm-handle-media-type handle))
             (mail-content-type-get (mm-handle-type handle) 'url)
             ""))
        (gnus-tmp-type (mm-handle-media-type handle))
-       (gnus-tmp-description
-        (mail-decode-encoded-word-string (or (mm-handle-description handle)
-                                             "")))
+       (gnus-tmp-description (or (mm-handle-description handle) ""))
        (gnus-tmp-dots
         (if (if displayed (car displayed)
               (mm-handle-displayed-p handle))
        (gnus-tmp-dots
         (if (if displayed (car displayed)
               (mm-handle-displayed-p handle))
@@ -5431,8 +5761,9 @@ If displaying \"text/html\" is discouraged \(see
            (gnus-article-insert-newline)
            (mm-insert-inline
             handle
            (gnus-article-insert-newline)
            (mm-insert-inline
             handle
-            (let ((charset (mail-content-type-get (mm-handle-type handle)
-                                                  'charset)))
+            (let ((charset (or (mail-content-type-get (mm-handle-type handle)
+                                                      'charset)
+                               (and (equal type "text/calendar") 'utf-8))))
               (cond ((not charset)
                      (mm-string-as-multibyte (mm-get-part handle)))
                     ((eq charset 'gnus-decoded)
               (cond ((not charset)
                      (mm-string-as-multibyte (mm-get-part handle)))
                     ((eq charset 'gnus-decoded)
@@ -5445,10 +5776,21 @@ If displaying \"text/html\" is discouraged \(see
          (save-excursion
            (save-restriction
              (narrow-to-region beg (point))
          (save-excursion
            (save-restriction
              (narrow-to-region beg (point))
-             (gnus-treat-article
-              nil id
-              (gnus-article-mime-total-parts)
-              (mm-handle-media-type handle)))))))))
+             (if (eq handle gnus-article-mime-handles)
+                 ;; The format=flowed case.
+                 (gnus-treat-article nil 1 1 (mm-handle-media-type handle))
+               ;; Don't count signature parts that are never displayed.
+               ;; The part number should be re-calculated supposing this
+               ;; might be a message/rfc822 part.
+               (let (handles)
+                 (dolist (part gnus-article-mime-handles)
+                   (unless (or (stringp part)
+                               (equal (car (mm-handle-type part))
+                                      "application/pgp-signature"))
+                     (push part handles)))
+                 (gnus-treat-article
+                  nil (length (memq handle handles)) (length handles)
+                  (mm-handle-media-type handle)))))))))))
 
 (defun gnus-unbuttonized-mime-type-p (type)
   "Say whether TYPE is to be unbuttonized."
 
 (defun gnus-unbuttonized-mime-type-p (type)
   "Say whether TYPE is to be unbuttonized."
@@ -5707,41 +6049,52 @@ the coding cookie."
 If given a numerical ARG, move forward ARG pages."
   (interactive "P")
   (setq arg (if arg (prefix-numeric-value arg) 0))
 If given a numerical ARG, move forward ARG pages."
   (interactive "P")
   (setq arg (if arg (prefix-numeric-value arg) 0))
-  (save-excursion
-    (set-buffer gnus-article-buffer)
-    (goto-char (point-min))
+  (with-current-buffer gnus-article-buffer
     (widen)
     ;; Remove any old next/prev buttons.
     (when (gnus-visual-p 'page-marker)
       (let ((inhibit-read-only t))
        (gnus-remove-text-with-property 'gnus-prev)
        (gnus-remove-text-with-property 'gnus-next)))
     (widen)
     ;; Remove any old next/prev buttons.
     (when (gnus-visual-p 'page-marker)
       (let ((inhibit-read-only t))
        (gnus-remove-text-with-property 'gnus-prev)
        (gnus-remove-text-with-property 'gnus-next)))
-    (if
-       (cond ((< arg 0)
-              (re-search-backward page-delimiter nil 'move (1+ (abs arg))))
-             ((> arg 0)
-              (re-search-forward page-delimiter nil 'move arg)))
-       (goto-char (match-end 0))
-      (save-excursion
-       (goto-char (point-min))
-       (setq gnus-page-broken
-             (and (re-search-forward page-delimiter nil t) t))))
-    (when gnus-page-broken
-      (narrow-to-region
-       (point)
-       (if (re-search-forward page-delimiter nil 'move)
-          (match-beginning 0)
-        (point)))
-      (when (and (gnus-visual-p 'page-marker)
-                (> (point-min) (save-restriction (widen) (point-min))))
-       (save-excursion
-         (goto-char (point-min))
-         (gnus-insert-prev-page-button)))
-      (when (and (gnus-visual-p 'page-marker)
-                (< (point-max) (save-restriction (widen) (point-max))))
-       (save-excursion
-         (goto-char (point-max))
-         (gnus-insert-next-page-button))))))
+    (let (st nd pt)
+      (when (save-excursion
+             (cond ((< arg 0)
+                    (if (re-search-backward page-delimiter nil 'move (abs arg))
+                        (prog1
+                            (setq nd (match-beginning 0)
+                                  pt nd)
+                          (when (re-search-backward page-delimiter nil t)
+                            (setq st (match-end 0))))
+                      (when (re-search-forward page-delimiter nil t)
+                        (setq nd (match-beginning 0)
+                              pt (point-min)))))
+                   ((> arg 0)
+                    (if (re-search-forward page-delimiter nil 'move arg)
+                        (prog1
+                            (setq st (match-end 0)
+                                  pt st)
+                          (when (re-search-forward page-delimiter nil t)
+                            (setq nd (match-beginning 0))))
+                      (when (re-search-backward page-delimiter nil t)
+                        (setq st (match-end 0)
+                              pt (point-max)))))
+                   (t
+                    (when (re-search-backward page-delimiter nil t)
+                      (goto-char (setq st (match-end 0))))
+                    (when (re-search-forward page-delimiter nil t)
+                      (setq nd (match-beginning 0)))
+                    (or st nd))))
+       (setq gnus-page-broken t)
+       (when pt (goto-char pt))
+       (narrow-to-region (or st (point-min)) (or nd (point-max)))
+       (when (gnus-visual-p 'page-marker)
+         (save-excursion
+           (when nd
+             (goto-char nd)
+             (gnus-insert-next-page-button))
+           (when st
+             (goto-char st)
+             (gnus-insert-prev-page-button))))))))
 
 ;; Article mode commands
 
 
 ;; Article mode commands
 
@@ -5756,7 +6109,7 @@ If given a numerical ARG, move forward ARG pages."
 (defun gnus-article-goto-prev-page ()
   "Show the previous page of the article."
   (interactive)
 (defun gnus-article-goto-prev-page ()
   "Show the previous page of the article."
   (interactive)
-  (if (bobp)
+  (if (save-restriction (widen) (bobp)) ;; Real beginning-of-buffer?
       (gnus-article-read-summary-keys nil (gnus-character-to-event ?p))
     (gnus-article-prev-page nil)))
 
       (gnus-article-read-summary-keys nil (gnus-character-to-event ?p))
     (gnus-article-prev-page nil)))
 
@@ -5780,10 +6133,16 @@ If end of article, return non-nil.  Otherwise return nil.
 Argument LINES specifies lines to be scrolled up."
   (interactive "p")
   (move-to-window-line -1)
 Argument LINES specifies lines to be scrolled up."
   (interactive "p")
   (move-to-window-line -1)
-  (if (save-excursion
-       (end-of-line)
-       (and (pos-visible-in-window-p)  ;Not continuation line.
-            (>= (1+ (point)) (point-max)))) ;Allow for trailing newline.
+  (if (and (not (and gnus-article-over-scroll
+                    (> (count-lines (window-start) (point-max))
+                       (+ (or lines (1- (window-height)))
+                          (or (and (boundp 'scroll-margin)
+                                   (symbol-value 'scroll-margin))
+                              0)))))
+          (save-excursion
+            (end-of-line)
+            (and (pos-visible-in-window-p)     ;Not continuation line.
+                 (>= (1+ (point)) (point-max))))) ;Allow for trailing newline.
       ;; Nothing in this page.
       (if (or (not gnus-page-broken)
              (save-excursion
       ;; Nothing in this page.
       (if (or (not gnus-page-broken)
              (save-excursion
@@ -5843,7 +6202,14 @@ Argument LINES specifies lines to be scrolled down."
       (progn
        (gnus-narrow-to-page -1)        ;Go to previous page.
        (goto-char (point-max))
       (progn
        (gnus-narrow-to-page -1)        ;Go to previous page.
        (goto-char (point-max))
-       (recenter -1))
+       (recenter (if gnus-article-over-scroll
+                     (if lines
+                         (max (+ lines (or (and (boundp 'scroll-margin)
+                                                (symbol-value 'scroll-margin))
+                                           0))
+                              3)
+                       (- (window-height) 2))
+                   -1)))
     (prog1
        (condition-case ()
            (let ((scroll-in-place nil))
     (prog1
        (condition-case ()
            (let ((scroll-in-place nil))
@@ -5914,7 +6280,7 @@ not have a face in `gnus-article-boring-faces'."
   "Execute the last keystroke in the summary buffer."
   (interactive)
   (let (func)
   "Execute the last keystroke in the summary buffer."
   (interactive)
   (let (func)
-    (pop-to-buffer gnus-article-current-summary nil 'norecord)
+    (pop-to-buffer gnus-article-current-summary)
     (setq func (lookup-key (current-local-map) (this-command-keys)))
     (call-interactively func)))
 
     (setq func (lookup-key (current-local-map) (this-command-keys)))
     (call-interactively func)))
 
@@ -5932,46 +6298,51 @@ not have a face in `gnus-article-boring-faces'."
           "Zc" "ZC" "ZE" "ZQ" "ZZ" "Zn" "ZR" "ZG" "ZN" "ZP"
           "=" "^" "\M-^" "|"))
        (nosave-but-article
           "Zc" "ZC" "ZE" "ZQ" "ZZ" "Zn" "ZR" "ZG" "ZN" "ZP"
           "=" "^" "\M-^" "|"))
        (nosave-but-article
-        '("A\r"))
+        '("A " "A<" "A>" "AM" "AP" "AR" "AT" "A\C-?" "A\M-\r" "A\r" "Ab" "Ae"
+          "An" "Ap" [?A (meta return)] [?A delete]))
        (nosave-in-article
        (nosave-in-article
-        '("\C-d"))
+        '("AS" "\C-d"))
        (up-to-top
         '("n" "Gn" "p" "Gp"))
        keys new-sum-point)
        (up-to-top
         '("n" "Gn" "p" "Gp"))
        keys new-sum-point)
-    (save-excursion
-      (set-buffer gnus-article-current-summary)
+    (with-current-buffer gnus-article-current-summary
       (let (gnus-pick-mode)
       (let (gnus-pick-mode)
-       (push (or key last-command-event) unread-command-events)
-       (setq keys (if (featurep 'xemacs)
-                      (events-to-keys (read-key-sequence nil))
-                    (read-key-sequence nil)))))
+       (setq unread-command-events (nconc unread-command-events
+                                          (list (or key last-command-event)))
+             keys (if (featurep 'xemacs)
+                      (events-to-keys (read-key-sequence nil t))
+                    (read-key-sequence nil t)))))
 
     (message "")
 
 
     (message "")
 
-    (if (or (member keys nosaves)
-           (member keys nosave-but-article)
-           (member keys nosave-in-article))
-       (let (func)
-         (save-window-excursion
-           (pop-to-buffer gnus-article-current-summary nil 'norecord)
-           ;; We disable the pick minor mode commands.
-           (let (gnus-pick-mode)
-             (setq func (lookup-key (current-local-map) keys))))
-         (if (or (not func)
-                 (numberp func))
-             (ding)
-           (unless (member keys nosave-in-article)
-             (set-buffer gnus-article-current-summary))
-           (call-interactively func)
-           (setq new-sum-point (point)))
-         (when (member keys nosave-but-article)
-           (pop-to-buffer gnus-article-buffer nil 'norecord)))
+    (cond
+     ((eq (aref keys (1- (length keys))) ?\C-h)
+      (gnus-article-describe-bindings (substring keys 0 -1)))
+     ((or (member keys nosaves)
+         (member keys nosave-but-article)
+         (member keys nosave-in-article))
+      (let (func)
+       (save-window-excursion
+         (pop-to-buffer gnus-article-current-summary)
+         ;; We disable the pick minor mode commands.
+         (let (gnus-pick-mode)
+           (setq func (lookup-key (current-local-map) keys))))
+       (if (or (not func)
+               (numberp func))
+           (ding)
+         (unless (member keys nosave-in-article)
+           (set-buffer gnus-article-current-summary))
+         (call-interactively func)
+         (setq new-sum-point (point)))
+       (when (member keys nosave-but-article)
+         (pop-to-buffer gnus-article-buffer))))
+     (t
       ;; These commands should restore window configuration.
       (let ((obuf (current-buffer))
            (owin (current-window-configuration))
            win func in-buffer selected new-sum-start new-sum-hscroll err)
        (cond (not-restore-window
       ;; These commands should restore window configuration.
       (let ((obuf (current-buffer))
            (owin (current-window-configuration))
            win func in-buffer selected new-sum-start new-sum-hscroll err)
        (cond (not-restore-window
-              (pop-to-buffer gnus-article-current-summary nil 'norecord)
+              (pop-to-buffer gnus-article-current-summary)
               (setq win (selected-window)))
              ((setq win (get-buffer-window gnus-article-current-summary))
               (select-window win))
               (setq win (selected-window)))
              ((setq win (get-buffer-window gnus-article-current-summary))
               (select-window win))
@@ -5993,7 +6364,9 @@ not have a face in `gnus-article-boring-faces'."
                              (lookup-key (current-local-map) keys)))
                 (functionp func)
                 (condition-case code
                              (lookup-key (current-local-map) keys)))
                 (functionp func)
                 (condition-case code
-                    (call-interactively func)
+                    (progn
+                      (call-interactively func)
+                      t)
                   (error
                    (setq err code)
                    nil)))
                   (error
                    (setq err code)
                    nil)))
@@ -6028,55 +6401,112 @@ not have a face in `gnus-article-boring-faces'."
          (set-window-configuration owin)
          (if err
              (signal (car err) (cdr err))
          (set-window-configuration owin)
          (if err
              (signal (car err) (cdr err))
-           (ding)))))))
+           (ding))))))))
+
+(defun gnus-article-read-summary-send-keys ()
+  (interactive)
+  (let ((unread-command-events (list (gnus-character-to-event ?S))))
+    (gnus-article-read-summary-keys)))
 
 (defun gnus-article-describe-key (key)
 
 (defun gnus-article-describe-key (key)
-  "Display documentation of the function invoked by KEY.  KEY is a string."
-  (interactive "kDescribe key: ")
+  "Display documentation of the function invoked by KEY.
+KEY is a string or a vector."
+  (interactive (list (let ((cursor-in-echo-area t)) ;; better for XEmacs.
+                      (read-key-sequence "Describe key: "))))
   (gnus-article-check-buffer)
   (gnus-article-check-buffer)
-  (if (eq (key-binding key) 'gnus-article-read-summary-keys)
-      (save-excursion
-       (set-buffer gnus-article-current-summary)
-       (let (gnus-pick-mode)
-         (if (featurep 'xemacs)
-             (progn
-               (push (elt key 0) unread-command-events)
-               (setq key (events-to-keys
-                          (read-key-sequence "Describe key: "))))
-           (setq unread-command-events
-                 (mapcar
-                  (lambda (x) (if (>= x 128) (list 'meta (- x 128)) x))
-                  (string-to-list key)))
-           (setq key (read-key-sequence "Describe key: "))))
-       (describe-key key))
+  (if (memq (key-binding key t) '(gnus-article-read-summary-keys
+                                 gnus-article-read-summary-send-keys))
+      (with-current-buffer gnus-article-current-summary
+       (setq unread-command-events
+             (if (featurep 'xemacs)
+                 (append key nil)
+               (mapcar (lambda (x) (if (and (integerp x) (>= x 128))
+                                       (list 'meta (- x 128))
+                                     x))
+                       key)))
+       (let ((cursor-in-echo-area t)
+             gnus-pick-mode)
+         (describe-key (read-key-sequence nil t))))
     (describe-key key)))
 
 (defun gnus-article-describe-key-briefly (key &optional insert)
     (describe-key key)))
 
 (defun gnus-article-describe-key-briefly (key &optional insert)
-  "Display documentation of the function invoked by KEY.  KEY is a string."
-  (interactive "kDescribe key: \nP")
+  "Display documentation of the function invoked by KEY.
+KEY is a string or a vector."
+  (interactive (list (let ((cursor-in-echo-area t)) ;; better for XEmacs.
+                      (read-key-sequence "Describe key: "))
+                    current-prefix-arg))
   (gnus-article-check-buffer)
   (gnus-article-check-buffer)
-  (if (eq (key-binding key) 'gnus-article-read-summary-keys)
-      (save-excursion
-       (set-buffer gnus-article-current-summary)
-       (let (gnus-pick-mode)
-         (if (featurep 'xemacs)
-             (progn
-               (push (elt key 0) unread-command-events)
-               (setq key (events-to-keys
-                          (read-key-sequence "Describe key: "))))
-           (setq unread-command-events
-                 (mapcar
-                  (lambda (x) (if (>= x 128) (list 'meta (- x 128)) x))
-                  (string-to-list key)))
-           (setq key (read-key-sequence "Describe key: "))))
-       (describe-key-briefly key insert))
+  (if (memq (key-binding key t) '(gnus-article-read-summary-keys
+                                 gnus-article-read-summary-send-keys))
+      (with-current-buffer gnus-article-current-summary
+       (setq unread-command-events
+             (if (featurep 'xemacs)
+                 (append key nil)
+               (mapcar (lambda (x) (if (and (integerp x) (>= x 128))
+                                       (list 'meta (- x 128))
+                                     x))
+                       key)))
+       (let ((cursor-in-echo-area t)
+             gnus-pick-mode)
+         (describe-key-briefly (read-key-sequence nil t) insert)))
     (describe-key-briefly key insert)))
 
     (describe-key-briefly key insert)))
 
+;;`gnus-agent-mode' in gnus-agent.el will define it.
+(defvar gnus-agent-summary-mode)
+(defvar gnus-draft-mode)
+
+(defun gnus-article-describe-bindings (&optional prefix)
+  "Show a list of all defined keys, and their definitions.
+The optional argument PREFIX, if non-nil, should be a key sequence;
+then we display only bindings that start with that prefix."
+  (interactive)
+  (gnus-article-check-buffer)
+  (let ((keymap (copy-keymap gnus-article-mode-map))
+       (map (copy-keymap gnus-article-send-map))
+       (sumkeys (where-is-internal 'gnus-article-read-summary-keys))
+       agent draft)
+    (define-key keymap "S" map)
+    (define-key map [t] nil)
+    (with-current-buffer gnus-article-current-summary
+      (set-keymap-parent map (key-binding "S"))
+      (let (key def gnus-pick-mode)
+       (while sumkeys
+         (setq key (pop sumkeys))
+         (cond ((and (vectorp key) (= (length key) 1)
+                     (consp (setq def (aref key 0)))
+                     (numberp (car def)) (numberp (cdr def)))
+                (when (< (max (car def) (cdr def)) 128)
+                  (setq sumkeys
+                        (append (mapcar
+                                 #'vector
+                                 (nreverse (gnus-uncompress-range def)))
+                                sumkeys))))
+               ((setq def (key-binding key))
+                (unless (eq def 'undefined)
+                  (define-key keymap key def))))))
+      (when (boundp 'gnus-agent-summary-mode)
+       (setq agent gnus-agent-summary-mode))
+      (when (boundp 'gnus-draft-mode)
+       (setq draft gnus-draft-mode)))
+    (with-temp-buffer
+      (use-local-map keymap)
+      (set (make-local-variable 'gnus-agent-summary-mode) agent)
+      (set (make-local-variable 'gnus-draft-mode) draft)
+      (describe-bindings prefix))
+    (let ((item `((lambda (prefix)
+                   (with-current-buffer ,(current-buffer)
+                     (gnus-article-describe-bindings prefix)))
+                 ,prefix)))
+      (with-current-buffer (if (fboundp 'help-buffer)
+                              (let (help-xref-following) (help-buffer))
+                            "*Help*") ;; Emacs 21
+       (setq help-xref-stack-item item)))))
+
 (defun gnus-article-reply-with-original (&optional wide)
   "Start composing a reply mail to the current message.
 The text in the region will be yanked.  If the region isn't active,
 the entire article will be yanked."
 (defun gnus-article-reply-with-original (&optional wide)
   "Start composing a reply mail to the current message.
 The text in the region will be yanked.  If the region isn't active,
 the entire article will be yanked."
-  (interactive "P")
+  (interactive)
   (let ((article (cdr gnus-article-current))
        contents)
     (if (not (gnus-region-active-p))
   (let ((article (cdr gnus-article-current))
        contents)
     (if (not (gnus-region-active-p))
@@ -6091,6 +6521,13 @@ the entire article will be yanked."
        (gnus-summary-reply
         (list (list article contents)) wide)))))
 
        (gnus-summary-reply
         (list (list article contents)) wide)))))
 
+(defun gnus-article-wide-reply-with-original ()
+  "Start composing a wide reply mail to the current message.
+The text in the region will be yanked.  If the region isn't active,
+the entire article will be yanked."
+  (interactive)
+  (gnus-article-reply-with-original t))
+
 (defun gnus-article-followup-with-original ()
   "Compose a followup to the current article.
 The text in the region will be yanked.  If the region isn't active,
 (defun gnus-article-followup-with-original ()
   "Compose a followup to the current article.
 The text in the region will be yanked.  If the region isn't active,
@@ -6161,8 +6598,7 @@ If given a prefix, show the hidden text instead."
                     gnus-summary-buffer
                     (get-buffer gnus-summary-buffer)
                     (gnus-buffer-exists-p gnus-summary-buffer))
                     gnus-summary-buffer
                     (get-buffer gnus-summary-buffer)
                     (gnus-buffer-exists-p gnus-summary-buffer))
-           (save-excursion
-             (set-buffer gnus-summary-buffer)
+           (with-current-buffer gnus-summary-buffer
              (let ((header (gnus-summary-article-header article)))
                (when (< article 0)
                  (cond
              (let ((header (gnus-summary-article-header article)))
                (when (< article 0)
                  (cond
@@ -6208,7 +6644,13 @@ If given a prefix, show the hidden text instead."
                 (with-current-buffer gnus-original-article-buffer
                   (and (equal (car gnus-original-article) group)
                        (eq (cdr gnus-original-article) article))))
                 (with-current-buffer gnus-original-article-buffer
                   (and (equal (car gnus-original-article) group)
                        (eq (cdr gnus-original-article) article))))
-           (insert-buffer-substring gnus-original-article-buffer)
+            ;; `insert-buffer-substring' would incorrectly use the
+            ;; equivalent of string-make-multibyte which amount to decoding
+            ;; with locale-coding-system, causing failure of
+            ;; subsequent decoding.
+            (insert (mm-string-to-multibyte
+                     (with-current-buffer gnus-original-article-buffer
+                       (buffer-substring (point-min) (point-max)))))
            'article)
           ;; Check the backlog.
           ((and gnus-keep-backlog
            'article)
           ;; Check the backlog.
           ((and gnus-keep-backlog
@@ -6454,9 +6896,8 @@ groups."
       (gnus-backlog-remove-article
        (car gnus-article-current) (cdr gnus-article-current)))
     ;; Flush original article as well.
       (gnus-backlog-remove-article
        (car gnus-article-current) (cdr gnus-article-current)))
     ;; Flush original article as well.
-    (save-excursion
-      (when (get-buffer gnus-original-article-buffer)
-       (set-buffer gnus-original-article-buffer)
+    (when (get-buffer gnus-original-article-buffer)
+      (with-current-buffer gnus-original-article-buffer
        (setq gnus-original-article nil)))
     (when gnus-use-cache
       (gnus-cache-update-article
        (setq gnus-original-article nil)))
     (when gnus-use-cache
       (gnus-cache-update-article
@@ -6521,7 +6962,8 @@ groups."
         (concat
          "\\(?:"
          ;; Match paired parentheses, e.g. in Wikipedia URLs:
         (concat
          "\\(?:"
          ;; Match paired parentheses, e.g. in Wikipedia URLs:
-         "[" chars punct "]+" "(" "[" chars punct "]+" "[" chars "]*)" "[" chars "]"
+         ;; http://thread.gmane.org/47B4E3B2.3050402@gmail.com
+         "[" chars punct "]+" "(" "[" chars punct "]+" "[" chars "]*)" "[" chars "]*"
          "\\|"
          "[" chars punct     "]+" "[" chars "]"
          "\\)"))
          "\\|"
          "[" chars punct     "]+" "[" chars "]"
          "\\)"))
@@ -6657,6 +7099,7 @@ must return `mid', `mail', `invalid' or `ask'."
     (-20.0 . "\\.fsf@")        ;; Gnus
     (-20.0 . "^slrn")
     (-20.0 . "^Pine")
     (-20.0 . "\\.fsf@")        ;; Gnus
     (-20.0 . "^slrn")
     (-20.0 . "^Pine")
+    (-20.0 . "^alpine\\.")
     (-20.0 . "_-_") ;; Subject change in thread
     ;;
     (-20.0 . "\\.ln@") ;; leafnode
     (-20.0 . "_-_") ;; Subject change in thread
     ;;
     (-20.0 . "\\.ln@") ;; leafnode
@@ -6965,9 +7408,9 @@ positives are possible."
      1 (>= gnus-button-emacs-level 1) gnus-button-handle-info-url-kde 2)
     ("\\((Info-goto-node\\|(info\\)[ \t\n]*\\(\"[^\"]*\"\\))" 0
      (>= gnus-button-emacs-level 1) gnus-button-handle-info-url 2)
      1 (>= gnus-button-emacs-level 1) gnus-button-handle-info-url-kde 2)
     ("\\((Info-goto-node\\|(info\\)[ \t\n]*\\(\"[^\"]*\"\\))" 0
      (>= gnus-button-emacs-level 1) gnus-button-handle-info-url 2)
-    ("\\b\\(C-h\\|<?[Ff]1>?\\)[ \t\n]+i[ \t\n]+d?[ \t\n]?m[ \t\n]+\\([^ ]+ ?[^ ]+\\)[ \t\n]+RET"
-     ;; Info links like `C-h i d m CC Mode RET'
-     0 (>= gnus-button-emacs-level 1) gnus-button-handle-info-keystrokes 2)
+    ("\\b\\(C-h\\|<?[Ff]1>?\\)[ \t\n]+i[ \t\n]+d?[ \t\n]?m[ \t\n]+[^ ]+ ?[^ ]+[ \t\n]+RET\\([ \t\n]+i[ \t\n]+[^ ]+ ?[^ ]+[ \t\n]+RET\\([ \t\n,]*\\)\\)?"
+     ;; Info links like `C-h i d m Gnus RET' or `C-h i d m Gnus RET i partial RET'
+     0 (>= gnus-button-emacs-level 1) gnus-button-handle-info-keystrokes 0)
     ;; This is custom
     ("M-x[ \t\n]\\(customize-[^ ]+\\)[ \t\n]RET[ \t\n]\\([^ ]+\\)[ \t\n]RET" 0
      (>= gnus-button-emacs-level 1) gnus-button-handle-custom 1 2)
     ;; This is custom
     ("M-x[ \t\n]\\(customize-[^ ]+\\)[ \t\n]RET[ \t\n]\\([^ ]+\\)[ \t\n]RET" 0
      (>= gnus-button-emacs-level 1) gnus-button-handle-custom 1 2)
@@ -7005,13 +7448,10 @@ positives are possible."
      ;; here to determine where it ends.
      1 (>= gnus-button-emacs-level 1) gnus-button-handle-describe-key 3)
     ;; This is how URLs _should_ be embedded in text (RFC 1738, RFC 2396)...
      ;; here to determine where it ends.
      1 (>= gnus-button-emacs-level 1) gnus-button-handle-describe-key 3)
     ;; This is how URLs _should_ be embedded in text (RFC 1738, RFC 2396)...
-    ("<URL: *\\([^<>]*\\)>"
-     1 (>= gnus-button-browse-level 0) gnus-button-embedded-url 1)
-    ;; RFC 2396 (2.4.3., delims) ...
-    ("\"URL: *\\([^\"]*\\)\""
+    ("<URL: *\\([^\n<>]*\\)>"
      1 (>= gnus-button-browse-level 0) gnus-button-embedded-url 1)
     ;; RFC 2396 (2.4.3., delims) ...
      1 (>= gnus-button-browse-level 0) gnus-button-embedded-url 1)
     ;; RFC 2396 (2.4.3., delims) ...
-    ("\"URL: *\\([^\"]*\\)\""
+    ("\"URL: *\\([^\n\"]*\\)\""
      1 (>= gnus-button-browse-level 0) gnus-button-embedded-url 1)
     ;; Raw URLs.
     (gnus-button-url-regexp
      1 (>= gnus-button-browse-level 0) gnus-button-embedded-url 1)
     ;; Raw URLs.
     (gnus-button-url-regexp
@@ -7063,6 +7503,7 @@ variable it the real callback function."
                       (repeat :tag "Par"
                               :inline t
                               (integer :tag "Regexp group")))))
                       (repeat :tag "Par"
                               :inline t
                               (integer :tag "Regexp group")))))
+(put 'gnus-button-alist 'risky-local-variable t)
 
 (defcustom gnus-header-button-alist
   '(("^\\(References\\|Message-I[Dd]\\|^In-Reply-To\\):" "<[^<>]+>"
 
 (defcustom gnus-header-button-alist
   '(("^\\(References\\|Message-I[Dd]\\|^In-Reply-To\\):" "<[^<>]+>"
@@ -7102,6 +7543,7 @@ HEADER is a regexp to match a header.  For a fuller explanation, see
                       (repeat :tag "Par"
                               :inline t
                               (integer :tag "Regexp group")))))
                       (repeat :tag "Par"
                               :inline t
                               (integer :tag "Regexp group")))))
+(put 'gnus-header-button-alist 'risky-local-variable t)
 
 ;;; Commands:
 
 
 ;;; Commands:
 
@@ -7193,7 +7635,7 @@ It does this by highlighting everything after
       (save-restriction
        (when (and gnus-signature-face
                   (gnus-article-narrow-to-signature))
       (save-restriction
        (when (and gnus-signature-face
                   (gnus-article-narrow-to-signature))
-         (gnus-overlay-put (gnus-make-overlay (point-min) (point-max))
+         (gnus-overlay-put (gnus-make-overlay (point-min) (point-max) nil t)
                            'face gnus-signature-face)
          (widen)
          (gnus-article-search-signature)
                            'face gnus-signature-face)
          (widen)
          (gnus-article-search-signature)
@@ -7235,19 +7677,79 @@ specified by `gnus-button-alist'."
        (setq regexp (eval (car entry)))
        (goto-char beg)
        (while (re-search-forward regexp nil t)
        (setq regexp (eval (car entry)))
        (goto-char beg)
        (while (re-search-forward regexp nil t)
-         (let* ((start (and entry (match-beginning (nth 1 entry))))
-                (end (and entry (match-end (nth 1 entry))))
-                (from (match-beginning 0)))
+         (let ((start (match-beginning (nth 1 entry)))
+               (end (match-end (nth 1 entry)))
+               (from (match-beginning 0)))
            (when (and (or (eq t (nth 2 entry))
                           (eval (nth 2 entry)))
                       (not (gnus-button-in-region-p
                             start end 'gnus-callback)))
              ;; That optional form returned non-nil, so we add the
              ;; button.
            (when (and (or (eq t (nth 2 entry))
                           (eval (nth 2 entry)))
                       (not (gnus-button-in-region-p
                             start end 'gnus-callback)))
              ;; That optional form returned non-nil, so we add the
              ;; button.
-             (gnus-article-add-button
-              start end 'gnus-button-push
-              (car (push (set-marker (make-marker) from)
-                         gnus-button-marker-list))))))))))
+             (setq from (set-marker (make-marker) from))
+             (push from gnus-button-marker-list)
+             (unless (and (eq (car entry) 'gnus-button-url-regexp)
+                          (gnus-article-extend-url-button from start end))
+               (gnus-article-add-button start end
+                                        'gnus-button-push from)))))))))
+
+(defun gnus-article-extend-url-button (beg start end)
+  "Extend url button if url is folded into two or more lines.
+Return non-nil if button is extended.  BEG is a marker that points to
+the beginning position of a text containing url.  START and END are
+the endpoints of a url button before it is extended.  The concatenated
+url is put as the `gnus-button-url' overlay property on the button."
+  (let ((opoint (point))
+       (points (list start end))
+       url delim regexp)
+    (prog1
+       (when (and (progn
+                    (goto-char end)
+                    (not (looking-at "[\t ]*[\">]")))
+                  (progn
+                    (goto-char start)
+                    (string-match
+                     "\\(?:\"\\|\\(<\\)\\)[\t ]*\\(?:url[\t ]*:[\t ]*\\)?\\'"
+                     (buffer-substring (point-at-bol) start)))
+                  (progn
+                    (setq url (list (buffer-substring start end))
+                          delim (if (match-beginning 1) ">" "\""))
+                    (beginning-of-line)
+                    (setq regexp (concat
+                                  (when (and (looking-at
+                                              message-cite-prefix-regexp)
+                                             (< (match-end 0) start))
+                                    (regexp-quote (match-string 0)))
+                                  "\
+\[\t ]*\\(?:\\([^\t\n \">]+\\)[\t ]*$\\|\\([^\t\n \">]*\\)[\t ]*"
+                                  delim "\\)"))
+                    (while (progn
+                             (forward-line 1)
+                             (and (looking-at regexp)
+                                  (prog1
+                                      (match-beginning 1)
+                                    (push (or (match-string 2)
+                                              (match-string 1))
+                                          url)
+                                    (push (setq end (or (match-end 2)
+                                                        (match-end 1)))
+                                          points)
+                                    (push (or (match-beginning 2)
+                                              (match-beginning 1))
+                                          points)))))
+                    (match-beginning 2)))
+         (let (gnus-article-mouse-face widget-mouse-face)
+           (while points
+             (gnus-article-add-button (pop points) (pop points)
+                                      'gnus-button-push beg)))
+         (let ((overlay (gnus-make-overlay start end)))
+           (gnus-overlay-put overlay 'evaporate t)
+           (gnus-overlay-put overlay 'gnus-button-url
+                             (list (mapconcat 'identity (nreverse url) "")))
+           (when gnus-article-mouse-face
+             (gnus-overlay-put overlay 'mouse-face gnus-article-mouse-face)))
+         t)
+      (goto-char opoint))))
 
 ;; Add buttons to the head of an article.
 (defun gnus-article-add-buttons-to-head ()
 
 ;; Add buttons to the head of an article.
 (defun gnus-article-add-buttons-to-head ()
@@ -7284,7 +7786,7 @@ specified by `gnus-button-alist'."
 (defun gnus-article-add-button (from to fun &optional data)
   "Create a button between FROM and TO with callback FUN and data DATA."
   (when gnus-article-button-face
 (defun gnus-article-add-button (from to fun &optional data)
   "Create a button between FROM and TO with callback FUN and data DATA."
   (when gnus-article-button-face
-    (gnus-overlay-put (gnus-make-overlay from to)
+    (gnus-overlay-put (gnus-make-overlay from to nil t)
                      'face gnus-article-button-face))
   (gnus-add-text-properties
    from to
                      'face gnus-article-button-face))
   (gnus-add-text-properties
    from to
@@ -7337,12 +7839,14 @@ specified by `gnus-button-alist'."
     (let* ((entry (gnus-button-entry))
           (inhibit-point-motion-hooks t)
           (fun (nth 3 entry))
     (let* ((entry (gnus-button-entry))
           (inhibit-point-motion-hooks t)
           (fun (nth 3 entry))
-          (args (mapcar (lambda (group)
-                          (let ((string (match-string group)))
-                            (set-text-properties
-                             0 (length string) nil string)
-                            string))
-                        (nthcdr 4 entry))))
+          (args (or (and (eq (car entry) 'gnus-button-url-regexp)
+                         (get-char-property marker 'gnus-button-url))
+                    (mapcar (lambda (group)
+                              (let ((string (match-string group)))
+                                (set-text-properties
+                                 0 (length string) nil string)
+                                string))
+                            (nthcdr 4 entry)))))
       (cond
        ((fboundp fun)
        (apply fun args))
       (cond
        ((fboundp fun)
        (apply fun args))
@@ -7386,8 +7890,7 @@ specified by `gnus-button-alist'."
       (gnus-parse-news-url url)
     (cond
      (message-id
       (gnus-parse-news-url url)
     (cond
      (message-id
-      (save-excursion
-       (set-buffer gnus-summary-buffer)
+      (with-current-buffer gnus-summary-buffer
        (if server
            (let ((gnus-refer-article-method
                   (nconc (list (list 'nntp server))
        (if server
            (let ((gnus-refer-article-method
                   (nconc (list (list 'nntp server))
@@ -7451,12 +7954,45 @@ specified by `gnus-button-alist'."
   "Fetch KDE style info URL."
   (gnus-info-find-node (gnus-url-unhex-string url)))
 
   "Fetch KDE style info URL."
   (gnus-info-find-node (gnus-url-unhex-string url)))
 
+;; (info) will autoload info.el
+(declare-function Info-menu "info" (menu-item &optional fork))
+(declare-function Info-index-next "info" (num))
+
 (defun gnus-button-handle-info-keystrokes (url)
   "Call `info' when pushing the corresponding URL button."
 (defun gnus-button-handle-info-keystrokes (url)
   "Call `info' when pushing the corresponding URL button."
-  ;; For links like `C-h i d m gnus RET', `C-h i d m CC Mode RET'.
-  (info)
-  (Info-directory)
-  (Info-menu url))
+  ;; For links like `C-h i d m gnus RET part RET , ,', `C-h i d m CC Mode RET'.
+  (let (node indx comma)
+    (if (string-match
+        (concat "\\b\\(C-h\\|<?[Ff]1>?\\)[ \t\n]+i[ \t\n]+d?[ \t\n]?m[ \t\n]+"
+                "\\([^ ]+ ?[^ ]+\\)[ \t\n]+RET"
+                "\\([ \t\n]+i[ \t\n]+[^ ]+ ?[^ ]+[ \t\n]+RET"
+                "\\(?:[ \t\n,]*\\)\\)?")
+        url)
+       (setq node (match-string 2 url)
+             indx (match-string 3 url))
+      (error "Can't parse %s" url))
+    (info)
+    (Info-directory)
+    (Info-menu node)
+    (when (> (length indx) 0)
+      (string-match (concat "[ \t\n]+i[ \t\n]+\\([^ ]+ ?[^ ]+\\)[ \t\n]+RET"
+                           "\\([ \t\n,]*\\)")
+                   indx)
+      (setq comma (match-string 2 indx))
+      (setq indx  (match-string 1 indx))
+      (Info-index indx)
+      (when comma
+       (dotimes (i (with-temp-buffer
+                     (insert comma)
+                     ;; Note: the XEmacs version of `how-many' takes
+                     ;; no optional argument.
+                     (goto-char (point-min))
+                     (how-many ",")))
+         (Info-index-next 1)))
+      nil)))
+
+;; Called after pgg-snarf-keys-region, which autoloads pgg.el.
+(declare-function pgg-display-output-buffer "pgg" (start end status))
 
 (defun gnus-button-openpgp (url)
   "Retrieve and add an OpenPGP key given URL from an OpenPGP header."
 
 (defun gnus-button-openpgp (url)
   "Retrieve and add an OpenPGP key given URL from an OpenPGP header."
@@ -7579,7 +8115,7 @@ specified by `gnus-button-alist'."
                (1- (point))
              (point)))
     (when gnus-article-button-face
                (1- (point))
              (point)))
     (when gnus-article-button-face
-      (gnus-overlay-put (gnus-make-overlay b e)
+      (gnus-overlay-put (gnus-make-overlay b e nil t)
                         'face gnus-article-button-face))
     (widget-convert-button
      'link b e
                         'face gnus-article-button-face))
     (widget-convert-button
      'link b e
@@ -7615,7 +8151,7 @@ specified by `gnus-button-alist'."
                (1- (point))
              (point)))
     (when gnus-article-button-face
                (1- (point))
              (point)))
     (when gnus-article-button-face
-      (gnus-overlay-put (gnus-make-overlay b e)
+      (gnus-overlay-put (gnus-make-overlay b e nil t)
                         'face gnus-article-button-face))
     (widget-convert-button
      'link b e
                         'face gnus-article-button-face))
     (widget-convert-button
      'link b e
@@ -7661,14 +8197,13 @@ For example:
               (eq gnus-newsgroup-name
                   (car gnus-decode-header-methods-cache)))
     (setq gnus-decode-header-methods-cache (list gnus-newsgroup-name))
               (eq gnus-newsgroup-name
                   (car gnus-decode-header-methods-cache)))
     (setq gnus-decode-header-methods-cache (list gnus-newsgroup-name))
-    (mapcar (lambda (x)
-             (if (symbolp x)
-                 (nconc gnus-decode-header-methods-cache (list x))
-               (if (and gnus-newsgroup-name
-                        (string-match (car x) gnus-newsgroup-name))
-                   (nconc gnus-decode-header-methods-cache
-                          (list (cdr x))))))
-         gnus-decode-header-methods))
+    (dolist (x gnus-decode-header-methods)
+      (if (symbolp x)
+         (nconc gnus-decode-header-methods-cache (list x))
+       (if (and gnus-newsgroup-name
+                (string-match (car x) gnus-newsgroup-name))
+           (nconc gnus-decode-header-methods-cache
+                  (list (cdr x)))))))
   (let ((xlist gnus-decode-header-methods-cache))
     (pop xlist)
     (save-restriction
   (let ((xlist gnus-decode-header-methods-cache))
     (pop xlist)
     (save-restriction
@@ -7709,12 +8244,11 @@ For example:
          (funcall (cadr elem)))))))
 
 ;; Dynamic variables.
          (funcall (cadr elem)))))))
 
 ;; Dynamic variables.
-(eval-when-compile
-  (defvar part-number)
-  (defvar total-parts)
-  (defvar type)
-  (defvar condition)
-  (defvar length))
+(defvar part-number)
+(defvar total-parts)
+(defvar type)
+(defvar condition)
+(defvar length)
 
 (defun gnus-treat-predicate (val)
   (cond
 
 (defun gnus-treat-predicate (val)
   (cond
@@ -7762,6 +8296,11 @@ For example:
                         gnus-article-encrypt-protocol-alist
                         nil t))
     current-prefix-arg))
                         gnus-article-encrypt-protocol-alist
                         nil t))
     current-prefix-arg))
+  ;; User might hit `K E' instead of `K e', so prompt once.
+  (when (and gnus-article-encrypt-protocol
+            gnus-novice-user)
+    (unless (gnus-y-or-n-p "Really encrypt article(s)? ")
+      (error "Encrypt aborted.")))
   (let ((func (cdr (assoc protocol gnus-article-encrypt-protocol-alist))))
     (unless func
       (error "Can't find the encrypt protocol %s" protocol))
   (let ((func (cdr (assoc protocol gnus-article-encrypt-protocol-alist))))
     (unless func
       (error "Can't find the encrypt protocol %s" protocol))
@@ -7771,8 +8310,7 @@ For example:
        (error "Can't encrypt the article in group %s"
               gnus-newsgroup-name))
     (gnus-summary-iterate n
        (error "Can't encrypt the article in group %s"
               gnus-newsgroup-name))
     (gnus-summary-iterate n
-      (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)
              (summary-buffer gnus-summary-buffer)
        (let ((mail-parse-charset gnus-newsgroup-charset)
              (mail-parse-ignored-charsets gnus-newsgroup-ignored-charsets)
              (summary-buffer gnus-summary-buffer)
@@ -7818,9 +8356,8 @@ For example:
          (when gnus-keep-backlog
            (gnus-backlog-remove-article
             (car gnus-article-current) (cdr gnus-article-current)))
          (when gnus-keep-backlog
            (gnus-backlog-remove-article
             (car gnus-article-current) (cdr gnus-article-current)))
-         (save-excursion
-           (when (get-buffer gnus-original-article-buffer)
-             (set-buffer gnus-original-article-buffer)
+          (when (get-buffer gnus-original-article-buffer)
+            (with-current-buffer gnus-original-article-buffer
              (setq gnus-original-article nil)))
          (when gnus-use-cache
            (gnus-cache-update-article
              (setq gnus-original-article nil)))
          (when gnus-use-cache
            (gnus-cache-update-article
@@ -7904,18 +8441,15 @@ For example:
        point (inhibit-read-only t))
     (if region
        (goto-char (car region)))
        point (inhibit-read-only t))
     (if region
        (goto-char (car region)))
-    (save-restriction
-      (narrow-to-region (point) (point))
-      (with-current-buffer (mm-handle-multipart-original-buffer handle)
-       (let* ((mm-verify-option 'known)
-              (mm-decrypt-option 'known)
-              (nparts (mm-possibly-verify-or-decrypt (cdr handle) handle)))
-         (unless (eq nparts (cdr handle))
-           (mm-destroy-parts (cdr handle))
-           (setcdr handle nparts))))
-      (setq point (point))
-      (gnus-mime-display-security handle)
-      (goto-char (point-max)))
+    (setq point (point))
+    (with-current-buffer (mm-handle-multipart-original-buffer handle)
+      (let* ((mm-verify-option 'known)
+            (mm-decrypt-option 'known)
+            (nparts (mm-possibly-verify-or-decrypt (cdr handle) handle)))
+       (unless (eq nparts (cdr handle))
+         (mm-destroy-parts (cdr handle))
+         (setcdr handle nparts))))
+    (gnus-mime-display-security handle)
     (when region
       (delete-region (point) (cdr region))
       (set-marker (car region) nil)
     (when region
       (delete-region (point) (cdr region))
       (set-marker (car region) nil)
@@ -8004,7 +8538,7 @@ For example:
                (1- (point))
              (point)))
     (when gnus-article-button-face
                (1- (point))
              (point)))
     (when gnus-article-button-face
-      (gnus-overlay-put (gnus-make-overlay b e)
+      (gnus-overlay-put (gnus-make-overlay b e nil t)
                         'face gnus-article-button-face))
     (widget-convert-button
      'link b e
                         'face gnus-article-button-face))
     (widget-convert-button
      'link b e
@@ -8037,7 +8571,8 @@ For example:
     (mm-set-handle-multipart-parameter
      handle 'gnus-region
      (cons (set-marker (make-marker) (point-min))
     (mm-set-handle-multipart-parameter
      handle 'gnus-region
      (cons (set-marker (make-marker) (point-min))
-          (set-marker (make-marker) (point-max))))))
+          (set-marker (make-marker) (point-max))))
+    (goto-char (point-max))))
 
 (defun gnus-mime-security-run-function (function)
   "Run FUNCTION with the security part under point."
 
 (defun gnus-mime-security-run-function (function)
   "Run FUNCTION with the security part under point."
@@ -8073,5 +8608,5 @@ For example:
 
 (run-hooks 'gnus-art-load-hook)
 
 
 (run-hooks 'gnus-art-load-hook)
 
-;;; arch-tag: 2654516f-6279-48f9-a83b-05c1fa450c33
+;; arch-tag: 2654516f-6279-48f9-a83b-05c1fa450c33
 ;;; gnus-art.el ends here
 ;;; gnus-art.el ends here