Synch to No Gnus 200401141432.
[elisp/gnus.git-] / lisp / gnus-art.el
index 549117a..57f481f 100644 (file)
@@ -60,6 +60,7 @@
 (autoload 'gnus-msg-mail "gnus-msg" nil t)
 (autoload 'gnus-button-mailto "gnus-msg")
 (autoload 'gnus-button-reply "gnus-msg" nil t)
+(autoload 'ansi-color-apply-on-region "ansi-color")
 
 (defgroup gnus-article nil
   "Article display."
@@ -303,7 +304,7 @@ asynchronously.      The compressed face will be piped to this command."
                         '(function-item
                           x-face-mule-gnus-article-display-x-face))
                     'function))))
-  ;;:version "21.1"
+  :version "21.1"
   :group 'gnus-picon
   :group 'gnus-article-washing)
 
@@ -950,8 +951,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.
@@ -1204,6 +1204,14 @@ See Info node `(gnus)Customizing Articles' for details."
   :type gnus-article-treat-custom)
 (put 'gnus-treat-overstrike 'highlight 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."
+  :group 'gnus-article-treat
+  :link '(custom-manual "(gnus)Customizing Articles")
+  :type gnus-article-treat-custom)
+
 (make-obsolete-variable 'gnus-treat-display-xface
                        'gnus-treat-display-x-face)
 
@@ -1224,7 +1232,7 @@ Valid values are nil, t, `head', `last', an integer or a predicate.
 See Info node `(gnus)Customizing Articles' and Info node
 `(gnus)X-Face' for details."
   :group 'gnus-article-treat
-  ;;:version "21.1"
+  :version "21.1"
   :link '(custom-manual "(gnus)Customizing Articles")
   :link '(custom-manual "(gnus)X-Face")
   :type gnus-article-treat-head-custom
@@ -1251,10 +1259,9 @@ See Info node `(gnus)Customizing Articles' and Info node
   (not (or (featurep 'xemacs)
           (gnus-image-type-available-p 'xpm)
           (gnus-image-type-available-p 'pbm)))
-  "If non-nil, gnus uses `smiley-mule' for displaying smileys rather than
-`smiley'.  It defaults to t when Emacs 20 or earlier is running.
+  "Non-nil means use `smiley-mule' to show smileys rather than `smiley'.
 `smiley-mule' is boundled in BITMAP-MULE package.  You can set it to t
-even if you are using Emacs 21+.  It has no effect on XEmacs."
+even if your Emacs supports images.  It has no effect on XEmacs."
   :group 'gnus-article-various
   :type 'boolean
   :get (lambda (symbol)
@@ -1311,7 +1318,7 @@ Valid values are nil, t, `head', `last', an integer or a predicate.
 See Info node `(gnus)Customizing Articles' and Info node
 `(gnus)Smileys' for details."
   :group 'gnus-article-treat
-  ;;:version "21.1"
+  :version "21.1"
   :link '(custom-manual "(gnus)Customizing Articles")
   :link '(custom-manual "(gnus)Smileys")
   :type gnus-article-treat-custom)
@@ -1523,6 +1530,7 @@ This requires GNU Libidn, and by default only enabled if it is found."
     (gnus-treat-strip-multiple-blank-lines
      gnus-article-strip-multiple-blank-lines)
     (gnus-treat-overstrike gnus-article-treat-overstrike)
+    (gnus-treat-ansi-sequences gnus-article-treat-ansi-sequences)
     (gnus-treat-unfold-headers gnus-article-treat-unfold-headers)
     (gnus-treat-fold-headers gnus-article-treat-fold-headers)
     ;; Displaying X-Face should be done after unfolding headers
@@ -1760,7 +1768,7 @@ always hide."
              (while (re-search-forward "^[^: \t]+:[ \t]*\n[^ \t]" nil t)
                (forward-line -1)
                (gnus-article-hide-text-type
-                (gnus-point-at-bol)
+                (point-at-bol)
                 (progn
                   (end-of-line)
                   (if (re-search-forward "^[^ \t]" nil t)
@@ -1886,7 +1894,7 @@ always hide."
     (goto-char (point-min))
     (when (re-search-forward (concat "^" header ":") nil t)
       (gnus-article-hide-text-type
-       (gnus-point-at-bol)
+       (point-at-bol)
        (progn
         (end-of-line)
         (if (re-search-forward "^[^ \t]" nil t)
@@ -1907,7 +1915,7 @@ always hide."
        (article-narrow-to-head)
        (while (not (eobp))
          (cond
-          ((< (setq column (- (gnus-point-at-eol) (point)))
+          ((< (setq column (- (point-at-eol) (point)))
               gnus-article-normalized-header-length)
            (end-of-line)
            (insert (make-string
@@ -1918,7 +1926,7 @@ always hide."
             (progn
               (forward-char gnus-article-normalized-header-length)
               (point))
-            (gnus-point-at-eol)
+            (point-at-eol)
             'invisible t))
           (t
            ;; Do nothing.
@@ -1999,6 +2007,14 @@ MAP is an alist where the elements are on the form (\"from\" \"to\")."
              (put-text-property
               (point) end 'face 'underline)))))))))
 
+(defun article-treat-ansi-sequences ()
+  "Translate ANSI SGR control sequences into overlays or extents."
+  (interactive)
+  (save-excursion
+    (when (article-goto-body)
+      (let ((buffer-read-only nil))
+       (ansi-color-apply-on-region (point) (point-max))))))
+
 (defun gnus-article-treat-unfold-headers ()
   "Unfold folded message headers.
 Only the headers that fit into the current window width will be
@@ -2035,8 +2051,7 @@ unfolded."
   "Toggle display of textual emoticons (\"smileys\") as small graphical icons."
   (interactive)
   (unless (featurep 'xemacs)
-    (when (and (>= emacs-major-version 21)
-              (not gnus-article-should-use-smiley-mule)
+    (when (and (not gnus-article-should-use-smiley-mule)
               gnus-article-smiley-mule-loaded-p)
       (load "smiley" nil t)
       (setq gnus-article-smiley-mule-loaded-p nil))
@@ -2105,7 +2120,7 @@ unfolded."
            (end-of-line)
            (when (>= (current-column) (min fill-column width))
              (narrow-to-region (min (1+ (point)) (point-max))
-                               (gnus-point-at-bol))
+                               (point-at-bol))
              (let ((goback (point-marker)))
                (fill-paragraph nil)
                (goto-char (marker-position goback)))
@@ -2147,7 +2162,7 @@ unfolded."
         (while (and (not (bobp))
                     (looking-at "^[ \t]*$")
                     (not (gnus-annotation-in-region-p
-                          (point) (gnus-point-at-eol))))
+                          (point) (point-at-eol))))
           (forward-line -1))
         (forward-line 1)
         (point))))))
@@ -2257,14 +2272,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)
@@ -2528,11 +2541,14 @@ If READ-CHARSET, ask for a coding system."
                                 "\\`cid:"))
          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.
@@ -2634,11 +2650,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)))
@@ -2918,11 +2932,11 @@ should replace the \"Date:\" one, or should be added below it."
                    (re-search-forward "^Date:[ \t]" nil t)
                    ;; If Date is missing, try again for X-Sent.
                    (re-search-forward "^X-Sent:[ \t]" nil t))
-           (setq bface (get-text-property (gnus-point-at-bol) 'face)
-                 date (or (get-text-property (gnus-point-at-bol)
+           (setq bface (get-text-property (point-at-bol) 'face)
+                 date (or (get-text-property (point-at-bol)
                                              'original-date)
                           date)
-                 eface (get-text-property (1- (gnus-point-at-eol))
+                 eface (get-text-property (1- (point-at-eol))
                                           'face)))
          (let ((buffer-read-only nil))
            ;; Delete any old X-Sent headers.
@@ -2954,7 +2968,7 @@ should replace the \"Date:\" one, or should be added below it."
              (goto-char (point-min)))
            (insert (article-make-date-line date type))
            (when (eq type 'lapsed)
-             (put-text-property (gnus-point-at-bol) (point)
+             (put-text-property (point-at-bol) (point)
                                 'article-date-lapsed t))
            (insert "\n")
            (forward-line -1)
@@ -3131,7 +3145,7 @@ is to run."
     (setq n 1))
   (gnus-stop-date-timer)
   (setq article-lapsed-timer
-       (nnheader-run-at-time 1 n 'article-update-date-lapsed)))
+       (run-at-time 1 n 'article-update-date-lapsed)))
 
 (defun gnus-stop-date-timer ()
   "Stop the X-Sent timer."
@@ -3465,17 +3479,9 @@ The directory to save in defaults to `gnus-article-save-directory'."
       (shell-command-on-region (point-min) (point-max) command nil)))
   (setq gnus-last-shell-command command))
 
-(defmacro gnus-read-string (prompt &optional initial-contents history
-                           default-value)
-  "Like `read-string' but allow for older XEmacsen that don't have the 5th arg."
-  (if (and (featurep 'xemacs)
-          (< emacs-minor-version 2))
-      `(read-string ,prompt ,initial-contents ,history)
-    `(read-string ,prompt ,initial-contents ,history ,default-value)))
-
 (defun gnus-summary-pipe-to-muttprint (&optional command)
   "Pipe this article to muttprint."
-  (setq command (gnus-read-string
+  (setq command (read-string
                 "Print using command: " gnus-summary-muttprint-program
                 nil gnus-summary-muttprint-program))
   (gnus-summary-save-in-pipe command))
@@ -3598,8 +3604,8 @@ If variable `gnus-use-long-file-name' is non-nil, it is
                (message-narrow-to-head)
                (goto-char (point-max))
                (forward-line -1)
-               (setq bface (get-text-property (gnus-point-at-bol) 'face)
-                     eface (get-text-property (1- (gnus-point-at-eol)) 'face))
+               (setq bface (get-text-property (point-at-bol) 'face)
+                     eface (get-text-property (1- (point-at-eol)) 'face))
                (message-remove-header "X-Gnus-PGP-Verify")
                (if (re-search-forward "^X-PGP-Sig:" nil t)
                    (forward-line)
@@ -3666,6 +3672,7 @@ If variable `gnus-use-long-file-name' is non-nil, it is
      article-monafy
      article-hide-boring-headers
      article-treat-overstrike
+     article-treat-ansi-sequences
      article-fill-long-lines
      article-capitalize-sentences
      article-remove-cr
@@ -3778,6 +3785,7 @@ If variable `gnus-use-long-file-name' is non-nil, it is
        ["Hide signature" gnus-article-hide-signature t]
        ["Hide citation" gnus-article-hide-citation t]
        ["Treat overstrike" gnus-article-treat-overstrike t]
+       ["Treat ANSI sequences" gnus-article-treat-ansi-sequences t]
        ["Remove carriage return" gnus-article-remove-cr t]
        ["Remove leading whitespace" gnus-article-remove-leading-whitespace t]
        ["Treat html" gnus-article-wash-html t]
@@ -4260,9 +4268,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)
@@ -4276,22 +4281,6 @@ General format specifiers can also be used.  See Info node
                (vector (caddr c) (car c) :enable t))
              gnus-mime-button-commands)))
 
-(eval-when-compile
-  (define-compiler-macro popup-menu (&whole form
-                                           menu &optional position prefix)
-    (if (and (fboundp 'popup-menu)
-            (not (memq 'popup-menu (assoc "lmenu" load-history))))
-       form
-      ;; Gnus is probably running under Emacs 20.
-      `(let* ((menu (cdr ,menu))
-             (response (x-popup-menu
-                        t (list (car menu)
-                                (cons "" (mapcar (lambda (c)
-                                                   (cons (caddr c) (car c)))
-                                                 (cdr menu)))))))
-        (if response
-            (call-interactively (nth 3 (assq response menu))))))))
-
 (defun gnus-mime-button-menu (event prefix)
  "Construct a context-sensitive menu of MIME commands."
  (interactive "e\nP")
@@ -4305,8 +4294,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
@@ -4669,8 +4657,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)
@@ -4686,8 +4674,8 @@ 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))
+         (with-current-buffer gnus-summary-buffer
+           gnus-newsgroup-ignored-charsets))
         buffer-read-only)
     (when handle
       (if (mm-handle-undisplayer handle)
@@ -4704,8 +4692,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)
@@ -4771,8 +4758,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))
@@ -4800,8 +4786,7 @@ N is the numerical prefix."
              (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
@@ -4869,11 +4854,11 @@ N is the numerical prefix."
     (setq b (point))
     (gnus-eval-format
      gnus-mime-button-line-format gnus-mime-button-line-format-alist
-     `(,@(gnus-local-map-property gnus-mime-button-map)
-        gnus-callback gnus-mm-display-part
-        gnus-part ,gnus-tmp-id
-        article-type annotation
-        gnus-data ,handle))
+     `(keymap ,gnus-mime-button-map
+             gnus-callback gnus-mm-display-part
+             gnus-part ,gnus-tmp-id
+             article-type annotation
+             gnus-data ,handle))
     (setq e (if (bolp)
                ;; Exclude a newline.
                (1- (point))
@@ -5151,7 +5136,7 @@ If displaying \"text/html\" is discouraged \(see
                       ',gnus-article-mime-handle-alist))
               (gnus-mime-display-alternative
                ',ihandles ',not-pref ',begend ,id))
-            ,@(gnus-local-map-property gnus-mime-button-map)
+            keymap ,gnus-mime-button-map
             ,gnus-mouse-face-prop ,gnus-article-mouse-face
             face ,gnus-article-button-face
             gnus-part ,id
@@ -5175,7 +5160,7 @@ If displaying \"text/html\" is discouraged \(see
                         ',gnus-article-mime-handle-alist))
                 (gnus-mime-display-alternative
                  ',ihandles ',handle ',begend ,id))
-              ,@(gnus-local-map-property gnus-mime-button-map)
+              keymap ,gnus-mime-button-map
               ,gnus-mouse-face-prop ,gnus-article-mouse-face
               face ,gnus-article-button-face
               gnus-part ,id
@@ -5190,8 +5175,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
@@ -5243,8 +5228,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))
@@ -5294,8 +5278,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)))
 
@@ -5310,8 +5294,7 @@ Provided for backwards compatibility."
       ;; save it to file.
       (goto-char (point-max))
       (insert "\n")
-      (let ((file-name-coding-system nnmail-pathname-coding-system)
-           (pathname-coding-system nnmail-pathname-coding-system))
+      (let ((file-name-coding-system nnmail-pathname-coding-system))
        (write-region-as-binary (point-min) (point-max) file-name 'append))
       t)))
 
@@ -5463,9 +5446,9 @@ not have a face in `gnus-article-boring-faces'."
   "Read article specified by message-id around point."
   (interactive)
   (save-excursion
-    (re-search-backward "[ \t]\\|^" (gnus-point-at-bol) t)
-    (re-search-forward "<?news:<?\\|<" (gnus-point-at-eol) t)
-    (if (re-search-forward "[^@ ]+@[^ \t>]+" (gnus-point-at-eol) t)
+    (re-search-backward "[ \t]\\|^" (point-at-bol) t)
+    (re-search-forward "<?news:<?\\|<" (point-at-eol) t)
+    (if (re-search-forward "[^@ ]+@[^ \t>]+" (point-at-eol) t)
        (let ((msg-id (concat "<" (match-string 0) ">")))
          (set-buffer gnus-summary-buffer)
          (gnus-summary-refer-article msg-id))
@@ -5756,16 +5739,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)
@@ -6029,7 +6010,7 @@ groups."
        (car gnus-article-current) (cdr gnus-article-current)))
     ;; We remove all text props from the article buffer.
     (kill-all-local-variables)
-    (gnus-set-text-properties (point-min) (point-max) nil)
+    (set-text-properties (point-min) (point-max) nil)
     (gnus-article-mode)
     (set-window-configuration winconf)
     (set-buffer buf)
@@ -6240,7 +6221,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"
@@ -6824,41 +6805,35 @@ 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.
@@ -6866,10 +6841,8 @@ It does this by highlighting everything after
 `gnus-signature-separator' using `gnus-signature-face'."
   (interactive)
   (when gnus-signature-face
-    (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 (gnus-article-narrow-to-signature)
            (gnus-overlay-put (gnus-make-overlay (point-min) (point-max))
@@ -6897,10 +6870,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)
@@ -6941,40 +6912,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:
 
@@ -6990,22 +6954,17 @@ specified by `gnus-button-alist'."
          (list 'gnus-callback fun)
          (and data (list 'gnus-data data))))
   (widget-convert-button 'link from to :action 'gnus-widget-press-button
-                        ;; Quote `:button-keymap' for Mule 2.3
-                        ;; but it won't work.
-                        ':button-keymap gnus-widget-button-keymap))
+                        :button-keymap gnus-widget-button-keymap))
 
 ;;; 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)
          (limit (next-single-property-change end 'mime-view-entity
                                              nil (point-max))))
       (if (text-property-any end limit 'article-type 'signature)
@@ -7043,7 +7002,7 @@ specified by `gnus-button-alist'."
           (fun (nth 3 entry))
           (args (mapcar (lambda (group)
                           (let ((string (match-string group)))
-                            (gnus-set-text-properties
+                            (set-text-properties
                              0 (length string) nil string)
                             string))
                         (nthcdr 4 entry))))
@@ -7143,8 +7102,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)
@@ -7224,20 +7182,23 @@ 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)
        (situation (get-text-property (point-min) 'mime-view-situation)))
     (gnus-eval-format
      gnus-prev-page-line-format nil
-     `(,@(gnus-local-map-property gnus-prev-page-map)
+     `(keymap ,gnus-prev-page-map
         gnus-prev t
         gnus-callback gnus-article-button-prev-page
         article-type annotation
@@ -7250,15 +7211,6 @@ specified by `gnus-button-alist'."
      :action 'gnus-button-prev-page
      :button-keymap gnus-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)
@@ -7280,7 +7232,7 @@ specified by `gnus-button-alist'."
        (buffer-read-only nil)
        (situation (get-text-property (point-min) 'mime-view-situation)))
     (gnus-eval-format gnus-next-page-line-format nil
-                     `(,@(gnus-local-map-property gnus-next-page-map)
+                     `(keymap ,gnus-next-page-map
                          gnus-next t
                          gnus-callback gnus-article-button-next-page
                          article-type annotation
@@ -7535,8 +7487,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))
@@ -7643,7 +7593,7 @@ For example:
     (gnus-eval-format
      gnus-mime-security-button-line-format
      gnus-mime-security-button-line-format-alist
-     `(,@(gnus-local-map-property gnus-mime-security-button-map)
+     `(keymap ,gnus-mime-security-button-map
         gnus-callback gnus-mime-security-press-button
         gnus-line-format ,gnus-mime-security-button-line-format
         gnus-mime-details ,gnus-mime-security-button-pressed