2002-01-04 ShengHuo ZHU <zsh@cs.rochester.edu>
[gnus] / lisp / gnus-art.el
index 3235a05..08f1bf3 100644 (file)
@@ -1,5 +1,5 @@
 ;;; gnus-art.el --- article mode commands for Gnus
-;; Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001
+;; Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002
 ;;        Free Software Foundation, Inc.
 
 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
     "^X-UIDL:" "^MIME-Version:" "^Return-Path:" "^In-Reply-To:"
     "^Content-Type:" "^Content-Transfer-Encoding:" "^X-WebTV-Signature:"
     "^X-MimeOLE:" "^X-MSMail-Priority:" "^X-Priority:" "^X-Loop:"
-    "^X-Authentication-Warning:" "^X-MIME-Autoconverted:" "^X-Face:"
+    "^X-Authentication-Warning:" "^X-MIME-Autoconverted:" "^X-Face"
     "^X-Attribution:" "^X-Originating-IP:" "^Delivered-To:"
     "^NNTP-[-A-Za-z]+:" "^Distribution:" "^X-no-archive:" "^X-Trace:"
     "^X-Complaints-To:" "^X-NNTP-Posting-Host:" "^X-Orig.*:"
      "^X-Content-length:" "^X-Posting-Agent:" "^Original-Received:"
      "^X-Request-PGP:" "^X-Fingerprint:" "^X-WRIEnvto:" "^X-WRIEnvfrom:"
      "^X-Virus-Scanned:" "^X-Delivery-Agent:" "^Posted-Date:" "^X-Gateway:"
