(article-display-face, article-display-x-face): Use buffer-read-only.
[gnus] / lisp / gnus-art.el
index e33d710..e98f8fc 100644 (file)
@@ -1,5 +1,5 @@
 ;;; gnus-art.el --- article mode commands for Gnus
-;; Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003
+;; Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004
 ;;        Free Software Foundation, Inc.
 
 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
@@ -50,7 +50,7 @@
 
 (defgroup gnus-article nil
   "Article display."
-  :link '(custom-manual "(gnus)The Article Buffer")
+  :link '(custom-manual "(gnus)Article Buffer")
   :group 'gnus)
 
 (defgroup gnus-article-treat nil
@@ -232,7 +232,9 @@ that number.  If it is a floating point number, no signature may be
 longer (in lines) than that number.  If it is a function, the function
 will be called without any parameters, and if it returns nil, there is
 no signature in the buffer.  If it is a string, it will be used as a
-regexp.  If it matches, the text in question is not a signature."
+regexp.  If it matches, the text in question is not a signature.
+
+This can also be a list of the above values."
   :type '(choice (integer :value 200)
                 (number :value 4.0)
                 (function :value fun)
@@ -337,8 +339,6 @@ advertisements.  For example:
            (format format (car spec) (cadr spec))
            2 3 (intern (format "gnus-emphasis-%s" (nth 2 spec)))))
         types)
-       ("\\(\\s-\\|^\\)\\(-\\(\\(\\w\\|-[^-]\\)+\\)-\\)\\(\\s-\\|[?!.,;]\\)"
-        2 3 gnus-emphasis-strikethru)
        ("\\(\\s-\\|^\\)\\(_\\(\\(\\w\\|_[^_]\\)+\\)_\\)\\(\\s-\\|[?!.,;]\\)"
         2 3 gnus-emphasis-underline)))
   "*Alist that says how to fontify certain phrases.
@@ -408,14 +408,14 @@ Example: (_/*word*/_)."
   "Face used for displaying highlighted words."
   :group 'gnus-article-emphasis)
 
