gnus-art.el (gnus-button-alist, gnus-button-handle-info-keystrokes): Don't confuse...
[gnus] / lisp / gnus-art.el
index 40f80f1..430590d 100644 (file)
@@ -1,7 +1,7 @@
 ;;; gnus-art.el --- article mode commands for Gnus
 
 ;; Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
-;;   2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
+;;   2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
 
 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
 ;; Keywords: news
@@ -25,7 +25,7 @@
 
 ;;; Code:
 
-;; For Emacs < 22.2.
+;; For Emacs <22.2 and XEmacs.
 (eval-and-compile
   (unless (fboundp 'declare-function) (defmacro declare-function (&rest r))))
 (eval-when-compile
@@ -916,25 +916,25 @@ image type in XEmacs if it is built with the libcompface library."
   "Function used to decode addresses.")
 
 (defvar gnus-article-dumbquotes-map
-  '(("\200" "EUR")
-    ("\202" ",")
-    ("\203" "f")
-    ("\204" ",,")
-    ("\205" "...")
-    ("\213" "<")
-    ("\214" "OE")
-    ("\221" "`")
-    ("\222" "'")
-    ("\223" "``")
-    ("\224" "\"")
-    ("\225" "*")
-    ("\226" "-")
-    ("\227" "--")
-    ("\230" "~")
-    ("\231" "(TM)")
-    ("\233" ">")
-    ("\234" "oe")
-    ("\264" "'"))
+  '((?\200 "EUR")
+    (?\202 ",")
+    (?\203 "f")
+    (?\204 ",,")
+    (?\205 "...")
+    (?\213 "<")
+    (?\214 "OE")
+    (?\221 "`")
+    (?\222 "'")
+    (?\223 "``")
+    (?\224 "\"")
+    (?\225 "*")
+    (?\226 "-")
+    (?\227 "--")
+    (?\230 "~")
+    (?\231 "(TM)")
+    (?\233 ">")
+    (?\234 "oe")
+    (?\264 "'"))
   "Table for MS-to-Latin1 translation.")
 
 (defcustom gnus-ignored-mime-types nil
@@ -1590,10 +1590,11 @@ predicate.  See Info node `(gnus)Customizing Articles'."
   :link '(custom-manual "(gnus)Customizing Articles")
   :type gnus-article-treat-custom)
 
-(defcustom gnus-treat-fill-long-lines nil
+(defcustom gnus-treat-fill-long-lines '(typep "text/plain")
   "Fill long lines.
 Valid values are nil, t, `head', `first', `last', an integer or a
 predicate.  See Info node `(gnus)Customizing Articles'."
+  :version "24.1"
   :group 'gnus-article-treat
   :link '(custom-manual "(gnus)Customizing Articles")
   :type gnus-article-treat-custom)
@@ -1621,9 +1622,6 @@ It is a string, such as \"PGP\". If nil, ask user."
   :type 'string
   :group 'mime-security)
 
-(defvar gnus-article-wash-function nil
-  "Function used for converting HTML into text.")
-
 (defcustom gnus-use-idna (and (condition-case nil (require 'idna) (file-error))
                              (mm-coding-system-p 'utf-8)
                              (executable-find idna-program))
@@ -1639,8 +1637,17 @@ This requires GNU Libidn, and by default only enabled if it is found."
   :group 'gnus-article
   :type 'boolean)
 
-(defcustom gnus-blocked-images "."
-  "Images that have URLs matching this regexp will be blocked."
+(defcustom gnus-inhibit-images nil
+  "Non-nil means inhibit displaying of images inline in the article body."
+  :version "24.1"
+  :group 'gnus-article
+  :type 'boolean)
+
+(defcustom gnus-blocked-images 'gnus-block-private-groups
+  "Images that have URLs matching this regexp will be blocked.
+This can also be a function to be evaluated.  If so, it will be
+called with the group name as the parameter, and should return a
+regexp."
   :version "24.1"
   :group 'gnus-art
   :type 'regexp)
@@ -1664,7 +1671,7 @@ This requires GNU Libidn, and by default only enabled if it is found."
     (gnus-treat-highlight-signature gnus-article-highlight-signature)
     (gnus-treat-buttonize gnus-article-add-buttons)
     (gnus-treat-fill-article gnus-article-fill-cited-article)
-    (gnus-treat-fill-long-lines gnus-article-fill-long-lines)
+    (gnus-treat-fill-long-lines gnus-article-fill-cited-long-lines)
     (gnus-treat-strip-cr gnus-article-remove-cr)
     (gnus-treat-unsplit-urls gnus-article-unsplit-urls)
     (gnus-treat-date-ut gnus-article-date-ut)
@@ -2114,6 +2121,35 @@ try this wash."
   (interactive)
   (article-translate-strings gnus-article-dumbquotes-map))
 
+(defvar org-entities)
+
+(defun article-treat-non-ascii ()
+  "Translate many Unicode characters into their ASCII equivalents."
+  (interactive)
+  (require 'org-entities)
+  (let ((table (make-char-table (if (featurep 'xemacs) 'generic))))
+    (dolist (elem org-entities)
+      (when (and (listp elem)
+                (= (length (nth 6 elem)) 1))
+       (if (featurep 'xemacs)
+           (put-char-table (aref (nth 6 elem) 0) (nth 4 elem) table)
+         (set-char-table-range table (aref (nth 6 elem) 0) (nth 4 elem)))))
+    (save-excursion
+      (when (article-goto-body)
+       (let ((inhibit-read-only t)
+             replace props)
+         (while (not (eobp))
+           (if (not (setq replace (if (featurep 'xemacs)
+                                      (get-char-table (following-char) table)
+                                    (aref table (following-char)))))
+               (forward-char 1)
+             (if (prog1
+                     (setq props (text-properties-at (point)))
+                   (delete-char 1))
+                 (add-text-properties (point) (progn (insert replace) (point))
+                                      props)
+               (insert replace)))))))))
+
 (defun article-translate-characters (from to)
   "Translate all characters in the body of the article according to FROM and TO.
 FROM is a string of characters to translate from; to is a string of
@@ -2138,9 +2174,18 @@ MAP is an alist where the elements are on the form (\"from\" \"to\")."
     (when (article-goto-body)
       (let ((inhibit-read-only t))
        (dolist (elem map)
-         (save-excursion
-           (while (search-forward (car elem) nil t)
-             (replace-match (cadr elem)))))))))
+         (let ((from (car elem))
+               (to (cadr elem)))
+           (save-excursion
+             (if (stringp from)
+                 (while (search-forward from nil t)
+                   (replace-match to))
+               (while (not (eobp))
+                 (if (eq (following-char) from)
+                     (progn
+                       (delete-char 1)
+                       (insert to))
+                   (forward-char 1)))))))))))
 
 (defun article-treat-overstrike ()
   "Translate overstrikes into bold text."
@@ -2230,8 +2275,23 @@ unfolded."
   "Remove all images from the article buffer."
   (interactive)
   (gnus-with-article-buffer
-    (dolist (elem gnus-article-image-alist)
-      (gnus-delete-images (car elem)))))
+    (save-restriction
+      (widen)
+      (dolist (elem gnus-article-image-alist)
+       (gnus-delete-images (car elem))))))
+
+(defun gnus-article-show-images ()
+  "Show any images that are in the HTML-rendered article buffer.
+This only works if the article in question is HTML."
+  (interactive)
+  (gnus-with-article-buffer
+    (save-restriction
+      (widen)
+      (dolist (region (gnus-find-text-property-region (point-min) (point-max)
+                                                     'image-displayer))
+       (destructuring-bind (start end function) region
+         (funcall function (get-text-property start 'image-url)
+                  start end))))))
 
 (defun gnus-article-treat-fold-newsgroups ()
   "Unfold folded message headers.
@@ -2685,118 +2745,16 @@ If READ-CHARSET, ask for a coding system."
     (when (interactive-p)
       (gnus-treat-article nil))))
 
-
-(defun article-wash-html (&optional read-charset)
-  "Format an HTML article.
-If READ-CHARSET, ask for a coding system.  If it is a number, the
-charset defined in `gnus-summary-show-article-charset-alist' is used."
-  (interactive "P")
-  (save-excursion
-    (let ((inhibit-read-only t)
-         charset)
-      (if read-charset
-         (if (or (and (numberp read-charset)
-                      (setq charset
-                            (cdr
-                             (assq read-charset
-                                   gnus-summary-show-article-charset-alist))))
-                 (setq charset (mm-read-coding-system "Charset: ")))
-             (let ((gnus-summary-show-article-charset-alist
-                    (list (cons 1 charset))))
-               (with-current-buffer gnus-summary-buffer
-                 (gnus-summary-show-article 1)))
-           (error "No charset is given"))
-       (when (gnus-buffer-live-p gnus-original-article-buffer)
-         (with-current-buffer gnus-original-article-buffer
-           (let* ((ct (gnus-fetch-field "content-type"))
-                  (ctl (and ct (mail-header-parse-content-type ct))))
-             (setq charset (and ctl
-                                (mail-content-type-get ctl 'charset)))
-             (when (stringp charset)
-               (setq charset (intern (downcase charset)))))))
-       (unless charset
-         (setq charset gnus-newsgroup-charset)))
-      (article-goto-body)
-      (save-window-excursion
-       (save-restriction
-         (narrow-to-region (point) (point-max))
-         (let* ((func (or gnus-article-wash-function mm-text-html-renderer))
-                (entry (assq func mm-text-html-washer-alist)))
-           (when entry
-             (setq func (cdr entry)))
-           (cond
-            ((functionp func)
-             (funcall func))
-            (t
-             (apply (car func) (cdr func))))))))))
-
-;; External.
-(declare-function w3-region "ext:w3-display" (st nd))
-
-(defun gnus-article-wash-html-with-w3 ()
-  "Wash the current buffer with w3."
-  (mm-setup-w3)
-  (let ((w3-strict-width (window-width))
-       (url-standalone-mode t)
-       (url-gateway-unplugged t)
-       (w3-honor-stylesheets nil))
-    (condition-case ()
-       (w3-region (point-min) (point-max))
-      (error))))
-
-;; External.
-(declare-function w3m-region "ext:w3m" (start end &optional url charset))
-
-(defun gnus-article-wash-html-with-w3m ()
-  "Wash the current buffer with emacs-w3m."
-  (mm-setup-w3m)
-  (let ((w3m-safe-url-regexp mm-w3m-safe-url-regexp)
-       w3m-force-redisplay)
-    (w3m-region (point-min) (point-max)))
-  ;; Put the mark meaning this part was rendered by emacs-w3m.
-  (put-text-property (point-min) (point-max) 'mm-inline-text-html-with-w3m t)
-  (when (and mm-inline-text-html-with-w3m-keymap
-            (boundp 'w3m-minor-mode-map)
-            w3m-minor-mode-map)
-    (if (and (boundp 'w3m-link-map)
-            w3m-link-map)
-       (let* ((start (point-min))
-              (end (point-max))
-              (on (get-text-property start 'w3m-href-anchor))
-              (map (copy-keymap w3m-link-map))
-              next)
-         (set-keymap-parent map w3m-minor-mode-map)
-         (while (< start end)
-           (if on
-               (progn
-                 (setq next (or (text-property-any start end
-                                                   'w3m-href-anchor nil)
-                                end))
-                 (put-text-property start next 'keymap map))
-             (setq next (or (text-property-not-all start end
-                                                   'w3m-href-anchor nil)
-                            end))
-             (put-text-property start next 'keymap w3m-minor-mode-map))
-           (setq start next
-                 on (not on))))
-      (put-text-property (point-min) (point-max) 'keymap w3m-minor-mode-map))))
-
-(defvar charset) ;; Bound by `article-wash-html'.
-
-(defun gnus-article-wash-html-with-w3m-standalone ()
-  "Wash the current buffer with w3m."
-  (if (mm-w3m-standalone-supports-m17n-p)
-      (progn
-       (unless (mm-coding-system-p charset) ;; Bound by `article-wash-html'.
-         ;; The default.
-         (setq charset 'iso-8859-1))
-       (let ((coding-system-for-write charset)
-             (coding-system-for-read charset))
-         (call-process-region
-          (point-min) (point-max)
-          "w3m" t t nil "-dump" "-T" "text/html"
-          "-I" (symbol-name charset) "-O" (symbol-name charset))))
-    (mm-inline-wash-with-stdin nil "w3m" "-dump" "-T" "text/html")))
+(defun article-wash-html ()
+  "Format an HTML article."
+  (interactive)
+  (let ((handles nil)
+       (buffer-read-only nil))
+    (when (gnus-buffer-live-p gnus-original-article-buffer)
+      (setq handles (mm-dissect-buffer t t)))
+    (article-goto-body)
+    (delete-region (point) (point-max))
+    (mm-inline-text-html handles)))
 
 (defvar gnus-article-browse-html-temp-list nil
   "List of temporary files created by `gnus-article-browse-html-parts'.
@@ -4341,6 +4299,7 @@ If variable `gnus-use-long-file-name' is non-nil, it is
      article-date-lapsed
      article-emphasize
      article-treat-dumbquotes
+     article-treat-non-ascii
      article-normalize-headers
      ;;(article-show-all . gnus-article-show-all-headers)
      )))
@@ -4393,7 +4352,6 @@ If variable `gnus-use-long-file-name' is non-nil, it is
 (defun gnus-article-make-menu-bar ()
   (unless (boundp 'gnus-article-commands-menu)
     (gnus-summary-make-menu-bar))
-  (gnus-turn-off-edit-menu 'article)
   (unless (boundp 'gnus-article-article-menu)
     (easy-menu-define
      gnus-article-article-menu gnus-article-mode-map ""
@@ -4459,7 +4417,6 @@ commands:
   (gnus-update-format-specifications nil 'article-mode)
   (set (make-local-variable 'page-delimiter) gnus-page-delimiter)
   (set (make-local-variable 'gnus-page-broken) nil)
-  (make-local-variable 'gnus-button-marker-list)
   (make-local-variable 'gnus-article-current-summary)
   (make-local-variable 'gnus-article-mime-handles)
   (make-local-variable 'gnus-article-decoded-p)
@@ -4474,7 +4431,6 @@ commands:
   ;; face.
   (set (make-local-variable 'nobreak-char-display) nil)
   (setq cursor-in-non-selected-windows nil)
-  (setq truncate-lines gnus-article-truncate-lines)
   (gnus-set-default-directory)
   (buffer-disable-undo)
   (setq buffer-read-only t
@@ -4483,10 +4439,6 @@ commands:
   (mm-enable-multibyte)
   (gnus-run-mode-hooks 'gnus-article-mode-hook))
 
-(defvar gnus-button-marker-list nil
-  "Regexp matching any of the regexps from `gnus-button-alist'.
-Internal variable.")
-
 (defun gnus-article-setup-buffer ()
   "Initialize the article buffer."
   (let* ((name (if gnus-single-article-buffer "*Article*"
@@ -4530,13 +4482,13 @@ Internal variable.")
          (setq gnus-article-mime-handle-alist nil)
          (buffer-disable-undo)
          (setq buffer-read-only t)
-         ;; This list just keeps growing if we don't reset it.
-         (setq gnus-button-marker-list nil)
          (unless (eq major-mode 'gnus-article-mode)
            (gnus-article-mode))
+         (setq truncate-lines gnus-article-truncate-lines)
          (current-buffer))
       (with-current-buffer (gnus-get-buffer-create name)
        (gnus-article-mode)
+       (setq truncate-lines gnus-article-truncate-lines)
        (make-local-variable 'gnus-summary-buffer)
        (setq gnus-summary-buffer
              (gnus-summary-buffer-name gnus-newsgroup-name))
@@ -4904,11 +4856,17 @@ General format specifiers can also be used.  See Info node
 (defun gnus-article-jump-to-part (n)
   "Jump to MIME part N."
   (interactive "P")
-  (pop-to-buffer gnus-article-buffer)
-  ;; FIXME: why is it necessary?
-  (sit-for 0)
-  (let ((parts (length gnus-article-mime-handle-alist)))
-    (or n (setq n (read-number (format "Jump to part (2..%s): " parts))))
+  (let ((parts (with-current-buffer gnus-article-buffer
+                (length gnus-article-mime-handle-alist))))
+    (when (zerop parts)
+      (error "No such part"))
+    (pop-to-buffer gnus-article-buffer)
+    ;; FIXME: why is it necessary?
+    (sit-for 0)
+    (or n
+       (setq n (if (= parts 1)
+                   1
+                 (read-number (format "Jump to part (1..%s): " parts)))))
     (unless (and (integerp n) (<= n parts) (>= n 1))
       (setq n
            (progn
@@ -5139,7 +5097,9 @@ available media-types."
          (let ((default (gnus-mime-view-part-as-type-internal)))
            (gnus-completing-read
             "View as MIME type"
-            (gnus-remove-if-not pred (mailcap-mime-types))
+            (if pred
+                (gnus-remove-if-not pred (mailcap-mime-types))
+              (mailcap-mime-types))
             nil nil nil
             (car default)))))
   (gnus-article-check-buffer)
@@ -5206,7 +5166,7 @@ are decompressed."
       (if (or coding-system
              (and charset
                   (setq coding-system (mm-charset-to-coding-system charset))
-                  (not (eq charset 'ascii))))
+                  (not (eq coding-system 'ascii))))
          (progn
            (mm-enable-multibyte)
            (insert (mm-decode-coding-string contents coding-system))
@@ -5293,15 +5253,7 @@ Compressed files like .gz and .bz2 are decompressed."
          (if (mm-handle-undisplayer handle)
              (mm-remove-part handle))))
        (forward-line 2)
-       (mm-insert-inline
-        handle
-        (if (or coding-system
-                (and charset
-                     (setq coding-system
-                           (mm-charset-to-coding-system charset))
-                     (not (eq coding-system 'ascii))))
-            (mm-decode-coding-string contents coding-system)
-          (mm-string-to-multibyte contents)))
+        (mm-display-inline handle)
        (goto-char b)))))
 
 (defun gnus-mime-set-charset-parameters (handle charset)
@@ -5379,11 +5331,9 @@ specified charset."
          (mm-enable-external t))
     (if (not (stringp method))
        (gnus-mime-view-part-as-type
-        nil (lambda (types) (stringp (mailcap-mime-info (car types)))))
+        nil (lambda (type) (stringp (mailcap-mime-info type))))
       (when handle
-       (if (mm-handle-undisplayer handle)
-           (mm-remove-part handle)
-         (mm-display-part handle))))))
+       (mm-display-part handle nil t)))))
 
 (defun gnus-mime-view-part-internally (&optional handle)
   "View the MIME part under point with an internal viewer.
@@ -5400,11 +5350,9 @@ If no internal viewer is available, use an external viewer."
         (inhibit-read-only t))
     (if (not (mm-inlinable-p handle))
         (gnus-mime-view-part-as-type
-         nil (lambda (types) (mm-inlinable-p handle (car types))))
+         nil (lambda (type) (mm-inlinable-p handle type)))
       (when handle
-       (if (mm-handle-undisplayer handle)
-           (mm-remove-part handle)
-         (gnus-bind-safe-url-regexp (mm-display-part handle)))))))
+       (gnus-bind-safe-url-regexp (mm-display-part handle))))))
 
 (defun gnus-mime-action-on-part (&optional action)
   "Do something with the MIME attachment at \(point\)."
@@ -5467,6 +5415,10 @@ If INTERACTIVE, call FUNCTION interactivly."
          (when (gnus-article-goto-part n)
            ;; We point the cursor and the arrow at the MIME button
            ;; when the `function' prompt the user for something.
+           (unless (and (pos-visible-in-window-p)
+                        (> (count-lines (point) (window-end))
+                           (/ (1- (window-height)) 3)))
+             (recenter (/ (1- (window-height)) 3)))
            (let ((cursor-in-non-selected-windows t)
                  (overlay-arrow-string "=>")
                  (overlay-arrow-position (point-marker)))
@@ -5478,11 +5430,10 @@ If INTERACTIVE, call FUNCTION interactivly."
                    (funcall function))
                   (interactive
                    (call-interactively
-                    function
-                    (cdr (assq n gnus-article-mime-handle-alist))))
+                    function (get-text-property (point) 'gnus-data)))
                   (t
                    (funcall function
-                            (cdr (assq n gnus-article-mime-handle-alist)))))
+                            (get-text-property (point) 'gnus-data))))
                (set-marker overlay-arrow-position nil)
                (unless gnus-auto-select-part
                  (gnus-select-frame-set-input-focus frame)
@@ -5647,7 +5598,41 @@ all parts."
 
 (defun gnus-article-goto-part (n)
   "Go to MIME part N."
-  (gnus-goto-char (text-property-any (point-min) (point-max) 'gnus-part n)))
+  (when gnus-break-pages
+    (widen))
+  (prog1
+      (let ((start (text-property-any (point-min) (point-max) 'gnus-part n))
+           part handle end next handles)
+       (when start
+         (goto-char start)
+         (if (setq handle (get-text-property start 'gnus-data))
+             start
+           ;; Go to the displayed subpart, assuming this is
+           ;; multipart/alternative.
+           (setq part start
+                 end (point-at-eol))
+           (while (and (not handle)
+                       part
+                       (< part end)
+                       (setq next (text-property-not-all part end
+                                                         'gnus-data nil)))
+             (setq part next
+                   handle (get-text-property part 'gnus-data))
+             (push (cons handle part) handles)
+             (unless (mm-handle-displayed-p handle)
+               (setq handle nil
+                     part (text-property-any part end 'gnus-data nil))))
+           (unless handle
+             ;; No subpart is displayed, so we find preferred one.
+             (setq part
+                   (cdr (assq (mm-preferred-alternative
+                               (nreverse (mapcar 'car handles)))
+                              handles))))
+           (if part
+               (goto-char (1+ part))
+             start))))
+    (when gnus-break-pages
+      (gnus-narrow-to-page))))
 
 (defun gnus-insert-mime-button (handle gnus-tmp-id &optional displayed)
   (let ((gnus-tmp-name
@@ -5756,7 +5741,7 @@ all parts."
          (save-restriction
            (article-goto-body)
            (narrow-to-region (point) (point-max))
-           (gnus-treat-article nil 1 1)
+           (gnus-treat-article nil 1 1 "text/plain")
            (widen)))
        (unless ihandles
          ;; Highlight the headers.
@@ -5856,7 +5841,12 @@ If displaying \"text/html\" is discouraged \(see
        (while ignored
          (when (string-match (pop ignored) type)
            (throw 'ignored nil)))
-       (if (and (setq not-attachment
+       (if (and (not (and (if (gnus-buffer-live-p gnus-summary-buffer)
+                              (with-current-buffer gnus-summary-buffer
+                                gnus-inhibit-images)
+                            gnus-inhibit-images)
+                          (string-match "\\`image/" type)))
+                (setq not-attachment
                       (and (not (mm-inline-override-p handle))
                            (or (not (mm-handle-disposition handle))
                                (equal (car (mm-handle-disposition handle))
@@ -5907,18 +5897,7 @@ If displaying \"text/html\" is discouraged \(see
              (forward-line -1)
              (setq beg (point)))
            (gnus-article-insert-newline)
-           (mm-insert-inline
-            handle
-            (let ((charset (or (mail-content-type-get (mm-handle-type handle)
-                                                      'charset)
-                               (and (equal type "text/calendar") 'utf-8))))
-              (cond ((not charset)
-                     (mm-string-as-multibyte (mm-get-part handle)))
-                    ((eq charset 'gnus-decoded)
-                     (with-current-buffer (mm-handle-buffer handle)
-                       (buffer-string)))
-                    (t
-                     (mm-decode-string (mm-get-part handle) charset)))))
+           (mm-display-inline handle)
            (goto-char (point-max))))
          ;; Do highlighting.
          (save-excursion
@@ -6044,7 +6023,7 @@ If displaying \"text/html\" is discouraged \(see
                  (gnus-treat-article
                   nil (length gnus-article-mime-handle-alist)
                   (gnus-article-mime-total-parts)
-                  (mm-handle-media-type handle))))))
+                  (mm-handle-media-type preferred))))))
          (goto-char (point-max))
          (setcdr begend (point-marker)))))
     (when ibegend
@@ -6885,6 +6864,18 @@ If given a prefix, show the hidden text instead."
                            (point))
          (set-buffer buf))))))
 
+(defun gnus-block-private-groups (group)
+  (if (gnus-news-group-p group)
+      ;; Block nothing in news groups.
+      nil
+    ;; Block everything anywhere else.
+    "."))
+
+(defun gnus-blocked-images ()
+  (if (functionp gnus-blocked-images)
+      (funcall gnus-blocked-images gnus-newsgroup-name)
+    gnus-blocked-images))
+
 ;;;
 ;;; Article editing
 ;;;
@@ -7477,17 +7468,17 @@ positives are possible."
      ;; Info links like `C-h i d m Gnus RET' or `C-h i d m Gnus RET i partial RET'
      0 (>= gnus-button-emacs-level 1) gnus-button-handle-info-keystrokes 0)
     ;; This is custom
-    ("M-x[ \t\n]\\(customize-[^ ]+\\)[ \t\n]RET[ \t\n]\\([^ ]+\\)[ \t\n]RET" 0
+    ("M-x[ \t\n]\\(customize-[^ ]+\\)[ \t\n]RET[ \t\n]\\([^ ]+\\)[ \t\n]RET\\>" 0
      (>= gnus-button-emacs-level 1) gnus-button-handle-custom 1 2)
     ;; Emacs help commands
-    ("M-x[ \t\n]+apropos[ \t\n]+RET[ \t\n]+\\([^ \t\n]+\\)[ \t\n]+RET"
+    ("M-x[ \t\n]+apropos[ \t\n]+RET[ \t\n]+\\([^ \t\n]+\\)[ \t\n]+RET\\>"
      ;; regexp doesn't match arguments containing ` '.
      0 (>= gnus-button-emacs-level 1) gnus-button-handle-apropos 1)
-    ("M-x[ \t\n]+apropos-command[ \t\n]+RET[ \t\n]+\\([^ \t\n]+\\)[ \t\n]+RET"
+    ("M-x[ \t\n]+apropos-command[ \t\n]+RET[ \t\n]+\\([^ \t\n]+\\)[ \t\n]+RET\\>"
      0 (>= gnus-button-emacs-level 1) gnus-button-handle-apropos-command 1)
-    ("M-x[ \t\n]+apropos-variable[ \t\n]+RET[ \t\n]+\\([^ \t\n]+\\)[ \t\n]+RET"
+    ("M-x[ \t\n]+apropos-variable[ \t\n]+RET[ \t\n]+\\([^ \t\n]+\\)[ \t\n]+RET\\>"
      0 (>= gnus-button-emacs-level 1) gnus-button-handle-apropos-variable 1)
-    ("M-x[ \t\n]+apropos-documentation[ \t\n]+RET[ \t\n]+\\([^ \t\n]+\\)[ \t\n]+RET"
+    ("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).
@@ -7502,11 +7493,11 @@ positives are possible."
      0 (>= gnus-button-emacs-level 9) gnus-button-handle-symbol 1)
     ("(setq[ \t\n]+\\([a-z][a-z0-9]+-[-a-z0-9]+\\)[ \t\n]+.+)"
      1 (>= gnus-button-emacs-level 7) gnus-button-handle-describe-variable 1)
-    ("\\bM-x[ \t\n]+\\([^ \t\n]+\\)[ \t\n]+RET"
+    ("\\bM-x[ \t\n]+\\([^ \t\n]+\\)[ \t\n]+RET\\>"
      1 (>= gnus-button-emacs-level 7) gnus-button-handle-describe-function 1)
-    ("\\b\\(C-h\\|<?[Ff]1>?\\)[ \t\n]+f[ \t\n]+\\([^ \t\n]+\\)[ \t\n]+RET"
+    ("\\b\\(C-h\\|<?[Ff]1>?\\)[ \t\n]+f[ \t\n]+\\([^ \t\n]+\\)[ \t\n]+RET\\>"
      0 (>= gnus-button-emacs-level 1) gnus-button-handle-describe-function 2)
-    ("\\b\\(C-h\\|<?[Ff]1>?\\)[ \t\n]+v[ \t\n]+\\([^ \t\n]+\\)[ \t\n]+RET"
+    ("\\b\\(C-h\\|<?[Ff]1>?\\)[ \t\n]+v[ \t\n]+\\([^ \t\n]+\\)[ \t\n]+RET\\>"
      0 (>= gnus-button-emacs-level 1) gnus-button-handle-describe-variable 2)
     ("`\\(\\(C-h\\|<?[Ff]1>?\\)[ \t\n]+k[ \t\n]+\\([^']+\\)\\)'"
      ;; Unlike the other regexps we really have to require quoting
@@ -7645,7 +7636,7 @@ do the highlighting.  See the documentation for those functions."
   (gnus-article-highlight-headers)
   (gnus-article-highlight-citation force)
   (gnus-article-highlight-signature)
-  (gnus-article-add-buttons force)
+  (gnus-article-add-buttons)
   (gnus-article-add-buttons-to-head))
 
 (defun gnus-article-highlight-some (&optional force)
@@ -7713,28 +7704,16 @@ It does this by highlighting everything after
   "Say whether PROP exists in the region."
   (text-property-not-all b e prop nil))
 
-(defun gnus-article-add-buttons (&optional force)
+(defun gnus-article-add-buttons ()
   "Find external references in the article and make buttons of them.
 \"External references\" are things like Message-IDs and URLs, as
 specified by `gnus-button-alist'."
-  (interactive (list 'force))
+  (interactive)
   (gnus-with-article-buffer
     (let ((inhibit-point-motion-hooks t)
          (case-fold-search t)
          (alist gnus-button-alist)
          beg entry regexp)
-      ;; Remove all old markers.
-      (let (marker entry new-list)
-       (while (setq marker (pop gnus-button-marker-list))
-         (if (or (< marker (point-min)) (>= marker (point-max)))
-             (push marker new-list)
-           (goto-char marker)
-           (when (setq entry (gnus-button-entry))
-             (put-text-property (match-beginning (nth 1 entry))
-                                (match-end (nth 1 entry))
-                                'gnus-callback nil))
-           (set-marker marker nil)))
-       (setq gnus-button-marker-list new-list))
       ;; We skip the headers.
       (article-goto-body)
       (setq beg (point))
@@ -7745,18 +7724,16 @@ specified by `gnus-button-alist'."
          (let ((start (match-beginning (nth 1 entry)))
                (end (match-end (nth 1 entry)))
                (from (match-beginning 0)))
-           (when (and (or (eq t (nth 2 entry))
-                          (eval (nth 2 entry)))
+           (when (and (eval (nth 2 entry))
                       (not (gnus-button-in-region-p
                             start end 'gnus-callback)))
              ;; That optional form returned non-nil, so we add the
              ;; button.
              (setq from (set-marker (make-marker) from))
-             (push from gnus-button-marker-list)
              (unless (and (eq (car entry) 'gnus-button-url-regexp)
                           (gnus-article-extend-url-button from start end))
                (gnus-article-add-button start end
-                                        'gnus-button-push from)
+                                        'gnus-button-push (list from entry))
                (gnus-put-text-property
                 start end
                 'gnus-string (buffer-substring-no-properties
@@ -7903,41 +7880,38 @@ url is put as the `gnus-button-url' overlay property on the button."
     (let ((gnus-article-mime-handle-alist-1 gnus-article-mime-handle-alist))
       (gnus-set-mode-line 'article))))
 
-(defun gnus-button-entry ()
-  ;; Return the first entry in `gnus-button-alist' matching this place.
-  (let ((alist gnus-button-alist)
-       (entry nil))
-    (while alist
-      (setq entry (pop alist))
-      (if (looking-at (eval (car entry)))
-         (setq alist nil)
-       (setq entry nil)))
-    entry))
-
-(defun gnus-button-push (marker)
+(defun gnus-button-push (marker-and-entry)
   ;; Push button starting at MARKER.
   (save-excursion
-    (goto-char marker)
-    (let* ((entry (gnus-button-entry))
-          (inhibit-point-motion-hooks t)
-          (fun (nth 3 entry))
-          (args (or (and (eq (car entry) 'gnus-button-url-regexp)
-                         (get-char-property marker 'gnus-button-url))
-                    (mapcar (lambda (group)
-                              (let ((string (match-string group)))
-                                (set-text-properties
-                                 0 (length string) nil string)
-                                string))
-                            (nthcdr 4 entry)))))
-      (cond
-       ((fboundp fun)
-       (apply fun args))
-       ((and (boundp fun)
-            (fboundp (symbol-value fun)))
-       (apply (symbol-value fun) args))
-       (t
-       (gnus-message 1 "You must define `%S' to use this button"
-                     (cons fun args)))))))
+    (let* ((marker (car marker-and-entry))
+           (entry (cadr marker-and-entry))
+           (regexp (car entry))
+           (inhibit-point-motion-hooks t))
+      (goto-char marker)
+      ;; This is obviously true, or something bad is happening :)
+      ;; But we need it to have the match-data
+      (when (looking-at (or (if (symbolp regexp)
+                                (symbol-value regexp)
+                              regexp)))
+        (let ((fun (nth 3 entry))
+              (args (or (and (eq (car entry) 'gnus-button-url-regexp)
+                             (get-char-property marker 'gnus-button-url))
+                        (mapcar (lambda (group)
+                                  (let ((string (match-string group)))
+                                    (set-text-properties
+                                     0 (length string) nil string)
+                                    string))
+                                (nthcdr 4 entry)))))
+
+          (cond
+           ((fboundp fun)
+            (apply fun args))
+           ((and (boundp fun)
+                 (fboundp (symbol-value fun)))
+            (apply (symbol-value fun) args))
+           (t
+            (gnus-message 1 "You must define `%S' to use this button"
+                          (cons fun args)))))))))
 
 (defun gnus-parse-news-url (url)
   (let (scheme server port group message-id articles)
@@ -8048,7 +8022,7 @@ url is put as the `gnus-button-url' overlay property on the button."
     (if (string-match
         (concat "\\b\\(C-h\\|<?[Ff]1>?\\)[ \t\n]+i[ \t\n]+d?[ \t\n]?m[ \t\n]+"
                 "\\([^ ]+ ?[^ ]+\\)[ \t\n]+RET"
-                "\\([ \t\n]+i[ \t\n]+[^ ]+ ?[^ ]+[ \t\n]+RET"
+                "\\([ \t\n]+i[ \t\n]+[^ ]+ ?[^ ]+[ \t\n]+RET\\>"
                 "\\(?:[ \t\n,]*\\)\\)?")
         url)
        (setq node (match-string 2 url)
@@ -8058,7 +8032,7 @@ url is put as the `gnus-button-url' overlay property on the button."
     (Info-directory)
     (Info-menu node)
     (when (> (length indx) 0)
-      (string-match (concat "[ \t\n]+i[ \t\n]+\\([^ ]+ ?[^ ]+\\)[ \t\n]+RET"
+      (string-match (concat "[ \t\n]+i[ \t\n]+\\([^ ]+ ?[^ ]+\\)[ \t\n]+RET\\>"
                            "\\([ \t\n,]*\\)")
                    indx)
       (setq comma (match-string 2 indx))
@@ -8074,6 +8048,7 @@ url is put as the `gnus-button-url' overlay property on the button."
          (Info-index-next 1)))
       nil)))
 
+(autoload 'pgg-snarf-keys-region "pgg")
 ;; Called after pgg-snarf-keys-region, which autoloads pgg.el.
 (declare-function pgg-display-output-buffer "pgg" (start end status))
 
@@ -8134,6 +8109,7 @@ url is put as the `gnus-button-url' overlay property on the button."
 
 (defun gnus-url-mailto (url)
   ;; Send mail to someone
+  (setq url (replace-regexp-in-string "\n" " " url))
   (when (string-match "mailto:/*\\(.*\\)" url)
     (setq url (substring url (match-beginning 1) nil)))
   (let (to args subject func)
@@ -8143,8 +8119,7 @@ url is put as the `gnus-button-url' overlay property on the button."
                  (if (string-match "^\\([^?]+\\)\\?\\(.*\\)" url)
                      (concat "to=" (match-string 1 url) "&"
                              (match-string 2 url))
-                   (concat "to=" url)))
-               t)
+                   (concat "to=" url))))
          subject (cdr-safe (assoc "subject" args)))
     (gnus-msg-mail)
     (while args
@@ -8295,16 +8270,19 @@ For example:
 ;;; Treatment top-level handling.
 ;;;
 
-(defun gnus-treat-article (condition &optional part-number total-parts type)
-  (let ((length (- (point-max) (point-min)))
+(defvar gnus-inhibit-article-treatments nil)
+
+(defun gnus-treat-article (gnus-treat-condition
+                          &optional part-number total-parts gnus-treat-type)
+  (let ((gnus-treat-length (- (point-max) (point-min)))
        (alist gnus-treatment-function-alist)
        (article-goto-body-goes-to-point-min-p t)
        (treated-type
-        (or (not type)
+        (or (not gnus-treat-type)
             (catch 'found
               (let ((list gnus-article-treat-types))
                 (while list
-                  (when (string-match (pop list) type)
+                  (when (string-match (pop list) gnus-treat-type)
                     (throw 'found t)))))))
        (highlightp (gnus-visual-p 'article-highlight 'highlight))
        val elem)
@@ -8317,6 +8295,8 @@ For example:
              (symbol-value (car elem))))
       (when (and (or (consp val)
                     treated-type)
+                (or (not gnus-inhibit-article-treatments)
+                    (eq gnus-treat-condition 'head))
                 (gnus-treat-predicate val)
                 (or (not (get (car elem) 'highlight))
                     highlightp))
@@ -8326,16 +8306,16 @@ For example:
 ;; Dynamic variables.
 (defvar part-number)
 (defvar total-parts)
-(defvar type)
-(defvar condition)
-(defvar length)
+(defvar gnus-treat-type)
+(defvar gnus-treat-condition)
+(defvar gnus-treat-length)
 
 (defun gnus-treat-predicate (val)
   (cond
    ((null val)
     nil)
-   (condition
-    (eq condition val))
+   (gnus-treat-condition
+    (eq gnus-treat-condition val))
    ((and (listp val)
         (stringp (car val)))
     (apply 'gnus-or (mapcar `(lambda (s)
@@ -8351,7 +8331,7 @@ For example:
        ((eq pred 'not)
        (not (gnus-treat-predicate (car val))))
        ((eq pred 'typep)
-       (equal (car val) type))
+       (equal (car val) gnus-treat-type))
        (t
        (error "%S is not a valid predicate" pred)))))
    ((eq val t)
@@ -8363,7 +8343,7 @@ For example:
    ((eq val 'last)
     (eq part-number total-parts))
    ((numberp val)
-    (< length val))
+    (< gnus-treat-length val))
    (t
     (error "%S is not a valid value" val))))