-     "^X-Local-Origin:" "^X-Local-Destination:")
+     "^X-Local-Origin:" "^X-Local-Destination:" "^X-UserInfo1:"
+     "^X-Received-Date:")
   "*All headers that start with this regexp will be hidden.
 This variable can also be a list of regexps of headers to be ignored.
 If `gnus-visible-headers' is non-nil, this variable will be ignored."
@@ -227,23 +228,18 @@ regexp.  If it matches, the text in question is not a signature."
 (defcustom gnus-article-x-face-command
   (if (featurep 'xemacs)
       (if (or (gnus-image-type-available-p 'xface)
-             (gnus-image-type-available-p 'xpm))
-         'gnus-xmas-article-display-xface
+             (gnus-image-type-available-p 'pbm))
+         'gnus-display-x-face-in-from
        "{ echo '/* Width=48, Height=48 */'; uncompface; } | icontopbm | ee -")
-    (if (gnus-image-type-available-p 'xbm)
-       'gnus-article-display-xface
-      (if gnus-article-compface-xbm
-         "{ echo '/* Width=48, Height=48 */'; uncompface; } | display -"
-       "{ echo '/* Width=48, Height=48 */'; uncompface; } | icontopbm | \
-display -")))
+    (if (gnus-image-type-available-p 'pbm)
+       'gnus-display-x-face-in-from
+      "{ echo '/* Width=48, Height=48 */'; uncompface; } | icontopbm | \
+display -"))
   "*String or function to be executed to display an X-Face header.
 If it is a string, the command will be executed in a sub-shell
 asynchronously.         The compressed face will be piped to this command."
   :type `(choice string
-                (function-item
-                 ,(if (featurep 'xemacs)
-                      'gnus-xmas-article-display-xface
-                    'gnus-article-display-xface))
+                (function-item gnus-display-x-face-in-from)
                 function)
   :version "21.1"
   :group 'gnus-article-washing)
@@ -362,19 +358,6 @@ Esample: (_/*word*/_)."
   "Face used for displaying highlighted words."
   :group 'gnus-article-emphasis)
 
-(defface gnus-body-separator-face
-  '((((class color)
-      (background dark))
-     (:background "white")
-     (:foreground "black"))
-    (((class color)
-      (background light))
-     (:background "black")
-     (:foreground "white"))
-    (t
-     ()))
-  "Face for the body separator.")
-
 (defcustom gnus-article-time-format "%a, %b %d %Y %T %Z"
   "Format for display of Date headers in article bodies.
 See `format-time-string' for the possible values.
@@ -713,6 +696,14 @@ To see e.g. security buttons you could set this to
   :group 'gnus-article-mime
   :type '(repeat regexp))
 
+(defcustom gnus-body-boundary-delimiter "_"
+  "String used to delimit header and body.
+This variable is used by `gnus-article-treat-body-boundary' which can
+be controlled by `gnus-treat-body-boundary'."
+  :group 'gnus-article-various
+  :type '(choice (item :tag "None" :value nil)
+                string))
+
 (defcustom gnus-article-mime-part-function nil
   "Function called with a MIME handle as the argument.
 This is meant for people who want to do something automatic based
@@ -820,7 +811,7 @@ See Info node `(gnus)Customizing Articles'."
 (defcustom gnus-treat-buttonize 100000
   "Add buttons.
 Valid values are nil, t, `head', `last', an integer or a predicate.
-See the manual for details."
+See Info node `(gnus)Customizing Articles'."
   :group 'gnus-article-treat
   :type gnus-article-treat-custom)
 (put 'gnus-treat-buttonize 'highlight t)
@@ -828,7 +819,7 @@ See the manual for details."
 (defcustom gnus-treat-buttonize-head 'head
   "Add buttons to the head.
 Valid values are nil, t, `head', `last', an integer or a predicate.
-See the manual for details."
+See Info node `(gnus)Customizing Articles' for details."
   :group 'gnus-article-treat
   :type gnus-article-treat-head-custom)
 (put 'gnus-treat-buttonize-head 'highlight t)
@@ -840,7 +831,7 @@ See the manual for details."
        50000)
   "Emphasize text.
 Valid values are nil, t, `head', `last', an integer or a predicate.
-See the manual for details."
+See Info node `(gnus)Customizing Articles' for details."
   :group 'gnus-article-treat
   :type gnus-article-treat-custom)
 (put 'gnus-treat-emphasize 'highlight t)
@@ -848,63 +839,63 @@ See the manual for details."
 (defcustom gnus-treat-strip-cr nil
   "Remove carriage returns.
 Valid values are nil, t, `head', `last', an integer or a predicate.
-See the manual for details."
+See Info node `(gnus)Customizing Articles' for details."
   :group 'gnus-article-treat
   :type gnus-article-treat-custom)
 
 (defcustom gnus-treat-leading-whitespace nil
   "Remove leading whitespace in headers.
 Valid values are nil, t, `head', `last', an integer or a predicate.
-See the manual for details."
+See Info node `(gnus)Customizing Articles' for details."
   :group 'gnus-article-treat
   :type gnus-article-treat-custom)
 
 (defcustom gnus-treat-hide-headers 'head
   "Hide headers.
 Valid values are nil, t, `head', `last', an integer or a predicate.
-See the manual for details."
+See Info node `(gnus)Customizing Articles' for details."
   :group 'gnus-article-treat
   :type gnus-article-treat-head-custom)
 
 (defcustom gnus-treat-hide-boring-headers nil
   "Hide boring headers.
 Valid values are nil, t, `head', `last', an integer or a predicate.
-See the manual for details."
+See Info node `(gnus)Customizing Articles' for details."
   :group 'gnus-article-treat
   :type gnus-article-treat-head-custom)
 
 (defcustom gnus-treat-hide-signature nil
   "Hide the signature.
 Valid values are nil, t, `head', `last', an integer or a predicate.
-See the manual for details."
+See Info node `(gnus)Customizing Articles' for details."
   :group 'gnus-article-treat
   :type gnus-article-treat-custom)
 
 (defcustom gnus-treat-fill-article nil
   "Fill the article.
 Valid values are nil, t, `head', `last', an integer or a predicate.
-See the manual for details."
+See Info node `(gnus)Customizing Articles' for details."
   :group 'gnus-article-treat
   :type gnus-article-treat-custom)
 
 (defcustom gnus-treat-hide-citation nil
   "Hide cited text.
 Valid values are nil, t, `head', `last', an integer or a predicate.
-See the manual for details."
+See Info node `(gnus)Customizing Articles' for details."
   :group 'gnus-article-treat
   :type gnus-article-treat-custom)
 
 (defcustom gnus-treat-hide-citation-maybe nil
   "Hide cited text.
 Valid values are nil, t, `head', `last', an integer or a predicate.
-See the manual for details."
+See Info node `(gnus)Customizing Articles' for details."
   :group 'gnus-article-treat
   :type gnus-article-treat-custom)
 
 (defcustom gnus-treat-strip-list-identifiers 'head
   "Strip list identifiers from `gnus-list-identifiers`.
 Valid values are nil, t, `head', `last', an integer or a predicate.
-See the manual for details."
+See Info node `(gnus)Customizing Articles' for details."
   :version "21.1"
   :group 'gnus-article-treat
   :type gnus-article-treat-custom)
@@ -912,14 +903,14 @@ See the manual for details."
 (defcustom gnus-treat-strip-pgp t
   "Strip PGP signatures.
 Valid values are nil, t, `head', `last', an integer or a predicate.
-See the manual for details."
+See Info node `(gnus)Customizing Articles' for details."
   :group 'gnus-article-treat
   :type gnus-article-treat-custom)
 
 (defcustom gnus-treat-strip-pem nil
   "Strip PEM signatures.
 Valid values are nil, t, `head', `last', an integer or a predicate.
-See the manual for details."
+See Info node `(gnus)Customizing Articles' for details."
   :group 'gnus-article-treat
   :type gnus-article-treat-custom)
 
@@ -927,14 +918,14 @@ See the manual for details."
   "Strip banners from articles.
 The banner to be stripped is specified in the `banner' group parameter.
 Valid values are nil, t, `head', `last', an integer or a predicate.
-See the manual for details."
+See Info node `(gnus)Customizing Articles' for details."
   :group 'gnus-article-treat
   :type gnus-article-treat-custom)
 
 (defcustom gnus-treat-highlight-headers 'head
   "Highlight the headers.
 Valid values are nil, t, `head', `last', an integer or a predicate.
-See the manual for details."
+See Info node `(gnus)Customizing Articles' for details."
   :group 'gnus-article-treat
   :type gnus-article-treat-head-custom)
 (put 'gnus-treat-highlight-headers 'highlight t)
@@ -942,7 +933,7 @@ See the manual for details."
 (defcustom gnus-treat-highlight-citation t
   "Highlight cited text.
 Valid values are nil, t, `head', `last', an integer or a predicate.
-See the manual for details."
+See Info node `(gnus)Customizing Articles' for details."
   :group 'gnus-article-treat
   :type gnus-article-treat-custom)
 (put 'gnus-treat-highlight-citation 'highlight t)
@@ -950,42 +941,42 @@ See the manual for details."
 (defcustom gnus-treat-date-ut nil
   "Display the Date in UT (GMT).
 Valid values are nil, t, `head', `last', an integer or a predicate.
-See the manual for details."
+See Info node `(gnus)Customizing Articles' for details."
   :group 'gnus-article-treat
   :type gnus-article-treat-head-custom)
 
 (defcustom gnus-treat-date-local nil
   "Display the Date in the local timezone.
 Valid values are nil, t, `head', `last', an integer or a predicate.
-See the manual for details."
+See Info node `(gnus)Customizing Articles' for details."
   :group 'gnus-article-treat
   :type gnus-article-treat-head-custom)
 
 (defcustom gnus-treat-date-english nil
   "Display the Date in a format that can be read aloud in English.
 Valid values are nil, t, `head', `last', an integer or a predicate.
-See the manual for details."
+See Info node `(gnus)Customizing Articles' for details."
   :group 'gnus-article-treat
   :type gnus-article-treat-head-custom)
 
 (defcustom gnus-treat-date-lapsed nil
   "Display the Date header in a way that says how much time has elapsed.
 Valid values are nil, t, `head', `last', an integer or a predicate.
-See the manual for details."
+See Info node `(gnus)Customizing Articles' for details."
   :group 'gnus-article-treat
   :type gnus-article-treat-head-custom)
 
 (defcustom gnus-treat-date-original nil
   "Display the date in the original timezone.
 Valid values are nil, t, `head', `last', an integer or a predicate.
-See the manual for details."
+See Info node `(gnus)Customizing Articles' for details."
   :group 'gnus-article-treat
   :type gnus-article-treat-head-custom)
 
 (defcustom gnus-treat-date-iso8601 nil
   "Display the date in the ISO8601 format.
 Valid values are nil, t, `head', `last', an integer or a predicate.
-See the manual for details."
+See Info node `(gnus)Customizing Articles' for details."
   :version "21.1"
   :group 'gnus-article-treat
   :type gnus-article-treat-head-custom)
@@ -994,14 +985,14 @@ See the manual for details."
   "Display the date in a user-defined format.
 The format is defined by the `gnus-article-time-format' variable.
 Valid values are nil, t, `head', `last', an integer or a predicate.
-See the manual for details."
+See Info node `(gnus)Customizing Articles' for details."
   :group 'gnus-article-treat
   :type gnus-article-treat-head-custom)
 
 (defcustom gnus-treat-strip-headers-in-body t
   "Strip the X-No-Archive header line from the beginning of the body.
 Valid values are nil, t, `head', `last', an integer or a predicate.
-See the manual for details."
+See Info node `(gnus)Customizing Articles' for details."
   :version "21.1"
   :group 'gnus-article-treat
   :type gnus-article-treat-custom)
@@ -1009,35 +1000,49 @@ See the manual for details."
 (defcustom gnus-treat-strip-trailing-blank-lines nil
   "Strip trailing blank lines.
 Valid values are nil, t, `head', `last', an integer or a predicate.
-See the manual for details."
+See Info node `(gnus)Customizing Articles' for details."
   :group 'gnus-article-treat
   :type gnus-article-treat-custom)
 
 (defcustom gnus-treat-strip-leading-blank-lines nil
   "Strip leading blank lines.
 Valid values are nil, t, `head', `last', an integer or a predicate.
-See the manual for details."
+See Info node `(gnus)Customizing Articles' for details."
   :group 'gnus-article-treat
   :type gnus-article-treat-custom)
 
 (defcustom gnus-treat-strip-multiple-blank-lines nil
   "Strip multiple blank lines.
 Valid values are nil, t, `head', `last', an integer or a predicate.
-See the manual for details."
+See Info node `(gnus)Customizing Articles' for details."
   :group 'gnus-article-treat
   :type gnus-article-treat-custom)
 
 (defcustom gnus-treat-unfold-headers 'head
   "Unfold folded header lines.
 Valid values are nil, t, `head', `last', an integer or a predicate.
-See the manual for details."
+See Info node `(gnus)Customizing Articles' for details."
+  :group 'gnus-article-treat
+  :type gnus-article-treat-custom)
+
+(defcustom gnus-treat-fold-headers nil
+  "Fold headers.
+Valid values are nil, t, `head', `last', an integer or a predicate.
+See Info node `(gnus)Customizing Articles' for details."
+  :group 'gnus-article-treat
+  :type gnus-article-treat-custom)
+
+(defcustom gnus-treat-fold-newsgroups 'head
+  "Fold the Newsgroups and Followup-To headers.
+Valid values are nil, t, `head', `last', an integer or a predicate.
+See Info node `(gnus)Customizing Articles' for details."
   :group 'gnus-article-treat
   :type gnus-article-treat-custom)
 
 (defcustom gnus-treat-overstrike t
   "Treat overstrike highlighting.
 Valid values are nil, t, `head', `last', an integer or a predicate.
-See the manual for details."
+See Info node `(gnus)Customizing Articles' for details."
   :group 'gnus-article-treat
   :type gnus-article-treat-custom)
 (put 'gnus-treat-overstrike 'highlight t)
@@ -1046,11 +1051,13 @@ See the manual for details."
   (and (or (and (fboundp 'image-type-available-p)
                (image-type-available-p 'xbm)
                (string-match "^0x" (shell-command-to-string "uncompface")))
-          (and (featurep 'xemacs) (featurep 'xface)))
+          (and (featurep 'xemacs)
+               (featurep 'xface)))
        'head)
   "Display X-Face headers.
 Valid values are nil, t, `head', `last', an integer or a predicate.
-See the manual for details."
+See Info node `(gnus)Customizing Articles' and Info node
+`(gnus)X-Face' for details."
   :group 'gnus-article-treat
   :version "21.1"
   :type gnus-article-treat-head-custom)
@@ -1064,7 +1071,8 @@ See the manual for details."
       t nil)
   "Display smileys.
 Valid values are nil, t, `head', `last', an integer or a predicate.