-(defcustom gnus-article-time-format "%a, %b %d %Y %T %Z"
+(defcustom gnus-article-time-format "%a, %d %b %Y %T %Z"
   "Format for display of Date headers in article bodies.
 See `format-time-string' for the possible values.
 
 The variable can also be function, which should return a complete Date
 header.  The function is called with one argument, the time, which can
 be fed to `format-time-string'."
-  :type '(choice string symbol)
+  :type '(choice string function)
   :link '(custom-manual "(gnus)Article Date")
   :group 'gnus-article-washing)
 
@@ -755,6 +755,7 @@ When nil (the default value), then some MIME parts do not get buttons,
 as described by the variables `gnus-buttonized-mime-types' and
 `gnus-unbuttonized-mime-types'."
   :version "21.3"
+  :group 'gnus-article-mime
   :type 'boolean)
 
 (defcustom gnus-body-boundary-delimiter "_"
@@ -765,7 +766,8 @@ be controlled by `gnus-treat-body-boundary'."
   :type '(choice (item :tag "None" :value nil)
                 string))
 
-(defcustom gnus-picon-databases '("/usr/lib/picon" "/usr/local/faces")
+(defcustom gnus-picon-databases '("/usr/lib/picon" "/usr/local/faces"
+                                 "/usr/share/picons")
   "Defines the location of the faces database.
 For information on obtaining this database of pretty pictures, please
 see http://www.cs.indiana.edu/picons/ftp/index.html"
@@ -794,7 +796,7 @@ on parts -- for instance, adding Vcard info to a database."
   "An alist of MIME types to functions to display them."
   :version "21.1"
   :group 'gnus-article-mime
-  :type 'alist)
+  :type '(repeat (cons :format "%v" (string :tag "MIME type") function)))
 
 (defcustom gnus-article-date-lapsed-new-header nil
   "Whether the X-Sent and Date headers can coexist.
@@ -894,8 +896,7 @@ See Info node `(gnus)Customizing Articles' for details."
 
 (defcustom gnus-treat-emphasize
   (and (or window-system
-          (featurep 'xemacs)
-          (>= (string-to-number emacs-version) 21))
+          (featurep 'xemacs))
        50000)
   "Emphasize text.
 Valid values are nil, t, `head', `last', an integer or a predicate.
@@ -1148,7 +1149,7 @@ See Info node `(gnus)Customizing Articles' for details."
   :type gnus-article-treat-custom)
 (put 'gnus-treat-overstrike 'highlight t)
 
-(defcustom gnus-treat-ansi-sequences t
+(defcustom gnus-treat-ansi-sequences (if (locate-library "ansi-color") t)
   "Treat ANSI SGR control sequences.
 Valid values are nil, t, `head', `last', an integer or a predicate.
 See Info node `(gnus)Customizing Articles' for details."
@@ -1375,6 +1376,13 @@ This requires GNU Libidn, and by default only enabled if it is found."
   '("January" "February" "March" "April" "May" "June" "July" "August"
     "September" "October" "November" "December"))
 
+(defvar gnus-button-regexp nil)
+(defvar gnus-button-marker-list nil)
+;; Regexp matching any of the regexps from `gnus-button-alist'.
+
+(defvar gnus-button-last nil)
+;; The value of `gnus-button-alist' when `gnus-button-regexp' was build.
+
 (defvar article-goto-body-goes-to-point-min-p nil)
 (defvar gnus-article-wash-types nil)
 (defvar gnus-article-emphasis-alist nil)
@@ -1461,13 +1469,15 @@ Initialized from `text-mode-syntax-table.")
 
 (defvar gnus-inhibit-hiding nil)
 
+(defvar gnus-article-edit-mode nil)
+
 ;;; Macros for dealing with the article buffer.
 
 (defmacro gnus-with-article-headers (&rest forms)
   `(save-excursion
      (set-buffer gnus-article-buffer)
      (save-restriction
-       (let ((buffer-read-only nil)
+       (let ((inhibit-read-only t)
             (inhibit-point-motion-hooks t)
             (case-fold-search t))
         (article-narrow-to-head)
@@ -1479,7 +1489,7 @@ Initialized from `text-mode-syntax-table.")
 (defmacro gnus-with-article-buffer (&rest forms)
   `(save-excursion
      (set-buffer gnus-article-buffer)
-     (let ((buffer-read-only nil))
+     (let ((inhibit-read-only t))
        ,@forms)))
 
 (put 'gnus-with-article-buffer 'lisp-indent-function 0)
@@ -1565,25 +1575,35 @@ Initialized from `text-mode-syntax-table.")
   (interactive)
   ;; This function might be inhibited.
   (unless gnus-inhibit-hiding
-    (save-excursion
-      (save-restriction
-       (let ((buffer-read-only nil)
-             (case-fold-search t)
-             (max (1+ (length gnus-sorted-header-list)))
-             (ignored (when (not gnus-visible-headers)
-                        (cond ((stringp gnus-ignored-headers)
-                               gnus-ignored-headers)
-                              ((listp gnus-ignored-headers)
-                               (mapconcat 'identity gnus-ignored-headers
-                                          "\\|")))))
-             (visible
-              (cond ((stringp gnus-visible-headers)
-                     gnus-visible-headers)
-                    ((and gnus-visible-headers
-                          (listp gnus-visible-headers))
-                     (mapconcat 'identity gnus-visible-headers "\\|"))))
-             (inhibit-point-motion-hooks t)
-             beg)
+    (let ((inhibit-read-only nil)
+         (case-fold-search t)
+         (max (1+ (length gnus-sorted-header-list)))
+         (inhibit-point-motion-hooks t)
+         (cur (current-buffer))
+         ignored visible beg)
+      (save-excursion
+       ;; `gnus-ignored-headers' and `gnus-visible-headers' may be
+       ;; group parameters, so we should go to the summary buffer.
+       (when (prog1
+                 (condition-case nil
+                     (progn (set-buffer gnus-summary-buffer) t)
+                   (error nil))
+               (setq ignored (when (not gnus-visible-headers)
+                               (cond ((stringp gnus-ignored-headers)
+                                      gnus-ignored-headers)
+                                     ((listp gnus-ignored-headers)
+                                      (mapconcat 'identity
+                                                 gnus-ignored-headers
+                                                 "\\|"))))
+                     visible (cond ((stringp gnus-visible-headers)
+                                    gnus-visible-headers)
+                                   ((and gnus-visible-headers
+                                         (listp gnus-visible-headers))
+                                    (mapconcat 'identity
+                                               gnus-visible-headers
+                                               "\\|")))))
+         (set-buffer cur))
+       (save-restriction
          ;; First we narrow to just the headers.
          (article-narrow-to-head)
          ;; Hide any "From " lines at the beginning of (mail) articles.
@@ -1625,7 +1645,7 @@ always hide."
             (not gnus-show-all-headers))
     (save-excursion
       (save-restriction
-       (let ((buffer-read-only nil)
+       (let ((inhibit-read-only t)
              (list gnus-boring-article-headers)
              (inhibit-point-motion-hooks t)
              elem)
@@ -1779,7 +1799,7 @@ always hide."
 (defun article-normalize-headers ()
   "Make all header lines 40 characters long."
   (interactive)
-  (let ((buffer-read-only nil)
+  (let ((inhibit-read-only t)
        column)
     (save-excursion
       (save-restriction
@@ -1823,7 +1843,7 @@ FROM is a string of characters to translate from; to is a string of
 characters to translate to."
   (save-excursion
     (when (article-goto-body)
-      (let ((buffer-read-only nil)
+      (let ((inhibit-read-only t)
            (x (make-string 225 ?x))
            (i -1))
        (while (< (incf i) (length x))
@@ -1839,7 +1859,7 @@ characters to translate to."
 MAP is an alist where the elements are on the form (\"from\" \"to\")."
   (save-excursion
     (when (article-goto-body)
-      (let ((buffer-read-only nil)
+      (let ((inhibit-read-only t)
            elem)
        (while (setq elem (pop map))
          (save-excursion
@@ -1851,7 +1871,7 @@ MAP is an alist where the elements are on the form (\"from\" \"to\")."
   (interactive)
   (save-excursion
     (when (article-goto-body)
-      (let ((buffer-read-only nil))
+      (let ((inhibit-read-only t))
        (while (search-forward "\b" nil t)
          (let ((next (char-after))
                (previous (char-after (- (point) 2))))
@@ -1877,7 +1897,7 @@ MAP is an alist where the elements are on the form (\"from\" \"to\")."
   (interactive)
   (save-excursion
     (when (article-goto-body)
-      (let ((buffer-read-only nil))
+      (let ((inhibit-read-only t))
        (ansi-color-apply-on-region (point) (point-max))))))
 
 (defun gnus-article-treat-unfold-headers ()
@@ -1967,7 +1987,7 @@ unfolded."
   "Fill lines that are wider than the window width."
   (interactive)
   (save-excursion
-    (let ((buffer-read-only nil)
+    (let ((inhibit-read-only t)
          (width (window-width (get-buffer-window (current-buffer)))))
       (save-restriction
        (article-goto-body)
@@ -1987,7 +2007,7 @@ unfolded."
   "Capitalize the first word in each sentence."
   (interactive)
   (save-excursion
-    (let ((buffer-read-only nil)
+    (let ((inhibit-read-only t)
          (paragraph-start "^[\n\^L]"))
       (article-goto-body)
       (while (not (eobp))
@@ -1998,7 +2018,7 @@ unfolded."
   "Remove trailing CRs and then translate remaining CRs into LFs."
   (interactive)
   (save-excursion
-    (let ((buffer-read-only nil))
+    (let ((inhibit-read-only t))
       (goto-char (point-min))
       (while (re-search-forward "\r+$" nil t)
        (replace-match "" t t))
@@ -2010,7 +2030,7 @@ unfolded."
   "Remove all trailing blank lines from the article."
   (interactive)
   (save-excursion
-    (let ((buffer-read-only nil))
+    (let ((inhibit-read-only t))
       (goto-char (point-max))
       (delete-region
        (point)
@@ -2023,6 +2043,9 @@ unfolded."
         (forward-line 1)
         (point))))))
 
+(eval-when-compile
+  (defvar gnus-face-properties-alist))
+
 (defun article-display-face ()
   "Display any Face headers in the header."
   (interactive)
@@ -2046,12 +2069,14 @@ unfolded."
            (save-restriction
              (mail-narrow-to-head)
              (while (gnus-article-goto-header "Face")
-               (push (mail-header-field-value) faces))))
+               (setq faces (nconc faces (list (mail-header-field-value)))))))
          (while (setq face (pop faces))
            (let ((png (gnus-convert-face-to-png face))
                  image)
              (when png
-               (setq image (gnus-create-image png 'png t))
+               (setq image
+                     (apply 'gnus-create-image png 'png t
+                            (cdr (assq 'png gnus-face-properties-alist))))
                (gnus-article-goto-header "from")
                (when (bobp)
                  (insert "From: [no `from' set]\n")
@@ -2128,14 +2153,12 @@ unfolded."
 (defun article-decode-mime-words ()
   "Decode all MIME-encoded words in the article."
   (interactive)
-  (save-excursion
-    (set-buffer gnus-article-buffer)
+  (gnus-with-article-buffer
     (let ((inhibit-point-motion-hooks t)
-         buffer-read-only
          (mail-parse-charset gnus-newsgroup-charset)
          (mail-parse-ignored-charsets
-          (save-excursion (set-buffer gnus-summary-buffer)
-                          gnus-newsgroup-ignored-charsets)))
+          (with-current-buffer gnus-summary-buffer
+            gnus-newsgroup-ignored-charsets)))
       (mail-decode-encoded-word-region (point-min) (point-max)))))
 
 (defun article-decode-charset (&optional prompt)
@@ -2143,7 +2166,7 @@ unfolded."
 If PROMPT (the prefix), prompt for a coding system to use."
   (interactive "P")
   (let ((inhibit-point-motion-hooks t) (case-fold-search t)
-       buffer-read-only
+       (inhibit-read-only t)
        (mail-parse-charset gnus-newsgroup-charset)
        (mail-parse-ignored-charsets
         (save-excursion (condition-case nil
@@ -2193,7 +2216,7 @@ If PROMPT (the prefix), prompt for a coding system to use."
                             (set-buffer gnus-summary-buffer)
                           (error))
                         gnus-newsgroup-ignored-charsets))
-       buffer-read-only)
+       (inhibit-read-only t))
     (save-restriction
       (article-narrow-to-head)
       (funcall gnus-decode-header-function (point-min) (point-max)))))
@@ -2201,7 +2224,7 @@ If PROMPT (the prefix), prompt for a coding system to use."
 (defun article-decode-group-name ()
   "Decode group names in `Newsgroups:'."
   (let ((inhibit-point-motion-hooks t)
-       buffer-read-only
+       (inhibit-read-only t)
        (method (gnus-find-method-for-group gnus-newsgroup-name)))
     (when (and (or gnus-group-name-charset-method-alist
                   gnus-group-name-charset-group-alist)
@@ -2245,18 +2268,16 @@ If PROMPT (the prefix), prompt for a coding system to use."
   (when gnus-use-idna
     (save-restriction
       (let ((inhibit-point-motion-hooks t)
-           buffer-read-only)
+           (inhibit-read-only t))
        (article-narrow-to-head)
        (goto-char (point-min))
-       (while (re-search-forward "\\(xn--[-A-Za-z0-9.]*\\)[ \t\n\r,>]" nil t)
+       (while (re-search-forward "@.*\\(xn--[-A-Za-z0-9.]*\\)[ \t\n\r,>]" nil t)
          (let (ace unicode)
            (when (save-match-data
                    (and (setq ace (match-string 1))
                         (save-excursion
                           (and (re-search-backward "^[^ \t]" nil t)
                                (looking-at "From\\|To\\|Cc")))
-                        (save-excursion (backward-char)
-                                        (message-idna-inside-rhs-p))
                         (setq unicode (idna-to-unicode ace))))
              (unless (string= ace unicode)
                (replace-match unicode nil nil nil 1)))))))))
@@ -2268,7 +2289,7 @@ or not.
 If READ-CHARSET, ask for a coding system."
   (interactive (list 'force current-prefix-arg))
   (save-excursion
-    (let ((buffer-read-only nil) type charset)
+    (let ((inhibit-read-only t) type charset)
       (if (gnus-buffer-live-p gnus-original-article-buffer)
          (with-current-buffer gnus-original-article-buffer
            (setq type
@@ -2298,7 +2319,7 @@ If FORCE, decode the article whether it is marked as base64 not.
 If READ-CHARSET, ask for a coding system."
   (interactive (list 'force current-prefix-arg))
   (save-excursion
-    (let ((buffer-read-only nil) type charset)
+    (let ((inhibit-read-only t) type charset)
       (if (gnus-buffer-live-p gnus-original-article-buffer)
          (with-current-buffer gnus-original-article-buffer
            (setq type
@@ -2333,14 +2354,14 @@ If READ-CHARSET, ask for a coding system."
   (interactive)
   (require 'rfc1843)
   (save-excursion
-    (let ((buffer-read-only nil))
+    (let ((inhibit-read-only t))
       (rfc1843-decode-region (point-min) (point-max)))))
 
 (defun article-unsplit-urls ()
   "Remove the newlines that some other mailers insert into URLs."
   (interactive)
   (save-excursion
-    (let ((buffer-read-only nil))
+    (let ((inhibit-read-only t))
       (goto-char (point-min))
       (while (re-search-forward
              "^\\(\\(https?\\|ftp\\)://\\S-+\\) *\n\\(\\S-+\\)" nil t)
@@ -2354,7 +2375,7 @@ If READ-CHARSET, ask for a coding system."
 If READ-CHARSET, ask for a coding system."
   (interactive "P")
   (save-excursion
-    (let ((buffer-read-only nil)
+    (let ((inhibit-read-only t)
          charset)
       (when (gnus-buffer-live-p gnus-original-article-buffer)
        (with-current-buffer gnus-original-article-buffer
@@ -2400,16 +2421,17 @@ If READ-CHARSET, ask for a coding system."
   (mm-setup-w3m)
   (save-restriction
     (narrow-to-region (point) (point-max))
-    (let ((w3m-safe-url-regexp (if mm-inline-text-html-with-images
-                                  nil
-                                "\\`cid:"))
+    (let ((w3m-safe-url-regexp mm-w3m-safe-url-regexp)
          w3m-force-redisplay)
       (w3m-region (point-min) (point-max)))
-    (when mm-inline-text-html-with-w3m-keymap
+    (when (and mm-inline-text-html-with-w3m-keymap
+              (boundp 'w3m-minor-mode-map)
+              w3m-minor-mode-map)
       (add-text-properties
        (point-min) (point-max)
-       (nconc (mm-w3m-local-map-property)
-             '(mm-inline-text-html-with-w3m t))))))
+       (list 'keymap w3m-minor-mode-map
+            ;; Put the mark meaning this part was rendered by emacs-w3m.
+            'mm-inline-text-html-with-w3m t)))))
 
 (defun article-hide-list-identifiers ()
   "Remove list identifies from the Subject header.
@@ -2419,7 +2441,7 @@ The `gnus-list-identifiers' variable specifies what to do."
        (regexp (if (consp gnus-list-identifiers)
                    (mapconcat 'identity gnus-list-identifiers " *\\|")
                  gnus-list-identifiers))
-       buffer-read-only)
+       (inhibit-read-only t))
     (when regexp
       (save-excursion
        (save-restriction
@@ -2441,7 +2463,7 @@ always hide."
   (interactive (gnus-article-hidden-arg))
   (unless (gnus-article-check-hidden-text 'pem arg)
     (save-excursion
-      (let (buffer-read-only end)
+      (let ((inhibit-read-only t) end)
        (goto-char (point-min))
        ;; Hide the horrendously ugly "header".
        (when (and (search-forward
@@ -2472,18 +2494,25 @@ always hide."
          (article-really-strip-banner
           (gnus-parameter-banner gnus-newsgroup-name)))
        (when gnus-article-address-banner-alist
-         (article-really-strip-banner
-          (let ((from (save-restriction
-                        (widen)
-                        (article-narrow-to-head)
-                        (mail-fetch-field "from"))))
-            (when (and from
-                       (setq from
-                             (caar (mail-header-parse-addresses from))))
-              (catch 'found
-                (dolist (pair gnus-article-address-banner-alist)
-                  (when (string-match (car pair) from)
-                    (throw 'found (cdr pair)))))))))))))
+         ;; It is necessary to encode from fields before checking,
+         ;; because `mail-header-parse-addresses' does not work
+         ;; (reliably) on decoded headers.  And more, it is
+         ;; impossible to use `gnus-fetch-original-field' here,
+         ;; because `article-strip-banner' may be called in draft
+         ;; buffers to preview them.
+         (let ((from (save-restriction
+                       (widen)
+                       (article-narrow-to-head)
+                       (mail-fetch-field "from"))))
+           (when (and from
+                      (setq from
+                            (caar (mail-header-parse-addresses
+                                   (mail-encode-encoded-word-string from)))))
+             (catch 'found
+               (dolist (pair gnus-article-address-banner-alist)
+                 (when (string-match (car pair) from)
+                   (throw 'found
+                          (article-really-strip-banner (cdr pair)))))))))))))
 
 (defun article-really-strip-banner (banner)
   "Strip the banner specified by the argument."
@@ -2491,7 +2520,7 @@ always hide."
     (save-restriction
       (let ((inhibit-point-motion-hooks t)
            (gnus-signature-limit nil)
-           buffer-read-only)
+           (inhibit-read-only t))
        (article-goto-body)
        (cond
         ((eq banner 'signature)
@@ -2511,11 +2540,9 @@ always hide."
   "Translate article using an online translation service."
   (interactive)
   (require 'babel)
-  (save-excursion
-    (set-buffer gnus-article-buffer)
+  (gnus-with-article-buffer
     (when (article-goto-body)
-      (let* ((buffer-read-only nil)
-            (start (point))
+      (let* ((start (point))
             (end (point-max))
             (orig (buffer-substring start end))
             (trans (babel-as-string orig)))
@@ -2532,7 +2559,7 @@ always hide."
   (unless (gnus-article-check-hidden-text 'signature arg)
     (save-excursion
       (save-restriction
-       (let ((buffer-read-only nil))
+       (let ((inhibit-read-only t))
          (when (gnus-article-narrow-to-signature)
            (gnus-article-hide-text-type
             (point-min) (point-max) 'signature))))))
@@ -2552,7 +2579,7 @@ always hide."
   (interactive)
   (save-excursion
     (let ((inhibit-point-motion-hooks t)
-         buffer-read-only)
+         (inhibit-read-only t))
       (when (article-goto-body)
        (while (and (not (eobp))
                    (looking-at "[ \t]*$"))
@@ -2587,7 +2614,7 @@ Point is left at the beginning of the narrowed-to region."
   (interactive)
   (save-excursion
     (let ((inhibit-point-motion-hooks t)
-         buffer-read-only)
+         (inhibit-read-only t))
       ;; First make all blank lines empty.
       (article-goto-body)
       (while (re-search-forward "^[ \t]+$" nil t)
@@ -2606,7 +2633,7 @@ Point is left at the beginning of the narrowed-to region."
   (interactive)
   (save-excursion
     (let ((inhibit-point-motion-hooks t)
-         buffer-read-only)
+         (inhibit-read-only t))
       (article-goto-body)
       (while (re-search-forward "^[ \t]+" nil t)
        (replace-match "" t t)))))
@@ -2616,7 +2643,7 @@ Point is left at the beginning of the narrowed-to region."
   (interactive)
   (save-excursion
     (let ((inhibit-point-motion-hooks t)
-         buffer-read-only)
+         (inhibit-read-only t))
       (article-goto-body)
       (while (re-search-forward "[ \t]+$" nil t)
        (replace-match "" t t)))))
@@ -2633,7 +2660,7 @@ Point is left at the beginning of the narrowed-to region."
   (interactive)
   (save-excursion
     (let ((inhibit-point-motion-hooks t)
-         buffer-read-only)
+         (inhibit-read-only t))
       (article-goto-body)
       (while (re-search-forward "^[ \t]*\n" nil t)
        (replace-match "" t t)))))
@@ -2722,7 +2749,7 @@ means show, 0 means toggle."
 (defun gnus-article-show-hidden-text (type &optional dummy)
   "Show all hidden text of type TYPE.
 Originally it is hide instead of DUMMY."
-  (let ((buffer-read-only nil)
+  (let ((inhibit-read-only t)
        (inhibit-point-motion-hooks t))
     (gnus-remove-text-properties-when
      'article-type type
@@ -2787,23 +2814,22 @@ should replace the \"Date:\" one, or should be added below it."
          (forward-line 1))
        (when (and date (not (string= date "")))
          (goto-char (point-min))
-         (let ((buffer-read-only nil))
+         (let ((inhibit-read-only t))
            ;; Delete any old Date headers.
            (while (re-search-forward date-regexp nil t)
              (if pos
-                 (delete-region (progn (beginning-of-line) (point))
-                                (progn (gnus-article-forward-header)
-                                       (point)))
-               (delete-region (progn (beginning-of-line) (point))
+                 (delete-region (point-at-bol)
                                 (progn (gnus-article-forward-header)
-                                       (forward-char -1)
                                        (point)))
+               (delete-region (point-at-bol)
+                              (progn (gnus-article-forward-header)
+                                     (forward-char -1)
+                                     (point)))
                (setq pos (point))))
            (when (and (not pos)
                       (re-search-forward tdate-regexp nil t))
              (forward-line 1))
-           (when pos
-             (goto-char pos))
+           (gnus-goto-char pos)
            (insert (article-make-date-line date (or type 'ut)))
            (unless pos
              (insert "\n")
@@ -2811,10 +2837,8 @@ should replace the \"Date:\" one, or should be added below it."
            ;; Do highlighting.
            (beginning-of-line)
            (when (looking-at "\\([^:]+\\): *\\(.*\\)$")
-             (put-text-property (match-beginning 1) (1+ (match-end 1))
-                                'original-date date)
-             (put-text-property (match-beginning 1) (1+ (match-end 1))
-                                'face bface)
+             (add-text-properties (match-beginning 1) (1+ (match-end 1))
+                                  (list 'original-date date 'face bface))
              (put-text-property (match-beginning 2) (match-end 2)
                                 'face eface))))))))
 
@@ -2827,22 +2851,21 @@ should replace the \"Date:\" one, or should be added below it."
        (cond
         ;; Convert to the local timezone.
         ((eq type 'local)
-         (let ((tz (car (current-time-zone time))))
-           (format "Date: %s %s%02d%02d" (current-time-string time)
-                   (if (> tz 0) "+" "-") (/ (abs tz) 3600)
-                   (/ (% (abs tz) 3600) 60))))
+         (concat "Date: " (message-make-date time)))
         ;; Convert to Universal Time.
         ((eq type 'ut)
          (concat "Date: "
-                 (current-time-string
-                  (let* ((e (parse-time-string date))
-                         (tm (apply 'encode-time e))
-                         (ms (car tm))
-                         (ls (- (cadr tm) (car (current-time-zone time)))))
-                    (cond ((< ls 0) (list (1- ms) (+ ls 65536)))
-                          ((> ls 65535) (list (1+ ms) (- ls 65536)))
-                          (t (list ms ls)))))
-                 " UT"))
+                 (substring
+                  (message-make-date
+                   (let* ((e (parse-time-string date))
+                          (tm (apply 'encode-time e))
+                          (ms (car tm))
+                          (ls (- (cadr tm) (car (current-time-zone time)))))
+                     (cond ((< ls 0) (list (1- ms) (+ ls 65536)))
+                           ((> ls 65535) (list (1+ ms) (- ls 65536)))
+                           (t (list ms ls)))))
+                  0 -5)
+                 "UT"))
         ;; Get the original date from the article.
         ((eq type 'original)
          (concat "Date: " (if (string-match "\n+$" date)
@@ -3006,7 +3029,7 @@ This format is defined by the `gnus-article-time-format' variable."
 ;;   "Show all hidden text in the article buffer."
 ;;   (interactive)
 ;;   (save-excursion
-;;     (let ((buffer-read-only nil))
+;;     (let ((inhibit-read-only t))
 ;;       (gnus-article-unhide-text (point-min) (point-max)))))
 
 (defun article-remove-leading-whitespace ()
@@ -3014,7 +3037,7 @@ This format is defined by the `gnus-article-time-format' variable."
   (interactive)
   (save-excursion
     (save-restriction
-      (let ((buffer-read-only nil))
+      (let ((inhibit-read-only t))
        (article-narrow-to-head)
        (goto-char (point-min))
        (while (re-search-forward "^[^ :]+: \\([ \t]+\\)" nil t)
@@ -3031,7 +3054,7 @@ This format is defined by the `gnus-article-time-format' variable."
                          gnus-article-emphasis-alist)
                      (error))
                    gnus-emphasis-alist))
-           (buffer-read-only nil)
+           (inhibit-read-only t)
            (props (append '(article-type emphasis)
                           gnus-hidden-properties))
            regexp elem beg invisible visible face)
@@ -3423,7 +3446,7 @@ If variable `gnus-use-long-file-name' is non-nil, it is
                        (mm-handle-multipart-ctl-parameter
                         mm-security-handle 'gnus-info)))))
          (when info
-           (let (buffer-read-only bface eface)
+           (let ((inhibit-read-only t) bface eface)
              (save-restriction
                (message-narrow-to-head)
                (goto-char (point-max))
@@ -3635,7 +3658,8 @@ commands:
   (make-local-variable 'gnus-article-ignored-charsets)
   (gnus-set-default-directory)
   (buffer-disable-undo)
-  (setq buffer-read-only t)
+  (setq buffer-read-only t
+       show-trailing-whitespace nil)
   (set-syntax-table gnus-article-mode-syntax-table)
   (mm-enable-multibyte)
   (gnus-run-hooks 'gnus-article-mode-hook))
@@ -3730,7 +3754,7 @@ If ALL-HEADERS is non-nil, no headers are hidden."
        (when (and (boundp 'transient-mark-mode)
                   transient-mark-mode)
          (setq mark-active nil))
-       (if (not (setq result (let ((buffer-read-only nil))
+       (if (not (setq result (let ((inhibit-read-only t))
                                (gnus-request-article-this-buffer
                                 article group))))
            ;; There is no such article.
@@ -3825,7 +3849,8 @@ If ALL-HEADERS is non-nil, no headers are hidden."
   ;; Hooks for getting information from the article.
   ;; This hook must be called before being narrowed.
   (let ((gnus-article-buffer (current-buffer))
-       buffer-read-only)
+       buffer-read-only
+       (inhibit-read-only t))
     (unless (eq major-mode 'gnus-article-mode)
       (gnus-article-mode))
     (setq buffer-read-only nil
@@ -3889,9 +3914,6 @@ General format specifiers can also be used.  See Info node
 
 (defvar gnus-mime-button-map
   (let ((map (make-sparse-keymap)))
-    (unless (>= (string-to-number emacs-version) 21)
-      ;; XEmacs doesn't care.
-      (set-keymap-parent map gnus-article-mode-map))
     (define-key map gnus-mouse-2 'gnus-article-push-button)
     (define-key map gnus-down-mouse-3 'gnus-mime-button-menu)
     (dolist (c gnus-mime-button-commands)
@@ -3918,8 +3940,7 @@ General format specifiers can also be used.  See Info node
 (defun gnus-mime-view-all-parts (&optional handles)
   "View all the MIME parts."
   (interactive)
-  (save-current-buffer
-    (set-buffer gnus-article-buffer)
+  (with-current-buffer gnus-article-buffer
     (let ((handles (or handles gnus-article-mime-handles))
          (mail-parse-charset gnus-newsgroup-charset)
          (mail-parse-ignored-charsets
@@ -3929,80 +3950,99 @@ General format specifiers can also be used.  See Info node
        (mm-remove-parts handles)
        (goto-char (point-min))
        (or (search-forward "\n\n") (goto-char (point-max)))
-       (let (buffer-read-only)
+       (let ((inhibit-read-only t))
          (delete-region (point) (point-max))
          (mm-display-parts handles))))))
 
+(eval-when-compile
+  (defsubst gnus-article-edit-part (handles)
+    "Edit an article in order to delete a mime part.
+This function is exclusively used by `gnus-mime-save-part-and-strip'
+and `gnus-mime-delete-part', and not provided at run-time normally."
+    (gnus-article-edit-article
+     `(lambda ()
+       (erase-buffer)
+       (let ((mail-parse-charset (or gnus-article-charset
+                                     ',gnus-newsgroup-charset))
+             (mail-parse-ignored-charsets
+              (or gnus-article-ignored-charsets
+                  ',gnus-newsgroup-ignored-charsets))
+             (mbl mml-buffer-list))
+         (setq mml-buffer-list nil)
+         (insert-buffer gnus-original-article-buffer)
+         (mime-to-mml ',handles)
+         (setq gnus-article-mime-handles nil)
+         (let ((mbl1 mml-buffer-list))
+           (setq mml-buffer-list mbl)
+           (set (make-local-variable 'mml-buffer-list) mbl1))
+         (gnus-make-local-hook 'kill-buffer-hook)
+         (add-hook 'kill-buffer-hook 'mml-destroy-buffers t t)))
+     `(lambda (no-highlight)
+       (let ((mail-parse-charset (or gnus-article-charset
+                                     ',gnus-newsgroup-charset))
+             (message-options message-options)
+             (message-options-set-recipient)
+             (mail-parse-ignored-charsets
+              (or gnus-article-ignored-charsets
+                  ',gnus-newsgroup-ignored-charsets)))
+         (mml-to-mime)
+         (mml-destroy-buffers)
+         (remove-hook 'kill-buffer-hook
+                      'mml-destroy-buffers t)
+         (kill-local-variable 'mml-buffer-list))
+       (gnus-summary-edit-article-done
+        ,(or (mail-header-references gnus-current-headers) "")
+        ,(gnus-group-read-only-p)
+        ,gnus-summary-buffer no-highlight)))
+    (gnus-article-edit-done)
+    (gnus-summary-expand-window)
+    (gnus-summary-show-article)))
+
 (defun gnus-mime-save-part-and-strip ()
   "Save the MIME part under point then replace it with an external body."
   (interactive)
   (gnus-article-check-buffer)
-  (let* ((data (get-text-property (point) 'gnus-data))
-        file param
-        (handles gnus-article-mime-handles))
-    (if (mm-multiple-handles gnus-article-mime-handles)
-       (error "This function is not implemented"))
-    (setq file (and data (mm-save-part data)))
-    (when file
-      (with-current-buffer (mm-handle-buffer data)
-       (erase-buffer)
-       (insert "Content-Type: " (mm-handle-media-type data))
-       (mml-insert-parameter-string (cdr (mm-handle-type data))
-                                    '(charset))
-       (insert "\n")
-       (insert "Content-ID: " (message-make-message-id) "\n")
-       (insert "Content-Transfer-Encoding: binary\n")
-       (insert "\n"))
-      (setcdr data
-             (cdr (mm-make-handle nil
-                                  `("message/external-body"
-                                    (access-type . "LOCAL-FILE")
-                                    (name . ,file)))))
-      (set-buffer gnus-summary-buffer)
-      (gnus-article-edit-article
-       `(lambda ()
-          (erase-buffer)
-          (let ((mail-parse-charset (or gnus-article-charset
-                                        ',gnus-newsgroup-charset))
-                (mail-parse-ignored-charsets
-                 (or gnus-article-ignored-charsets
-                     ',gnus-newsgroup-ignored-charsets))
-                (mbl mml-buffer-list))
-            (setq mml-buffer-list nil)
-            (insert-buffer gnus-original-article-buffer)
-            (mime-to-mml ',handles)
-            (setq gnus-article-mime-handles nil)
-            (let ((mbl1 mml-buffer-list))
-              (setq mml-buffer-list mbl)
-              (set (make-local-variable 'mml-buffer-list) mbl1))
-            (gnus-make-local-hook 'kill-buffer-hook)
-            (add-hook 'kill-buffer-hook 'mml-destroy-buffers t t)))
-       `(lambda (no-highlight)
-         (let ((mail-parse-charset (or gnus-article-charset
-                                       ',gnus-newsgroup-charset))
-               (message-options message-options)
-               (message-options-set-recipient)
-               (mail-parse-ignored-charsets
-                (or gnus-article-ignored-charsets
-                    ',gnus-newsgroup-ignored-charsets)))
-          (mml-to-mime)
-          (mml-destroy-buffers)
-          (remove-hook 'kill-buffer-hook
-                       'mml-destroy-buffers t)
-          (kill-local-variable 'mml-buffer-list))
-         (gnus-summary-edit-article-done
-          ,(or (mail-header-references gnus-current-headers) "")
-          ,(gnus-group-read-only-p)
-          ,gnus-summary-buffer no-highlight))))))
+  (when (gnus-group-read-only-p)
+    (error "The current group does not support deleting of parts"))
+  (when (mm-complicated-handles gnus-article-mime-handles)
+    (error "\
+The current article has a complicated MIME structure, giving up..."))
+  (when (gnus-yes-or-no-p "\
+Deleting parts may malfunction or destroy the article; continue? ")
+    (let* ((data (get-text-property (point) 'gnus-data))
+          file param
+          (handles gnus-article-mime-handles))
+      (setq file (and data (mm-save-part data)))
+      (when file
+       (with-current-buffer (mm-handle-buffer data)
+         (erase-buffer)
+         (insert "Content-Type: " (mm-handle-media-type data))
+         (mml-insert-parameter-string (cdr (mm-handle-type data))
+                                      '(charset))
+         (insert "\n")
+         (insert "Content-ID: " (message-make-message-id) "\n")
+         (insert "Content-Transfer-Encoding: binary\n")
+         (insert "\n"))
+       (setcdr data
+               (cdr (mm-make-handle nil
+                                    `("message/external-body"
+                                      (access-type . "LOCAL-FILE")
+                                      (name . ,file)))))
+       (set-buffer gnus-summary-buffer)
+       (gnus-article-edit-part handles)))))
 
 (defun gnus-mime-delete-part ()
   "Delete the MIME part under point.
 Replace it with some information about the removed part."
   (interactive)
   (gnus-article-check-buffer)
-  (unless (and gnus-novice-user
-              (not (gnus-yes-or-no-p
-                    "Really delete attachment forever? ")))
+  (when (gnus-group-read-only-p)
+    (error "The current group does not support deleting of parts"))
+  (when (mm-complicated-handles gnus-article-mime-handles)
+    (error "\
+The current article has a complicated MIME structure, giving up..."))
+  (when (gnus-yes-or-no-p "\
+Deleting parts may malfunction or destroy the article; continue? ")
     (let* ((data (get-text-property (point) 'gnus-data))
           (handles gnus-article-mime-handles)
           (none "(none)")
@@ -4014,8 +4054,8 @@ Replace it with some information about the removed part."
            (or (mail-content-type-get (mm-handle-disposition data) 'filename)
                none))
           (type (mm-handle-media-type data)))
-      (if (mm-multiple-handles gnus-article-mime-handles)
-         (error "This function is not implemented"))
+      (unless data
+       (error "No MIME part under point"))
       (with-current-buffer (mm-handle-buffer data)
        (let ((bsize (format "%s" (buffer-size))))
          (erase-buffer)
@@ -4035,47 +4075,7 @@ Replace it with some information about the removed part."
                        (list "attachment")
                        (format "Deleted attachment (%s bytes)" bsize))))))
       (set-buffer gnus-summary-buffer)
-      ;; FIXME: maybe some of the following code (borrowed from
-      ;; `gnus-mime-save-part-and-strip') isn't necessary?
-      (gnus-article-edit-article
-       `(lambda ()
-         (erase-buffer)
-         (let ((mail-parse-charset (or gnus-article-charset
-                                       ',gnus-newsgroup-charset))
-               (mail-parse-ignored-charsets
-                (or gnus-article-ignored-charsets
-                    ',gnus-newsgroup-ignored-charsets))
-               (mbl mml-buffer-list))
-           (setq mml-buffer-list nil)
-           (insert-buffer gnus-original-article-buffer)
-           (mime-to-mml ',handles)
-           (setq gnus-article-mime-handles nil)
-           (let ((mbl1 mml-buffer-list))
-             (setq mml-buffer-list mbl)
-             (set (make-local-variable 'mml-buffer-list) mbl1))
-           (gnus-make-local-hook 'kill-buffer-hook)
-           (add-hook 'kill-buffer-hook 'mml-destroy-buffers t t)))
-       `(lambda (no-highlight)
-         (let ((mail-parse-charset (or gnus-article-charset
-                                       ',gnus-newsgroup-charset))
-               (message-options message-options)
-               (message-options-set-recipient)
-               (mail-parse-ignored-charsets
-                (or gnus-article-ignored-charsets
-                    ',gnus-newsgroup-ignored-charsets)))
-           (mml-to-mime)
-           (mml-destroy-buffers)
-           (remove-hook 'kill-buffer-hook
-                        'mml-destroy-buffers t)
-           (kill-local-variable 'mml-buffer-list))
-         (gnus-summary-edit-article-done
-          ,(or (mail-header-references gnus-current-headers) "")
-          ,(gnus-group-read-only-p)
-          ,gnus-summary-buffer no-highlight)))))
-  ;; Not in `gnus-mime-save-part-and-strip':
-  (gnus-article-edit-done)
-  (gnus-summary-expand-window)
-  (gnus-summary-show-article))
+      (gnus-article-edit-part handles))))
 
 (defun gnus-mime-save-part ()
   "Save the MIME part under point."
@@ -4227,7 +4227,7 @@ are decompressed."
   (let* ((handle (or handle (get-text-property (point) 'gnus-data)))
         contents charset
         (b (point))
-        buffer-read-only)
+        (inhibit-read-only t))
     (when handle
       (if (and (not arg) (mm-handle-undisplayer handle))
          (mm-remove-part handle)
@@ -4262,7 +4262,7 @@ specified charset."
   (let* ((handle (or handle (get-text-property (point) 'gnus-data)))
         contents charset
         (b (point))
-        buffer-read-only)
+        (inhibit-read-only t))
     (when handle
       (if (mm-handle-undisplayer handle)
          (mm-remove-part handle))
@@ -4282,8 +4282,8 @@ specified charset."
         (mm-inlined-types nil)
         (mail-parse-charset gnus-newsgroup-charset)
         (mail-parse-ignored-charsets
-         (save-excursion (set-buffer gnus-summary-buffer)
-                         gnus-newsgroup-ignored-charsets)))
+         (with-current-buffer gnus-summary-buffer
+           gnus-newsgroup-ignored-charsets)))
     (when handle
       (if (mm-handle-undisplayer handle)
          (mm-remove-part handle)
@@ -4299,9 +4299,9 @@ If no internal viewer is available, use an external viewer."
         (mm-inline-large-images t)
         (mail-parse-charset gnus-newsgroup-charset)
         (mail-parse-ignored-charsets
-         (save-excursion (set-buffer gnus-summary-buffer)
-                         gnus-newsgroup-ignored-charsets))
-        buffer-read-only)
+         (with-current-buffer gnus-summary-buffer
+           gnus-newsgroup-ignored-charsets))
+        (inhibit-read-only t))
     (when handle
       (if (mm-handle-undisplayer handle)
          (mm-remove-part handle)
@@ -4317,8 +4317,7 @@ If no internal viewer is available, use an external viewer."
        (funcall (cdr action-pair)))))
 
 (defun gnus-article-part-wrapper (n function)
-  (save-current-buffer
-    (set-buffer gnus-article-buffer)
+  (with-current-buffer gnus-article-buffer
     (when (> n (length gnus-article-mime-handle-alist))
       (error "No such part"))
     (gnus-article-goto-part n)
@@ -4384,8 +4383,7 @@ N is the numerical prefix."
 (defun gnus-article-view-part (&optional n)
   "View MIME part N, which is the numerical prefix."
   (interactive "P")
-  (save-current-buffer
-    (set-buffer gnus-article-buffer)
+  (with-current-buffer gnus-article-buffer
     (or (numberp n) (setq n (gnus-article-mime-match-handle-first
                             gnus-article-mime-match-handle-function)))
     (when (> n (length gnus-article-mime-handle-alist))
@@ -4406,15 +4404,14 @@ N is the numerical prefix."
   "Display HANDLE and fix MIME button."
   (let ((id (get-text-property (point) 'gnus-part))
        (point (point))
-       buffer-read-only)
+       (inhibit-read-only t))
     (forward-line 1)
     (prog1
        (let ((window (selected-window))
              (mail-parse-charset gnus-newsgroup-charset)
              (mail-parse-ignored-charsets
               (if (gnus-buffer-live-p gnus-summary-buffer)
-                  (save-excursion
-                    (set-buffer gnus-summary-buffer)
+                  (with-current-buffer gnus-summary-buffer
                     gnus-newsgroup-ignored-charsets)
                 nil)))
          (save-excursion
@@ -4537,7 +4534,7 @@ N is the numerical prefix."
                          (mm-dissect-buffer nil gnus-article-loose-mime)
                          (and gnus-article-emulate-mime
                               (mm-uu-dissect))))
-            buffer-read-only handle name type b e display)
+            (inhibit-read-only t) handle name type b e display)
        (when (and (not ihandles)
                   (not gnus-displaying-mime))
          ;; Top-level call; we clean up.
@@ -4672,11 +4669,9 @@ If displaying \"text/html\" is discouraged \(see
          (push (cons id handle) gnus-article-mime-handle-alist)
          (when (or (not display)
                    (not (gnus-unbuttonized-mime-type-p type)))
-           ;(gnus-article-insert-newline)
            (gnus-insert-mime-button
             handle id (list (or display (and not-attachment text))))
            (gnus-article-insert-newline)
-           ;(gnus-article-insert-newline)
            ;; Remember modify the number of forward lines.
            (setq move t))
          (setq beg (point))
@@ -4732,7 +4727,7 @@ If displaying \"text/html\" is discouraged \(see
   (let* ((preferred (or preferred (mm-preferred-alternative handles)))
         (ihandles handles)
         (point (point))
-        handle buffer-read-only from props begend not-pref)
+        handle (inhibit-read-only t) from props begend not-pref)
     (save-window-excursion
       (save-restriction
        (when ibegend
@@ -4803,8 +4798,8 @@ If displaying \"text/html\" is discouraged \(see
              (gnus-display-mime preferred)
            (let ((mail-parse-charset gnus-newsgroup-charset)
                  (mail-parse-ignored-charsets
-                  (save-excursion (set-buffer gnus-summary-buffer)
-                                  gnus-newsgroup-ignored-charsets)))
+                  (with-current-buffer gnus-summary-buffer
+                    gnus-newsgroup-ignored-charsets)))
              (mm-display-part preferred)
              ;; Do highlighting.
              (save-excursion
@@ -4854,8 +4849,7 @@ is the string to use when it is inactive.")
 
 (defun gnus-article-wash-status ()
   "Return a string which display status of article washing."
-  (save-excursion
-    (set-buffer gnus-article-buffer)
+  (with-current-buffer gnus-article-buffer
     (let ((cite (memq 'cite gnus-article-wash-types))
          (headers (memq 'headers gnus-article-wash-types))
          (boring (memq 'boring-headers gnus-article-wash-types))
@@ -4904,8 +4898,8 @@ is the string to use when it is inactive.")
   "Hide unwanted headers if `gnus-have-all-headers' is nil.
 Provided for backwards compatibility."
   (when (and (or (not (gnus-buffer-live-p gnus-summary-buffer))
-                (not (save-excursion (set-buffer gnus-summary-buffer)
-                                     gnus-have-all-headers)))
+                (not (with-current-buffer gnus-summary-buffer
+                       gnus-have-all-headers)))
             (not gnus-inhibit-hiding))
     (gnus-article-hide-headers)))
 
@@ -4935,7 +4929,7 @@ If given a numerical ARG, move forward ARG pages."
     (widen)
     ;; Remove any old next/prev buttons.
     (when (gnus-visual-p 'page-marker)
-      (let ((buffer-read-only nil))
+      (let ((inhibit-read-only t))
        (gnus-remove-text-with-property 'gnus-prev)
        (gnus-remove-text-with-property 'gnus-next)))
     (if
@@ -4955,7 +4949,7 @@ If given a numerical ARG, move forward ARG pages."
           (match-beginning 0)
         (point)))
       (when (and (gnus-visual-p 'page-marker)
-                (not (= (point-min) 1)))
+                (> (point-min) (save-restriction (widen) (point-min))))
        (save-excursion
          (goto-char (point-min))
          (gnus-insert-prev-page-button)))
@@ -5011,6 +5005,7 @@ Argument LINES specifies lines to be scrolled up."
              (save-excursion
                (save-restriction
                  (widen)
+                 (forward-line)
                  (eobp)))) ;Real end-of-buffer?
          (progn
            (when gnus-article-over-scroll
@@ -5168,11 +5163,13 @@ not have a face in `gnus-article-boring-faces'."
       (let ((obuf (current-buffer))
            (owin (current-window-configuration))
            (opoint (point))
-           (summary gnus-article-current-summary)
-           func in-buffer selected)
-       (if not-restore-window
-           (pop-to-buffer summary 'norecord)
-         (switch-to-buffer summary 'norecord))
+           win func in-buffer selected new-sum-start new-sum-hscroll)
+       (cond (not-restore-window
+              (pop-to-buffer gnus-article-current-summary 'norecord))
+             ((setq win (get-buffer-window gnus-article-current-summary))
+              (select-window win))
+             (t
+              (switch-to-buffer gnus-article-current-summary 'norecord)))
        (setq in-buffer (current-buffer))
        ;; We disable the pick minor mode commands.
        (if (and (setq func (let (gnus-pick-mode)
@@ -5180,7 +5177,10 @@ not have a face in `gnus-article-boring-faces'."
                 (functionp func))
            (progn
              (call-interactively func)
-             (setq new-sum-point (point))
+             (when (eq win (selected-window))
+               (setq new-sum-point (point)
+                     new-sum-start (window-start win)
+                     new-sum-hscroll (window-hscroll win))
              (when (eq in-buffer (current-buffer))
                (setq selected (gnus-summary-select-article))
                (set-buffer obuf)
@@ -5192,10 +5192,12 @@ not have a face in `gnus-article-boring-faces'."
                                    1)
                  (set-window-point (get-buffer-window (current-buffer))
                                    (point)))
-               (let ((win (get-buffer-window gnus-article-current-summary)))
-                 (when win
-                   (set-window-point win new-sum-point))))    )
-         (switch-to-buffer gnus-article-buffer)
+               (when (and (not not-restore-window)
+                          new-sum-point)
+                 (set-window-point win new-sum-point)
+                 (set-window-start win new-sum-start)
+                 (set-window-hscroll win new-sum-hscroll)))))
+         (set-window-configuration owin)
          (ding))))))
 
 (defun gnus-article-describe-key (key)
@@ -5247,7 +5249,7 @@ the entire article will be yanked."
   (interactive "P")
   (let ((article (cdr gnus-article-current))
        contents)
-    (if (not (gnus-mark-active-p))
+    (if (not (gnus-region-active-p))
        (with-current-buffer gnus-summary-buffer
          (gnus-summary-reply (list (list article)) wide))
       (setq contents (buffer-substring (point) (mark t)))
@@ -5266,7 +5268,7 @@ the entire article will be yanked."
   (interactive)
   (let ((article (cdr gnus-article-current))
        contents)
-      (if (not (gnus-mark-active-p))
+      (if (not (gnus-region-active-p))
          (with-current-buffer gnus-summary-buffer
            (gnus-summary-followup (list (list article))))
        (setq contents (buffer-substring (point) (mark t)))
@@ -5366,16 +5368,14 @@ If given a prefix, show the hidden text instead."
                 gnus-summary-buffer
                 (get-buffer gnus-summary-buffer)
                 (gnus-buffer-exists-p gnus-summary-buffer)
-                (eq (cdr (save-excursion
-                           (set-buffer gnus-summary-buffer)
+                (eq (cdr (with-current-buffer gnus-summary-buffer
                            (assq article gnus-newsgroup-reads)))
                     gnus-canceled-mark))
            nil)
           ;; We first check `gnus-original-article-buffer'.
           ((and (get-buffer gnus-original-article-buffer)
                 (numberp article)
-                (save-excursion
-                  (set-buffer gnus-original-article-buffer)
+                (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)
@@ -5407,7 +5407,7 @@ If given a prefix, show the hidden text instead."
                  (backend (car (gnus-find-method-for-group
                                 gnus-newsgroup-name)))
                  result
-                 (buffer-read-only nil))
+                 (inhibit-read-only t))
              (if (or (not (listp methods))
                      (and (symbolp (car methods))
                           (assq (car methods) nnoo-definition-alist)))
@@ -5459,7 +5459,7 @@ If given a prefix, show the hidden text instead."
            (buffer-disable-undo)
            (setq major-mode 'gnus-original-article-mode)
            (setq buffer-read-only t))
-         (let (buffer-read-only)
+         (let ((inhibit-read-only t))
            (erase-buffer)
            (insert-buffer-substring gnus-article-buffer))
          (setq gnus-original-article (cons group article)))
@@ -5493,7 +5493,6 @@ If given a prefix, show the hidden text instead."
 (defvar gnus-article-edit-done-function nil)
 
 (defvar gnus-article-edit-mode-map nil)
-(defvar gnus-article-edit-mode nil)
 
 ;; Should we be using derived.el for this?
 (unless gnus-article-edit-mode-map
@@ -5558,7 +5557,7 @@ If given a prefix, show the hidden text instead."
     ["Body" message-goto-body t]
     ["Signature" message-goto-signature t]))
 
-(define-derived-mode gnus-article-edit-mode text-mode "Article Edit"
+(define-derived-mode gnus-article-edit-mode message-mode "Article Edit"
   "Major mode for editing articles.
 This is an extended text-mode.
 
@@ -5727,7 +5726,7 @@ The function must take one argument, the string naming the URL."
 
 (defcustom gnus-button-ctan-directory-regexp
   (concat
-   "\\("; Cannot use `\(?: ... \)' (compatibility with Emacs 20).
+   "\\(?:"
    "biblio\\|digests\\|dviware\\|fonts\\|graphics\\|help\\|"
    "indexing\\|info\\|language\\|macros\\|support\\|systems\\|"
    "tds\\|tools\\|usergrps\\|web\\|nonfree\\|obsolete"
@@ -6060,7 +6059,7 @@ positives are possible."
     ("\\(<URL: *\\)mailto: *\\([^> \n\t]+\\)>"
      0 (>= gnus-button-message-level 0) gnus-url-mailto 2)
     ;; RFC 2368 (The mailto URL scheme)
-    ("mailto:\\([-a-z.@_+0-9%=?&]+\\)"
+    ("\\bmailto:\\([-a-z.@_+0-9%=?&/]+\\)"
      0 (>= gnus-button-message-level 0) gnus-url-mailto 1)
     ("\\bmailto:\\([^ \n\t]+\\)"
      0 (>= gnus-button-message-level 0) gnus-url-mailto 1)
@@ -6108,8 +6107,9 @@ positives are possible."
     ("M-x[ \t\n]+apropos-documentation[ \t\n]+RET[ \t\n]+\\([^ \t\n]+\\)[ \t\n]+RET"
      0 (>= gnus-button-emacs-level 1) gnus-button-handle-apropos-documentation 1)
     ;; The following entries may lead to many false positives so don't enable
-    ;; them by default (use a high button level):
-    ("/\\([a-z][-a-z0-9]+\\.el\\)\\>"
+    ;; them by default (use a high button level).
+    ("/\\([a-z][-a-z0-9]+\\.el\\)\\>[^.?]"
+     ;; Exclude [.?] for URLs in gmane.emacs.cvs
      1 (>= gnus-button-emacs-level 8) gnus-button-handle-library 1)
     ("`\\([a-z][-a-z0-9]+\\.el\\)'"
      1 (>= gnus-button-emacs-level 8) gnus-button-handle-library 1)
@@ -6142,16 +6142,16 @@ positives are possible."
     (gnus-button-url-regexp
      0 (>= gnus-button-browse-level 0) browse-url 0)
     ;; man pages
-    ("\\b\\([a-z][a-z]+\\)([1-9])\\W"
+    ("\\b\\([a-z][a-z]+([1-9])\\)\\W"
      0 (and (>= gnus-button-man-level 1) (< gnus-button-man-level 3))
      gnus-button-handle-man 1)
     ;; more man pages: resolv.conf(5), iso_8859-1(7), xterm(1x)
-    ("\\b\\([a-z][-_.a-z0-9]+\\)([1-9])\\W"
+    ("\\b\\([a-z][-_.a-z0-9]+([1-9])\\)\\W"
      0 (and (>= gnus-button-man-level 3) (< gnus-button-man-level 5))
      gnus-button-handle-man 1)
     ;; even more: Apache::PerlRun(3pm), PDL::IO::FastRaw(3pm),
     ;; SoWWWAnchor(3iv), XSelectInput(3X11), X(1), X(7)
-    ("\\b\\([a-z][-+_.:a-z0-9]+\\)([1-9][X1a-z]*)\\W\\|\\b\\(X\\)([1-9])\\W"
+    ("\\b\\(\\(?:[a-z][-+_.:a-z0-9]+([1-9][X1a-z]*)\\)\\|\\b\\(?:X([1-9])\\)\\)\\W"
      0 (>= gnus-button-man-level 5) gnus-button-handle-man 1)
     ;; MID or mail: To avoid too many false positives we don't try to catch
     ;; all kind of allowed MIDs or mail addresses.  Domain part must contain
@@ -6195,7 +6195,7 @@ variable it the real callback function."
      0 (>= gnus-button-browse-level 0) browse-url 0)
     ("^[^:]+:" gnus-button-url-regexp
      0 (>= gnus-button-browse-level 0) browse-url 0)
-    ("^[^:]+:" "\\bmailto:\\([-a-z.@_+0-9%=?&]+\\)"
+    ("^[^:]+:" "\\bmailto:\\([-a-z.@_+0-9%=?&/]+\\)"
      0 (>= gnus-button-message-level 0) gnus-url-mailto 1)
     ("^[^:]+:" "\\(<\\(url: \\)?\\(nntp\\|news\\):\\([^>\n ]*\\)>\\)"
      1 (>= gnus-button-message-level 0) gnus-button-message-id 4))
@@ -6219,13 +6219,6 @@ HEADER is a regexp to match a header.  For a fuller explanation, see
                               :inline t
                               (integer :tag "Regexp group")))))
 
-(defvar gnus-button-regexp nil)
-(defvar gnus-button-marker-list nil)
-;; Regexp matching any of the regexps from `gnus-button-alist'.
-
-(defvar gnus-button-last nil)
-;; The value of `gnus-button-alist' when `gnus-button-regexp' was build.
-
 ;;; Commands:
 
 (defun gnus-article-push-button (event)
@@ -6277,51 +6270,43 @@ do the highlighting.  See the documentation for those functions."
 (defun gnus-article-highlight-headers ()
   "Highlight article headers as specified by `gnus-header-face-alist'."
   (interactive)
-  (save-excursion
-    (set-buffer gnus-article-buffer)
-    (save-restriction
-      (let ((alist gnus-header-face-alist)
-           (buffer-read-only nil)
-           (case-fold-search t)
-           (inhibit-point-motion-hooks t)
-           entry regexp header-face field-face from hpoints fpoints)
-       (article-narrow-to-head)
-       (while (setq entry (pop alist))
-         (goto-char (point-min))
-         (setq regexp (concat "^\\("
-                              (if (string-equal "" (nth 0 entry))
-                                  "[^\t ]"
-                                (nth 0 entry))
-                              "\\)")
-               header-face (nth 1 entry)
-               field-face (nth 2 entry))
-         (while (and (re-search-forward regexp nil t)
-                     (not (eobp)))
-           (beginning-of-line)
-           (setq from (point))
-           (unless (search-forward ":" nil t)
-             (forward-char 1))
-           (when (and header-face
-                      (not (memq (point) hpoints)))
-             (push (point) hpoints)
-             (gnus-put-text-property from (point) 'face header-face))
-           (when (and field-face
-                      (not (memq (setq from (point)) fpoints)))
-             (push from fpoints)
-             (if (re-search-forward "^[^ \t]" nil t)
-                 (forward-char -2)
-               (goto-char (point-max)))
-             (gnus-put-text-property from (point) 'face field-face))))))))
+  (gnus-with-article-headers
+    (let ((alist gnus-header-face-alist)
+         entry regexp header-face field-face from hpoints fpoints)
+      (while (setq entry (pop alist))
+       (goto-char (point-min))
+       (setq regexp (concat "^\\("
+                            (if (string-equal "" (nth 0 entry))
+                                "[^\t ]"
+                              (nth 0 entry))
+                            "\\)")
+             header-face (nth 1 entry)
+             field-face (nth 2 entry))
+       (while (and (re-search-forward regexp nil t)
+                   (not (eobp)))
+         (beginning-of-line)
+         (setq from (point))
+         (unless (search-forward ":" nil t)
+           (forward-char 1))
+         (when (and header-face
+                    (not (memq (point) hpoints)))
+           (push (point) hpoints)
+           (gnus-put-text-property from (point) 'face header-face))
+         (when (and field-face
+                    (not (memq (setq from (point)) fpoints)))
+           (push from fpoints)
+           (if (re-search-forward "^[^ \t]" nil t)
+               (forward-char -2)
+             (goto-char (point-max)))
+           (gnus-put-text-property from (point) 'face field-face)))))))
 
 (defun gnus-article-highlight-signature ()
   "Highlight the signature in an article.
 It does this by highlighting everything after
 `gnus-signature-separator' using `gnus-signature-face'."
   (interactive)
-  (save-excursion
-    (set-buffer gnus-article-buffer)
-    (let ((buffer-read-only nil)
-         (inhibit-point-motion-hooks t))
+  (gnus-with-article-buffer
+    (let ((inhibit-point-motion-hooks t))
       (save-restriction
        (when (and gnus-signature-face
                   (gnus-article-narrow-to-signature))
@@ -6343,10 +6328,8 @@ It does this by highlighting everything after
 \"External references\" are things like Message-IDs and URLs, as
 specified by `gnus-button-alist'."
   (interactive (list 'force))
-  (save-excursion
-    (set-buffer gnus-article-buffer)
-    (let ((buffer-read-only nil)
-         (inhibit-point-motion-hooks t)
+  (gnus-with-article-buffer
+    (let ((inhibit-point-motion-hooks t)
          (case-fold-search t)
          (alist gnus-button-alist)
          beg entry regexp)
@@ -6387,40 +6370,33 @@ specified by `gnus-button-alist'."
 (defun gnus-article-add-buttons-to-head ()
   "Add buttons to the head of the article."
   (interactive)
-  (save-excursion
-    (set-buffer gnus-article-buffer)
-    (save-restriction
-      (let ((buffer-read-only nil)
-           (inhibit-point-motion-hooks t)
-           (case-fold-search t)
-           (alist gnus-header-button-alist)
-           entry beg end)
-       (article-narrow-to-head)
-       (while alist
-         ;; Each alist entry.
-         (setq entry (car alist)
-               alist (cdr alist))
-         (goto-char (point-min))
-         (while (re-search-forward (car entry) nil t)
-           ;; Each header matching the entry.
-           (setq beg (match-beginning 0))
-           (setq end (or (and (re-search-forward "^[^ \t]" nil t)
-                              (match-beginning 0))
-                         (point-max)))
-           (goto-char beg)
-           (while (re-search-forward (eval (nth 1 entry)) end t)
-             ;; Each match within a header.
-             (let* ((entry (cdr entry))
-                    (start (match-beginning (nth 1 entry)))
-                    (end (match-end (nth 1 entry)))
-                    (form (nth 2 entry)))
-               (goto-char (match-end 0))
-               (when (eval form)
-                 (gnus-article-add-button
-                  start end (nth 3 entry)
-                  (buffer-substring (match-beginning (nth 4 entry))
-                                    (match-end (nth 4 entry)))))))
-           (goto-char end)))))))
+  (gnus-with-article-headers
+    (let ((alist gnus-header-button-alist)
+         entry beg end)
+      (while alist
+       ;; Each alist entry.
+       (setq entry (pop alist))
+       (goto-char (point-min))
+       (while (re-search-forward (car entry) nil t)
+         ;; Each header matching the entry.
+         (setq beg (match-beginning 0))
+         (setq end (or (and (re-search-forward "^[^ \t]" nil t)
+                            (match-beginning 0))
+                       (point-max)))
+         (goto-char beg)
+         (while (re-search-forward (eval (nth 1 entry)) end t)
+           ;; Each match within a header.
+           (let* ((entry (cdr entry))
+                  (start (match-beginning (nth 1 entry)))
+                  (end (match-end (nth 1 entry)))
+                  (form (nth 2 entry)))
+             (goto-char (match-end 0))
+             (when (eval form)
+               (gnus-article-add-button
+                start end (nth 3 entry)
+                (buffer-substring (match-beginning (nth 4 entry))
+                                  (match-end (nth 4 entry)))))))
+         (goto-char end))))))
 
 ;;; External functions:
 
@@ -6441,15 +6417,12 @@ specified by `gnus-button-alist'."
 ;;; Internal functions:
 
 (defun gnus-article-set-globals ()
-  (save-excursion
-    (set-buffer gnus-summary-buffer)
+  (with-current-buffer gnus-summary-buffer
     (gnus-set-global-variables)))
 
 (defun gnus-signature-toggle (end)
-  (save-excursion
-    (set-buffer gnus-article-buffer)
-    (let ((buffer-read-only nil)
-         (inhibit-point-motion-hooks t))
+  (gnus-with-article-buffer
+    (let ((inhibit-point-motion-hooks t))
       (if (text-property-any end (point-max) 'article-type 'signature)
          (progn
            (gnus-delete-wash-type 'signature)
@@ -6540,6 +6513,10 @@ specified by `gnus-button-alist'."
 
 (defun gnus-button-handle-man (url)
   "Fetch a man page."
+  (gnus-message 9 "`%s' `%s'" gnus-button-man-handler url)
+  (when (eq gnus-button-man-handler 'woman)
+    (setq url (gnus-replace-in-string url "([1-9][X1a-z]*).*\\'" "")))
+  (gnus-message 9 "`%s' `%s'" gnus-button-man-handler url)
   (funcall gnus-button-man-handler url))
 
 (defun gnus-button-handle-info-url (url)
@@ -6585,8 +6562,7 @@ specified by `gnus-button-alist'."
 
 (defun gnus-button-message-id (message-id)
   "Fetch MESSAGE-ID."
-  (save-excursion
-    (set-buffer gnus-summary-buffer)
+  (with-current-buffer gnus-summary-buffer
     (gnus-summary-refer-article message-id)))
 
 (defun gnus-button-fetch-group (address)
@@ -6666,16 +6642,19 @@ specified by `gnus-button-alist'."
 
 (defvar gnus-prev-page-map
   (let ((map (make-sparse-keymap)))
-    (unless (>= emacs-major-version 21)
-      ;; XEmacs doesn't care.
-      (set-keymap-parent map gnus-article-mode-map))
     (define-key map gnus-mouse-2 'gnus-button-prev-page)
     (define-key map "\r" 'gnus-button-prev-page)
     map))
 
+(defvar gnus-next-page-map
+  (let ((map (make-sparse-keymap)))
+    (define-key map gnus-mouse-2 'gnus-button-next-page)
+    (define-key map "\r" 'gnus-button-next-page)
+    map))
+
 (defun gnus-insert-prev-page-button ()
   (let ((b (point))
-       (buffer-read-only nil))
+       (inhibit-read-only t))
     (gnus-eval-format
      gnus-prev-page-line-format nil
      `(keymap ,gnus-prev-page-map
@@ -6690,24 +6669,6 @@ specified by `gnus-button-alist'."
      :action 'gnus-button-prev-page
      :button-keymap gnus-prev-page-map)))
 
-(defvar gnus-prev-page-map
-  (let ((map (make-sparse-keymap)))
-    (unless (>= emacs-major-version 21)
-      ;; XEmacs doesn't care.
-      (set-keymap-parent map gnus-article-mode-map))
-    (define-key map gnus-mouse-2 'gnus-button-prev-page)
-    (define-key map "\r" 'gnus-button-prev-page)
-    map))
-
-(defvar gnus-next-page-map
-  (let ((map (make-sparse-keymap)))
-    (unless (>= emacs-major-version 21)
-      ;; XEmacs doesn't care.
-      (set-keymap-parent map gnus-article-mode-map))
-    (define-key map gnus-mouse-2 'gnus-button-next-page)
-    (define-key map "\r" 'gnus-button-next-page)
-    map))
-
 (defun gnus-button-next-page (&optional args more-args)
   "Go to the next page."
   (interactive)
@@ -6726,7 +6687,7 @@ specified by `gnus-button-alist'."
 
 (defun gnus-insert-next-page-button ()
   (let ((b (point))
-       (buffer-read-only nil))
+       (inhibit-read-only t))
     (gnus-eval-format gnus-next-page-line-format nil
                      `(keymap ,gnus-next-page-map
                          gnus-next t
@@ -6761,7 +6722,7 @@ specified by `gnus-button-alist'."
   "List of methods used to decode headers.
 
 This variable is a list of FUNCTION or (REGEXP . FUNCTION).  If item
-is FUNCTION, FUNCTION will be apply to all newsgroups.  If item is a
+is FUNCTION, FUNCTION will be applied to all newsgroups.  If item is a
 \(REGEXP . FUNCTION), FUNCTION will be only apply to the newsgroups
 whose names match REGEXP.
 
@@ -6900,7 +6861,7 @@ For example:
          (setq references
              (or (mail-header-references gnus-current-headers) ""))
          (set-buffer gnus-article-buffer)
-         (let* ((buffer-read-only nil)
+         (let* ((inhibit-read-only t)
                 (headers
                  (mapcar (lambda (field)
                            (and (save-restriction
@@ -6964,8 +6925,6 @@ For example:
 
 (defvar gnus-mime-security-button-map
   (let ((map (make-sparse-keymap)))
-    (unless (>= (string-to-number emacs-version) 21)
-      (set-keymap-parent map gnus-article-mode-map))
     (define-key map gnus-mouse-2 'gnus-article-push-button)
     (define-key map "\r" 'gnus-article-press-button)
     map))
@@ -6980,7 +6939,7 @@ For example:
 (defun gnus-mime-security-verify-or-decrypt (handle)
   (mm-remove-parts (cdr handle))
   (let ((region (mm-handle-multipart-ctl-parameter handle 'gnus-region))
-       point buffer-read-only)
+       point (inhibit-read-only t))
     (if region
        (goto-char (car region)))
     (save-restriction
@@ -7010,7 +6969,7 @@ For example:
                 (not (get-text-property (point) 'gnus-mime-details)))
                (gnus-mime-security-button-line-format
                 (get-text-property (point) 'gnus-line-format))
-               buffer-read-only)
+               (inhibit-read-only t))
            (forward-char -1)
            (while (eq (get-text-property (point) 'gnus-line-format)
                       gnus-mime-security-button-line-format)
@@ -7120,4 +7079,5 @@ For example:
 
 (run-hooks 'gnus-art-load-hook)
 
+;;; arch-tag: 2654516f-6279-48f9-a83b-05c1fa450c33
 ;;; gnus-art.el ends here