-See the manual for details."
+See Info node `(gnus)Customizing Articles' and Info node
+`(gnus)Smileys' for details."
   :group 'gnus-article-treat
   :version "21.1"
   :type gnus-article-treat-custom)
@@ -1075,7 +1083,8 @@ See the manual for details."
       'head nil)
   "Display picons in the From header.
 Valid values are nil, t, `head', `last', an integer or a predicate.
-See the manual for details."
+See Info node `(gnus)Customizing Articles' and Info node
+`(gnus)Picons' for details."
   :group 'gnus-article-treat
   :type gnus-article-treat-head-custom)
 (put 'gnus-treat-from-picon 'highlight t)
@@ -1085,7 +1094,8 @@ See the manual for details."
       'head nil)
   "Display picons in To and Cc headers.
 Valid values are nil, t, `head', `last', an integer or a predicate.
-See the manual for details."
+See Info node `(gnus)Customizing Articles' and Info node
+`(gnus)Picons' for details."
   :group 'gnus-article-treat
   :type gnus-article-treat-head-custom)
 (put 'gnus-treat-mail-picon 'highlight t)
@@ -1095,7 +1105,8 @@ See the manual for details."
       'head nil)
   "Display picons in the Newsgroups and Followup-To headers.
 Valid values are nil, t, `head', `last', an integer or a predicate.
-See the manual for details."
+See Info node `(gnus)Customizing Articles' and Info node
+`(gnus)Picons' for details."
   :group 'gnus-article-treat
   :type gnus-article-treat-head-custom)
 (put 'gnus-treat-newsgroups-picon 'highlight t)
@@ -1107,7 +1118,7 @@ See the manual for details."
       'head nil)
   "Draw a boundary at the end of the headers.
 Valid values are nil, t, `head', `last', an integer or a predicate.
-See the manual for details."
+See Info node `(gnus)Customizing Articles' for details."
   :version "21.1"
   :group 'gnus-article-treat
   :type gnus-article-treat-custom)
@@ -1115,7 +1126,7 @@ See the manual for details."
 (defcustom gnus-treat-capitalize-sentences nil
   "Capitalize sentence-starting words.
 Valid values are nil, t, `head', `last', an integer or a predicate.
-See the manual for details."
+See Info node `(gnus)Customizing Articles' for details."
   :version "21.1"
   :group 'gnus-article-treat
   :type gnus-article-treat-custom)
@@ -1123,14 +1134,14 @@ See the manual for details."
 (defcustom gnus-treat-fill-long-lines nil
   "Fill long lines.
 Valid values are nil, t, `head', `last', an integer or a predicate.
-See the manual for details."
+See Info node `(gnus)Customizing Articles' for details."
   :group 'gnus-article-treat
   :type gnus-article-treat-custom)
 
 (defcustom gnus-treat-play-sounds nil
   "Play sounds.
 Valid values are nil, t, `head', `last', an integer or a predicate.
-See the manual for details."
+See Info node `(gnus)Customizing Articles' for details."
   :version "21.1"
   :group 'gnus-article-treat
   :type gnus-article-treat-custom)
@@ -1138,7 +1149,7 @@ See the manual for details."
 (defcustom gnus-treat-translate nil
   "Translate articles from one language to another.
 Valid values are nil, t, `head', `last', an integer or a predicate.
-See the manual for details."
+See Info node `(gnus)Customizing Articles' for details."
   :version "21.1"
   :group 'gnus-article-treat
   :type gnus-article-treat-custom)
@@ -1147,7 +1158,7 @@ See the manual for details."
   "Verify X-PGP-Sig.
 To automatically treat X-PGP-Sig, set it to head.
 Valid values are nil, t, `head', `last', an integer or a predicate.
-See the manual for details."
+See Info node `(gnus)Customizing Articles' for details."
   :group 'gnus-article-treat
   :group 'mime-security
   :type gnus-article-treat-custom)
@@ -1172,6 +1183,7 @@ It is a string, such as \"PGP\". If nil, ask user."
 (defvar article-goto-body-goes-to-point-min-p nil)
 (defvar gnus-article-wash-types nil)
 (defvar gnus-article-emphasis-alist nil)
+(defvar gnus-article-image-alist nil)
 
 (defvar gnus-article-mime-handle-alist-1 nil)
 (defvar gnus-treatment-function-alist
@@ -1183,7 +1195,6 @@ It is a string, such as \"PGP\". If nil, ask user."
     (gnus-treat-fill-article gnus-article-fill-cited-article)
     (gnus-treat-fill-long-lines gnus-article-fill-long-lines)
     (gnus-treat-strip-cr gnus-article-remove-cr)
-    (gnus-treat-display-xface gnus-article-display-x-face)
     (gnus-treat-date-ut gnus-article-date-ut)
     (gnus-treat-date-local gnus-article-date-local)
     (gnus-treat-date-english gnus-article-date-english)
@@ -1200,6 +1211,9 @@ It is a string, such as \"PGP\". If nil, ask user."
     (gnus-treat-leading-whitespace gnus-article-remove-leading-whitespace)
     (gnus-treat-strip-pgp gnus-article-hide-pgp)
     (gnus-treat-strip-pem gnus-article-hide-pem)
+    (gnus-treat-from-picon gnus-treat-from-picon)
+    (gnus-treat-mail-picon gnus-treat-mail-picon)
+    (gnus-treat-newsgroups-picon gnus-treat-newsgroups-picon)
     (gnus-treat-highlight-headers gnus-article-highlight-headers)
     (gnus-treat-highlight-citation gnus-article-highlight-citation)
     (gnus-treat-highlight-signature gnus-article-highlight-signature)
@@ -1211,13 +1225,13 @@ It is a string, such as \"PGP\". If nil, ask user."
      gnus-article-strip-multiple-blank-lines)
     (gnus-treat-overstrike gnus-article-treat-overstrike)
     (gnus-treat-unfold-headers gnus-article-treat-unfold-headers)
+    (gnus-treat-fold-headers gnus-article-treat-fold-headers)
+    (gnus-treat-fold-newsgroups gnus-article-treat-fold-newsgroups)
     (gnus-treat-buttonize-head gnus-article-add-buttons-to-head)
-    (gnus-treat-display-smileys gnus-smiley-display)
+    (gnus-treat-display-smileys gnus-treat-smiley)
     (gnus-treat-capitalize-sentences gnus-article-capitalize-sentences)
     (gnus-treat-emphasize gnus-article-emphasize)
-    (gnus-treat-from-picon gnus-treat-from-picon)
-    (gnus-treat-mail-picon gnus-treat-mail-picon)
-    (gnus-treat-newsgroups-picon gnus-treat-newsgroups-picon)
+    (gnus-treat-display-xface gnus-article-display-x-face)
     (gnus-treat-body-boundary gnus-article-treat-body-boundary)
     (gnus-treat-play-sounds gnus-earcon-display)))
 
@@ -1246,6 +1260,34 @@ Initialized from `text-mode-syntax-table.")
 
 (defvar gnus-inhibit-hiding 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)
+            (inhibit-point-motion-hooks t)
+            (case-fold-search t))
+        (article-narrow-to-head)
+        ,@forms))))
+
+(put 'gnus-with-article-headers 'lisp-indent-function 0)
+(put 'gnus-with-article-headers 'edebug-form-spec '(body))
+
+(defmacro gnus-with-article-buffer (&rest forms)
+  `(save-excursion
+     (set-buffer gnus-article-buffer)
+     (let ((buffer-read-only nil))
+       ,@forms)))
+
+(put 'gnus-with-article-buffer 'lisp-indent-function 0)
+(put 'gnus-with-article-buffer 'edebug-form-spec '(body))
+
+(defun gnus-article-goto-header (header)
+  "Go to HEADER, which is a regular expression."
+  (re-search-forward (concat "^\\(" header "\\):") nil t))
+
 (defsubst gnus-article-hide-text (b e props)
   "Set text PROPS on the B to E region, extending `intangible' 1 past B."
   (gnus-add-text-properties-when 'article-type nil b e props)
@@ -1263,14 +1305,13 @@ Initialized from `text-mode-syntax-table.")
 
 (defun gnus-article-hide-text-type (b e type)
   "Hide text of TYPE between B and E."
-  (push type gnus-article-wash-types)
+  (gnus-add-wash-type type)
   (gnus-article-hide-text
    b e (cons 'article-type (cons type gnus-hidden-properties))))
 
 (defun gnus-article-unhide-text-type (b e type)
   "Unhide text of TYPE between B and E."
-  (setq gnus-article-wash-types
-       (delq type gnus-article-wash-types))
+  (gnus-delete-wash-type type)
   (remove-text-properties
    b e (cons 'article-type (cons type gnus-hidden-properties)))
   (when (memq 'intangible gnus-hidden-properties)
@@ -1369,7 +1410,7 @@ Initialized from `text-mode-syntax-table.")
          (when (setq beg (text-property-any
                           (point-min) (point-max) 'message-rank (+ 2 max)))
            ;; We delete the unwanted headers.
-           (push 'headers gnus-article-wash-types)
+           (gnus-add-wash-type 'headers)
            (add-text-properties (point-min) (+ 5 (point-min))
                                 '(article-type headers dummy-invisible t))
            (delete-region beg (point-max))))))))
@@ -1615,15 +1656,65 @@ unfolded."
              (replace-match " " t t)))
          (goto-char (point-max)))))))
 
+(defun gnus-article-treat-fold-headers ()
+  "Fold message headers."
+  (interactive)
+  (gnus-with-article-headers
+    (while (not (eobp))
+      (save-restriction
+       (mail-header-narrow-to-field)
+       (mail-header-fold-field)
+       (goto-char (point-max))))))
+
+(defun gnus-treat-smiley ()
+  "Display textual emoticons (\"smileys\") as small graphical icons."
+  (interactive "P")
+  (gnus-with-article-buffer
+    (if (memq 'smiley gnus-article-wash-types)
+       (gnus-delete-images 'smiley)
+      (article-goto-body)
+      (let ((images (smiley-region (point) (point-max))))
+       (when images
+         (gnus-add-wash-type 'smiley)
+         (dolist (image images)
+           (gnus-add-image 'smiley image)))))))
+
+(defun gnus-article-remove-images ()
+  "Remove all images from the article buffer."
+  (interactive)
+  (gnus-with-article-buffer
+    (dolist (elem gnus-article-image-alist)
+      (gnus-delete-images (car elem)))))
+
+(defun gnus-article-treat-fold-newsgroups ()
+  "Unfold folded message headers.
+Only the headers that fit into the current window width will be
+unfolded."
+  (interactive)
+  (gnus-with-article-headers
+    (while (gnus-article-goto-header "newsgroups\\|followup-to")
+      (save-restriction
+       (mail-header-narrow-to-field)
+       (while (re-search-forward ", *" nil t)
+         (replace-match ", " t t))
+       (mail-header-fold-field)
+       (goto-char (point-max))))))
+
 (defun gnus-article-treat-body-boundary ()
   "Place a boundary line at the end of the headers."
   (interactive)
-  (gnus-with-article-headers
-    (goto-char (point-max))
-    (insert (make-string (1- (window-width)) ? )
-           "\n")
-    (put-text-property (point) (progn (forward-line -1) (point))
-                      'face 'gnus-body-separator-face)))
+  (when (and gnus-body-boundary-delimiter 
+            (> (length gnus-body-boundary-delimiter) 0))
+    (gnus-with-article-headers
+      (goto-char (point-max))
+      (let ((start (point)))
+       (insert "X-Boundary: ")
+       (gnus-add-text-properties start (point) '(invisible t intangible t))
+       (insert (let (str)
+                 (while (>= (1- (window-width)) (length str))
+                   (setq str (concat str gnus-body-boundary-delimiter)))
+                 (substring str 0 (1- (window-width))))
+               "\n")))))
 
 (defun article-fill-long-lines ()
   "Fill lines that are wider than the window width."
@@ -1686,90 +1777,64 @@ unfolded."
 (defun article-display-x-face (&optional force)
   "Look for an X-Face header and display it if present."
   (interactive (list 'force))
-  (save-excursion
+  (gnus-with-article-headers
     ;; Delete the old process, if any.
     (when (process-status "article-x-face")
       (delete-process "article-x-face"))
-    (let ((inhibit-point-motion-hooks t)
-         x-faces
-         (case-fold-search t)
-         from last)
-      (save-restriction
-       (article-narrow-to-head)
-       (when (and buffer-read-only ;; When type `W f'
-                  (progn
-                    (goto-char (point-min))
-                    (not (re-search-forward "^X-Face:[\t ]*" nil t)))
-                  (gnus-buffer-live-p gnus-original-article-buffer))
-         (with-current-buffer gnus-original-article-buffer
-           (save-restriction
-             (article-narrow-to-head)
-             (while (re-search-forward "^X-Face:" nil t)
-               (setq x-faces
-                     (concat
-                      (or x-faces "")
-                      (buffer-substring
-                       (match-beginning 0)
-                       (1- (re-search-forward
-                            "^\\($\\|[^ \t]\\)" nil t))))))))
-         (if x-faces
-             (let (point start bface eface buffer-read-only)
-               (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))
-               (goto-char (point-max))
-               (setq point (point))
-               (insert x-faces)
-               (goto-char point)
-               (while (looking-at "\\([^:]+\\): *")
-                 (put-text-property (match-beginning 1) (1+ (match-end 1))
-                                    'face bface)
-                 (setq start (match-end 0))
-                 (forward-line 1)
-                 (while (looking-at "[\t ]")
-                   (forward-line 1))
-                 (put-text-property start (point)
-                                    'face eface)))))
-       (goto-char (point-min))
-       (setq from (message-fetch-field "from"))
-       (goto-char (point-min))
-       (while (and gnus-article-x-face-command
-                   (not last)
-                   (or force
-                       ;; Check whether this face is censored.
-                       (not gnus-article-x-face-too-ugly)
-                       (and gnus-article-x-face-too-ugly from
-                            (not (string-match gnus-article-x-face-too-ugly
-                                               from))))
-                   ;; Has to be present.
-                   (re-search-forward "^X-Face:[\t ]*" nil t))
-         ;; This used to try to do multiple faces (`while' instead of
-         ;; `when' above), but (a) sending multiple EOFs to xv doesn't
-         ;; work (b) it can crash some versions of Emacs (c) are
-         ;; multiple faces really something to encourage?
+    (if (memq 'xface gnus-article-wash-types)
+       ;; We have already displayed X-Faces, so we remove them
+       ;; instead.
+       (gnus-delete-images 'xface)
+      ;; Display X-Faces.
+      (let (x-faces from face grey)
+       (save-excursion
+         (set-buffer gnus-original-article-buffer)
+         (save-restriction
+           (mail-narrow-to-head)
+           (while (gnus-article-goto-header "x-face\\(-[0-9]+\\)?")
+             (when (match-beginning 2)
+               (setq grey t))
+             (push (mail-header-field-value) x-faces))
+           (setq from (message-fetch-field "from"))))
+       (if grey
+           (let ((xpm (gnus-convert-gray-x-face-to-xpm x-faces))
+                 image)
+             (when xpm
+               (setq image (gnus-create-image xpm 'xpm t))
+               (gnus-article-goto-header "from")
+               (gnus-add-wash-type 'xface)
+               (gnus-add-image 'xface image)
+               (gnus-put-image image)))
+         ;; Sending multiple EOFs to xv doesn't work, so we only do a
+         ;; single external face.
          (when (stringp gnus-article-x-face-command)
-           (setq last t))
-         ;; We now have the area of the buffer where the X-Face is stored.
-         (save-excursion
-           (let ((beg (point))
-                 (end (1- (re-search-forward "^\\($\\|[^ \t]\\)" nil t)))
-                 buffer-read-only)
-             ;; We display the face.
-             (if (symbolp gnus-article-x-face-command)
-                 ;; The command is a lisp function, so we call it.
-                 (if (gnus-functionp gnus-article-x-face-command)
-                     (funcall gnus-article-x-face-command beg end)
-                   (error "%s is not a function" gnus-article-x-face-command))
-               ;; The command is a string, so we interpret the command
-               ;; as a, well, command, and fork it off.
-               (let ((process-connection-type nil))
-                 (process-kill-without-query
-                  (start-process
-                   "article-x-face" nil shell-file-name shell-command-switch
-                   gnus-article-x-face-command))
-                 (process-send-region "article-x-face" beg end)
-                 (process-send-eof "article-x-face"))))))))))
+           (setq x-faces (list (car x-faces))))
+         (while (and (setq face (pop x-faces))
+                     gnus-article-x-face-command
+                     (or force
+                         ;; Check whether this face is censored.
+                         (not gnus-article-x-face-too-ugly)
+                         (and gnus-article-x-face-too-ugly from
+                              (not (string-match gnus-article-x-face-too-ugly
+                                                 from)))))
+           ;; We display the face.
+           (if (symbolp gnus-article-x-face-command)
+               ;; The command is a lisp function, so we call it.
+               (if (gnus-functionp gnus-article-x-face-command)
+                   (funcall gnus-article-x-face-command face)
+                 (error "%s is not a function" gnus-article-x-face-command))
+             ;; The command is a string, so we interpret the command
+             ;; as a, well, command, and fork it off.
+             (let ((process-connection-type nil))
+               (process-kill-without-query
+                (start-process
+                 "article-x-face" nil shell-file-name shell-command-switch
+                 gnus-article-x-face-command))
+               (with-temp-buffer
+                 (insert face)
+                 (process-send-region "article-x-face"
+                                      (point-min) (point-max)))
+               (process-send-eof "article-x-face")))))))))
 
 (defun article-decode-mime-words ()
   "Decode all MIME-encoded words in the article."
@@ -2009,7 +2074,7 @@ The `gnus-list-identifiers' variable specifies what to do."
        (article-goto-body)
        ;; Hide the "header".
        (when (re-search-forward "^-----BEGIN PGP SIGNED MESSAGE-----\n" nil t)
-         (push 'pgp gnus-article-wash-types)
+         (gnus-add-wash-type 'pgp)
          (delete-region (match-beginning 0) (match-end 0))
          ;; Remove armor headers (rfc2440 6.2)
          (delete-region (point) (or (re-search-forward "^[ \t]*\n" nil t)
@@ -2049,7 +2114,7 @@ always hide."
                    "\n-----BEGIN PRIVACY-ENHANCED MESSAGE-----\n"
                    nil t)
                   (setq end (1+ (match-beginning 0))))
-         (push 'pem gnus-article-wash-types)
+         (gnus-add-wash-type 'pem)
          (gnus-article-hide-text-type
           end
           (if (search-forward "\n\n" nil t)
@@ -2309,7 +2374,7 @@ Originally it is hide instead of DUMMY."
      (point-min) (point-max)
      (cons 'article-type (cons type
                               gnus-hidden-properties)))
-    (setq gnus-article-wash-types (delq type gnus-article-wash-types))))
+    (gnus-delete-wash-type type)))
 
 (defconst article-time-units
   `((year . ,(* 365.25 24 60 60))
@@ -2609,7 +2674,7 @@ This format is defined by the `gnus-article-time-format' variable."
               (match-beginning visible) (match-end visible) 'emphasis)
              (gnus-put-overlay-excluding-newlines
               (match-beginning visible) (match-end visible) 'face face)
-             (push 'emphasis gnus-article-wash-types)
+             (gnus-add-wash-type 'emphasis)
              (goto-char (match-end invisible)))))))))
 
 (defun gnus-article-setup-highlight-words (&optional highlight-words)
@@ -3022,7 +3087,7 @@ If variable `gnus-use-long-file-name' is non-nil, it is
         (setq afunc func
               gfunc (intern (format "gnus-%s" func))))
        (defalias gfunc
-        (if (fboundp afunc)
+        (when (fboundp afunc)
           `(lambda (&optional interactive &rest args)
              ,(documentation afunc t)
              (interactive (list t))
@@ -3098,6 +3163,8 @@ If variable `gnus-use-long-file-name' is non-nil, it is
   ">" end-of-buffer
   "\C-c\C-i" gnus-info-find-node
   "\C-c\C-b" gnus-bug
+  "R" gnus-article-reply-with-original
+  "F" gnus-article-followup-with-original
   "\C-hk" gnus-article-describe-key
   "\C-hc" gnus-article-describe-key-briefly
 
@@ -3181,6 +3248,7 @@ commands:
   (make-local-variable 'gnus-article-decoded-p)
   (make-local-variable 'gnus-article-mime-handle-alist)
   (make-local-variable 'gnus-article-wash-types)
+  (make-local-variable 'gnus-article-image-alist)
   (make-local-variable 'gnus-article-charset)
   (make-local-variable 'gnus-article-ignored-charsets)
   (gnus-set-default-directory)
@@ -3369,7 +3437,8 @@ If ALL-HEADERS is non-nil, no headers are hidden."
     (unless (eq major-mode 'gnus-article-mode)
       (gnus-article-mode))
     (setq buffer-read-only nil
-         gnus-article-wash-types nil)
+         gnus-article-wash-types nil
+         gnus-article-image-alist nil)
     (gnus-run-hooks 'gnus-tmp-internal-hook)
     (when gnus-display-mime-function
       (funcall gnus-display-mime-function))
@@ -3380,14 +3449,19 @@ If ALL-HEADERS is non-nil, no headers are hidden."
 ;;;
 
 (defvar gnus-mime-button-line-format "%{%([%p. %d%T]%)%}%e\n"
-  "The following specs can be used:
+  "Format of the MIME buttons.
+
+Valid specifiers include:
 %t  The MIME type
 %T  MIME type, along with additional info
 %n  The `name' parameter
 %d  The description, if any
 %l  The length of the encoded part
 %p  The part identifier number
-%e  Dots if the part isn't displayed")
+%e  Dots if the part isn't displayed
+
+General format specifiers can also be used.  See
+(gnus)Formatting Variables.")
 
 (defvar gnus-mime-button-line-format-alist
   '((?t gnus-tmp-type ?s)
@@ -3409,6 +3483,7 @@ If ALL-HEADERS is non-nil, no headers are hidden."
     (gnus-mime-inline-part "i" "View As Text, In This Buffer")
     (gnus-mime-internalize-part "E" "View Internally")
     (gnus-mime-externalize-part "e" "View Externally")
+    (gnus-mime-print-part "p" "Print")
     (gnus-mime-pipe-part "|" "Pipe To Command...")
     (gnus-mime-action-on-part "." "Take action on the part")))
 
@@ -3589,7 +3664,7 @@ If ALL-HEADERS is non-nil, no headers are hidden."
       (gnus-mm-display-part handle))))
 
 (defun gnus-mime-copy-part (&optional handle)
-  "Put the the MIME part under point into a new buffer."
+  "Put the MIME part under point into a new buffer."
   (interactive)
   (gnus-article-check-buffer)
   (let* ((handle (or handle (get-text-property (point) 'gnus-data)))
@@ -3613,6 +3688,31 @@ If ALL-HEADERS is non-nil, no headers are hidden."
        (setq buffer-file-name nil))
       (goto-char (point-min)))))
 
+(defun gnus-mime-print-part (&optional handle)
+  "Print the MIME part under point."
+  (interactive)
+  (gnus-article-check-buffer)
+  (let* ((handle (or handle (get-text-property (point) 'gnus-data)))
+        (contents (and handle (mm-get-part handle)))
+        (file (make-temp-name (expand-file-name "mm." mm-tmp-directory)))
+        (printer (mailcap-mime-info (mm-handle-type handle) "print")))
+    (when contents
+       (if printer
+           (unwind-protect
+               (progn
+                 (with-temp-file file
+                   (insert contents))
+                 (call-process shell-file-name nil
+                               (generate-new-buffer " *mm*")
+                               nil
+                               shell-command-switch
+                               (mm-mailcap-command
+                                printer file (mm-handle-type handle))))
+             (delete-file file))
+         (with-temp-buffer
+           (insert contents)
+           (gnus-print-buffer))))))
+
 (defun gnus-mime-inline-part (&optional handle arg)
   "Insert the MIME part under point into the current buffer."
   (interactive (list nil current-prefix-arg))
@@ -3993,12 +4093,10 @@ If no internal viewer is available, use an external viewer."
     ;;;!!! No, w3 can display everything just fine.
     (gnus-mime-display-part (cadr handle)))
    ((equal (car handle) "multipart/signed")
-    (or (memq 'signed gnus-article-wash-types)
-       (push 'signed gnus-article-wash-types))
+    (gnus-add-wash-type 'signed)
     (gnus-mime-display-security handle))
    ((equal (car handle) "multipart/encrypted")
-    (or (memq 'encrypted gnus-article-wash-types)
-       (push 'encrypted gnus-article-wash-types))
+    (gnus-add-wash-type 'encrypted)
     (gnus-mime-display-security handle))
    ;; Other multiparts are handled like multipart/mixed.
    (t
@@ -4224,7 +4322,7 @@ representing the particular washing function, ON is the string to use
 in the article mode line when the washing function is active, and OFF
 is the string to use when it is inactive.")
 
-(defun gnus-gnus-article-wash-status-entry (key value)
+(defun gnus-article-wash-status-entry (key value)
   (let ((entry (assoc key gnus-article-wash-status-strings)))
     (if value (nth 1 entry) (nth 2 entry))))
 
@@ -4242,14 +4340,37 @@ is the string to use when it is inactive.")
          (signature (memq 'signature gnus-article-wash-types))
          (overstrike (memq 'overstrike gnus-article-wash-types))
          (emphasis (memq 'emphasis gnus-article-wash-types)))
-      (concat (gnus-gnus-article-wash-status-entry 'cite cite)
-             (gnus-gnus-article-wash-status-entry 'headers
-                                                  (or headers boring))
-             (gnus-gnus-article-wash-status-entry
-              'pgp (or pgp pem signed encrypted))
-             (gnus-gnus-article-wash-status-entry 'signature signature)
-             (gnus-gnus-article-wash-status-entry 'overstrike overstrike)
-             (gnus-gnus-article-wash-status-entry 'emphasis emphasis)))))
+      (concat
+       (gnus-article-wash-status-entry 'cite cite)
+       (gnus-article-wash-status-entry 'headers (or headers boring))
+       (gnus-article-wash-status-entry 'pgp (or pgp pem signed encrypted))
+       (gnus-article-wash-status-entry 'signature signature)
+       (gnus-article-wash-status-entry 'overstrike overstrike)
+       (gnus-article-wash-status-entry 'emphasis emphasis)))))
+
+(defun gnus-add-wash-type (type)
+  "Add a washing of TYPE to the current status."
+  (push type gnus-article-wash-types))
+
+(defun gnus-delete-wash-type (type)
+  "Add a washing of TYPE to the current status."
+  (setq gnus-article-wash-types (delq type gnus-article-wash-types)))
+
+(defun gnus-add-image (category image)
+  "Add IMAGE of CATEGORY to the list of displayed images."
+  (let ((entry (assq category gnus-article-image-alist)))
+    (unless entry
+      (setq entry (list category))
+      (push entry gnus-article-image-alist))
+    (nconc entry (list image))))
+
+(defun gnus-delete-images (category)
+  "Delete all images in CATEGORY."
+  (let ((entry (assq category gnus-article-image-alist)))
+    (dolist (image (cdr entry))
+      (gnus-remove-image image))
+    (setq gnus-article-image-alist (delq entry gnus-article-image-alist))
+    (gnus-delete-wash-type category)))
 
 (defalias 'gnus-article-hide-headers-if-wanted 'gnus-article-maybe-hide-headers)
 
@@ -4435,7 +4556,7 @@ Argument LINES specifies lines to be scrolled down."
   (interactive "P")
   (gnus-article-check-buffer)
   (let ((nosaves
-        '("q" "Q"  "c" "r" "R" "\C-c\C-f" "m"  "a" "f" "F"
+        '("q" "Q"  "c" "r" "\C-c\C-f" "m"  "a" "f"
           "Zc" "ZC" "ZE" "ZQ" "ZZ" "Zn" "ZR" "ZG" "ZN" "ZP"
           "=" "^" "\M-^" "|"))
        (nosave-but-article
@@ -4549,6 +4670,28 @@ Argument LINES specifies lines to be scrolled down."
        (describe-key-briefly key insert))
     (describe-key-briefly key insert)))
 
+(defun gnus-article-reply-with-original (&optional wide)
+  "Start composing a reply mail to the current message.
+The text in the region will be yanked.  If the region isn't active,
+the entire article will be yanked."
+  (interactive "P")
+  (let ((article (cdr gnus-article-current)))
+    (if (not mark-active)
+       (gnus-summary-reply (list (list article)) wide)
+      (gnus-summary-reply
+       (list (list article (buffer-substring (point) (mark)))) wide))))
+
+(defun gnus-article-followup-with-original ()
+  "Compose a followup to the current article.
+The text in the region will be yanked.  If the region isn't active,
+the entire article will be yanked."
+  (interactive)
+  (let ((article (cdr gnus-article-current)))
+    (if (not mark-active)
+       (gnus-summary-followup (list (list article)))
+      (gnus-summary-followup
+       (list (list article (buffer-substring (point) (mark))))))))
+
 (defun gnus-article-hide (&optional arg force)
   "Hide all the gruft in the current article.
 This means that PGP stuff, signatures, cited text and (some)
@@ -5200,14 +5343,12 @@ specified by `gnus-button-alist'."
          (inhibit-point-motion-hooks t))
       (if (text-property-any end (point-max) 'article-type 'signature)
          (progn
-           (setq gnus-article-wash-types
-                 (delq 'signature gnus-article-wash-types))
+           (gnus-delete-wash-type 'signature)
            (gnus-remove-text-properties-when
             'article-type 'signature end (point-max)
             (cons 'article-type (cons 'signature
                                       gnus-hidden-properties))))
-       (or (memq 'signature gnus-article-wash-types)
-           (push 'signature gnus-article-wash-types))
+       (gnus-add-wash-type 'signature)
        (gnus-add-text-properties-when
         'article-type nil end (point-max)
         (cons 'article-type (cons 'signature
@@ -5790,24 +5931,6 @@ For example:
      (cons (set-marker (make-marker) (point-min))
           (set-marker (make-marker) (point-max))))))
 
-;;; 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)
-            (inhibit-point-motion-hooks t)
-            (case-fold-search t))
-        (article-narrow-to-head)
-        ,@forms))))
-
-(put 'gnus-with-article-headers 'lisp-indent-function 0)
-(put 'gnus-with-article-headers 'edebug-form-spec '(body))
-
-(defun gnus-article-goto-header (header)
-  (re-search-forward (concat "^" header ":") nil t))
-
 (gnus-ems-redefine)
 
 (provide 'gnus-art)