03b229e1409aabc07b9c73c672e3b9a966fc44b8
[elisp/gnus.git-] / lisp / gnus-art.el
1 ;;; gnus-art.el --- article mode commands for Semi-gnus
2
3 ;; Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
4 ;;   2005 Free Software Foundation, Inc.
5
6 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
7 ;;      MORIOKA Tomohiko <morioka@jaist.ac.jp>
8 ;;      Katsumi Yamaoka <yamaoka@jpl.org>
9 ;; Keywords: mail, news, MIME
10
11 ;; This file is part of GNU Emacs.
12
13 ;; GNU Emacs is free software; you can redistribute it and/or modify
14 ;; it under the terms of the GNU General Public License as published by
15 ;; the Free Software Foundation; either version 2, or (at your option)
16 ;; any later version.
17
18 ;; GNU Emacs is distributed in the hope that it will be useful,
19 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
20 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21 ;; GNU General Public License for more details.
22
23 ;; You should have received a copy of the GNU General Public License
24 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
25 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
26 ;; Boston, MA 02110-1301, USA.
27
28 ;;; Commentary:
29
30 ;;; Code:
31
32 (eval-when-compile
33   (require 'cl)
34   (require 'static)
35   (defvar tool-bar-map)
36   (defvar w3m-minor-mode-map))
37
38 (require 'path-util)
39 (require 'gnus)
40 ;; Avoid the "Recursive load suspected" error in Emacs 21.1.
41 (eval-and-compile
42   (let ((recursive-load-depth-limit 100))
43     (require 'gnus-sum)))
44 (require 'gnus-spec)
45 (require 'gnus-int)
46 (require 'gnus-win)
47 (require 'alist)
48 (require 'mime-view)
49 (when (static-if (featurep 'xemacs)
50           (console-on-window-system-p)
51         window-system)
52   (require 'mime-image))
53 (require 'wid-edit)
54
55 ;; Avoid byte-compile warnings.
56 (eval-when-compile
57   (require 'mm-bodies)
58   (require 'mail-parse)
59   (require 'mm-decode)
60   (require 'mm-view)
61   (require 'mm-uu))
62
63 (require 'message)
64
65 (autoload 'gnus-msg-mail "gnus-msg" nil t)
66 (autoload 'gnus-button-mailto "gnus-msg")
67 (autoload 'gnus-button-reply "gnus-msg" nil t)
68 (autoload 'parse-time-string "parse-time" nil nil)
69 (autoload 'ansi-color-apply-on-region "ansi-color")
70
71 (defgroup gnus-article nil
72   "Article display."
73   :link '(custom-manual "(gnus)Article Buffer")
74   :group 'gnus)
75
76 (defgroup gnus-article-treat nil
77   "Treating article parts."
78   :link '(custom-manual "(gnus)Article Hiding")
79   :group 'gnus-article)
80
81 (defgroup gnus-article-hiding nil
82   "Hiding article parts."
83   :link '(custom-manual "(gnus)Article Hiding")
84   :group 'gnus-article)
85
86 (defgroup gnus-article-highlight nil
87   "Article highlighting."
88   :link '(custom-manual "(gnus)Article Highlighting")
89   :group 'gnus-article
90   :group 'gnus-visual)
91
92 (defgroup gnus-article-signature nil
93   "Article signatures."
94   :link '(custom-manual "(gnus)Article Signature")
95   :group 'gnus-article)
96
97 (defgroup gnus-article-headers nil
98   "Article headers."
99   :link '(custom-manual "(gnus)Hiding Headers")
100   :group 'gnus-article)
101
102 (defgroup gnus-article-washing nil
103   "Special commands on articles."
104   :link '(custom-manual "(gnus)Article Washing")
105   :group 'gnus-article)
106
107 (defgroup gnus-article-emphasis nil
108   "Fontisizing articles."
109   :link '(custom-manual "(gnus)Article Fontisizing")
110   :group 'gnus-article)
111
112 (defgroup gnus-article-saving nil
113   "Saving articles."
114   :link '(custom-manual "(gnus)Saving Articles")
115   :group 'gnus-article)
116
117 (defgroup gnus-article-mime nil
118   "Worshiping the MIME wonder."
119   :link '(custom-manual "(gnus)Using MIME")
120   :group 'gnus-article)
121
122 (defgroup gnus-article-buttons nil
123   "Pushable buttons in the article buffer."
124   :link '(custom-manual "(gnus)Article Buttons")
125   :group 'gnus-article)
126
127 (defgroup gnus-article-various nil
128   "Other article options."
129   :link '(custom-manual "(gnus)Misc Article")
130   :group 'gnus-article)
131
132 (defcustom gnus-ignored-headers
133   (mapcar
134    (lambda (header)
135      (concat "^" header ":"))
136    '("Path" "Expires" "Date-Received" "References" "Xref" "Lines"
137      "Relay-Version" "Message-ID" "Approved" "Sender" "Received"
138      "X-UIDL" "MIME-Version" "Return-Path" "In-Reply-To"
139      "Content-Type" "Content-Transfer-Encoding" "X-WebTV-Signature"
140      "X-MimeOLE" "X-MSMail-Priority" "X-Priority" "X-Loop"
141      "X-Authentication-Warning" "X-MIME-Autoconverted" "X-Face"
142      "X-Attribution" "X-Originating-IP" "Delivered-To"
143      "NNTP-[-A-Za-z]+" "Distribution" "X-no-archive" "X-Trace"
144      "X-Complaints-To" "X-NNTP-Posting-Host" "X-Orig.*"
145      "Abuse-Reports-To" "Cache-Post-Path" "X-Article-Creation-Date"
146      "X-Poster" "X-Mail2News-Path" "X-Server-Date" "X-Cache"
147      "Originator" "X-Problems-To" "X-Auth-User" "X-Post-Time"
148      "X-Admin" "X-UID" "Resent-[-A-Za-z]+" "X-Mailing-List"
149      "Precedence" "Original-[-A-Za-z]+" "X-filename" "X-Orcpt"
150      "Old-Received" "X-Pgp" "X-Auth" "X-From-Line"
151      "X-Gnus-Article-Number" "X-Majordomo" "X-Url" "X-Sender"
152      "MBOX-Line" "Priority" "X400-[-A-Za-z]+"
153      "Status" "X-Gnus-Mail-Source" "Cancel-Lock"
154      "X-FTN" "X-EXP32-SerialNo" "Encoding" "Importance"
155      "Autoforwarded" "Original-Encoded-Information-Types" "X-Ya-Pop3"
156      "X-Face-Version" "X-Vms-To" "X-ML-NAME" "X-ML-COUNT"
157      "Mailing-List" "X-finfo" "X-md5sum" "X-md5sum-Origin"
158      "X-Sun-Charset" "X-Accept-Language" "X-Envelope-Sender"
159      "List-[A-Za-z]+" "X-Listprocessor-Version"
160      "X-Received" "X-Distribute" "X-Sequence" "X-Juno-Line-Breaks"
161      "X-Notes-Item" "X-MS-TNEF-Correlator" "x-uunet-gateway"
162      "X-Received" "Content-length" "X-precedence"
163      "X-Authenticated-User" "X-Comment" "X-Report" "X-Abuse-Info"
164      "X-HTTP-Proxy" "X-Mydeja-Info" "X-Copyright" "X-No-Markup"
165      "X-Abuse-Info" "X-From_" "X-Accept-Language" "Errors-To"
166      "X-BeenThere" "X-Mailman-Version" "List-Help" "List-Post"
167      "List-Subscribe" "List-Id" "List-Unsubscribe" "List-Archive"
168      "X-Content-length" "X-Posting-Agent" "Original-Received"
169      "X-Request-PGP" "X-Fingerprint" "X-WRIEnvto" "X-WRIEnvfrom"
170      "X-Virus-Scanned" "X-Delivery-Agent" "Posted-Date" "X-Gateway"
171      "X-Local-Origin" "X-Local-Destination" "X-UserInfo1"
172      "X-Received-Date" "X-Hashcash" "Face" "X-DMCA-Notifications"
173      "X-Abuse-and-DMCA-Info" "X-Postfilter" "X-Gpg-.*" "X-Disclaimer"))
174   "*All headers that start with this regexp will be hidden.
175 This variable can also be a list of regexps of headers to be ignored.
176 If `gnus-visible-headers' is non-nil, this variable will be ignored."
177   :type '(choice :custom-show nil
178                  regexp
179                  (repeat regexp))
180   :group 'gnus-article-hiding)
181
182 (defcustom gnus-visible-headers
183   "^From:\\|^Newsgroups:\\|^Subject:\\|^Date:\\|^Followup-To:\\|^Reply-To:\\|^Organization:\\|^Summary:\\|^Keywords:\\|^To:\\|^[BGF]?Cc:\\|^Posted-To:\\|^Mail-Copies-To:\\|^Mail-Followup-To:\\|^Apparently-To:\\|^Gnus-Warning:\\|^Resent-From:\\|^X-Sent:"
184   "*All headers that do not match this regexp will be hidden.
185 This variable can also be a list of regexp of headers to remain visible.
186 If this variable is non-nil, `gnus-ignored-headers' will be ignored."
187   :type '(repeat :value-to-internal (lambda (widget value)
188                                       (custom-split-regexp-maybe value))
189                  :match (lambda (widget value)
190                           (or (stringp value)
191                               (widget-editable-list-match widget value)))
192                  regexp)
193   :group 'gnus-article-hiding)
194
195 (defcustom gnus-sorted-header-list
196   '("^From:" "^Subject:" "^Summary:" "^Keywords:" "^Newsgroups:"
197     "^Followup-To:" "^To:" "^Cc:" "^Date:" "^Organization:")
198   "*This variable is a list of regular expressions.
199 If it is non-nil, headers that match the regular expressions will
200 be placed first in the article buffer in the sequence specified by
201 this list."
202   :type '(repeat regexp)
203   :group 'gnus-article-hiding)
204
205 (defcustom gnus-boring-article-headers '(empty followup-to reply-to)
206   "Headers that are only to be displayed if they have interesting data.
207 Possible values in this list are:
208
209   'empty       Headers with no content.
210   'newsgroups  Newsgroup identical to Gnus group.
211   'to-address  To identical to To-address.
212   'to-list     To identical to To-list.
213   'cc-list     CC identical to To-list.
214   'followup-to Followup-to identical to Newsgroups.
215   'reply-to    Reply-to identical to From.
216   'date        Date less than four days old.
217   'long-to     To and/or Cc longer than 1024 characters.
218   'many-to     Multiple To and/or Cc."
219   :type '(set (const :tag "Headers with no content." empty)
220               (const :tag "Newsgroups identical to Gnus group." newsgroups)
221               (const :tag "To identical to To-address." to-address)
222               (const :tag "To identical to To-list." to-list)
223               (const :tag "CC identical to To-list." cc-list)
224               (const :tag "Followup-to identical to Newsgroups." followup-to)
225               (const :tag "Reply-to identical to From." reply-to)
226               (const :tag "Date less than four days old." date)
227               (const :tag "To and/or Cc longer than 1024 characters." long-to)
228               (const :tag "Multiple To and/or Cc headers." many-to))
229   :group 'gnus-article-hiding)
230
231 (defcustom gnus-article-skip-boring nil
232   "Skip over text that is not worth reading.
233 By default, if you set this t, then Gnus will display citations and
234 signatures, but will never scroll down to show you a page consisting
235 only of boring text.  Boring text is controlled by
236 `gnus-article-boring-faces'."
237   :version "22.1"
238   :type 'boolean
239   :group 'gnus-article-hiding)
240
241 (defcustom gnus-signature-separator '("^-- $" "^-- *$")
242   "Regexp matching signature separator.
243 This can also be a list of regexps.  In that case, it will be checked
244 from head to tail looking for a separator.  Searches will be done from
245 the end of the buffer."
246   :type '(repeat string)
247   :group 'gnus-article-signature)
248
249 (defcustom gnus-signature-limit nil
250   "Provide a limit to what is considered a signature.
251 If it is a number, no signature may not be longer (in characters) than
252 that number.  If it is a floating point number, no signature may be
253 longer (in lines) than that number.  If it is a function, the function
254 will be called without any parameters, and if it returns nil, there is
255 no signature in the buffer.  If it is a string, it will be used as a
256 regexp.  If it matches, the text in question is not a signature.
257
258 This can also be a list of the above values."
259   :type '(choice (const nil)
260                  (integer :value 200)
261                  (number :value 4.0)
262                  (function :value fun)
263                  (regexp :value ".*"))
264   :group 'gnus-article-signature)
265
266 (defcustom gnus-hidden-properties '(invisible t intangible t)
267   "Property list to use for hiding text."
268   :type 'sexp
269   :group 'gnus-article-hiding)
270
271 ;; Fixme: This isn't the right thing for mixed graphical and non-graphical
272 ;; frames in a session.
273 (defcustom gnus-article-x-face-command
274   (cond
275    (noninteractive
276     'ignore)
277    ((featurep 'xemacs)
278     (if (or (gnus-image-type-available-p 'xface)
279             (gnus-image-type-available-p 'pbm))
280         'gnus-display-x-face-in-from
281       "{ echo '/* Width=48, Height=48 */'; uncompface; } | icontopbm | ee -"))
282    ((gnus-image-type-available-p 'pbm)
283     'gnus-display-x-face-in-from)
284    (t
285     "{ echo '/* Width=48, Height=48 */'; uncompface; } | icontopbm | \
286 display -"))
287   "*String or function to be executed to display an X-Face header.
288 If it is a string, the command will be executed in a sub-shell
289 asynchronously.  The compressed face will be piped to this command."
290   :type `(choice
291           :format "%{%t%}:\n%[Value Menu%] %v"
292           ,@(delq nil
293                   (list
294                    'string
295                    (if (or (gnus-image-type-available-p 'xface)
296                            (gnus-image-type-available-p 'pbm))
297                        '(function-item gnus-display-x-face-in-from))
298                    'function)))
299   :version "21.1"
300   :group 'gnus-picon
301   :group 'gnus-article-washing)
302
303 (defcustom gnus-article-x-face-too-ugly nil
304   "Regexp matching posters whose face shouldn't be shown automatically."
305   :type '(choice regexp (const nil))
306   :group 'gnus-article-washing)
307
308 (defcustom gnus-article-banner-alist nil
309   "Banner alist for stripping.
310 For example,
311      ((egroups . \"^[ \\t\\n]*-------------------+\\\\( \\\\(e\\\\|Yahoo! \\\\)Groups Sponsor -+\\\\)?....\\n\\\\(.+\\n\\\\)+\"))"
312   :version "21.1"
313   :type '(repeat (cons symbol regexp))
314   :group 'gnus-article-washing)
315
316 (gnus-define-group-parameter
317  banner
318  :variable-document
319  "Alist of regexps (to match group names) and banner."
320  :variable-group gnus-article-washing
321  :parameter-type
322  '(choice :tag "Banner"
323           :value nil
324           (const :tag "Remove signature" signature)
325           (symbol :tag "Item in `gnus-article-banner-alist'" none)
326           regexp
327           (const :tag "None" nil))
328  :parameter-document
329  "If non-nil, specify how to remove `banners' from articles.
330
331 Symbol `signature' means to remove signatures delimited by
332 `gnus-signature-separator'.  Any other symbol is used to look up a
333 regular expression to match the banner in `gnus-article-banner-alist'.
334 A string is used as a regular expression to match the banner
335 directly.")
336
337 (defcustom gnus-article-address-banner-alist nil
338   "Alist of mail addresses and banners.
339 Each element has the form (ADDRESS . BANNER), where ADDRESS is a regexp
340 to match a mail address in the From: header, BANNER is one of a symbol
341 `signature', an item in `gnus-article-banner-alist', a regexp and nil.
342 If ADDRESS matches author's mail address, it will remove things like
343 advertisements.  For example:
344
345 \((\"@yoo-hoo\\\\.co\\\\.jp\\\\'\" . \"\\n_+\\nDo You Yoo-hoo!\\\\?\\n.*\\n.*\\n\"))
346 "
347   :type '(repeat
348           (cons
349            (regexp :tag "Address")
350            (choice :tag "Banner" :value nil
351                    (const :tag "Remove signature" signature)
352                    (symbol :tag "Item in `gnus-article-banner-alist'" none)
353                    regexp
354                    (const :tag "None" nil))))
355   :version "22.1"
356   :group 'gnus-article-washing)
357
358 (defmacro gnus-emphasis-custom-with-format (&rest body)
359   `(let ((format "\
360 \\(\\s-\\|^\\|\\=\\|[-\"]\\|\\s(\\)\\(%s\\(\\w+\\(\\s-+\\w+\\)*[.,]?\\)%s\\)\
361 \\(\\([-,.;:!?\"]\\|\\s)\\)+\\s-\\|[?!.]\\s-\\|\\s)\\|\\s-\\)"))
362      ,@body))
363
364 (defun gnus-emphasis-custom-value-to-external (value)
365   (gnus-emphasis-custom-with-format
366    (if (consp (car value))
367        (list (format format (car (car value)) (cdr (car value)))
368              2
369              (if (nth 1 value) 2 3)
370              (nth 2 value))
371      value)))
372
373 (defun gnus-emphasis-custom-value-to-internal (value)
374   (gnus-emphasis-custom-with-format
375    (let ((regexp (concat "\\`"
376                          (format (regexp-quote format)
377                                  "\\([^()]+\\)" "\\([^()]+\\)")
378                          "\\'"))
379          pattern)
380      (if (string-match regexp (setq pattern (car value)))
381          (list (cons (match-string 1 pattern) (match-string 2 pattern))
382                (= (nth 2 value) 2)
383                (nth 3 value))
384        value))))
385
386 (defcustom gnus-emphasis-alist
387   (let ((types
388          '(("\\*" "\\*" bold nil 2)
389            ("_" "_" underline)
390            ("/" "/" italic)
391            ("_/" "/_" underline-italic)
392            ("_\\*" "\\*_" underline-bold)
393            ("\\*/" "/\\*" bold-italic)
394            ("_\\*/" "/\\*_" underline-bold-italic))))
395     (nconc
396      (gnus-emphasis-custom-with-format
397       (mapcar (lambda (spec)
398                 (list (format format (car spec) (cadr spec))
399                       (or (nth 3 spec) 2)
400                       (or (nth 4 spec) 3)
401                       (intern (format "gnus-emphasis-%s" (nth 2 spec)))))
402               types))
403      '(;; I've never seen anyone use this strikethru convention whereas I've
404        ;; several times seen it triggered by normal text.  --Stef
405        ;; Miles suggests that this form is sometimes used but for italics,
406        ;; so maybe we should map it to `italic'.
407        ;; ("\\(\\s-\\|^\\)\\(-\\(\\(\\w\\|-[^-]\\)+\\)-\\)\\(\\s-\\|[?!.,;]\\)"
408        ;; 2 3 gnus-emphasis-strikethru)
409        ("\\(\\s-\\|^\\)\\(_\\(\\(\\w\\|_[^_]\\)+\\)_\\)\\(\\s-\\|[?!.,;]\\)"
410         2 3 gnus-emphasis-underline))))
411   "*Alist that says how to fontify certain phrases.
412 Each item looks like this:
413
414   (\"_\\\\(\\\\w+\\\\)_\" 0 1 'underline)
415
416 The first element is a regular expression to be matched.  The second
417 is a number that says what regular expression grouping used to find
418 the entire emphasized word.  The third is a number that says what
419 regexp grouping should be displayed and highlighted.  The fourth
420 is the face used for highlighting."
421   :type
422   '(repeat
423     (menu-choice
424      :format "%[Customizing Style%]\n%v"
425      :indent 2
426      (group :tag "Default"
427             :value ("" 0 0 default)
428             :value-create
429             (lambda (widget)
430               (let ((value (widget-get
431                             (cadr (widget-get (widget-get widget :parent)
432                                               :args))
433                             :value)))
434                 (if (not (eq (nth 2 value) 'default))
435                     (widget-put
436                      widget
437                      :value
438                      (gnus-emphasis-custom-value-to-external value))))
439               (widget-group-value-create widget))
440             regexp
441             (integer :format "Match group: %v")
442             (integer :format "Emphasize group: %v")
443             face)
444      (group :tag "Simple"
445             :value (("_" . "_") nil default)
446             (cons :format "%v"
447                   (regexp :format "Start regexp: %v")
448                   (regexp :format "End regexp: %v"))
449             (boolean :format "Show start and end patterns: %[%v%]\n"
450                      :on " On " :off " Off ")
451             face)))
452   :get (lambda (symbol)
453          (mapcar 'gnus-emphasis-custom-value-to-internal
454                  (default-value symbol)))
455   :set (lambda (symbol value)
456          (set-default symbol (mapcar 'gnus-emphasis-custom-value-to-external
457                                      value)))
458   :group 'gnus-article-emphasis)
459
460 (defcustom gnus-emphasize-whitespace-regexp "^[ \t]+\\|[ \t]*\n"
461   "A regexp to describe whitespace which should not be emphasized.
462 Typical values are \"^[ \\t]+\\\\|[ \\t]*\\n\" and \"[ \\t]+\\\\|[ \\t]*\\n\".
463 The former avoids underlining of leading and trailing whitespace,
464 and the latter avoids underlining any whitespace at all."
465   :version "21.1"
466   :group 'gnus-article-emphasis
467   :type 'regexp)
468
469 (defface gnus-emphasis-bold '((t (:bold t)))
470   "Face used for displaying strong emphasized text (*word*)."
471   :group 'gnus-article-emphasis)
472
473 (defface gnus-emphasis-italic '((t (:italic t)))
474   "Face used for displaying italic emphasized text (/word/)."
475   :group 'gnus-article-emphasis)
476
477 (defface gnus-emphasis-underline '((t (:underline t)))
478   "Face used for displaying underlined emphasized text (_word_)."
479   :group 'gnus-article-emphasis)
480
481 (defface gnus-emphasis-underline-bold '((t (:bold t :underline t)))
482   "Face used for displaying underlined bold emphasized text (_*word*_)."
483   :group 'gnus-article-emphasis)
484
485 (defface gnus-emphasis-underline-italic '((t (:italic t :underline t)))
486   "Face used for displaying underlined italic emphasized text (_/word/_)."
487   :group 'gnus-article-emphasis)
488
489 (defface gnus-emphasis-bold-italic '((t (:bold t :italic t)))
490   "Face used for displaying bold italic emphasized text (/*word*/)."
491   :group 'gnus-article-emphasis)
492
493 (defface gnus-emphasis-underline-bold-italic
494   '((t (:bold t :italic t :underline t)))
495   "Face used for displaying underlined bold italic emphasized text.
496 Example: (_/*word*/_)."
497   :group 'gnus-article-emphasis)
498
499 (defface gnus-emphasis-strikethru (if (featurep 'xemacs)
500                                       '((t (:strikethru t)))
501                                     '((t (:strike-through t))))
502   "Face used for displaying strike-through text (-word-)."
503   :group 'gnus-article-emphasis)
504
505 (defface gnus-emphasis-highlight-words
506   '((t (:background "black" :foreground "yellow")))
507   "Face used for displaying highlighted words."
508   :group 'gnus-article-emphasis)
509
510 (defcustom gnus-article-time-format "%a, %d %b %Y %T %Z"
511   "Format for display of Date headers in article bodies.
512 See `format-time-string' for the possible values.
513
514 The variable can also be function, which should return a complete Date
515 header.  The function is called with one argument, the time, which can
516 be fed to `format-time-string'."
517   :type '(choice string function)
518   :link '(custom-manual "(gnus)Article Date")
519   :group 'gnus-article-washing)
520
521 (defcustom gnus-save-all-headers t
522   "*If non-nil, don't remove any headers before saving."
523   :group 'gnus-article-saving
524   :type 'boolean)
525
526 (defcustom gnus-prompt-before-saving 'always
527   "*This variable says how much prompting is to be done when saving articles.
528 If it is nil, no prompting will be done, and the articles will be
529 saved to the default files.  If this variable is `always', each and
530 every article that is saved will be preceded by a prompt, even when
531 saving large batches of articles.  If this variable is neither nil not
532 `always', there the user will be prompted once for a file name for
533 each invocation of the saving commands."
534   :group 'gnus-article-saving
535   :type '(choice (item always)
536                  (item :tag "never" nil)
537                  (sexp :tag "once" :format "%t\n" :value t)))
538
539 (defcustom gnus-saved-headers gnus-visible-headers
540   "Headers to keep if `gnus-save-all-headers' is nil.
541 If `gnus-save-all-headers' is non-nil, this variable will be ignored.
542 If that variable is nil, however, all headers that match this regexp
543 will be kept while the rest will be deleted before saving."
544   :group 'gnus-article-saving
545   :type 'regexp)
546
547 (defcustom gnus-default-article-saver 'gnus-summary-save-in-rmail
548   "A function to save articles in your favourite format.
549 The function must be interactively callable (in other words, it must
550 be an Emacs command).
551
552 Gnus provides the following functions:
553
554 * gnus-summary-save-in-rmail (Rmail format)
555 * gnus-summary-save-in-mail (Unix mail format)
556 * gnus-summary-save-in-folder (MH folder)
557 * gnus-summary-save-in-file (article format)
558 * gnus-summary-save-body-in-file (article body)
559 * gnus-summary-save-in-vm (use VM's folder format)
560 * gnus-summary-write-to-file (article format -- overwrite)."
561   :group 'gnus-article-saving
562   :type '(radio (function-item gnus-summary-save-in-rmail)
563                 (function-item gnus-summary-save-in-mail)
564                 (function-item gnus-summary-save-in-folder)
565                 (function-item gnus-summary-save-in-file)
566                 (function-item gnus-summary-save-body-in-file)
567                 (function-item gnus-summary-save-in-vm)
568                 (function-item gnus-summary-write-to-file)))
569
570 (defcustom gnus-rmail-save-name 'gnus-plain-save-name
571   "A function generating a file name to save articles in Rmail format.
572 The function is called with NEWSGROUP, HEADERS, and optional LAST-FILE."
573   :group 'gnus-article-saving
574   :type 'function)
575
576 (defcustom gnus-mail-save-name 'gnus-plain-save-name
577   "A function generating a file name to save articles in Unix mail format.
578 The function is called with NEWSGROUP, HEADERS, and optional LAST-FILE."
579   :group 'gnus-article-saving
580   :type 'function)
581
582 (defcustom gnus-folder-save-name 'gnus-folder-save-name
583   "A function generating a file name to save articles in MH folder.
584 The function is called with NEWSGROUP, HEADERS, and optional LAST-FOLDER."
585   :group 'gnus-article-saving
586   :type 'function)
587
588 (defcustom gnus-file-save-name 'gnus-numeric-save-name
589   "A function generating a file name to save articles in article format.
590 The function is called with NEWSGROUP, HEADERS, and optional
591 LAST-FILE."
592   :group 'gnus-article-saving
593   :type 'function)
594
595 (defcustom gnus-split-methods
596   '((gnus-article-archive-name)
597     (gnus-article-nndoc-name))
598   "*Variable used to suggest where articles are to be saved.
599 For instance, if you would like to save articles related to Gnus in
600 the file \"gnus-stuff\", and articles related to VM in \"vm-stuff\",
601 you could set this variable to something like:
602
603  '((\"^Subject:.*gnus\\|^Newsgroups:.*gnus\" \"gnus-stuff\")
604    (\"^Subject:.*vm\\|^Xref:.*vm\" \"vm-stuff\"))
605
606 This variable is an alist where the key is the match and the
607 value is a list of possible files to save in if the match is
608 non-nil.
609
610 If the match is a string, it is used as a regexp match on the
611 article.  If the match is a symbol, that symbol will be funcalled
612 from the buffer of the article to be saved with the newsgroup as
613 the parameter.  If it is a list, it will be evaled in the same
614 buffer.
615
616 If this form or function returns a string, this string will be
617 used as a possible file name; and if it returns a non-nil list,
618 that list will be used as possible file names."
619   :group 'gnus-article-saving
620   :type '(repeat (choice (list :value (fun) function)
621                          (cons :value ("" "") regexp (repeat string))
622                          (sexp :value nil))))
623
624 (defcustom gnus-article-display-method-for-mime
625   'gnus-article-display-mime-message
626   "Function to display a MIME message.
627 The function is called from the article buffer."
628   :group 'gnus-article-mime
629   :type 'function)
630
631 (defcustom gnus-article-display-method-for-traditional
632   'gnus-article-display-traditional-message
633   "*Function to display a traditional message.
634 The function is called from the article buffer."
635   :group 'gnus-article-mime
636   :type 'function)
637
638 (defcustom gnus-page-delimiter "^\^L"
639   "*Regexp describing what to use as article page delimiters.
640 The default value is \"^\^L\", which is a form linefeed at the
641 beginning of a line."
642   :type 'regexp
643   :group 'gnus-article-various)
644
645 (defcustom gnus-article-mode-line-format "Gnus: %g [%w] %S%m"
646   "*The format specification for the article mode line.
647 See `gnus-summary-mode-line-format' for a closer description.
648
649 The following additional specs are available:
650
651 %w  The article washing status.
652 %m  The number of MIME parts in the article."
653   :type 'string
654   :group 'gnus-article-various)
655
656 (defcustom gnus-article-mode-hook nil
657   "*A hook for Gnus article mode."
658   :type 'hook
659   :group 'gnus-article-various)
660
661 (when (featurep 'xemacs)
662   ;; Extracted from gnus-xmas-define in order to preserve user settings
663   (when (fboundp 'turn-off-scroll-in-place)
664     (add-hook 'gnus-article-mode-hook 'turn-off-scroll-in-place))
665   ;; Extracted from gnus-xmas-redefine in order to preserve user settings
666   (add-hook 'gnus-article-mode-hook 'gnus-xmas-article-menu-add))
667
668 (defcustom gnus-article-menu-hook nil
669   "*Hook run after the creation of the article mode menu."
670   :type 'hook
671   :group 'gnus-article-various)
672
673 (defcustom gnus-article-prepare-hook nil
674   "*A hook called after an article has been prepared in the article buffer."
675   :type 'hook
676   :group 'gnus-article-various)
677
678 (defcustom gnus-copy-article-ignored-headers nil
679   "List of headers to be removed when copying an article.
680 Each element is a regular expression."
681   :version "23.0" ;; No Gnus
682   :type '(repeat regexp)
683   :group 'gnus-article-various)
684
685 (make-obsolete-variable 'gnus-article-hide-pgp-hook
686                         "This variable is obsolete in Gnus 5.10.")
687
688 (defcustom gnus-article-button-face 'bold
689   "Face used for highlighting buttons in the article buffer.
690
691 An article button is a piece of text that you can activate by pressing
692 `RET' or `mouse-2' above it."
693   :type 'face
694   :group 'gnus-article-buttons)
695
696 (defcustom gnus-article-mouse-face 'highlight
697   "Face used for mouse highlighting in the article buffer.
698
699 Article buttons will be displayed in this face when the cursor is
700 above them."
701   :type 'face
702   :group 'gnus-article-buttons)
703
704 (defcustom gnus-signature-face 'gnus-signature
705   "Face used for highlighting a signature in the article buffer.
706 Obsolete; use the face `gnus-signature' for customizations instead."
707   :type 'face
708   :group 'gnus-article-highlight
709   :group 'gnus-article-signature)
710
711 (defface gnus-signature
712   '((t
713      (:italic t)))
714   "Face used for highlighting a signature in the article buffer."
715   :group 'gnus-article-highlight
716   :group 'gnus-article-signature)
717 ;; backward-compatibility alias
718 (put 'gnus-signature-face 'face-alias 'gnus-signature)
719
720 (defface gnus-header-from
721   '((((class color)
722       (background dark))
723      (:foreground "spring green"))
724     (((class color)
725       (background light))
726      (:foreground "red3"))
727     (t
728      (:italic t)))
729   "Face used for displaying from headers."
730   :group 'gnus-article-headers
731   :group 'gnus-article-highlight)
732 ;; backward-compatibility alias
733 (put 'gnus-header-from-face 'face-alias 'gnus-header-from)
734
735 (defface gnus-header-subject
736   '((((class color)
737       (background dark))
738      (:foreground "SeaGreen3"))
739     (((class color)
740       (background light))
741      (:foreground "red4"))
742     (t
743      (:bold t :italic t)))
744   "Face used for displaying subject headers."
745   :group 'gnus-article-headers
746   :group 'gnus-article-highlight)
747 ;; backward-compatibility alias
748 (put 'gnus-header-subject-face 'face-alias 'gnus-header-subject)
749
750 (defface gnus-header-newsgroups
751   '((((class color)
752       (background dark))
753      (:foreground "yellow" :italic t))
754     (((class color)
755       (background light))
756      (:foreground "MidnightBlue" :italic t))
757     (t
758      (:italic t)))
759   "Face used for displaying newsgroups headers.
760 In the default setup this face is only used for crossposted
761 articles."
762   :group 'gnus-article-headers
763   :group 'gnus-article-highlight)
764 ;; backward-compatibility alias
765 (put 'gnus-header-newsgroups-face 'face-alias 'gnus-header-newsgroups)
766
767 (defface gnus-header-name
768   '((((class color)
769       (background dark))
770      (:foreground "SeaGreen"))
771     (((class color)
772       (background light))
773      (:foreground "maroon"))
774     (t
775      (:bold t)))
776   "Face used for displaying header names."
777   :group 'gnus-article-headers
778   :group 'gnus-article-highlight)
779 ;; backward-compatibility alias
780 (put 'gnus-header-name-face 'face-alias 'gnus-header-name)
781
782 (defface gnus-header-content
783   '((((class color)
784       (background dark))
785      (:foreground "forest green" :italic t))
786     (((class color)
787       (background light))
788      (:foreground "indianred4" :italic t))
789     (t
790      (:italic t)))
791   "Face used for displaying header content."
792   :group 'gnus-article-headers
793   :group 'gnus-article-highlight)
794 ;; backward-compatibility alias
795 (put 'gnus-header-content-face 'face-alias 'gnus-header-content)
796
797 (defcustom gnus-header-face-alist
798   '(("From" nil gnus-header-from)
799     ("Subject" nil gnus-header-subject)
800     ("Newsgroups:.*," nil gnus-header-newsgroups)
801     ("" gnus-header-name gnus-header-content))
802   "*Controls highlighting of article headers.
803
804 An alist of the form (HEADER NAME CONTENT).
805
806 HEADER is a regular expression which should match the name of a
807 header and NAME and CONTENT are either face names or nil.
808
809 The name of each header field will be displayed using the face
810 specified by the first element in the list where HEADER matches
811 the header name and NAME is non-nil.  Similarly, the content will
812 be displayed by the first non-nil matching CONTENT face."
813   :group 'gnus-article-headers
814   :group 'gnus-article-highlight
815   :type '(repeat (list (regexp :tag "Header")
816                        (choice :tag "Name"
817                                (item :tag "skip" nil)
818                                (face :value default))
819                        (choice :tag "Content"
820                                (item :tag "skip" nil)
821                                (face :value default)))))
822
823 (defcustom gnus-article-decode-hook
824   '(article-decode-group-name article-decode-idna-rhs)
825   "*Hook run to decode charsets in articles."
826   :group 'gnus-article-headers
827   :type 'hook)
828
829 (defcustom gnus-display-mime-function 'gnus-display-mime
830   "Function to display MIME articles."
831   :group 'gnus-article-mime
832   :type 'function)
833
834 (defvar gnus-decode-header-function 'mail-decode-encoded-word-region
835   "Function used to decode headers.")
836
837 (defvar gnus-article-dumbquotes-map
838   '(("\200" "EUR")
839     ("\202" ",")
840     ("\203" "f")
841     ("\204" ",,")
842     ("\205" "...")
843     ("\213" "<")
844     ("\214" "OE")
845     ("\221" "`")
846     ("\222" "'")
847     ("\223" "``")
848     ("\224" "\"")
849     ("\225" "*")
850     ("\226" "-")
851     ("\227" "--")
852     ("\230" "~")
853     ("\231" "(TM)")
854     ("\233" ">")
855     ("\234" "oe")
856     ("\264" "'"))
857   "Table for MS-to-Latin1 translation.")
858
859 (defcustom gnus-ignored-mime-types nil
860   "List of MIME types that should be ignored by Gnus."
861   :version "21.1"
862   :group 'gnus-article-mime
863   :type '(repeat regexp))
864
865 (defcustom gnus-unbuttonized-mime-types '(".*/.*")
866   "List of MIME types that should not be given buttons when rendered inline.
867 See also `gnus-buttonized-mime-types' which may override this variable.
868 This variable is only used when `gnus-inhibit-mime-unbuttonizing' is nil."
869   :version "21.1"
870   :group 'gnus-article-mime
871   :type '(repeat regexp))
872
873 (defcustom gnus-buttonized-mime-types nil
874   "List of MIME types that should be given buttons when rendered inline.
875 If set, this variable overrides `gnus-unbuttonized-mime-types'.
876 To see e.g. security buttons you could set this to
877 `(\"multipart/signed\")'.
878 This variable is only used when `gnus-inhibit-mime-unbuttonizing' is nil."
879   :version "22.1"
880   :group 'gnus-article-mime
881   :type '(repeat regexp))
882
883 (defcustom gnus-inhibit-mime-unbuttonizing nil
884   "If non-nil, all MIME parts get buttons.
885 When nil (the default value), then some MIME parts do not get buttons,
886 as described by the variables `gnus-buttonized-mime-types' and
887 `gnus-unbuttonized-mime-types'."
888   :version "22.1"
889   :group 'gnus-article-mime
890   :type 'boolean)
891
892 (defcustom gnus-body-boundary-delimiter "_"
893   "String used to delimit header and body.
894 This variable is used by `gnus-article-treat-body-boundary' which can
895 be controlled by `gnus-treat-body-boundary'."
896   :version "22.1"
897   :group 'gnus-article-various
898   :type '(choice (item :tag "None" :value nil)
899                  string))
900
901 (defcustom gnus-picon-databases '("/usr/lib/picon" "/usr/local/faces"
902                                   "/usr/share/picons")
903   "Defines the location of the faces database.
904 For information on obtaining this database of pretty pictures, please
905 see http://www.cs.indiana.edu/picons/ftp/index.html"
906   :version "22.1"
907   :type '(repeat directory)
908   :link '(url-link :tag "download"
909                    "http://www.cs.indiana.edu/picons/ftp/index.html")
910   :link '(custom-manual "(gnus)Picons")
911   :group 'gnus-picon)
912
913 (defun gnus-picons-installed-p ()
914   "Say whether picons are installed on your machine."
915   (let ((installed nil))
916     (dolist (database gnus-picon-databases)
917       (when (file-exists-p database)
918         (setq installed t)))
919     installed))
920
921 (defcustom gnus-article-mime-part-function nil
922   "Function called with a MIME handle as the argument.
923 This is meant for people who want to do something automatic based
924 on parts -- for instance, adding Vcard info to a database."
925   :group 'gnus-article-mime
926   :type '(choice (const nil)
927                  function))
928
929 (defcustom gnus-mime-multipart-functions nil
930   "An alist of MIME types to functions to display them."
931   :version "21.1"
932   :group 'gnus-article-mime
933   :type '(repeat (cons :format "%v" (string :tag "MIME type") function)))
934
935 (defcustom gnus-article-date-lapsed-new-header nil
936   "Whether the X-Sent and Date headers can coexist.
937 When using `gnus-treat-date-lapsed', the \"X-Sent:\" header will
938 either replace the old \"Date:\" header (if this variable is nil), or
939 be added below it (otherwise)."
940   :version "21.1"
941   :group 'gnus-article-headers
942   :type 'boolean)
943
944 (defcustom gnus-article-mime-match-handle-function 'undisplayed-alternative
945   "Function called with a MIME handle as the argument.
946 This is meant for people who want to view first matched part.
947 For `undisplayed-alternative' (default), the first undisplayed
948 part or alternative part is used.  For `undisplayed', the first
949 undisplayed part is used.  For a function, the first part which
950 the function return t is used.  For nil, the first part is
951 used."
952   :version "21.1"
953   :group 'gnus-article-mime
954   :type '(choice
955           (item :tag "first" :value nil)
956           (item :tag "undisplayed" :value undisplayed)
957           (item :tag "undisplayed or alternative"
958                 :value undisplayed-alternative)
959           (function)))
960
961 (defcustom gnus-mime-action-alist
962   '(("save to file" . gnus-mime-save-part)
963     ("save and strip" . gnus-mime-save-part-and-strip)
964     ("delete part" . gnus-mime-delete-part)
965     ("display as text" . gnus-mime-inline-part)
966     ("view the part" . gnus-mime-view-part)
967     ("pipe to command" . gnus-mime-pipe-part)
968     ("toggle display" . gnus-article-press-button)
969     ("toggle display" . gnus-article-view-part-as-charset)
970     ("view as type" . gnus-mime-view-part-as-type)
971     ("view internally" . gnus-mime-view-part-internally)
972     ("view externally" . gnus-mime-view-part-externally))
973   "An alist of actions that run on the MIME attachment."
974   :group 'gnus-article-mime
975   :type '(repeat (cons (string :tag "name")
976                        (function))))
977
978 (defcustom gnus-auto-select-part 1
979   "Advance to next MIME part when deleting or stripping parts.
980
981 When 0, point will be placed on the same part as before.  When
982 positive (negative), move point forward (backwards) this many
983 parts.  When nil, redisplay article."
984   :version "23.0" ;; No Gnus
985   :group 'gnus-article-mime
986   :type '(choice (const nil :tag "Redisplay article.")
987                  (const 1 :tag "Next part.")
988                  (const 0 :tag "Current part.")
989                  integer))
990
991 ;;;
992 ;;; The treatment variables
993 ;;;
994
995 (defvar gnus-part-display-hook nil
996   "Hook called on parts that are to receive treatment.")
997
998 (defvar gnus-article-treat-custom
999   '(choice (const :tag "Off" nil)
1000            (const :tag "On" t)
1001            (const :tag "Header" head)
1002            (const :tag "Last" last)
1003            (const :tag "Mime" mime)
1004            (integer :tag "Less")
1005            (repeat :tag "Groups" regexp)
1006            (sexp :tag "Predicate")))
1007
1008 (defvar gnus-article-treat-head-custom
1009   '(choice (const :tag "Off" nil)
1010            (const :tag "Header" head)))
1011
1012 (defvar gnus-article-treat-types '("text/plain")
1013   "Parts to treat.")
1014
1015 (defvar gnus-inhibit-treatment nil
1016   "Whether to inhibit treatment.")
1017
1018 (defcustom gnus-treat-highlight-signature '(or t (typep "text/x-vcard"))
1019   "Highlight the signature.
1020 Valid values are nil, t, `head', `last', an integer or a predicate.
1021 See Info node `(gnus)Customizing Articles'."
1022   :group 'gnus-article-treat
1023   :link '(custom-manual "(gnus)Customizing Articles")
1024   :type gnus-article-treat-custom)
1025 (put 'gnus-treat-highlight-signature 'highlight t)
1026
1027 (defcustom gnus-treat-buttonize 100000
1028   "Add buttons.
1029 Valid values are nil, t, `head', `last', an integer or a predicate.
1030 See Info node `(gnus)Customizing Articles'."
1031   :group 'gnus-article-treat
1032   :link '(custom-manual "(gnus)Customizing Articles")
1033   :type gnus-article-treat-custom)
1034 (put 'gnus-treat-buttonize 'highlight t)
1035
1036 (defcustom gnus-treat-buttonize-head 'head
1037   "Add buttons to the head.
1038 Valid values are nil, t, `head', `last', an integer or a predicate.
1039 See Info node `(gnus)Customizing Articles' for details."
1040   :group 'gnus-article-treat
1041   :link '(custom-manual "(gnus)Customizing Articles")
1042   :type gnus-article-treat-head-custom)
1043 (put 'gnus-treat-buttonize-head 'highlight t)
1044
1045 (defcustom gnus-treat-emphasize
1046   (and (or window-system
1047            (featurep 'xemacs))
1048        50000)
1049   "Emphasize text.
1050 Valid values are nil, t, `head', `last', an integer or a predicate.
1051 See Info node `(gnus)Customizing Articles' for details."
1052   :group 'gnus-article-treat
1053   :link '(custom-manual "(gnus)Customizing Articles")
1054   :type gnus-article-treat-custom)
1055 (put 'gnus-treat-emphasize 'highlight t)
1056
1057 (defcustom gnus-treat-strip-cr nil
1058   "Remove carriage returns.
1059 Valid values are nil, t, `head', `last', an integer or a predicate.
1060 See Info node `(gnus)Customizing Articles' for details."
1061   :version "22.1"
1062   :group 'gnus-article-treat
1063   :link '(custom-manual "(gnus)Customizing Articles")
1064   :type gnus-article-treat-custom)
1065
1066 (defcustom gnus-treat-unsplit-urls nil
1067   "Remove newlines from within URLs.
1068 Valid values are nil, t, `head', `last', an integer or a predicate.
1069 See Info node `(gnus)Customizing Articles' for details."
1070   :version "22.1"
1071   :group 'gnus-article-treat
1072   :link '(custom-manual "(gnus)Customizing Articles")
1073   :type gnus-article-treat-custom)
1074
1075 (defcustom gnus-treat-leading-whitespace nil
1076   "Remove leading whitespace in headers.
1077 Valid values are nil, t, `head', `last', an integer or a predicate.
1078 See Info node `(gnus)Customizing Articles' for details."
1079   :version "22.1"
1080   :group 'gnus-article-treat
1081   :link '(custom-manual "(gnus)Customizing Articles")
1082   :type gnus-article-treat-custom)
1083
1084 (defcustom gnus-treat-hide-headers 'head
1085   "Hide headers.
1086 Valid values are nil, t, `head', `last', an integer or a predicate.
1087 See Info node `(gnus)Customizing Articles' for details."
1088   :group 'gnus-article-treat
1089   :link '(custom-manual "(gnus)Customizing Articles")
1090   :type gnus-article-treat-head-custom)
1091
1092 (defcustom gnus-treat-hide-boring-headers nil
1093   "Hide boring headers.
1094 Valid values are nil, t, `head', `last', an integer or a predicate.
1095 See Info node `(gnus)Customizing Articles' for details."
1096   :group 'gnus-article-treat
1097   :link '(custom-manual "(gnus)Customizing Articles")
1098   :type gnus-article-treat-head-custom)
1099
1100 (defcustom gnus-treat-hide-signature nil
1101   "Hide the signature.
1102 Valid values are nil, t, `head', `last', an integer or a predicate.
1103 See Info node `(gnus)Customizing Articles' for details."
1104   :group 'gnus-article-treat
1105   :link '(custom-manual "(gnus)Customizing Articles")
1106   :type gnus-article-treat-custom)
1107
1108 (defcustom gnus-treat-fill-article nil
1109   "Fill the article.
1110 Valid values are nil, t, `head', `last', an integer or a predicate.
1111 See Info node `(gnus)Customizing Articles' for details."
1112   :group 'gnus-article-treat
1113   :link '(custom-manual "(gnus)Customizing Articles")
1114   :type gnus-article-treat-custom)
1115
1116 (defcustom gnus-treat-hide-citation nil
1117   "Hide cited text.
1118 Valid values are nil, t, `head', `last', an integer or a predicate.
1119 See Info node `(gnus)Customizing Articles' for details."
1120   :group 'gnus-article-treat
1121   :link '(custom-manual "(gnus)Customizing Articles")
1122   :type gnus-article-treat-custom)
1123
1124 (defcustom gnus-treat-hide-citation-maybe nil
1125   "Hide cited text.
1126 Valid values are nil, t, `head', `last', an integer or a predicate.
1127 See Info node `(gnus)Customizing Articles' for details."
1128   :group 'gnus-article-treat
1129   :link '(custom-manual "(gnus)Customizing Articles")
1130   :type gnus-article-treat-custom)
1131
1132 (defcustom gnus-treat-strip-list-identifiers 'head
1133   "Strip list identifiers from `gnus-list-identifiers`.
1134 Valid values are nil, t, `head', `last', an integer or a predicate.
1135 See Info node `(gnus)Customizing Articles' for details."
1136   :version "21.1"
1137   :group 'gnus-article-treat
1138   :link '(custom-manual "(gnus)Customizing Articles")
1139   :type gnus-article-treat-custom)
1140
1141 (make-obsolete-variable 'gnus-treat-strip-pgp
1142                         "This option is obsolete in Gnus 5.10.")
1143
1144 (defcustom gnus-treat-strip-pem nil
1145   "Strip PEM signatures.
1146 Valid values are nil, t, `head', `last', an integer or a predicate.
1147 See Info node `(gnus)Customizing Articles' for details."
1148   :group 'gnus-article-treat
1149   :link '(custom-manual "(gnus)Customizing Articles")
1150   :type gnus-article-treat-custom)
1151
1152 (defcustom gnus-treat-strip-banner t
1153   "Strip banners from articles.
1154 The banner to be stripped is specified in the `banner' group parameter.
1155 Valid values are nil, t, `head', `last', an integer or a predicate.
1156 See Info node `(gnus)Customizing Articles' for details."
1157   :group 'gnus-article-treat
1158   :link '(custom-manual "(gnus)Customizing Articles")
1159   :type gnus-article-treat-custom)
1160
1161 (defcustom gnus-treat-highlight-headers 'head
1162   "Highlight the headers.
1163 Valid values are nil, t, `head', `last', an integer or a predicate.
1164 See Info node `(gnus)Customizing Articles' for details."
1165   :group 'gnus-article-treat
1166   :link '(custom-manual "(gnus)Customizing Articles")
1167   :type gnus-article-treat-head-custom)
1168 (put 'gnus-treat-highlight-headers 'highlight t)
1169
1170 (defcustom gnus-treat-highlight-citation t
1171   "Highlight cited text.
1172 Valid values are nil, t, `head', `last', an integer or a predicate.
1173 See Info node `(gnus)Customizing Articles' for details."
1174   :group 'gnus-article-treat
1175   :link '(custom-manual "(gnus)Customizing Articles")
1176   :type gnus-article-treat-custom)
1177 (put 'gnus-treat-highlight-citation 'highlight t)
1178
1179 (defcustom gnus-treat-date-ut nil
1180   "Display the Date in UT (GMT).
1181 Valid values are nil, t, `head', `last', an integer or a predicate.
1182 See Info node `(gnus)Customizing Articles' for details."
1183   :group 'gnus-article-treat
1184   :link '(custom-manual "(gnus)Customizing Articles")
1185   :type gnus-article-treat-head-custom)
1186
1187 (defcustom gnus-treat-date-local nil
1188   "Display the Date in the local timezone.
1189 Valid values are nil, t, `head', `last', an integer or a predicate.
1190 See Info node `(gnus)Customizing Articles' for details."
1191   :group 'gnus-article-treat
1192   :link '(custom-manual "(gnus)Customizing Articles")
1193   :type gnus-article-treat-head-custom)
1194
1195 (defcustom gnus-treat-date-english nil
1196   "Display the Date in a format that can be read aloud in English.
1197 Valid values are nil, t, `head', `last', an integer or a predicate.
1198 See Info node `(gnus)Customizing Articles' for details."
1199   :version "22.1"
1200   :group 'gnus-article-treat
1201   :link '(custom-manual "(gnus)Customizing Articles")
1202   :type gnus-article-treat-head-custom)
1203
1204 (defcustom gnus-treat-date-lapsed nil
1205   "Display the Date header in a way that says how much time has elapsed.
1206 Valid values are nil, t, `head', `last', an integer or a predicate.
1207 See Info node `(gnus)Customizing Articles' for details."
1208   :group 'gnus-article-treat
1209   :link '(custom-manual "(gnus)Customizing Articles")
1210   :type gnus-article-treat-head-custom)
1211
1212 (defcustom gnus-treat-date-original nil
1213   "Display the date in the original timezone.
1214 Valid values are nil, t, `head', `last', an integer or a predicate.
1215 See Info node `(gnus)Customizing Articles' for details."
1216   :group 'gnus-article-treat
1217   :link '(custom-manual "(gnus)Customizing Articles")
1218   :type gnus-article-treat-head-custom)
1219
1220 (defcustom gnus-treat-date-iso8601 nil
1221   "Display the date in the ISO8601 format.
1222 Valid values are nil, t, `head', `last', an integer or a predicate.
1223 See Info node `(gnus)Customizing Articles' for details."
1224   :version "21.1"
1225   :group 'gnus-article-treat
1226   :link '(custom-manual "(gnus)Customizing Articles")
1227   :type gnus-article-treat-head-custom)
1228
1229 (defcustom gnus-treat-date-user-defined nil
1230   "Display the date in a user-defined format.
1231 The format is defined by the `gnus-article-time-format' variable.
1232 Valid values are nil, t, `head', `last', an integer or a predicate.
1233 See Info node `(gnus)Customizing Articles' for details."
1234   :group 'gnus-article-treat
1235   :link '(custom-manual "(gnus)Customizing Articles")
1236   :type gnus-article-treat-head-custom)
1237
1238 (defcustom gnus-treat-strip-headers-in-body t
1239   "Strip the X-No-Archive header line from the beginning of the body.
1240 Valid values are nil, t, `head', `last', an integer or a predicate.
1241 See Info node `(gnus)Customizing Articles' for details."
1242   :version "21.1"
1243   :group 'gnus-article-treat
1244   :link '(custom-manual "(gnus)Customizing Articles")
1245   :type gnus-article-treat-custom)
1246
1247 (defcustom gnus-treat-strip-trailing-blank-lines nil
1248   "Strip trailing blank lines.
1249 Valid values are nil, t, `head', `last', an integer or a predicate.
1250 See Info node `(gnus)Customizing Articles' for details."
1251   :group 'gnus-article-treat
1252   :link '(custom-manual "(gnus)Customizing Articles")
1253   :type gnus-article-treat-custom)
1254
1255 (defcustom gnus-treat-strip-leading-blank-lines nil
1256   "Strip leading blank lines.
1257 Valid values are nil, t, `head', `last', an integer or a predicate.
1258 See Info node `(gnus)Customizing Articles' for details."
1259   :group 'gnus-article-treat
1260   :link '(custom-manual "(gnus)Customizing Articles")
1261   :type gnus-article-treat-custom)
1262
1263 (defcustom gnus-treat-strip-multiple-blank-lines nil
1264   "Strip multiple blank lines.
1265 Valid values are nil, t, `head', `last', an integer or a predicate.
1266 See Info node `(gnus)Customizing Articles' for details."
1267   :group 'gnus-article-treat
1268   :link '(custom-manual "(gnus)Customizing Articles")
1269   :type gnus-article-treat-custom)
1270
1271 (defcustom gnus-treat-unfold-headers 'head
1272   "Unfold folded header lines.
1273 Valid values are nil, t, `head', `last', an integer or a predicate.
1274 See Info node `(gnus)Customizing Articles' for details."
1275   :version "22.1"
1276   :group 'gnus-article-treat
1277   :link '(custom-manual "(gnus)Customizing Articles")
1278   :type gnus-article-treat-custom)
1279
1280 (defcustom gnus-treat-fold-headers nil
1281   "Fold headers.
1282 Valid values are nil, t, `head', `last', an integer or a predicate.
1283 See Info node `(gnus)Customizing Articles' for details."
1284   :version "22.1"
1285   :group 'gnus-article-treat
1286   :link '(custom-manual "(gnus)Customizing Articles")
1287   :type gnus-article-treat-custom)
1288
1289 (defcustom gnus-treat-fold-newsgroups 'head
1290   "Fold the Newsgroups and Followup-To headers.
1291 Valid values are nil, t, `head', `last', an integer or a predicate.
1292 See Info node `(gnus)Customizing Articles' for details."
1293   :version "22.1"
1294   :group 'gnus-article-treat
1295   :link '(custom-manual "(gnus)Customizing Articles")
1296   :type gnus-article-treat-custom)
1297
1298 (defcustom gnus-treat-overstrike t
1299   "Treat overstrike highlighting.
1300 Valid values are nil, t, `head', `last', an integer or a predicate.
1301 See Info node `(gnus)Customizing Articles' for details."
1302   :group 'gnus-article-treat
1303   :link '(custom-manual "(gnus)Customizing Articles")
1304   :type gnus-article-treat-custom)
1305 (put 'gnus-treat-overstrike 'highlight t)
1306
1307 (defcustom gnus-treat-ansi-sequences (if (locate-library "ansi-color") t)
1308   "Treat ANSI SGR control sequences.
1309 Valid values are nil, t, `head', `last', an integer or a predicate.
1310 See Info node `(gnus)Customizing Articles' for details."
1311   :group 'gnus-article-treat
1312   :link '(custom-manual "(gnus)Customizing Articles")
1313   :type gnus-article-treat-custom)
1314
1315 (make-obsolete-variable 'gnus-treat-display-xface
1316                         'gnus-treat-display-x-face)
1317
1318 (defcustom gnus-treat-display-x-face
1319   (and (not noninteractive)
1320        (or (and (fboundp 'image-type-available-p)
1321                 (image-type-available-p 'xbm)
1322                 (string-match "^0x" (shell-command-to-string "uncompface"))
1323                 (executable-find "icontopbm"))
1324            (and (featurep 'xemacs)
1325                 (featurep 'xface)))
1326        'head)
1327   "Display X-Face headers.
1328 Valid values are nil, t, `head', `last', an integer or a predicate.
1329 See Info node `(gnus)Customizing Articles' and Info node
1330 `(gnus)X-Face' for details."
1331   :group 'gnus-article-treat
1332   :version "21.1"
1333   :link '(custom-manual "(gnus)Customizing Articles")
1334   :link '(custom-manual "(gnus)X-Face")
1335   :type gnus-article-treat-head-custom
1336   :set (lambda (symbol value)
1337          (set-default
1338           symbol
1339           (cond ((or (boundp symbol) (get symbol 'saved-value))
1340                  value)
1341                 ((boundp 'gnus-treat-display-xface)
1342                  (message "\
1343 ** gnus-treat-display-xface is an obsolete variable;\
1344  use gnus-treat-display-x-face instead")
1345                  (default-value 'gnus-treat-display-xface))
1346                 ((get 'gnus-treat-display-xface 'saved-value)
1347                  (message "\
1348 ** gnus-treat-display-xface is an obsolete variable;\
1349  use gnus-treat-display-x-face instead")
1350                  (eval (car (get 'gnus-treat-display-xface 'saved-value))))
1351                 (t
1352                  value)))))
1353 (put 'gnus-treat-display-x-face 'highlight t)
1354
1355 (defcustom gnus-article-should-use-smiley-mule
1356   (not (or (featurep 'xemacs)
1357            (gnus-image-type-available-p 'xpm)
1358            (gnus-image-type-available-p 'pbm)))
1359   "Non-nil means use `smiley-mule' to show smileys rather than `smiley'.
1360 `smiley-mule' is boundled in BITMAP-MULE package.  You can set it to t
1361 even if your Emacs supports images.  It has no effect on XEmacs."
1362   :group 'gnus-article-various
1363   :type 'boolean
1364   :get (lambda (symbol)
1365          (and (not noninteractive)
1366               (default-value symbol)
1367               (not (featurep 'xemacs))
1368               (module-installed-p 'smiley-mule)
1369               t))
1370   :set (lambda (symbol value)
1371          (set-default symbol (and (not noninteractive)
1372                                   value
1373                                   (not (featurep 'xemacs))
1374                                   (module-installed-p 'smiley-mule)
1375                                   t))))
1376
1377 (defvar gnus-article-smiley-mule-loaded-p nil
1378   "Internal variable used to say whether `smiley-mule' is loaded (whether
1379 smiley functions are not overridden by `smiley').")
1380
1381 (defcustom gnus-treat-display-face
1382   (and (not noninteractive)
1383        (or (and (fboundp 'image-type-available-p)
1384                 (image-type-available-p 'png))
1385            (and (featurep 'xemacs)
1386                 (featurep 'png)))
1387        'head)
1388   "Display Face headers.
1389 Valid values are nil, t, `head', `last', an integer or a predicate.
1390 See Info node `(gnus)Customizing Articles' and Info node
1391 `(gnus)X-Face' for details."
1392   :group 'gnus-article-treat
1393   :version "22.1"
1394   :link '(custom-manual "(gnus)Customizing Articles")
1395   :link '(custom-manual "(gnus)X-Face")
1396   :type gnus-article-treat-head-custom)
1397 (put 'gnus-treat-display-face 'highlight t)
1398
1399 (defcustom gnus-treat-display-smileys
1400   (if (and (not noninteractive)
1401            (or (and (featurep 'xemacs)
1402                     (featurep 'xpm))
1403                (gnus-image-type-available-p 'xpm)
1404                (gnus-image-type-available-p 'pbm)
1405                (and (not (featurep 'xemacs))
1406                     window-system
1407                     (module-installed-p 'smiley-mule))))
1408       t
1409     nil)
1410   "Display smileys.
1411 Valid values are nil, t, `head', `last', an integer or a predicate.
1412 See Info node `(gnus)Customizing Articles' and Info node
1413 `(gnus)Smileys' for details."
1414   :group 'gnus-article-treat
1415   :version "21.1"
1416   :link '(custom-manual "(gnus)Customizing Articles")
1417   :link '(custom-manual "(gnus)Smileys")
1418   :type gnus-article-treat-custom)
1419 (put 'gnus-treat-display-smileys 'highlight t)
1420
1421 (defcustom gnus-treat-from-picon
1422   (if (and (gnus-image-type-available-p 'xpm)
1423            (gnus-picons-installed-p))
1424       'head nil)
1425   "Display picons in the From header.
1426 Valid values are nil, t, `head', `last', an integer or a predicate.
1427 See Info node `(gnus)Customizing Articles' and Info node
1428 `(gnus)Picons' for details."
1429   :version "22.1"
1430   :group 'gnus-article-treat
1431   :group 'gnus-picon
1432   :link '(custom-manual "(gnus)Customizing Articles")
1433   :link '(custom-manual "(gnus)Picons")
1434   :type gnus-article-treat-head-custom)
1435 (put 'gnus-treat-from-picon 'highlight t)
1436
1437 (defcustom gnus-treat-mail-picon
1438   (if (and (gnus-image-type-available-p 'xpm)
1439            (gnus-picons-installed-p))
1440       'head nil)
1441   "Display picons in To and Cc headers.
1442 Valid values are nil, t, `head', `last', an integer or a predicate.
1443 See Info node `(gnus)Customizing Articles' and Info node
1444 `(gnus)Picons' for details."
1445   :version "22.1"
1446   :group 'gnus-article-treat
1447   :group 'gnus-picon
1448   :link '(custom-manual "(gnus)Customizing Articles")
1449   :link '(custom-manual "(gnus)Picons")
1450   :type gnus-article-treat-head-custom)
1451 (put 'gnus-treat-mail-picon 'highlight t)
1452
1453 (defcustom gnus-treat-newsgroups-picon
1454   (if (and (gnus-image-type-available-p 'xpm)
1455            (gnus-picons-installed-p))
1456       'head nil)
1457   "Display picons in the Newsgroups and Followup-To headers.
1458 Valid values are nil, t, `head', `last', an integer or a predicate.
1459 See Info node `(gnus)Customizing Articles' and Info node
1460 `(gnus)Picons' for details."
1461   :version "22.1"
1462   :group 'gnus-article-treat
1463   :group 'gnus-picon
1464   :link '(custom-manual "(gnus)Customizing Articles")
1465   :link '(custom-manual "(gnus)Picons")
1466   :type gnus-article-treat-head-custom)
1467 (put 'gnus-treat-newsgroups-picon 'highlight t)
1468
1469 (defcustom gnus-treat-body-boundary
1470   (if (and (eq window-system 'x)
1471            (or gnus-treat-newsgroups-picon
1472                gnus-treat-mail-picon
1473                gnus-treat-from-picon))
1474       'head nil)
1475   "Draw a boundary at the end of the headers.
1476 Valid values are nil and `head'.
1477 See Info node `(gnus)Customizing Articles' for details."
1478   :version "22.1"
1479   :group 'gnus-article-treat
1480   :link '(custom-manual "(gnus)Customizing Articles")
1481   :type gnus-article-treat-head-custom)
1482
1483 (defcustom gnus-treat-capitalize-sentences nil
1484   "Capitalize sentence-starting words.
1485 Valid values are nil, t, `head', `last', an integer or a predicate.
1486 See Info node `(gnus)Customizing Articles' for details."
1487   :version "21.1"
1488   :group 'gnus-article-treat
1489   :link '(custom-manual "(gnus)Customizing Articles")
1490   :type gnus-article-treat-custom)
1491
1492 (defcustom gnus-treat-wash-html nil
1493   "Format as HTML.
1494 Valid values are nil, t, `head', `last', an integer or a predicate.
1495 See Info node `(gnus)Customizing Articles' for details."
1496   :version "22.1"
1497   :group 'gnus-article-treat
1498   :link '(custom-manual "(gnus)Customizing Articles")
1499   :type gnus-article-treat-custom)
1500
1501 (defcustom gnus-treat-fill-long-lines nil
1502   "Fill long lines.
1503 Valid values are nil, t, `head', `last', an integer or a predicate.
1504 See Info node `(gnus)Customizing Articles' for details."
1505   :group 'gnus-article-treat
1506   :link '(custom-manual "(gnus)Customizing Articles")
1507   :type gnus-article-treat-custom)
1508
1509 (defcustom gnus-treat-play-sounds nil
1510   "Play sounds.
1511 Valid values are nil, t, `head', `last', an integer or a predicate.
1512 See Info node `(gnus)Customizing Articles' for details."
1513   :version "21.1"
1514   :group 'gnus-article-treat
1515   :link '(custom-manual "(gnus)Customizing Articles")
1516   :type gnus-article-treat-custom)
1517
1518 (defcustom gnus-treat-decode-article-as-default-mime-charset nil
1519   "Decode an article as `default-mime-charset'.  For instance, if you want to
1520 attempt to decode an article even if the value of `gnus-show-mime' is nil,
1521 you could set this variable to something like: nil for don't decode, t for
1522 decode the body, '(or header t) for the whole article, etc."
1523   :group 'gnus-article-treat
1524   :type '(radio (const :tag "Off" nil)
1525                 (const :tag "Decode body" t)
1526                 (const :tag "Decode all" (or head t))))
1527
1528 (defcustom gnus-treat-translate nil
1529   "Translate articles from one language to another.
1530 Valid values are nil, t, `head', `last', an integer or a predicate.
1531 See Info node `(gnus)Customizing Articles' for details."
1532   :version "21.1"
1533   :group 'gnus-article-treat
1534   :link '(custom-manual "(gnus)Customizing Articles")
1535   :type gnus-article-treat-custom)
1536
1537 (defcustom gnus-treat-x-pgp-sig nil
1538   "Verify X-PGP-Sig.
1539 To automatically treat X-PGP-Sig, set it to head.
1540 Valid values are nil, t, `head', `last', an integer or a predicate.
1541 See Info node `(gnus)Customizing Articles' for details."
1542   :version "22.1"
1543   :group 'gnus-article-treat
1544   :group 'mime-security
1545   :type gnus-article-treat-custom)
1546
1547 (defcustom gnus-treat-monafy nil
1548   "Display body part with mona font.
1549 Valid values are nil, t, `head', `last', an integer or a predicate.
1550 See Info node `(gnus)Customizing Articles' for details."
1551   :group 'gnus-article-treat
1552   :group 'mime-security
1553   :link '(custom-manual "(gnus)Customizing Articles")
1554   :type gnus-article-treat-custom)
1555
1556 (defvar gnus-article-encrypt-protocol-alist
1557   '(("PGP" . mml2015-self-encrypt)))
1558
1559 ;; Set to nil if more than one protocol added to
1560 ;; gnus-article-encrypt-protocol-alist.
1561 (defcustom gnus-article-encrypt-protocol "PGP"
1562   "The protocol used for encrypt articles.
1563 It is a string, such as \"PGP\". If nil, ask user."
1564   :version "22.1"
1565   :type 'string
1566   :group 'mime-security)
1567
1568 (defvar gnus-article-wash-function nil
1569   "Function used for converting HTML into text.")
1570
1571 (defcustom gnus-use-idna (and (condition-case nil (require 'idna) (file-error))
1572                               (mm-coding-system-p 'utf-8)
1573                               (executable-find idna-program))
1574   "Whether IDNA decoding of headers is used when viewing messages.
1575 This requires GNU Libidn, and by default only enabled if it is found."
1576   :version "22.1"
1577   :group 'gnus-article-headers
1578   :type 'boolean)
1579
1580 (defcustom gnus-article-over-scroll nil
1581   "If non-nil, allow scrolling the article buffer even when there no more text."
1582   :version "22.1"
1583   :group 'gnus-article
1584   :type 'boolean)
1585
1586 ;;; Internal variables
1587
1588 (defvar gnus-english-month-names
1589   '("January" "February" "March" "April" "May" "June" "July" "August"
1590     "September" "October" "November" "December"))
1591
1592 (defvar gnus-button-regexp nil)
1593 (defvar gnus-button-marker-list nil)
1594 ;; Regexp matching any of the regexps from `gnus-button-alist'.
1595
1596 (defvar gnus-button-last nil)
1597 ;; The value of `gnus-button-alist' when `gnus-button-regexp' was build.
1598
1599 (defvar article-goto-body-goes-to-point-min-p nil)
1600 (defvar gnus-article-wash-types nil)
1601 (defvar gnus-article-emphasis-alist nil)
1602 (defvar gnus-article-image-alist nil)
1603
1604 (defvar gnus-article-mime-handle-alist-1 nil)
1605 (defvar gnus-treatment-function-alist
1606   '((gnus-treat-decode-article-as-default-mime-charset
1607      gnus-article-decode-article-as-default-mime-charset)
1608     (gnus-treat-x-pgp-sig gnus-article-verify-x-pgp-sig)
1609     (gnus-treat-monafy gnus-article-monafy)
1610     (gnus-treat-strip-banner gnus-article-strip-banner)
1611     (gnus-treat-strip-headers-in-body gnus-article-strip-headers-in-body)
1612     (gnus-treat-buttonize gnus-article-add-buttons)
1613     (gnus-treat-fill-article gnus-article-fill-cited-article)
1614     (gnus-treat-fill-long-lines gnus-article-fill-long-lines)
1615     (gnus-treat-strip-cr gnus-article-remove-cr)
1616     (gnus-treat-unsplit-urls gnus-article-unsplit-urls)
1617     (gnus-treat-date-ut gnus-article-date-ut)
1618     (gnus-treat-date-local gnus-article-date-local)
1619     (gnus-treat-date-english gnus-article-date-english)
1620     (gnus-treat-date-original gnus-article-date-original)
1621     (gnus-treat-date-user-defined gnus-article-date-user)
1622     (gnus-treat-date-iso8601 gnus-article-date-iso8601)
1623     (gnus-treat-date-lapsed gnus-article-date-lapsed)
1624     (gnus-treat-display-face gnus-article-display-face)
1625     (gnus-treat-hide-headers gnus-article-maybe-hide-headers)
1626     (gnus-treat-hide-boring-headers gnus-article-hide-boring-headers)
1627     (gnus-treat-hide-signature gnus-article-hide-signature)
1628     (gnus-treat-strip-list-identifiers gnus-article-hide-list-identifiers)
1629     (gnus-treat-leading-whitespace gnus-article-remove-leading-whitespace)
1630     (gnus-treat-strip-pem gnus-article-hide-pem)
1631     (gnus-treat-from-picon gnus-treat-from-picon)
1632     (gnus-treat-mail-picon gnus-treat-mail-picon)
1633     (gnus-treat-newsgroups-picon gnus-treat-newsgroups-picon)
1634     (gnus-treat-highlight-headers gnus-article-highlight-headers)
1635     (gnus-treat-highlight-signature gnus-article-highlight-signature)
1636     (gnus-treat-strip-trailing-blank-lines
1637      gnus-article-remove-trailing-blank-lines)
1638     (gnus-treat-strip-leading-blank-lines
1639      gnus-article-strip-leading-blank-lines)
1640     (gnus-treat-strip-multiple-blank-lines
1641      gnus-article-strip-multiple-blank-lines)
1642     (gnus-treat-overstrike gnus-article-treat-overstrike)
1643     (gnus-treat-ansi-sequences gnus-article-treat-ansi-sequences)
1644     (gnus-treat-unfold-headers gnus-article-treat-unfold-headers)
1645     (gnus-treat-fold-headers gnus-article-treat-fold-headers)
1646     ;; Displaying X-Face should be done after unfolding headers
1647     ;; to protect bitmap lines.
1648     (gnus-treat-display-x-face gnus-article-display-x-face)
1649     (gnus-treat-fold-newsgroups gnus-article-treat-fold-newsgroups)
1650     (gnus-treat-buttonize-head gnus-article-add-buttons-to-head)
1651     (gnus-treat-display-smileys gnus-treat-smiley)
1652     (gnus-treat-capitalize-sentences gnus-article-capitalize-sentences)
1653     (gnus-treat-wash-html gnus-article-wash-html)
1654     (gnus-treat-emphasize gnus-article-emphasize)
1655     (gnus-treat-hide-citation gnus-article-hide-citation)
1656     (gnus-treat-hide-citation-maybe gnus-article-hide-citation-maybe)
1657     (gnus-treat-highlight-citation gnus-article-highlight-citation)
1658     (gnus-treat-body-boundary gnus-article-treat-body-boundary)
1659     (gnus-treat-play-sounds gnus-earcon-display)))
1660
1661 (defvar gnus-article-mime-handle-alist nil)
1662 (defvar article-lapsed-timer nil)
1663 (defvar gnus-article-current-summary nil)
1664
1665 (defvar gnus-article-mode-syntax-table
1666   (let ((table (copy-syntax-table text-mode-syntax-table)))
1667     ;; This causes the citation match run O(2^n).
1668     ;; (modify-syntax-entry ?- "w" table)
1669     (modify-syntax-entry ?> ")<" table)
1670     (modify-syntax-entry ?< "(>" table)
1671     ;; make M-. in article buffers work for `foo' strings
1672     (modify-syntax-entry ?' " " table)
1673     (modify-syntax-entry ?` " " table)
1674     table)
1675   "Syntax table used in article mode buffers.
1676 Initialized from `text-mode-syntax-table.")
1677
1678 (defvar gnus-save-article-buffer nil)
1679
1680 (defvar gnus-article-mode-line-format-alist
1681   (nconc '((?w (gnus-article-wash-status) ?s)
1682            (?m (gnus-article-mime-part-status) ?s))
1683          gnus-summary-mode-line-format-alist))
1684
1685 (defvar gnus-number-of-articles-to-be-saved nil)
1686
1687 (defvar gnus-inhibit-hiding nil)
1688
1689 (defvar gnus-article-edit-mode nil)
1690
1691 ;;; Macros for dealing with the article buffer.
1692
1693 (defmacro gnus-with-article-headers (&rest forms)
1694   `(save-excursion
1695      (set-buffer gnus-article-buffer)
1696      (save-restriction
1697        (let ((inhibit-read-only t)
1698              (inhibit-point-motion-hooks t)
1699              (case-fold-search t))
1700          (article-narrow-to-head)
1701          ,@forms))))
1702
1703 (put 'gnus-with-article-headers 'lisp-indent-function 0)
1704 (put 'gnus-with-article-headers 'edebug-form-spec '(body))
1705
1706 (defmacro gnus-with-article-buffer (&rest forms)
1707   `(save-excursion
1708      (set-buffer gnus-article-buffer)
1709      (let ((inhibit-read-only t))
1710        ,@forms)))
1711
1712 (put 'gnus-with-article-buffer 'lisp-indent-function 0)
1713 (put 'gnus-with-article-buffer 'edebug-form-spec '(body))
1714
1715 (defun gnus-article-goto-header (header)
1716   "Go to HEADER, which is a regular expression."
1717   (re-search-forward (concat "^\\(" header "\\):") nil t))
1718
1719 (defsubst gnus-article-hide-text (b e props)
1720   "Set text PROPS on the B to E region, extending `intangible' 1 past B."
1721   (gnus-add-text-properties-when 'article-type nil b e props)
1722   (when (memq 'intangible props)
1723     (put-text-property
1724      (max (1- b) (point-min))
1725      b 'intangible (cddr (memq 'intangible props)))))
1726
1727 (defsubst gnus-article-unhide-text (b e)
1728   "Remove hidden text properties from region between B and E."
1729   (remove-text-properties b e gnus-hidden-properties)
1730   (when (memq 'intangible gnus-hidden-properties)
1731     (put-text-property (max (1- b) (point-min))
1732                        b 'intangible nil)))
1733
1734 (defun gnus-article-hide-text-type (b e type)
1735   "Hide text of TYPE between B and E."
1736   (gnus-add-wash-type type)
1737   (gnus-article-hide-text
1738    b e (cons 'article-type (cons type gnus-hidden-properties))))
1739
1740 (defun gnus-article-unhide-text-type (b e type)
1741   "Unhide text of TYPE between B and E."
1742   (gnus-delete-wash-type type)
1743   (remove-text-properties
1744    b e (cons 'article-type (cons type gnus-hidden-properties)))
1745   (when (memq 'intangible gnus-hidden-properties)
1746     (put-text-property (max (1- b) (point-min))
1747                        b 'intangible nil)))
1748
1749 (defun gnus-article-hide-text-of-type (type)
1750   "Hide text of TYPE in the current buffer."
1751   (save-excursion
1752     (let ((b (point-min))
1753           (e (point-max)))
1754       (while (setq b (text-property-any b e 'article-type type))
1755         (add-text-properties b (incf b) gnus-hidden-properties)))))
1756
1757 (defun gnus-article-delete-text-of-type (type)
1758   "Delete text of TYPE in the current buffer."
1759   (save-excursion
1760     (let ((b (point-min)))
1761       (while (setq b (text-property-any b (point-max) 'article-type type))
1762         (delete-region
1763          b (or (text-property-not-all b (point-max) 'article-type type)
1764                (point-max)))))))
1765
1766 (defun gnus-article-delete-invisible-text ()
1767   "Delete all invisible text in the current buffer."
1768   (save-excursion
1769     (let ((b (point-min)))
1770       (while (setq b (text-property-any b (point-max) 'invisible t))
1771         (delete-region
1772          b (or (text-property-not-all b (point-max) 'invisible t)
1773                (point-max)))))))
1774
1775 (defun gnus-article-text-type-exists-p (type)
1776   "Say whether any text of type TYPE exists in the buffer."
1777   (text-property-any (point-min) (point-max) 'article-type type))
1778
1779 (defsubst gnus-article-header-rank ()
1780   "Give the rank of the string HEADER as given by `gnus-sorted-header-list'."
1781   (let ((list gnus-sorted-header-list)
1782         (i 1))
1783     (while list
1784       (if (looking-at (car list))
1785           (setq list nil)
1786         (setq list (cdr list))
1787         (incf i)))
1788       i))
1789
1790 (defun article-hide-headers (&optional arg delete)
1791   "Hide unwanted headers and possibly sort them as well."
1792   (interactive (gnus-article-hidden-arg))
1793   ;; Lars said that this function might be inhibited.
1794   (if (gnus-article-check-hidden-text 'headers arg)
1795       (progn
1796         ;; Show boring headers as well.
1797         (gnus-article-show-hidden-text 'boring-headers)
1798         (when (eq 1 (point-min))
1799           (set-window-start (get-buffer-window (current-buffer)) 1)))
1800     (unless gnus-inhibit-hiding
1801       (let ((inhibit-read-only t)
1802             (case-fold-search t)
1803             (max (1+ (length gnus-sorted-header-list)))
1804             (inhibit-point-motion-hooks t)
1805             (cur (current-buffer))
1806             ignored visible beg)
1807         (save-excursion
1808           ;; `gnus-ignored-headers' and `gnus-visible-headers' may be
1809           ;; group parameters, so we should go to the summary buffer.
1810           (when (prog1
1811                     (condition-case nil
1812                         (progn (set-buffer gnus-summary-buffer) t)
1813                       (error nil))
1814                   (setq ignored (when (not gnus-visible-headers)
1815                                   (cond ((stringp gnus-ignored-headers)
1816                                          gnus-ignored-headers)
1817                                         ((listp gnus-ignored-headers)
1818                                          (mapconcat 'identity
1819                                                     gnus-ignored-headers
1820                                                     "\\|"))))
1821                         visible (cond ((stringp gnus-visible-headers)
1822                                        gnus-visible-headers)
1823                                       ((and gnus-visible-headers
1824                                             (listp gnus-visible-headers))
1825                                        (mapconcat 'identity
1826                                                   gnus-visible-headers
1827                                                   "\\|")))))
1828             (set-buffer cur))
1829           (save-restriction
1830             ;; First we narrow to just the headers.
1831             (article-narrow-to-head)
1832             ;; Hide any "From " lines at the beginning of (mail) articles.
1833             (while (looking-at "From ")
1834               (forward-line 1))
1835             (unless (bobp)
1836               (if delete
1837                   (delete-region (point-min) (point))
1838                 (gnus-article-hide-text (point-min) (point)
1839                                         (nconc (list 'article-type 'headers)
1840                                                gnus-hidden-properties))))
1841             ;; Then treat the rest of the header lines.
1842             ;; Then we use the two regular expressions
1843             ;; `gnus-ignored-headers' and `gnus-visible-headers' to
1844             ;; select which header lines is to remain visible in the
1845             ;; article buffer.
1846             (while (re-search-forward "^[^ \t:]*:" nil t)
1847               (beginning-of-line)
1848               ;; Mark the rank of the header.
1849               (put-text-property
1850                (point) (1+ (point)) 'message-rank
1851                (if (or (and visible (looking-at visible))
1852                        (and ignored
1853                             (not (looking-at ignored))))
1854                    (gnus-article-header-rank)
1855                  (+ 2 max)))
1856               (forward-line 1))
1857             (message-sort-headers-1)
1858             (when (setq beg (text-property-any
1859                              (point-min) (point-max) 'message-rank (+ 2 max)))
1860               ;; We delete or make invisible the unwanted headers.
1861               (gnus-add-wash-type 'headers)
1862               (if delete
1863                   (progn
1864                     (add-text-properties
1865                      (point-min) (+ 5 (point-min))
1866                      '(article-type headers dummy-invisible t))
1867                     (delete-region beg (point-max)))
1868                 (gnus-article-hide-text-type beg (point-max) 'headers)))))))))
1869
1870 (defun article-hide-boring-headers (&optional arg)
1871   "Toggle hiding of headers that aren't very interesting.
1872 If given a negative prefix, always show; if given a positive prefix,
1873 always hide."
1874   (interactive (gnus-article-hidden-arg))
1875   (when (and (not (gnus-article-check-hidden-text 'boring-headers arg))
1876              (not gnus-show-all-headers))
1877     (save-excursion
1878       (save-restriction
1879         (let ((inhibit-read-only t)
1880               (inhibit-point-motion-hooks t))
1881           (article-narrow-to-head)
1882           (dolist (elem gnus-boring-article-headers)
1883             (goto-char (point-min))
1884             (cond
1885              ;; Hide empty headers.
1886              ((eq elem 'empty)
1887               (while (re-search-forward "^[^: \t]+:[ \t]*\n[^ \t]" nil t)
1888                 (forward-line -1)
1889                 (gnus-article-hide-text-type
1890                  (point-at-bol)
1891                  (progn
1892                    (end-of-line)
1893                    (if (re-search-forward "^[^ \t]" nil t)
1894                        (match-beginning 0)
1895                      (point-max)))
1896                  'boring-headers)))
1897              ;; Hide boring Newsgroups header.
1898              ((eq elem 'newsgroups)
1899               (when (gnus-string-equal
1900                      (gnus-fetch-field "newsgroups")
1901                      (gnus-group-real-name
1902                       (if (boundp 'gnus-newsgroup-name)
1903                           gnus-newsgroup-name
1904                         "")))
1905                 (gnus-article-hide-header "newsgroups")))
1906              ((eq elem 'to-address)
1907               (let ((to (message-fetch-field "to"))
1908                     (to-address
1909                      (gnus-parameter-to-address
1910                       (if (boundp 'gnus-newsgroup-name)
1911                           gnus-newsgroup-name ""))))
1912                 (when (and to to-address
1913                            (ignore-errors
1914                              (gnus-string-equal
1915                               ;; only one address in To
1916                               (nth 1 (mail-extract-address-components to))
1917                               to-address)))
1918                   (gnus-article-hide-header "to"))))
1919              ((eq elem 'to-list)
1920               (let ((to (message-fetch-field "to"))
1921                     (to-list
1922                      (gnus-parameter-to-list
1923                       (if (boundp 'gnus-newsgroup-name)
1924                           gnus-newsgroup-name ""))))
1925                 (when (and to to-list
1926                            (ignore-errors
1927                              (gnus-string-equal
1928                               ;; only one address in To
1929                               (nth 1 (mail-extract-address-components to))
1930                               to-list)))
1931                   (gnus-article-hide-header "to"))))
1932              ((eq elem 'cc-list)
1933               (let ((cc (message-fetch-field "cc"))
1934                     (to-list
1935                      (gnus-parameter-to-list
1936                       (if (boundp 'gnus-newsgroup-name)
1937                           gnus-newsgroup-name ""))))
1938                 (when (and cc to-list
1939                            (ignore-errors
1940                              (gnus-string-equal
1941                               ;; only one address in CC
1942                               (nth 1 (mail-extract-address-components cc))
1943                               to-list)))
1944                   (gnus-article-hide-header "cc"))))
1945              ((eq elem 'followup-to)
1946               (when (gnus-string-equal
1947                      (message-fetch-field "followup-to")
1948                      (message-fetch-field "newsgroups"))
1949                 (gnus-article-hide-header "followup-to")))
1950              ((eq elem 'reply-to)
1951               (if (gnus-group-find-parameter
1952                    gnus-newsgroup-name 'broken-reply-to)
1953                   (gnus-article-hide-header "reply-to")
1954                 (let ((from (message-fetch-field "from"))
1955                       (reply-to (message-fetch-field "reply-to")))
1956                   (when
1957                       (and
1958                        from reply-to
1959                        (ignore-errors
1960                          (equal
1961                           (sort (mapcar
1962                                  (lambda (x) (downcase (cadr x)))
1963                                  (mail-extract-address-components from t))
1964                                 'string<)
1965                           (sort (mapcar
1966                                  (lambda (x) (downcase (cadr x)))
1967                                  (mail-extract-address-components reply-to t))
1968                                 'string<))))
1969                     (gnus-article-hide-header "reply-to")))))
1970              ((eq elem 'date)
1971               (let ((date (message-fetch-field "date")))
1972                 (when (and date
1973                            (< (days-between (current-time-string) date)
1974                               4))
1975                   (gnus-article-hide-header "date"))))
1976              ((eq elem 'long-to)
1977               (let ((to (message-fetch-field "to"))
1978                     (cc (message-fetch-field "cc")))
1979                 (when (> (length to) 1024)
1980                   (gnus-article-hide-header "to"))
1981                 (when (> (length cc) 1024)
1982                   (gnus-article-hide-header "cc"))))
1983              ((eq elem 'many-to)
1984               (let ((to-count 0)
1985                     (cc-count 0))
1986                 (goto-char (point-min))
1987                 (while (re-search-forward "^to:" nil t)
1988                   (setq to-count (1+ to-count)))
1989                 (when (> to-count 1)
1990                   (while (> to-count 0)
1991                     (goto-char (point-min))
1992                     (save-restriction
1993                       (re-search-forward "^to:" nil nil to-count)
1994                       (forward-line -1)
1995                       (narrow-to-region (point) (point-max))
1996                       (gnus-article-hide-header "to"))
1997                     (setq to-count (1- to-count))))
1998                 (goto-char (point-min))
1999                 (while (re-search-forward "^cc:" nil t)
2000                   (setq cc-count (1+ cc-count)))
2001                 (when (> cc-count 1)
2002                   (while (> cc-count 0)
2003                     (goto-char (point-min))
2004                     (save-restriction
2005                       (re-search-forward "^cc:" nil nil cc-count)
2006                       (forward-line -1)
2007                       (narrow-to-region (point) (point-max))
2008                       (gnus-article-hide-header "cc"))
2009                     (setq cc-count (1- cc-count)))))))))))))
2010
2011 (defun gnus-article-hide-header (header)
2012   (save-excursion
2013     (goto-char (point-min))
2014     (when (re-search-forward (concat "^" header ":") nil t)
2015       (gnus-article-hide-text-type
2016        (point-at-bol)
2017        (progn
2018          (end-of-line)
2019          (if (re-search-forward "^[^ \t]" nil t)
2020              (match-beginning 0)
2021            (point-max)))
2022        'boring-headers))))
2023
2024 (defvar gnus-article-normalized-header-length 40
2025   "Length of normalized headers.")
2026
2027 (defun article-normalize-headers ()
2028   "Make all header lines 40 characters long."
2029   (interactive)
2030   (let ((inhibit-read-only t)
2031         column)
2032     (save-excursion
2033       (save-restriction
2034         (article-narrow-to-head)
2035         (while (not (eobp))
2036           (cond
2037            ((< (setq column (- (point-at-eol) (point)))
2038                gnus-article-normalized-header-length)
2039             (end-of-line)
2040             (insert (make-string
2041                      (- gnus-article-normalized-header-length column)
2042                      ? )))
2043            ((> column gnus-article-normalized-header-length)
2044             (gnus-put-text-property
2045              (progn
2046                (forward-char gnus-article-normalized-header-length)
2047                (point))
2048              (point-at-eol)
2049              'invisible t))
2050            (t
2051             ;; Do nothing.
2052             ))
2053           (forward-line 1))))))
2054
2055 (defun article-treat-dumbquotes ()
2056   "Translate M****s*** sm*rtq**t*s and other symbols into proper text.
2057 Note that this function guesses whether a character is a sm*rtq**t* or
2058 not, so it should only be used interactively.
2059
2060 Sm*rtq**t*s are M****s***'s unilateral extension to the
2061 iso-8859-1 character map in an attempt to provide more quoting
2062 characters.  If you see something like \\222 or \\264 where
2063 you're expecting some kind of apostrophe or quotation mark, then
2064 try this wash."
2065   (interactive)
2066   (article-translate-strings gnus-article-dumbquotes-map))
2067
2068 (defun article-translate-characters (from to)
2069   "Translate all characters in the body of the article according to FROM and TO.
2070 FROM is a string of characters to translate from; to is a string of
2071 characters to translate to."
2072   (save-excursion
2073     (when (article-goto-body)
2074       (let ((inhibit-read-only t)
2075             (x (make-string 225 ?x))
2076             (i -1))
2077         (while (< (incf i) (length x))
2078           (aset x i i))
2079         (setq i 0)
2080         (while (< i (length from))
2081           (aset x (aref from i) (aref to i))
2082           (incf i))
2083         (translate-region (point) (point-max) x)))))
2084
2085 (defun article-translate-strings (map)
2086   "Translate all string in the body of the article according to MAP.
2087 MAP is an alist where the elements are on the form (\"from\" \"to\")."
2088   (save-excursion
2089     (when (article-goto-body)
2090       (let ((inhibit-read-only t))
2091         (dolist (elem map)
2092           (save-excursion
2093             (while (search-forward (car elem) nil t)
2094               (replace-match (cadr elem)))))))))
2095
2096 (defun article-treat-overstrike ()
2097   "Translate overstrikes into bold text."
2098   (interactive)
2099   (save-excursion
2100     (when (article-goto-body)
2101       (let ((inhibit-read-only t))
2102         (while (search-forward "\b" nil t)
2103           (let ((next (char-after))
2104                 start end previous)
2105             (backward-char 2)
2106             (setq start (point)
2107                   previous (char-after))
2108             (forward-char 3)
2109             (setq end (point))
2110             (backward-char)
2111             ;; We do the boldification/underlining by hiding the
2112             ;; overstrikes and putting the proper text property
2113             ;; on the letters.
2114             (cond
2115              ((eq next previous)
2116               (gnus-article-hide-text-type start (point) 'overstrike)
2117               (put-text-property (point) end 'face 'bold))
2118              ((eq next ?_)
2119               (gnus-article-hide-text-type
2120                (1- (point)) (1+ (point)) 'overstrike)
2121               (put-text-property
2122                start (1- (point)) 'face 'underline))
2123              ((eq previous ?_)
2124               (gnus-article-hide-text-type start (point) 'overstrike)
2125               (put-text-property
2126                (point) end 'face 'underline)))))))))
2127
2128 (defun article-treat-ansi-sequences ()
2129   "Translate ANSI SGR control sequences into overlays or extents."
2130   (interactive)
2131   (save-excursion
2132     (when (article-goto-body)
2133       (let ((inhibit-read-only t))
2134         (ansi-color-apply-on-region (point) (point-max))))))
2135
2136 (defun gnus-article-treat-unfold-headers ()
2137   "Unfold folded message headers.
2138 Only the headers that fit into the current window width will be
2139 unfolded."
2140   (interactive)
2141   (gnus-with-article-headers
2142     (let (length)
2143       (while (not (eobp))
2144         (save-restriction
2145           (mail-header-narrow-to-field)
2146           (let ((header (buffer-string)))
2147             (with-temp-buffer
2148               (insert header)
2149               (goto-char (point-min))
2150               (while (re-search-forward "\n[\t ]" nil t)
2151                 (replace-match " " t t)))
2152             (setq length (- (point-max) (point-min) 1)))
2153           (when (< length (window-width))
2154             (while (re-search-forward "\n[\t ]" nil t)
2155               (replace-match " " t t)))
2156           (goto-char (point-max)))))))
2157
2158 (defun gnus-article-treat-fold-headers ()
2159   "Fold message headers."
2160   (interactive)
2161   (gnus-with-article-headers
2162     (while (not (eobp))
2163       (save-restriction
2164         (mail-header-narrow-to-field)
2165         (mail-header-fold-field)
2166         (goto-char (point-max))))))
2167
2168 (defun gnus-treat-smiley ()
2169   "Toggle display of textual emoticons (\"smileys\") as small graphical icons."
2170   (interactive)
2171   (unless (featurep 'xemacs)
2172     (when (and (not gnus-article-should-use-smiley-mule)
2173                gnus-article-smiley-mule-loaded-p)
2174       (load "smiley" nil t)
2175       (setq gnus-article-smiley-mule-loaded-p nil))
2176     (when (and gnus-article-should-use-smiley-mule
2177                (not gnus-article-smiley-mule-loaded-p))
2178       (load "smiley-mule" nil t)
2179       (setq gnus-article-smiley-mule-loaded-p t)))
2180   (gnus-with-article-buffer
2181     (if (memq 'smiley gnus-article-wash-types)
2182         (gnus-delete-images 'smiley)
2183       (article-goto-body)
2184       (let ((images (smiley-region (point) (point-max))))
2185         (when images
2186           (gnus-add-wash-type 'smiley)
2187           (dolist (image images)
2188             (gnus-add-image 'smiley image)))))))
2189
2190 (defun gnus-article-remove-images ()
2191   "Remove all images from the article buffer."
2192   (interactive)
2193   (gnus-with-article-buffer
2194     (dolist (elem gnus-article-image-alist)
2195       (gnus-delete-images (car elem)))))
2196
2197 (defun gnus-article-treat-fold-newsgroups ()
2198   "Unfold folded message headers.
2199 Only the headers that fit into the current window width will be
2200 unfolded."
2201   (interactive)
2202   (gnus-with-article-headers
2203     (while (gnus-article-goto-header "newsgroups\\|followup-to")
2204       (save-restriction
2205         (mail-header-narrow-to-field)
2206         (while (re-search-forward ", *" nil t)
2207           (replace-match ", " t t))
2208         (mail-header-fold-field)
2209         (goto-char (point-max))))))
2210
2211 (defun gnus-article-treat-body-boundary ()
2212   "Place a boundary line at the end of the headers."
2213   (interactive)
2214   (when (and gnus-body-boundary-delimiter
2215              (> (length gnus-body-boundary-delimiter) 0))
2216     (gnus-with-article-headers
2217       (goto-char (point-max))
2218       (let ((start (point)))
2219         (insert "X-Boundary: ")
2220         (gnus-add-text-properties start (point) '(invisible t intangible t))
2221         (insert (let (str)
2222                   (while (>= (1- (window-width)) (length str))
2223                     (setq str (concat str gnus-body-boundary-delimiter)))
2224                   (substring str 0 (1- (window-width))))
2225                 "\n")
2226         (gnus-put-text-property start (point) 'gnus-decoration 'header)))))
2227
2228 (defun article-fill-long-lines ()
2229   "Fill lines that are wider than the window width."
2230   (interactive)
2231   (save-excursion
2232     (let ((inhibit-read-only t)
2233           (width (window-width (get-buffer-window (current-buffer)))))
2234       (save-restriction
2235         (article-goto-body)
2236         (let ((adaptive-fill-mode nil)) ;Why?  -sm
2237           (while (not (eobp))
2238             (end-of-line)
2239             (when (>= (current-column) (min fill-column width))
2240               (narrow-to-region (min (1+ (point)) (point-max))
2241                                 (point-at-bol))
2242               (let ((goback (point-marker)))
2243                 (fill-paragraph nil)
2244                 (goto-char (marker-position goback)))
2245               (widen))
2246             (forward-line 1)))))))
2247
2248 (defun article-capitalize-sentences ()
2249   "Capitalize the first word in each sentence."
2250   (interactive)
2251   (save-excursion
2252     (let ((inhibit-read-only t)
2253           (paragraph-start "^[\n\^L]"))
2254       (article-goto-body)
2255       (while (not (eobp))
2256         (capitalize-word 1)
2257         (forward-sentence)))))
2258
2259 (defun article-remove-cr ()
2260   "Remove trailing CRs and then translate remaining CRs into LFs."
2261   (interactive)
2262   (save-excursion
2263     (let ((inhibit-read-only t))
2264       (goto-char (point-min))
2265       (while (re-search-forward "\r+$" nil t)
2266         (replace-match "" t t))
2267       (goto-char (point-min))
2268       (while (search-forward "\r" nil t)
2269         (replace-match "\n" t t)))))
2270
2271 (defun article-remove-trailing-blank-lines ()
2272   "Remove all trailing blank lines from the article."
2273   (interactive)
2274   (save-excursion
2275     (let ((inhibit-read-only t))
2276       (goto-char (point-max))
2277       (delete-region
2278        (point)
2279        (progn
2280          (while (and (not (bobp))
2281                      (looking-at "^[ \t]*$")
2282                      (not (gnus-annotation-in-region-p
2283                            (point) (point-at-eol))))
2284            (forward-line -1))
2285          (forward-line 1)
2286          (point))))))
2287
2288 (eval-when-compile
2289   (defvar gnus-face-properties-alist))
2290
2291 (defun article-display-face ()
2292   "Display any Face headers in the header."
2293   (interactive)
2294   (let ((wash-face-p buffer-read-only))
2295     (gnus-with-article-headers
2296       ;; When displaying parts, this function can be called several times on
2297       ;; the same article, without any intended toggle semantic (as typing `W
2298       ;; D d' would have). So face deletion must occur only when we come from
2299       ;; an interactive command, that is when the *Article* buffer is
2300       ;; read-only.
2301       (if (and wash-face-p (memq 'face gnus-article-wash-types))
2302           (gnus-delete-images 'face)
2303         (let (face faces from)
2304           (save-current-buffer
2305             (when (and wash-face-p
2306                        (gnus-buffer-live-p gnus-original-article-buffer)
2307                        (not (re-search-forward "^Face:[\t ]*" nil t)))
2308               (set-buffer gnus-original-article-buffer))
2309             (save-restriction
2310               (mail-narrow-to-head)
2311               (while (gnus-article-goto-header "Face")
2312                 (push (mail-header-field-value) faces))))
2313           (when faces
2314             (goto-char (point-min))
2315             (let ((from (gnus-article-goto-header "from"))
2316                   png image)
2317               (unless from
2318                 (insert "From:")
2319                 (setq from (point))
2320                 (insert "[no `from' set]\n"))
2321               (while faces
2322                 (when (setq png (gnus-convert-face-to-png (pop faces)))
2323                   (setq image
2324                         (apply 'gnus-create-image png 'png t
2325                                (cdr (assq 'png gnus-face-properties-alist))))
2326                   (goto-char from)
2327                   (gnus-add-wash-type 'face)
2328                   (gnus-add-image 'face image)
2329                   (gnus-put-image image nil 'face))))))))))
2330
2331 (defun article-display-x-face (&optional force)
2332   "Look for an X-Face header and display it if present."
2333   (interactive (list 'force))
2334   (let ((wash-face-p buffer-read-only)) ;; When type `W f'
2335     (gnus-with-article-headers
2336       ;; Delete the old process, if any.
2337       (when (process-status "article-x-face")
2338         (delete-process "article-x-face"))
2339       ;; See the comment in `article-display-face'.
2340       (if (and wash-face-p (memq 'xface gnus-article-wash-types))
2341           ;; We have already displayed X-Faces, so we remove them
2342           ;; instead.
2343           (gnus-delete-images 'xface)
2344         ;; Display X-Faces.
2345         (let (x-faces from face)
2346           (save-current-buffer
2347             (when (and wash-face-p
2348                        (gnus-buffer-live-p gnus-original-article-buffer)
2349                        (not (re-search-forward "^X-Face:[\t ]*" nil t)))
2350               ;; If type `W f', use gnus-original-article-buffer,
2351               ;; otherwise use the current buffer because displaying
2352               ;; RFC822 parts calls this function too.
2353               (set-buffer gnus-original-article-buffer))
2354             (save-restriction
2355               (mail-narrow-to-head)
2356               (while (gnus-article-goto-header "X-Face")
2357                 (push (mail-header-field-value) x-faces))
2358               (setq from (message-fetch-field "from"))))
2359           ;; Sending multiple EOFs to xv doesn't work, so we only do a
2360           ;; single external face.
2361           (when (stringp gnus-article-x-face-command)
2362             (setq x-faces (list (car x-faces))))
2363           (when (and x-faces
2364                      gnus-article-x-face-command
2365                      (or force
2366                          ;; Check whether this face is censored.
2367                          (not gnus-article-x-face-too-ugly)
2368                          (and from
2369                               (not (string-match gnus-article-x-face-too-ugly
2370                                                  from)))))
2371             (while (setq face (pop x-faces))
2372               ;; We display the face.
2373               (cond ((stringp gnus-article-x-face-command)
2374                      ;; The command is a string, so we interpret the command
2375                      ;; as a, well, command, and fork it off.
2376                      (let ((process-connection-type nil))
2377                        (gnus-set-process-query-on-exit-flag
2378                         (start-process
2379                          "article-x-face" nil shell-file-name
2380                          shell-command-switch gnus-article-x-face-command)
2381                         nil)
2382                        (with-temp-buffer
2383                          (insert face)
2384                          (process-send-region "article-x-face"
2385                                               (point-min) (point-max)))
2386                        (process-send-eof "article-x-face")))
2387                     ((functionp gnus-article-x-face-command)
2388                      ;; The command is a lisp function, so we call it.
2389                      (funcall gnus-article-x-face-command face))
2390                     (t
2391                      (error "%s is not a function"
2392                             gnus-article-x-face-command))))))))))
2393
2394 (defun article-decode-mime-words ()
2395   "Decode all MIME-encoded words in the article."
2396   (interactive)
2397   (gnus-with-article-buffer
2398     (let ((inhibit-point-motion-hooks t)
2399           (mail-parse-charset gnus-newsgroup-charset)
2400           (mail-parse-ignored-charsets
2401            (with-current-buffer gnus-summary-buffer
2402              gnus-newsgroup-ignored-charsets)))
2403       (mail-decode-encoded-word-region (point-min) (point-max)))))
2404
2405 (defun article-decode-charset (&optional prompt)
2406   "Decode charset-encoded text in the article.
2407 If PROMPT (the prefix), prompt for a coding system to use."
2408   (interactive "P")
2409   (let ((inhibit-point-motion-hooks t) (case-fold-search t)
2410         (inhibit-read-only t)
2411         (mail-parse-charset gnus-newsgroup-charset)
2412         (mail-parse-ignored-charsets
2413          (save-excursion (condition-case nil
2414                              (set-buffer gnus-summary-buffer)
2415                            (error))
2416                          gnus-newsgroup-ignored-charsets))
2417         ct cte ctl charset format)
2418     (save-excursion
2419       (save-restriction
2420         (article-narrow-to-head)
2421         (setq ct (message-fetch-field "Content-Type" t)
2422               cte (message-fetch-field "Content-Transfer-Encoding" t)
2423               ctl (and ct (ignore-errors
2424                             (mail-header-parse-content-type ct)))
2425               charset (cond
2426                        (prompt
2427                         (mm-read-coding-system "Charset to decode: "))
2428                        (ctl
2429                         (mail-content-type-get ctl 'charset)))
2430               format (and ctl (mail-content-type-get ctl 'format)))
2431         (when cte
2432           (setq cte (mail-header-strip cte)))
2433         (if (and ctl (not (string-match "/" (car ctl))))
2434             (setq ctl nil))
2435         (goto-char (point-max)))
2436       (forward-line 1)
2437       (save-restriction
2438         (narrow-to-region (point) (point-max))
2439         (when (and (eq mail-parse-charset 'gnus-decoded)
2440                    (eq (mm-body-7-or-8) '8bit))
2441           ;; The text code could have been decoded.
2442           (setq charset mail-parse-charset))
2443         (when (and (or (not ctl)
2444                        (equal (car ctl) "text/plain"))
2445                    (not format)) ;; article with format will decode later.
2446           (mm-decode-body
2447            charset (and cte (intern (downcase
2448                                      (gnus-strip-whitespace cte))))
2449            (car ctl)))))))
2450
2451 (defun article-decode-encoded-words ()
2452   "Remove encoded-word encoding from headers."
2453   (let ((charset (save-excursion
2454                    (set-buffer gnus-summary-buffer)
2455                    default-mime-charset))
2456         (inhibit-read-only t))
2457     (mime-decode-header-in-buffer charset)))
2458
2459 (defun article-decode-group-name ()
2460   "Decode group names in `Newsgroups:'."
2461   (let ((inhibit-point-motion-hooks t)
2462         (inhibit-read-only t)
2463         (method (gnus-find-method-for-group gnus-newsgroup-name)))
2464     (when (and (or gnus-group-name-charset-method-alist
2465                    gnus-group-name-charset-group-alist)
2466                (gnus-buffer-live-p gnus-original-article-buffer))
2467       (save-restriction
2468         (article-narrow-to-head)
2469         (with-current-buffer gnus-original-article-buffer
2470           (goto-char (point-min)))
2471         (while (re-search-forward
2472                 "^Newsgroups:\\(\\(.\\|\n[\t ]\\)*\\)\n[^\t ]" nil t)
2473           (replace-match (save-match-data
2474                            (gnus-decode-newsgroups
2475                             ;; XXX how to use data in article buffer?
2476                             (with-current-buffer gnus-original-article-buffer
2477                               (re-search-forward
2478                                "^Newsgroups:\\(\\(.\\|\n[\t ]\\)*\\)\n[^\t ]"
2479                                nil t)
2480                               (match-string 1))
2481                             gnus-newsgroup-name method))
2482                          t t nil 1))
2483         (goto-char (point-min))
2484         (with-current-buffer gnus-original-article-buffer
2485           (goto-char (point-min)))
2486         (while (re-search-forward
2487                 "^Followup-To:\\(\\(.\\|\n[\t ]\\)*\\)\n[^\t ]" nil t)
2488           (replace-match (save-match-data
2489                            (gnus-decode-newsgroups
2490                             ;; XXX how to use data in article buffer?
2491                             (with-current-buffer gnus-original-article-buffer
2492                               (re-search-forward
2493                                "^Followup-To:\\(\\(.\\|\n[\t ]\\)*\\)\n[^\t ]"
2494                                nil t)
2495                               (match-string 1))
2496                             gnus-newsgroup-name method))
2497                          t t nil 1))))))
2498
2499 (autoload 'idna-to-unicode "idna")
2500
2501 (defun article-decode-idna-rhs ()
2502   "Decode IDNA strings in RHS in From:, To: and Cc: headers in current buffer."
2503   (when gnus-use-idna
2504     (save-restriction
2505       (let ((inhibit-point-motion-hooks t)
2506             (inhibit-read-only t))
2507         (article-narrow-to-head)
2508         (goto-char (point-min))
2509         (while (re-search-forward "@.*\\(xn--[-A-Za-z0-9.]*\\)[ \t\n\r,>]" nil t)
2510           (let (ace unicode)
2511             (when (save-match-data
2512                     (and (setq ace (match-string 1))
2513                          (save-excursion
2514                            (and (re-search-backward "^[^ \t]" nil t)
2515                                 (looking-at "From\\|To\\|Cc")))
2516                          (setq unicode (idna-to-unicode ace))))
2517               (unless (string= ace unicode)
2518                 (replace-match unicode nil nil nil 1)))))))))
2519
2520 (defun article-de-quoted-unreadable (&optional force read-charset)
2521   "Translate a quoted-printable-encoded article.
2522 If FORCE, decode the article whether it is marked as quoted-printable
2523 or not.
2524 If READ-CHARSET, ask for a coding system."
2525   (interactive (list 'force current-prefix-arg))
2526   (save-excursion
2527     (let ((inhibit-read-only t) type charset)
2528       (if (gnus-buffer-live-p gnus-original-article-buffer)
2529           (with-current-buffer gnus-original-article-buffer
2530             (setq type
2531                   (gnus-fetch-field "content-transfer-encoding"))
2532             (let* ((ct (gnus-fetch-field "content-type"))
2533                    (ctl (and ct
2534                              (ignore-errors
2535                                (mail-header-parse-content-type ct)))))
2536               (setq charset (and ctl
2537                                  (mail-content-type-get ctl 'charset)))
2538               (if (stringp charset)
2539                   (setq charset (intern (downcase charset)))))))
2540       (if read-charset
2541           (setq charset (mm-read-coding-system "Charset: " charset)))
2542       (unless charset
2543         (setq charset gnus-newsgroup-charset))
2544       (when (or force
2545                 (and type (let ((case-fold-search t))
2546                             (string-match "quoted-printable" type))))
2547         (article-goto-body)
2548         (quoted-printable-decode-region
2549          (point) (point-max) (mm-charset-to-coding-system charset))))))
2550
2551 (defun article-de-base64-unreadable (&optional force read-charset)
2552   "Translate a base64 article.
2553 If FORCE, decode the article whether it is marked as base64 not.
2554 If READ-CHARSET, ask for a coding system."
2555   (interactive (list 'force current-prefix-arg))
2556   (save-excursion
2557     (let ((inhibit-read-only t) type charset)
2558       (if (gnus-buffer-live-p gnus-original-article-buffer)
2559           (with-current-buffer gnus-original-article-buffer
2560             (setq type
2561                   (gnus-fetch-field "content-transfer-encoding"))
2562             (let* ((ct (gnus-fetch-field "content-type"))
2563                    (ctl (and ct
2564                              (ignore-errors
2565                                (mail-header-parse-content-type ct)))))
2566               (setq charset (and ctl
2567                                  (mail-content-type-get ctl 'charset)))
2568               (if (stringp charset)
2569                   (setq charset (intern (downcase charset)))))))
2570       (if read-charset
2571           (setq charset (mm-read-coding-system "Charset: " charset)))
2572       (unless charset
2573         (setq charset gnus-newsgroup-charset))
2574       (when (or force
2575                 (and type (let ((case-fold-search t))
2576                             (string-match "base64" type))))
2577         (article-goto-body)
2578         (save-restriction
2579           (narrow-to-region (point) (point-max))
2580           (base64-decode-region (point-min) (point-max))
2581           (mm-decode-coding-region
2582            (point-min) (point-max) (mm-charset-to-coding-system charset)))))))
2583
2584 (eval-when-compile
2585   (require 'rfc1843))
2586
2587 (defun article-decode-HZ ()
2588   "Translate a HZ-encoded article."
2589   (interactive)
2590   (require 'rfc1843)
2591   (save-excursion
2592     (let ((inhibit-read-only t))
2593       (rfc1843-decode-region (point-min) (point-max)))))
2594
2595 (defun article-unsplit-urls ()
2596   "Remove the newlines that some other mailers insert into URLs."
2597   (interactive)
2598   (save-excursion
2599     (let ((inhibit-read-only t))
2600       (goto-char (point-min))
2601       (while (re-search-forward
2602               "\\(\\(https?\\|ftp\\)://\\S-+\\) *\n\\(\\S-+\\)" nil t)
2603         (replace-match "\\1\\3" t)))
2604     (when (interactive-p)
2605       (gnus-treat-article nil))))
2606
2607
2608 (defun article-wash-html (&optional read-charset)
2609   "Format an HTML article.
2610 If READ-CHARSET, ask for a coding system."
2611   (interactive "P")
2612   (save-excursion
2613     (let ((inhibit-read-only t)
2614           charset)
2615       (when (gnus-buffer-live-p gnus-original-article-buffer)
2616         (with-current-buffer gnus-original-article-buffer
2617           (let* ((ct (gnus-fetch-field "content-type"))
2618                  (ctl (and ct
2619                            (ignore-errors
2620                              (mail-header-parse-content-type ct)))))
2621             (setq charset (and ctl
2622                                (mail-content-type-get ctl 'charset)))
2623             (when (stringp charset)
2624               (setq charset (intern (downcase charset)))))))
2625       (when read-charset
2626         (setq charset (mm-read-coding-system "Charset: " charset)))
2627       (unless charset
2628         (setq charset gnus-newsgroup-charset))
2629       (article-goto-body)
2630       (save-window-excursion
2631         (save-restriction
2632           (narrow-to-region (point) (point-max))
2633           (let* ((func (or gnus-article-wash-function mm-text-html-renderer))
2634                  (entry (assq func mm-text-html-washer-alist)))
2635             (when entry
2636               (setq func (cdr entry)))
2637             (cond
2638              ((functionp func)
2639               (funcall func))
2640              (t
2641               (apply (car func) (cdr func))))))))))
2642
2643 (defun gnus-article-wash-html-with-w3 ()
2644   "Wash the current buffer with w3."
2645   (mm-setup-w3)
2646   (let ((w3-strict-width (window-width))
2647         (url-standalone-mode t)
2648         (url-gateway-unplugged t)
2649         (w3-honor-stylesheets nil))
2650     (condition-case ()
2651         (w3-region (point-min) (point-max))
2652       (error))))
2653
2654 (defun gnus-article-wash-html-with-w3m ()
2655   "Wash the current buffer with emacs-w3m."
2656   (mm-setup-w3m)
2657   (save-restriction
2658     (narrow-to-region (point) (point-max))
2659     (let ((w3m-safe-url-regexp mm-w3m-safe-url-regexp)
2660           w3m-force-redisplay)
2661       (w3m-region (point-min) (point-max)))
2662     (when (and mm-inline-text-html-with-w3m-keymap
2663                (boundp 'w3m-minor-mode-map)
2664                w3m-minor-mode-map)
2665       (add-text-properties
2666        (point-min) (point-max)
2667        (list 'keymap w3m-minor-mode-map
2668              ;; Put the mark meaning this part was rendered by emacs-w3m.
2669              'mm-inline-text-html-with-w3m t)))))
2670
2671 (defun article-hide-list-identifiers ()
2672   "Remove list identifies from the Subject header.
2673 The `gnus-list-identifiers' variable specifies what to do."
2674   (interactive)
2675   (let ((inhibit-point-motion-hooks t)
2676         (regexp (if (consp gnus-list-identifiers)
2677                     (mapconcat 'identity gnus-list-identifiers " *\\|")
2678                   gnus-list-identifiers))
2679         (inhibit-read-only t))
2680     (when regexp
2681       (save-excursion
2682         (save-restriction
2683           (article-narrow-to-head)
2684           (goto-char (point-min))
2685           (while (re-search-forward
2686                   (concat "^Subject: +\\(R[Ee]: +\\)*\\(" regexp " *\\)")
2687                   nil t)
2688             (delete-region (match-beginning 2) (match-end 0))
2689             (beginning-of-line))
2690           (when (re-search-forward
2691                  "^Subject: +\\(\\(R[Ee]: +\\)+\\)R[Ee]: +" nil t)
2692             (delete-region (match-beginning 1) (match-end 1))))))))
2693
2694 (defun article-hide-pem (&optional arg)
2695   "Toggle hiding of any PEM headers and signatures in the current article.
2696 If given a negative prefix, always show; if given a positive prefix,
2697 always hide."
2698   (interactive (gnus-article-hidden-arg))
2699   (unless (gnus-article-check-hidden-text 'pem arg)
2700     (save-excursion
2701       (let ((inhibit-read-only t) end)
2702         (goto-char (point-min))
2703         ;; Hide the horrendously ugly "header".
2704         (when (and (search-forward
2705                     "\n-----BEGIN PRIVACY-ENHANCED MESSAGE-----\n"
2706                     nil t)
2707                    (setq end (1+ (match-beginning 0))))
2708           (gnus-add-wash-type 'pem)
2709           (gnus-article-hide-text-type
2710            end
2711            (if (search-forward "\n\n" nil t)
2712                (match-end 0)
2713              (point-max))
2714            'pem)
2715           ;; Hide the trailer as well
2716           (when (search-forward "\n-----END PRIVACY-ENHANCED MESSAGE-----\n"
2717                                 nil t)
2718             (gnus-article-hide-text-type
2719              (match-beginning 0) (match-end 0) 'pem)))))))
2720
2721 (defun article-strip-banner ()
2722   "Strip the banners specified by the `banner' group parameter and by
2723 `gnus-article-address-banner-alist'."
2724   (interactive)
2725   (save-excursion
2726     (save-restriction
2727       (let ((inhibit-point-motion-hooks t))
2728         (when (gnus-parameter-banner gnus-newsgroup-name)
2729           (article-really-strip-banner
2730            (gnus-parameter-banner gnus-newsgroup-name)))
2731         (when gnus-article-address-banner-alist
2732           ;; It is necessary to encode from fields before checking,
2733           ;; because `mail-header-parse-addresses' does not work
2734           ;; (reliably) on decoded headers.  And more, it is
2735           ;; impossible to use `gnus-fetch-original-field' here,
2736           ;; because `article-strip-banner' may be called in draft
2737           ;; buffers to preview them.
2738           (let ((from (save-restriction
2739                         (widen)
2740                         (article-narrow-to-head)
2741                         (mail-fetch-field "from"))))
2742             (when (and from
2743                        (setq from
2744                              (caar (mail-header-parse-addresses
2745                                     (mail-encode-encoded-word-string from)))))
2746               (catch 'found
2747                 (dolist (pair gnus-article-address-banner-alist)
2748                   (when (string-match (car pair) from)
2749                     (throw 'found
2750                            (article-really-strip-banner (cdr pair)))))))))))))
2751
2752 (defun article-really-strip-banner (banner)
2753   "Strip the banner specified by the argument."
2754   (save-excursion
2755     (save-restriction
2756       (let ((inhibit-point-motion-hooks t)
2757             (gnus-signature-limit nil)
2758             (inhibit-read-only t))
2759         (article-goto-body)
2760         (cond
2761          ((eq banner 'signature)
2762           (when (gnus-article-narrow-to-signature)
2763             (widen)
2764             (forward-line -1)
2765             (delete-region (point) (point-max))))
2766          ((symbolp banner)
2767           (if (setq banner (cdr (assq banner gnus-article-banner-alist)))
2768               (while (re-search-forward banner nil t)
2769                 (delete-region (match-beginning 0) (match-end 0)))))
2770          ((stringp banner)
2771           (while (re-search-forward banner nil t)
2772             (delete-region (match-beginning 0) (match-end 0)))))))))
2773
2774 (defun article-babel ()
2775   "Translate article using an online translation service."
2776   (interactive)
2777   (require 'babel)
2778   (gnus-with-article-buffer
2779     (when (article-goto-body)
2780       (let* ((start (point))
2781              (end (point-max))
2782              (orig (buffer-substring start end))
2783              (trans (babel-as-string orig)))
2784         (save-restriction
2785           (narrow-to-region start end)
2786           (delete-region start end)
2787           (insert trans))))))
2788
2789 (defun article-hide-signature (&optional arg)
2790   "Hide the signature in the current article.
2791 If given a negative prefix, always show; if given a positive prefix,
2792 always hide."
2793   (interactive (gnus-article-hidden-arg))
2794   (save-excursion
2795     (save-restriction
2796       (if (interactive-p)
2797           (progn
2798             (widen)
2799             (article-goto-body))
2800         (goto-char (point-min)))
2801       (unless (gnus-article-check-hidden-text 'signature arg)
2802         (let ((inhibit-read-only t)
2803               (button (point)))
2804           (while (setq button (text-property-any button (point-max)
2805                                                  'gnus-callback
2806                                                  'gnus-signature-toggle))
2807             (setq button (text-property-not-all button (point-max)
2808                                                 'gnus-callback
2809                                                 'gnus-signature-toggle))
2810             (when (and button (not (eobp)))
2811               (gnus-article-hide-text-type
2812                (1+ button)
2813                (next-single-property-change (1+ button) 'mime-view-entity
2814                                             nil (point-max))
2815                'signature)))))))
2816   (gnus-set-mode-line 'article))
2817
2818 (defun article-strip-headers-in-body ()
2819   "Strip offensive headers from bodies."
2820   (interactive)
2821   (save-excursion
2822     (article-goto-body)
2823     (let ((case-fold-search t))
2824       (when (looking-at "x-no-archive:")
2825         (gnus-delete-line)))))
2826
2827 (defun article-strip-leading-blank-lines ()
2828   "Remove all blank lines from the beginning of the article."
2829   (interactive)
2830   (save-excursion
2831     (let ((inhibit-point-motion-hooks t)
2832           (inhibit-read-only t))
2833       (when (article-goto-body)
2834         (while (and (not (eobp))
2835                     (looking-at "[ \t]*$"))
2836           (gnus-delete-line))))))
2837
2838 (defun article-narrow-to-head ()
2839   "Narrow the buffer to the head of the message.
2840 Point is left at the beginning of the narrowed-to region."
2841   (narrow-to-region
2842    (goto-char (point-min))
2843    (if (search-forward "\n\n" nil 1)
2844        (1- (point))
2845      (point-max)))
2846   (goto-char (point-min)))
2847
2848 (defun article-goto-body ()
2849   "Place point at the start of the body."
2850   (goto-char (point-min))
2851   (cond
2852    ;; This variable is only bound when dealing with separate
2853    ;; MIME body parts.
2854    (article-goto-body-goes-to-point-min-p
2855     t)
2856    ((search-forward "\n\n" nil t)
2857     t)
2858    (t
2859     (goto-char (point-max))
2860     nil)))
2861
2862 (defun article-strip-multiple-blank-lines ()
2863   "Replace consecutive blank lines with one empty line."
2864   (interactive)
2865   (save-excursion
2866     (let ((inhibit-point-motion-hooks t)
2867           (inhibit-read-only t))
2868       ;; First make all blank lines empty.
2869       (article-goto-body)
2870       (while (re-search-forward "^[ \t]+$" nil t)
2871         (unless (gnus-annotation-in-region-p
2872                  (match-beginning 0) (match-end 0))
2873           (replace-match "" nil t)))
2874       ;; Then replace multiple empty lines with a single empty line.
2875       (article-goto-body)
2876       (while (re-search-forward "\n\n\\(\n+\\)" nil t)
2877         (unless (gnus-annotation-in-region-p
2878                  (match-beginning 0) (match-end 0))
2879           (delete-region (match-beginning 1) (match-end 1)))))))
2880
2881 (defun article-strip-leading-space ()
2882   "Remove all white space from the beginning of the lines in the article."
2883   (interactive)
2884   (save-excursion
2885     (let ((inhibit-point-motion-hooks t)
2886           (inhibit-read-only t))
2887       (article-goto-body)
2888       (while (re-search-forward "^[ \t]+" nil t)
2889         (replace-match "" t t)))))
2890
2891 (defun article-strip-trailing-space ()
2892   "Remove all white space from the end of the lines in the article."
2893   (interactive)
2894   (save-excursion
2895     (let ((inhibit-point-motion-hooks t)
2896           (inhibit-read-only t))
2897       (article-goto-body)
2898       (while (re-search-forward "[ \t]+$" nil t)
2899         (replace-match "" t t)))))
2900
2901 (defun article-strip-blank-lines ()
2902   "Strip leading, trailing and multiple blank lines."
2903   (interactive)
2904   (article-strip-leading-blank-lines)
2905   (article-remove-trailing-blank-lines)
2906   (article-strip-multiple-blank-lines))
2907
2908 (defun article-strip-all-blank-lines ()
2909   "Strip all blank lines."
2910   (interactive)
2911   (save-excursion
2912     (let ((inhibit-point-motion-hooks t)
2913           (inhibit-read-only t))
2914       (article-goto-body)
2915       (while (re-search-forward "^[ \t]*\n" nil t)
2916         (replace-match "" t t)))))
2917
2918 (defun gnus-article-narrow-to-signature ()
2919   "Narrow to the signature; return t if a signature is found, else nil."
2920   (let ((inhibit-point-motion-hooks t))
2921     (when (gnus-article-search-signature)
2922       (forward-line 1)
2923       ;; Check whether we have some limits to what we consider
2924       ;; to be a signature.
2925       (let ((limits (if (listp gnus-signature-limit) gnus-signature-limit
2926                       (list gnus-signature-limit)))
2927             limit limited)
2928         (while (setq limit (pop limits))
2929           (if (or (and (integerp limit)
2930                        (< (- (point-max) (point)) limit))
2931                   (and (floatp limit)
2932                        (< (count-lines (point) (point-max)) limit))
2933                   (and (functionp limit)
2934                        (funcall limit))
2935                   (and (stringp limit)
2936                        (not (re-search-forward limit nil t))))
2937               ()                        ; This limit did not succeed.
2938             (setq limited t
2939                   limits nil)))
2940         (unless limited
2941           (narrow-to-region (point) (point-max))
2942           t)))))
2943
2944 (defun gnus-article-search-signature ()
2945   "Search the current buffer for the signature separator.
2946 Put point at the beginning of the signature separator."
2947   (let ((cur (point)))
2948     (goto-char (point-max))
2949     (if (if (stringp gnus-signature-separator)
2950             (re-search-backward gnus-signature-separator nil t)
2951           (let ((seps gnus-signature-separator))
2952             (while (and seps
2953                         (not (re-search-backward (car seps) nil t)))
2954               (pop seps))
2955             seps))
2956         t
2957       (goto-char cur)
2958       nil)))
2959
2960 (defun gnus-article-hidden-arg ()
2961   "Return the current prefix arg as a number, or 0 if no prefix."
2962   (list (if current-prefix-arg
2963             (prefix-numeric-value current-prefix-arg)
2964           0)))
2965
2966 (defun gnus-article-check-hidden-text (type arg)
2967   "Return nil if hiding is necessary.
2968 Arg can be nil or a number.  nil and positive means hide, negative
2969 means show, 0 means toggle."
2970   (save-excursion
2971     (save-restriction
2972       (let ((hide (gnus-article-hidden-text-p type)))
2973         (cond
2974          ((or (null arg)
2975               (> arg 0))
2976           nil)
2977          ((< arg 0)
2978           (gnus-article-show-hidden-text type)
2979           t)
2980          (t
2981           (if (eq hide 'hidden)
2982               (progn
2983                 (gnus-article-show-hidden-text type)
2984                 t)
2985             nil)))))))
2986
2987 (defun gnus-article-hidden-text-p (type)
2988   "Say whether the current buffer contains hidden text of type TYPE."
2989   (let ((pos (text-property-any (point-min) (point-max) 'article-type type)))
2990     (while (and pos
2991                 (not (get-text-property pos 'invisible))
2992                 (not (get-text-property pos 'dummy-invisible)))
2993       (setq pos
2994             (text-property-any (1+ pos) (point-max) 'article-type type)))
2995     (if pos
2996         'hidden
2997       nil)))
2998
2999 (defun gnus-article-show-hidden-text (type &optional dummy)
3000   "Show all hidden text of type TYPE.
3001 Originally it is hide instead of DUMMY."
3002   (let ((inhibit-read-only t)
3003         (inhibit-point-motion-hooks t))
3004     (gnus-remove-text-properties-when
3005      'article-type type
3006      (point-min) (point-max)
3007      (cons 'article-type (cons type
3008                                gnus-hidden-properties)))
3009     (gnus-delete-wash-type type)))
3010
3011 (defconst article-time-units
3012   `((year . ,(* 365.25 24 60 60))
3013     (week . ,(* 7 24 60 60))
3014     (day . ,(* 24 60 60))
3015     (hour . ,(* 60 60))
3016     (minute . 60)
3017     (second . 1))
3018   "Mapping from time units to seconds.")
3019
3020 (defun gnus-article-forward-header ()
3021   "Move point to the start of the next header.
3022 If the current header is a continuation header, this can be several
3023 lines forward."
3024   (let ((ended nil))
3025     (while (not ended)
3026       (forward-line 1)
3027       (if (looking-at "[ \t]+[^ \t]")
3028           (forward-line 1)
3029         (setq ended t)))))
3030
3031 (defun article-date-ut (&optional type highlight)
3032   "Convert DATE date to universal time in the current article.
3033 If TYPE is `local', convert to local time; if it is `lapsed', output
3034 how much time has lapsed since DATE.  For `lapsed', the value of
3035 `gnus-article-date-lapsed-new-header' says whether the \"X-Sent:\" header
3036 should replace the \"Date:\" one, or should be added below it."
3037   (interactive (list 'ut t))
3038   (let* ((tdate-regexp "^Date:[ \t]\\|^X-Sent:[ \t]")
3039          (date-regexp (cond ((not gnus-article-date-lapsed-new-header)
3040                              tdate-regexp)
3041                             ((eq type 'lapsed)
3042                              "^X-Sent:[ \t]")
3043                             (article-lapsed-timer
3044                              "^Date:[ \t]")
3045                             (t
3046                              tdate-regexp)))
3047          (case-fold-search t)
3048          (inhibit-read-only t)
3049          (inhibit-point-motion-hooks t)
3050          pos date bface eface)
3051     (save-excursion
3052       (save-restriction
3053         (widen)
3054         (goto-char (point-min))
3055         (while (or (setq date (get-text-property (setq pos (point))
3056                                                  'original-date))
3057                    (when (setq pos (next-single-property-change
3058                                     (point) 'original-date))
3059                      (setq date (get-text-property pos 'original-date))
3060                      t))
3061           (narrow-to-region pos (or (text-property-any pos (point-max)
3062                                                        'original-date nil)
3063                                     (point-max)))
3064           (goto-char (point-min))
3065           (when (re-search-forward tdate-regexp nil t)
3066             (setq bface (get-text-property (point-at-bol) 'face)
3067                   eface (get-text-property (1- (point-at-eol)) 'face)))
3068           (goto-char (point-min))
3069           (setq pos nil)
3070           ;; Delete any old Date headers.
3071           (while (re-search-forward date-regexp nil t)
3072             (if pos
3073                 (delete-region (point-at-bol) (progn
3074                                                 (gnus-article-forward-header)
3075                                                 (point)))
3076               (delete-region (point-at-bol) (progn
3077                                               (gnus-article-forward-header)
3078                                               (forward-char -1)
3079                                               (point)))
3080               (setq pos (point))))
3081           (when (and (not pos)
3082                      (re-search-forward tdate-regexp nil t))
3083             (forward-line 1))
3084           (gnus-goto-char pos)
3085           (insert (article-make-date-line date (or type 'ut)))
3086           (unless pos
3087             (insert "\n")
3088             (forward-line -1))
3089           ;; Do highlighting.
3090           (beginning-of-line)
3091           (when (looking-at "\\([^:]+\\): *\\(.*\\)$")
3092             (put-text-property (match-beginning 1) (1+ (match-end 1))
3093                                'face bface)
3094             (put-text-property (match-beginning 2) (match-end 2)
3095                                'face eface))
3096           (put-text-property (point-min) (1- (point-max)) 'original-date date)
3097           (goto-char (point-max))
3098           (widen))))))
3099
3100 (defun article-make-date-line (date type)
3101   "Return a DATE line of TYPE."
3102   (unless (memq type '(local ut original user iso8601 lapsed english))
3103     (error "Unknown conversion type: %s" type))
3104   (condition-case ()
3105       (let ((time (date-to-time date)))
3106         (cond
3107          ;; Convert to the local timezone.
3108          ((eq type 'local)
3109           (concat "Date: " (message-make-date time)))
3110          ;; Convert to Universal Time.
3111          ((eq type 'ut)
3112           (concat "Date: "
3113                   (substring
3114                    (message-make-date
3115                     (let* ((e (parse-time-string date))
3116                            (tm (apply 'encode-time e))
3117                            (ms (car tm))
3118                            (ls (- (cadr tm) (car (current-time-zone time)))))
3119                       (cond ((< ls 0) (list (1- ms) (+ ls 65536)))
3120                             ((> ls 65535) (list (1+ ms) (- ls 65536)))
3121                             (t (list ms ls)))))
3122                    0 -5)
3123                   "UT"))
3124          ;; Get the original date from the article.
3125          ((eq type 'original)
3126           (concat "Date: " (if (string-match "\n+$" date)
3127                                (substring date 0 (match-beginning 0))
3128                              date)))
3129          ;; Let the user define the format.
3130          ((eq type 'user)
3131           (let ((format (or (condition-case nil
3132                                 (with-current-buffer gnus-summary-buffer
3133                                   gnus-article-time-format)
3134                               (error nil))
3135                             gnus-article-time-format)))
3136             (if (functionp format)
3137                 (funcall format time)
3138               (concat "Date: " (format-time-string format time)))))
3139          ;; ISO 8601.
3140          ((eq type 'iso8601)
3141           (let ((tz (car (current-time-zone time))))
3142             (concat
3143              "Date: "
3144              (format-time-string "%Y%m%dT%H%M%S" time)
3145              (format "%s%02d%02d"
3146                      (if (> tz 0) "+" "-") (/ (abs tz) 3600)
3147                      (/ (% (abs tz) 3600) 60)))))
3148          ;; Do an X-Sent lapsed format.
3149          ((eq type 'lapsed)
3150           ;; If the date is seriously mangled, the timezone functions are
3151           ;; liable to bug out, so we ignore all errors.
3152           (let* ((now (current-time))
3153                  (real-time (subtract-time now time))
3154                  (real-sec (and real-time
3155                                 (+ (* (float (car real-time)) 65536)
3156                                    (cadr real-time))))
3157                  (sec (and real-time (abs real-sec)))
3158                  num prev)
3159             (cond
3160              ((null real-time)
3161               "X-Sent: Unknown")
3162              ((zerop sec)
3163               "X-Sent: Now")
3164              (t
3165               (concat
3166                "X-Sent: "
3167                ;; This is a bit convoluted, but basically we go
3168                ;; through the time units for years, weeks, etc,
3169                ;; and divide things to see whether that results
3170                ;; in positive answers.
3171                (mapconcat
3172                 (lambda (unit)
3173                   (if (zerop (setq num (ffloor (/ sec (cdr unit)))))
3174                       ;; The (remaining) seconds are too few to
3175                       ;; be divided into this time unit.
3176                       ""
3177                     ;; It's big enough, so we output it.
3178                     (setq sec (- sec (* num (cdr unit))))
3179                     (prog1
3180                         (concat (if prev ", " "") (int-to-string
3181                                                    (floor num))
3182                                 " " (symbol-name (car unit))
3183                                 (if (> num 1) "s" ""))
3184                       (setq prev t))))
3185                 article-time-units "")
3186                ;; If dates are odd, then it might appear like the
3187                ;; article was sent in the future.
3188                (if (> real-sec 0)
3189                    " ago"
3190                  " in the future"))))))
3191          ;; Display the date in proper English
3192          ((eq type 'english)
3193           (let ((dtime (decode-time time)))
3194             (concat
3195              "Date: the "
3196              (number-to-string (nth 3 dtime))
3197              (let ((digit (% (nth 3 dtime) 10)))
3198                (cond
3199                 ((memq (nth 3 dtime) '(11 12 13)) "th")
3200                 ((= digit 1) "st")
3201                 ((= digit 2) "nd")
3202                 ((= digit 3) "rd")
3203                 (t "th")))
3204              " of "
3205              (nth (1- (nth 4 dtime)) gnus-english-month-names)
3206              " "
3207              (number-to-string (nth 5 dtime))
3208              " at "
3209              (format "%02d" (nth 2 dtime))
3210              ":"
3211              (format "%02d" (nth 1 dtime)))))))
3212     (error
3213      (format "Date: %s (from T-gnus)" date))))
3214
3215 (defun article-date-local (&optional highlight)
3216   "Convert the current article date to the local timezone."
3217   (interactive (list t))
3218   (article-date-ut 'local highlight))
3219
3220 (defun article-date-english (&optional highlight)
3221   "Convert the current article date to something that is proper English."
3222   (interactive (list t))
3223   (article-date-ut 'english highlight))
3224
3225 (defun article-date-original (&optional highlight)
3226   "Convert the current article date to what it was originally.
3227 This is only useful if you have used some other date conversion
3228 function and want to see what the date was before converting."
3229   (interactive (list t))
3230   (article-date-ut 'original highlight))
3231
3232 (defun article-date-lapsed (&optional highlight)
3233   "Convert the current article date to time lapsed since it was sent."
3234   (interactive (list t))
3235   (article-date-ut 'lapsed highlight))
3236
3237 (defun article-update-date-lapsed ()
3238   "Function to be run from a timer to update the lapsed time line."
3239   (save-match-data
3240     (let (deactivate-mark)
3241       (save-excursion
3242         (ignore-errors
3243          (walk-windows
3244           (lambda (w)
3245             (set-buffer (window-buffer w))
3246             (when (or (and (eq major-mode 'mime-view-mode)
3247                            (eq (mime-preview-original-major-mode)
3248                                'gnus-original-article-mode))
3249                       (eq major-mode 'gnus-article-mode))
3250               (let ((mark (point-marker)))
3251                 (goto-char (point-min))
3252                 (when (re-search-forward "^X-Sent:" nil t)
3253                   (article-date-lapsed t))
3254                 (goto-char (marker-position mark))
3255                 (move-marker mark nil))))
3256           nil 'visible))))))
3257
3258 (defun gnus-start-date-timer (&optional n)
3259   "Start a timer to update the X-Sent header in the article buffers.
3260 The numerical prefix says how frequently (in seconds) the function
3261 is to run."
3262   (interactive "p")
3263   (unless n
3264     (setq n 1))
3265   (gnus-stop-date-timer)
3266   (setq article-lapsed-timer
3267         (run-at-time 1 n 'article-update-date-lapsed)))
3268
3269 (defun gnus-stop-date-timer ()
3270   "Stop the X-Sent timer."
3271   (interactive)
3272   (when article-lapsed-timer
3273     (nnheader-cancel-timer article-lapsed-timer)
3274     (setq article-lapsed-timer nil)))
3275
3276 (defun article-date-user (&optional highlight)
3277   "Convert the current article date to the user-defined format.
3278 This format is defined by the `gnus-article-time-format' variable."
3279   (interactive (list t))
3280   (article-date-ut 'user highlight))
3281
3282 (defun article-date-iso8601 (&optional highlight)
3283   "Convert the current article date to ISO8601."
3284   (interactive (list t))
3285   (article-date-ut 'iso8601 highlight))
3286
3287 (defmacro gnus-article-save-original-date (&rest forms)
3288   "Save the original date as a text property and evaluate FORMS."
3289   `(let* ((case-fold-search t)
3290           (start (progn
3291                    (goto-char (point-min))
3292                    (when (and (re-search-forward "^date:[\t\n ]+" nil t)
3293                               (not (bolp)))
3294                      (match-end 0))))
3295           (date (when (and start
3296                            (re-search-forward "[\t ]*\n\\(?:[^\t ]\\|\\'\\)"
3297                                               nil t))
3298                   (buffer-substring-no-properties start
3299                                                   (match-beginning 0)))))
3300      (goto-char (point-max))
3301      (skip-chars-backward "\n")
3302      (put-text-property (point-min) (point) 'original-date date)
3303      ,@forms
3304      (goto-char (point-max))
3305      (skip-chars-backward "\n")
3306      (put-text-property (point-min) (point) 'original-date date)))
3307
3308 ;; (defun article-show-all ()
3309 ;;   "Show all hidden text in the article buffer."
3310 ;;   (interactive)
3311 ;;   (save-excursion
3312 ;;     (widen)
3313 ;;     (let ((inhibit-read-only t))
3314 ;;       (gnus-article-unhide-text (point-min) (point-max))
3315 ;;       (gnus-remove-text-with-property 'gnus-prev)
3316 ;;       (gnus-remove-text-with-property 'gnus-next))))
3317
3318 (defun article-show-all-headers ()
3319   "Show all hidden headers in the article buffer."
3320   (interactive)
3321   (save-excursion
3322     (save-restriction
3323       (widen)
3324       (article-narrow-to-head)
3325       (let ((inhibit-read-only t))
3326         (gnus-article-unhide-text (point-min) (point-max))))))
3327
3328 (defun article-remove-leading-whitespace ()
3329   "Remove excessive whitespace from all headers."
3330   (interactive)
3331   (save-excursion
3332     (save-restriction
3333       (let ((inhibit-read-only t))
3334         (article-narrow-to-head)
3335         (goto-char (point-min))
3336         (while (re-search-forward "^[^ :]+: \\([ \t]+\\)" nil t)
3337           (delete-region (match-beginning 1) (match-end 1)))))))
3338
3339 (defun article-emphasize (&optional arg)
3340   "Emphasize text according to `gnus-emphasis-alist'."
3341   (interactive (gnus-article-hidden-arg))
3342   (unless (gnus-article-check-hidden-text 'emphasis arg)
3343     (save-excursion
3344       (let ((alist (or
3345                     (condition-case nil
3346                         (with-current-buffer gnus-summary-buffer
3347                           gnus-article-emphasis-alist)
3348                       (error))
3349                     gnus-emphasis-alist))
3350             (inhibit-read-only t)
3351             (props (append '(article-type emphasis)
3352                            gnus-hidden-properties))
3353             regexp elem beg invisible visible face)
3354         (article-goto-body)
3355         (setq beg (point))
3356         (while (setq elem (pop alist))
3357           (goto-char beg)
3358           (setq regexp (car elem)
3359                 invisible (nth 1 elem)
3360                 visible (nth 2 elem)
3361                 face (nth 3 elem))
3362           (while (re-search-forward regexp nil t)
3363             (when (and (match-beginning visible) (match-beginning invisible))
3364               (gnus-article-hide-text
3365                (match-beginning invisible) (match-end invisible) props)
3366               (gnus-article-unhide-text-type
3367                (match-beginning visible) (match-end visible) 'emphasis)
3368               (gnus-put-overlay-excluding-newlines
3369                (match-beginning visible) (match-end visible) 'face face)
3370               (gnus-add-wash-type 'emphasis)
3371               (goto-char (match-end invisible)))))))))
3372
3373 (defun gnus-article-setup-highlight-words (&optional highlight-words)
3374   "Setup newsgroup emphasis alist."
3375   (unless gnus-article-emphasis-alist
3376     (let ((name (and gnus-newsgroup-name
3377                      (gnus-group-real-name gnus-newsgroup-name))))
3378       (make-local-variable 'gnus-article-emphasis-alist)
3379       (setq gnus-article-emphasis-alist
3380             (nconc
3381              (let ((alist gnus-group-highlight-words-alist) elem highlight)
3382                (while (setq elem (pop alist))
3383                  (when (and name (string-match (car elem) name))
3384                    (setq alist nil
3385                          highlight (copy-sequence (cdr elem)))))
3386                highlight)
3387              (copy-sequence highlight-words)
3388              (if gnus-newsgroup-name
3389                  (copy-sequence (gnus-group-find-parameter
3390                                  gnus-newsgroup-name 'highlight-words t)))
3391              gnus-emphasis-alist)))))
3392
3393 (eval-when-compile
3394   (defvar gnus-summary-article-menu)
3395   (defvar gnus-summary-post-menu))
3396
3397 ;;; Saving functions.
3398
3399 (defun gnus-article-save (save-buffer file &optional num)
3400   "Save the currently selected article."
3401   (unless gnus-save-all-headers
3402     ;; Remove headers according to `gnus-saved-headers'.
3403     (let ((gnus-visible-headers
3404            (or gnus-saved-headers gnus-visible-headers))
3405           (gnus-article-buffer save-buffer))
3406       (save-excursion
3407         (set-buffer save-buffer)
3408         (article-hide-headers 1 t))))
3409   (save-window-excursion
3410     (if (not gnus-default-article-saver)
3411         (error "No default saver is defined")
3412       ;; !!! Magic!  The saving functions all save
3413       ;; `gnus-save-article-buffer' (or so they think), but we
3414       ;; bind that variable to our save-buffer.
3415       (set-buffer gnus-article-buffer)
3416       (let* ((gnus-save-article-buffer save-buffer)
3417              (filename
3418               (cond
3419                ((not gnus-prompt-before-saving) 'default)
3420                ((eq gnus-prompt-before-saving 'always) nil)
3421                (t file)))
3422              (gnus-number-of-articles-to-be-saved
3423               (when (eq gnus-prompt-before-saving t)
3424                 num)))                  ; Magic
3425         (set-buffer gnus-article-current-summary)
3426         (funcall gnus-default-article-saver filename)))))
3427
3428 (defun gnus-read-save-file-name (prompt &optional filename
3429                                         function group headers variable)
3430   (let ((default-name
3431           (funcall function group headers (symbol-value variable)))
3432         result)
3433     (setq result
3434           (expand-file-name
3435            (cond
3436             ((eq filename 'default)
3437              default-name)
3438             ((eq filename t)
3439              default-name)
3440             (filename filename)
3441             (t
3442              (let* ((split-name (gnus-get-split-value gnus-split-methods))
3443                     (prompt
3444                      (format prompt
3445                              (if (and gnus-number-of-articles-to-be-saved
3446                                       (> gnus-number-of-articles-to-be-saved 1))
3447                                  (format "these %d articles"
3448                                          gnus-number-of-articles-to-be-saved)
3449                                "this article")))
3450                     (file
3451                      ;; Let the split methods have their say.
3452                      (cond
3453                       ;; No split name was found.
3454                       ((null split-name)
3455                        (read-file-name
3456                         (concat prompt " (default "
3457                                 (file-name-nondirectory default-name) ") ")
3458                         (file-name-directory default-name)
3459                         default-name))
3460                       ;; A single group name is returned.
3461                       ((stringp split-name)
3462                        (setq default-name
3463                              (funcall function split-name headers
3464                                       (symbol-value variable)))
3465                        (read-file-name
3466                         (concat prompt " (default "
3467                                 (file-name-nondirectory default-name) ") ")
3468                         (file-name-directory default-name)
3469                         default-name))
3470                       ;; A single split name was found
3471                       ((= 1 (length split-name))
3472                        (let* ((name (expand-file-name
3473                                      (car split-name)
3474                                      gnus-article-save-directory))
3475                               (dir (cond ((file-directory-p name)
3476                                           (file-name-as-directory name))
3477                                          ((file-exists-p name) name)
3478                                          (t gnus-article-save-directory))))
3479                          (read-file-name
3480                           (concat prompt " (default " name ") ")
3481                           dir name)))
3482                       ;; A list of splits was found.
3483                       (t
3484                        (setq split-name (nreverse split-name))
3485                        (let (result)
3486                          (let ((file-name-history
3487                                 (nconc split-name file-name-history)))
3488                            (setq result
3489                                  (expand-file-name
3490                                   (read-file-name
3491                                    (concat prompt " (`M-p' for defaults) ")
3492                                    gnus-article-save-directory
3493                                    (car split-name))
3494                                   gnus-article-save-directory)))
3495                          (car (push result file-name-history)))))))
3496                ;; Create the directory.
3497                (gnus-make-directory (file-name-directory file))
3498                ;; If we have read a directory, we append the default file name.
3499                (when (file-directory-p file)
3500                  (setq file (expand-file-name (file-name-nondirectory
3501                                                default-name)
3502                                               (file-name-as-directory file))))
3503                ;; Possibly translate some characters.
3504                (nnheader-translate-file-chars file))))))
3505     (gnus-make-directory (file-name-directory result))
3506     (set variable result)))
3507
3508 (defun gnus-article-archive-name (group)
3509   "Return the first instance of an \"Archive-name\" in the current buffer."
3510   (let ((case-fold-search t))
3511     (when (re-search-forward "archive-name: *\\([^ \n\t]+\\)[ \t]*$" nil t)
3512       (nnheader-concat gnus-article-save-directory
3513                        (match-string 1)))))
3514
3515 (defun gnus-article-nndoc-name (group)
3516   "If GROUP is an nndoc group, return the name of the parent group."
3517   (when (eq (car (gnus-find-method-for-group group)) 'nndoc)
3518     (gnus-group-get-parameter group 'save-article-group)))
3519
3520 (defun gnus-summary-save-in-rmail (&optional filename)
3521   "Append this article to Rmail file.
3522 Optional argument FILENAME specifies file name.
3523 Directory to save to is default to `gnus-article-save-directory'."
3524   (setq filename (gnus-read-save-file-name
3525                   "Save %s in rmail file:" filename
3526                   gnus-rmail-save-name gnus-newsgroup-name
3527                   gnus-current-headers 'gnus-newsgroup-last-rmail))
3528   (gnus-eval-in-buffer-window gnus-save-article-buffer
3529     (save-excursion
3530       (save-restriction
3531         (widen)
3532         (gnus-output-to-rmail filename))))
3533   filename)
3534
3535 (defun gnus-summary-save-in-mail (&optional filename)
3536   "Append this article to Unix mail file.
3537 Optional argument FILENAME specifies file name.
3538 Directory to save to is default to `gnus-article-save-directory'."
3539   (setq filename (gnus-read-save-file-name
3540                   "Save %s in Unix mail file:" filename
3541                   gnus-mail-save-name gnus-newsgroup-name
3542                   gnus-current-headers 'gnus-newsgroup-last-mail))
3543   (gnus-eval-in-buffer-window gnus-save-article-buffer
3544     (save-excursion
3545       (save-restriction
3546         (widen)
3547         (if (and (file-readable-p filename)
3548                  (file-regular-p filename)
3549                  (mail-file-babyl-p filename))
3550             (rmail-output-to-rmail-file filename t)
3551           (gnus-output-to-mail filename)))))
3552   filename)
3553
3554 (defun gnus-summary-save-in-file (&optional filename overwrite)
3555   "Append this article to file.
3556 Optional argument FILENAME specifies file name.
3557 Directory to save to is default to `gnus-article-save-directory'."
3558   (setq filename (gnus-read-save-file-name
3559                   "Save %s in file:" filename
3560                   gnus-file-save-name gnus-newsgroup-name
3561                   gnus-current-headers 'gnus-newsgroup-last-file))
3562   (gnus-eval-in-buffer-window gnus-save-article-buffer
3563     (save-excursion
3564       (save-restriction
3565         (widen)
3566         (when (and overwrite
3567                    (file-exists-p filename))
3568           (delete-file filename))
3569         (gnus-output-to-file filename))))
3570   filename)
3571
3572 (defun gnus-summary-write-to-file (&optional filename)
3573   "Write this article to a file, overwriting it if the file exists.
3574 Optional argument FILENAME specifies file name.
3575 The directory to save in defaults to `gnus-article-save-directory'."
3576   (gnus-summary-save-in-file nil t))
3577
3578 (defun gnus-summary-save-body-in-file (&optional filename)
3579   "Append this article body to a file.
3580 Optional argument FILENAME specifies file name.
3581 The directory to save in defaults to `gnus-article-save-directory'."
3582   (setq filename (gnus-read-save-file-name
3583                   "Save %s body in file:" filename
3584                   gnus-file-save-name gnus-newsgroup-name
3585                   gnus-current-headers 'gnus-newsgroup-last-file))
3586   (gnus-eval-in-buffer-window gnus-save-article-buffer
3587     (save-excursion
3588       (save-restriction
3589         (widen)
3590         (when (article-goto-body)
3591           (narrow-to-region (point) (point-max)))
3592         (gnus-output-to-file filename))))
3593   filename)
3594
3595 (defun gnus-summary-save-in-pipe (&optional command)
3596   "Pipe this article to subprocess."
3597   (setq command
3598         (cond ((and (eq command 'default)
3599                     gnus-last-shell-command)
3600                gnus-last-shell-command)
3601               ((stringp command)
3602                command)
3603               (t (read-string
3604                   (format
3605                    "Shell command on %s: "
3606                    (if (and gnus-number-of-articles-to-be-saved
3607                             (> gnus-number-of-articles-to-be-saved 1))
3608                        (format "these %d articles"
3609                                gnus-number-of-articles-to-be-saved)
3610                      "this article"))
3611                   gnus-last-shell-command))))
3612   (when (string-equal command "")
3613     (if gnus-last-shell-command
3614         (setq command gnus-last-shell-command)
3615       (error "A command is required")))
3616   (gnus-eval-in-buffer-window gnus-article-buffer
3617     (save-restriction
3618       (widen)
3619       (shell-command-on-region (point-min) (point-max) command nil)))
3620   (setq gnus-last-shell-command command))
3621
3622 (defun gnus-summary-pipe-to-muttprint (&optional command)
3623   "Pipe this article to muttprint."
3624   (setq command (read-string
3625                  "Print using command: " gnus-summary-muttprint-program
3626                  nil gnus-summary-muttprint-program))
3627   (gnus-summary-save-in-pipe command))
3628
3629 ;;; Article file names when saving.
3630
3631 (defun gnus-capitalize-newsgroup (newsgroup)
3632   "Capitalize NEWSGROUP name."
3633   (when (not (zerop (length newsgroup)))
3634     (concat (char-to-string (upcase (aref newsgroup 0)))
3635             (substring newsgroup 1))))
3636
3637 (defun gnus-Numeric-save-name (newsgroup headers &optional last-file)
3638   "Generate file name from NEWSGROUP, HEADERS, and optional LAST-FILE.
3639 If variable `gnus-use-long-file-name' is non-nil, it is ~/News/News.group/num.
3640 Otherwise, it is like ~/News/news/group/num."
3641   (let ((default
3642           (expand-file-name
3643            (concat (if (gnus-use-long-file-name 'not-save)
3644                        (gnus-capitalize-newsgroup newsgroup)
3645                      (gnus-newsgroup-directory-form newsgroup))
3646                    "/" (int-to-string (mail-header-number headers)))
3647            gnus-article-save-directory)))
3648     (if (and last-file
3649              (string-equal (file-name-directory default)
3650                            (file-name-directory last-file))
3651              (string-match "^[0-9]+$" (file-name-nondirectory last-file)))
3652         default
3653       (or last-file default))))
3654
3655 (defun gnus-numeric-save-name (newsgroup headers &optional last-file)
3656   "Generate file name from NEWSGROUP, HEADERS, and optional LAST-FILE.
3657 If variable `gnus-use-long-file-name' is non-nil, it is
3658 ~/News/news.group/num.  Otherwise, it is like ~/News/news/group/num."
3659   (let ((default
3660           (expand-file-name
3661            (concat (if (gnus-use-long-file-name 'not-save)
3662                        newsgroup
3663                      (gnus-newsgroup-directory-form newsgroup))
3664                    "/" (int-to-string (mail-header-number headers)))
3665            gnus-article-save-directory)))
3666     (if (and last-file
3667              (string-equal (file-name-directory default)
3668                            (file-name-directory last-file))
3669              (string-match "^[0-9]+$" (file-name-nondirectory last-file)))
3670         default
3671       (or last-file default))))
3672
3673 (defun gnus-plain-save-name (newsgroup headers &optional last-file)
3674   "Generate file name from NEWSGROUP, HEADERS, and optional LAST-FILE.
3675 If variable `gnus-use-long-file-name' is non-nil, it is
3676 ~/News/news.group.  Otherwise, it is like ~/News/news/group/news."
3677   (or last-file
3678       (expand-file-name
3679        (if (gnus-use-long-file-name 'not-save)
3680            newsgroup
3681          (file-relative-name
3682           (expand-file-name "news" (gnus-newsgroup-directory-form newsgroup))
3683           default-directory))
3684        gnus-article-save-directory)))
3685
3686 (defun gnus-sender-save-name (newsgroup headers &optional last-file)
3687   "Generate file name from sender."
3688   (let ((from (mail-header-from headers)))
3689     (expand-file-name
3690      (if (and from (string-match "\\([^ <]+\\)@" from))
3691          (match-string 1 from)
3692        "nobody")
3693      gnus-article-save-directory)))
3694
3695 (defun article-verify-x-pgp-sig ()
3696   "Verify X-PGP-Sig."
3697   (interactive)
3698   (if (gnus-buffer-live-p gnus-original-article-buffer)
3699       (let ((sig (with-current-buffer gnus-original-article-buffer
3700                    (gnus-fetch-field "X-PGP-Sig")))
3701             items info headers)
3702         (when (and sig
3703                    mml2015-use
3704                    (mml2015-clear-verify-function))
3705           (with-temp-buffer
3706             (insert-buffer-substring gnus-original-article-buffer)
3707             (setq items (split-string sig))
3708             (message-narrow-to-head)
3709             (let ((inhibit-point-motion-hooks t)
3710                   (case-fold-search t))
3711               ;; Don't verify multiple headers.
3712               (setq headers (mapconcat (lambda (header)
3713                                          (concat header ": "
3714                                                  (mail-fetch-field header)
3715                                                  "\n"))
3716                                        (split-string (nth 1 items) ",") "")))
3717             (delete-region (point-min) (point-max))
3718             (insert "-----BEGIN PGP SIGNED MESSAGE-----\n\n")
3719             (insert "X-Signed-Headers: " (nth 1 items) "\n")
3720             (insert headers)
3721             (widen)
3722             (forward-line)
3723             (while (not (eobp))
3724               (if (looking-at "^-")
3725                   (insert "- "))
3726               (forward-line))
3727             (insert "\n-----BEGIN PGP SIGNATURE-----\n")
3728             (insert "Version: " (car items) "\n\n")
3729             (insert (mapconcat 'identity (cddr items) "\n"))
3730             (insert "\n-----END PGP SIGNATURE-----\n")
3731             (let ((mm-security-handle (list (format "multipart/signed"))))
3732               (mml2015-clean-buffer)
3733               (let ((coding-system-for-write (or gnus-newsgroup-charset
3734                                                  'iso-8859-1)))
3735                 (funcall (mml2015-clear-verify-function)))
3736               (setq info
3737                     (or (mm-handle-multipart-ctl-parameter
3738                          mm-security-handle 'gnus-details)
3739                         (mm-handle-multipart-ctl-parameter
3740                          mm-security-handle 'gnus-info)))))
3741           (when info
3742             (let ((inhibit-read-only t) bface eface)
3743               (save-restriction
3744                 (message-narrow-to-head)
3745                 (goto-char (point-max))
3746                 (forward-line -1)
3747                 (setq bface (get-text-property (point-at-bol) 'face)
3748                       eface (get-text-property (1- (point-at-eol)) 'face))
3749                 (message-remove-header "X-Gnus-PGP-Verify")
3750                 (if (re-search-forward "^X-PGP-Sig:" nil t)
3751                     (forward-line)
3752                   (goto-char (point-max)))
3753                 (narrow-to-region (point) (point))
3754                 (insert "X-Gnus-PGP-Verify: " info "\n")
3755                 (goto-char (point-min))
3756                 (forward-line)
3757                 (while (not (eobp))
3758                   (if (not (looking-at "^[ \t]"))
3759                       (insert " "))
3760                   (forward-line))
3761                 ;; Do highlighting.
3762                 (goto-char (point-min))
3763                 (when (looking-at "\\([^:]+\\): *")
3764                   (put-text-property (match-beginning 1) (1+ (match-end 1))
3765                                      'face bface)
3766                   (put-text-property (match-end 0) (point-max)
3767                                      'face eface)))))))))
3768
3769 (defun article-verify-cancel-lock ()
3770   "Verify Cancel-Lock header."
3771   (interactive)
3772   (if (gnus-buffer-live-p gnus-original-article-buffer)
3773       (canlock-verify gnus-original-article-buffer)))
3774
3775 (defun article-monafy ()
3776   "Display body part with mona font."
3777   (interactive)
3778   (unless (if (featurep 'xemacs)
3779               (find-face 'gnus-mona-face)
3780             (facep 'gnus-mona-face))
3781     (require 'navi2ch-mona)
3782     (set-face-font (make-face 'gnus-mona-face) navi2ch-mona-font))
3783   (save-excursion
3784     (let ((inhibit-read-only t))
3785       (article-goto-body)
3786       (gnus-overlay-put
3787        (gnus-make-overlay (point) (point-max))
3788        'face 'gnus-mona-face))))
3789
3790 (eval-and-compile
3791   (mapcar
3792    (lambda (func)
3793      (let (afunc gfunc)
3794        (if (consp func)
3795            (setq afunc (car func)
3796                  gfunc (cdr func))
3797          (setq afunc func
3798                gfunc (intern (format "gnus-%s" func))))
3799        (defalias gfunc
3800          (when (fboundp afunc)
3801            `(lambda (&optional interactive &rest args)
3802               ,(documentation afunc t)
3803               (interactive (list t))
3804               (save-excursion
3805                 (set-buffer gnus-article-buffer)
3806                 (if interactive
3807                     (call-interactively ',afunc)
3808                   (apply ',afunc args))))))))
3809    '(article-hide-headers
3810      article-verify-x-pgp-sig
3811      article-verify-cancel-lock
3812      article-monafy
3813      article-hide-boring-headers
3814      article-treat-overstrike
3815      article-treat-ansi-sequences
3816      article-fill-long-lines
3817      article-capitalize-sentences
3818      article-remove-cr
3819      article-remove-leading-whitespace
3820      article-display-x-face
3821      article-display-face
3822      article-de-quoted-unreadable
3823      article-de-base64-unreadable
3824      article-decode-HZ
3825      article-wash-html
3826      article-unsplit-urls
3827      article-hide-list-identifiers
3828      article-strip-banner
3829      article-babel
3830      article-hide-pem
3831      article-hide-signature
3832      article-strip-headers-in-body
3833      article-remove-trailing-blank-lines
3834      article-strip-leading-blank-lines
3835      article-strip-multiple-blank-lines
3836      article-strip-leading-space
3837      article-strip-trailing-space
3838      article-strip-blank-lines
3839      article-strip-all-blank-lines
3840      article-date-local
3841      article-date-english
3842      article-date-iso8601
3843      article-date-original
3844      article-date-ut
3845      article-decode-mime-words
3846      article-decode-charset
3847      article-decode-encoded-words
3848      article-date-user
3849      article-date-lapsed
3850      article-emphasize
3851      article-treat-dumbquotes
3852      article-normalize-headers
3853      (article-show-all-headers . gnus-article-show-all-headers)
3854 ;;      (article-show-all . gnus-article-show-all)
3855      )))
3856 \f
3857 ;;;
3858 ;;; Gnus article mode
3859 ;;;
3860
3861 (put 'gnus-article-mode 'mode-class 'special)
3862
3863 (gnus-define-keys gnus-article-mode-map
3864   " " gnus-article-goto-next-page
3865   "\177" gnus-article-goto-prev-page
3866   [delete] gnus-article-goto-prev-page
3867   [backspace] gnus-article-goto-prev-page
3868   "\C-c^" gnus-article-refer-article
3869   "h" gnus-article-show-summary
3870   "s" gnus-article-show-summary
3871   "\C-c\C-m" gnus-article-mail
3872   "?" gnus-article-describe-briefly
3873   "e" gnus-summary-edit-article
3874   "<" beginning-of-buffer
3875   ">" end-of-buffer
3876   "\C-c\C-i" gnus-info-find-node
3877   "\C-c\C-b" gnus-bug
3878   "R" gnus-article-reply-with-original
3879   "F" gnus-article-followup-with-original
3880   "\C-hk" gnus-article-describe-key
3881   "\C-hc" gnus-article-describe-key-briefly
3882
3883   "\C-d" gnus-article-read-summary-keys
3884   "\M-*" gnus-article-read-summary-keys
3885   "\M-#" gnus-article-read-summary-keys
3886   "\M-^" gnus-article-read-summary-keys
3887   "\M-g" gnus-article-read-summary-keys)
3888
3889 ;; Define almost undefined keys to `gnus-article-read-summary-keys'.
3890 (let (keys)
3891   (let ((key 32))
3892     (while (<= key 127)
3893       (push (char-to-string key) keys)
3894       (incf key))
3895     (dolist (elem (accessible-keymaps gnus-summary-mode-map))
3896       (setq key (car elem))
3897       (when (and (> (length key) 0)
3898                  (not (eq 'menu-bar (aref key 0)))
3899                  (symbolp (lookup-key gnus-summary-mode-map key)))
3900         (push key keys))))
3901   (dolist (key keys)
3902     (unless (lookup-key gnus-article-mode-map key)
3903       (define-key gnus-article-mode-map key 'gnus-article-read-summary-keys))))
3904
3905 (defun gnus-article-make-menu-bar ()
3906   (unless (boundp 'gnus-article-commands-menu)
3907     (gnus-summary-make-menu-bar))
3908   (gnus-turn-off-edit-menu 'article)
3909   (unless (boundp 'gnus-article-article-menu)
3910     (easy-menu-define
3911      gnus-article-article-menu gnus-article-mode-map ""
3912      '("Article"
3913        ["Scroll forwards" gnus-article-goto-next-page t]
3914        ["Scroll backwards" gnus-article-goto-prev-page t]
3915        ["Show summary" gnus-article-show-summary t]
3916        ["Fetch Message-ID at point" gnus-article-refer-article t]
3917        ["Mail to address at point" gnus-article-mail t]
3918        ["Send a bug report" gnus-bug t]))
3919
3920     (easy-menu-define
3921      gnus-article-treatment-menu gnus-article-mode-map ""
3922      ;; Fixme: this should use :active (and maybe :visible).
3923      '("Treatment"
3924        ["Hide headers" gnus-article-hide-headers t]
3925        ["Hide signature" gnus-article-hide-signature t]
3926        ["Hide citation" gnus-article-hide-citation t]
3927        ["Treat overstrike" gnus-article-treat-overstrike t]
3928        ["Treat ANSI sequences" gnus-article-treat-ansi-sequences t]
3929        ["Remove carriage return" gnus-article-remove-cr t]
3930        ["Remove leading whitespace" gnus-article-remove-leading-whitespace t]
3931        ["Treat html" gnus-article-wash-html t]
3932        ["Remove newlines from within URLs" gnus-article-unsplit-urls t]
3933        ["Decode HZ" gnus-article-decode-HZ t]))
3934
3935     ;; Note "Commands" menu is defined in gnus-sum.el for consistency
3936
3937     ;; Note "Post" menu is defined in gnus-sum.el for consistency
3938
3939     (gnus-run-hooks 'gnus-article-menu-hook)))
3940
3941 (defun gnus-article-mode ()
3942   "Major mode for displaying an article.
3943
3944 All normal editing commands are switched off.
3945
3946 The following commands are available in addition to all summary mode
3947 commands:
3948 \\<gnus-article-mode-map>
3949 \\[gnus-article-next-page]\t Scroll the article one page forwards
3950 \\[gnus-article-prev-page]\t Scroll the article one page backwards
3951 \\[gnus-article-refer-article]\t Go to the article referred to by an article id near point
3952 \\[gnus-article-show-summary]\t Display the summary buffer
3953 \\[gnus-article-mail]\t Send a reply to the address near point
3954 \\[gnus-article-describe-briefly]\t Describe the current mode briefly
3955 \\[gnus-info-find-node]\t Go to the Gnus info node"
3956   (interactive)
3957   (kill-all-local-variables)
3958   (gnus-simplify-mode-line)
3959   (setq mode-name "Article")
3960   (setq major-mode 'gnus-article-mode)
3961   (make-local-variable 'minor-mode-alist)
3962   (unless (assq 'gnus-show-mime minor-mode-alist)
3963     (push (list 'gnus-show-mime " MIME") minor-mode-alist))
3964   (use-local-map gnus-article-mode-map)
3965   (when (gnus-visual-p 'article-menu 'menu)
3966     (gnus-article-make-menu-bar)
3967     (when gnus-summary-tool-bar-map
3968       (set (make-local-variable 'tool-bar-map) gnus-summary-tool-bar-map)))
3969   (gnus-update-format-specifications nil 'article-mode)
3970   (set (make-local-variable 'page-delimiter) gnus-page-delimiter)
3971   (set (make-local-variable 'gnus-page-broken) nil)
3972   (make-local-variable 'gnus-button-marker-list)
3973   (make-local-variable 'gnus-article-current-summary)
3974   (make-local-variable 'gnus-article-mime-handles)
3975   (make-local-variable 'gnus-article-decoded-p)
3976   (make-local-variable 'gnus-article-mime-handle-alist)
3977   (make-local-variable 'gnus-article-wash-types)
3978   (make-local-variable 'gnus-article-image-alist)
3979   (make-local-variable 'gnus-article-charset)
3980   (make-local-variable 'gnus-article-ignored-charsets)
3981   ;; Prevent recent Emacsen from displaying non-break space as "\ ".
3982   (set (make-local-variable 'nobreak-char-display) nil)
3983   (gnus-set-default-directory)
3984   (buffer-disable-undo)
3985   (setq buffer-read-only t
3986         show-trailing-whitespace nil)
3987   (set-syntax-table gnus-article-mode-syntax-table)
3988   (gnus-run-mode-hooks 'gnus-article-mode-hook))
3989
3990 (defun gnus-article-setup-buffer ()
3991   "Initialize the article buffer."
3992   (let* ((name (if gnus-single-article-buffer "*Article*"
3993                  (concat "*Article " gnus-newsgroup-name "*")))
3994          (original
3995           (progn (string-match "\\*Article" name)
3996                  (concat " *Original Article"
3997                          (substring name (match-end 0))))))
3998     (setq gnus-article-buffer name)
3999     (setq gnus-original-article-buffer original)
4000     (setq gnus-article-mime-handle-alist nil)
4001     ;; This might be a variable local to the summary buffer.
4002     (unless gnus-single-article-buffer
4003       (save-excursion
4004         (set-buffer gnus-summary-buffer)
4005         (setq gnus-article-buffer name)
4006         (setq gnus-original-article-buffer original)
4007         (gnus-set-global-variables)))
4008     (gnus-article-setup-highlight-words)
4009     ;; Init original article buffer.
4010     (save-excursion
4011       (set-buffer (gnus-get-buffer-create gnus-original-article-buffer))
4012       (set-buffer-multibyte t)
4013       (setq major-mode 'gnus-original-article-mode)
4014       (make-local-variable 'gnus-original-article))
4015     (if (and (get-buffer name)
4016              (with-current-buffer name
4017                (if gnus-article-edit-mode
4018                    (if (y-or-n-p "Article mode edit in progress; discard? ")
4019                        (progn
4020                          (set-buffer-modified-p nil)
4021                          (gnus-kill-buffer name)
4022                          (message "")
4023                          nil)
4024                      (error "Action aborted"))
4025                  t)))
4026         (save-excursion
4027           (set-buffer name)
4028           (set (make-local-variable 'gnus-article-edit-mode) nil)
4029           (buffer-disable-undo)
4030           (setq buffer-read-only t)
4031           ;; This list just keeps growing if we don't reset it.
4032           (setq gnus-button-marker-list nil)
4033           (unless (eq major-mode 'gnus-article-mode)
4034             (gnus-article-mode))
4035           (current-buffer))
4036       (save-excursion
4037         (set-buffer (gnus-get-buffer-create name))
4038         (gnus-article-mode)
4039         (make-local-variable 'gnus-summary-buffer)
4040         (gnus-summary-set-local-parameters gnus-newsgroup-name)
4041         (current-buffer)))))
4042
4043 ;; Set article window start at LINE, where LINE is the number of lines
4044 ;; from the head of the article.
4045 (defun gnus-article-set-window-start (&optional line)
4046   (set-window-start
4047    (gnus-get-buffer-window gnus-article-buffer t)
4048    (save-excursion
4049      (set-buffer gnus-article-buffer)
4050      (goto-char (point-min))
4051      (if (not line)
4052          (point-min)
4053        (gnus-message 6 "Moved to bookmark")
4054        (search-forward "\n\n" nil t)
4055        (forward-line line)
4056        (point)))))
4057
4058 ;;; @@ article filters
4059 ;;;
4060
4061 (defun gnus-article-display-mime-message ()
4062   "Article display method for MIME message."
4063   ;; called from `gnus-original-article-buffer'.
4064   (let (charset all-headers)
4065     (with-current-buffer gnus-summary-buffer
4066       (setq charset default-mime-charset
4067             all-headers gnus-have-all-headers))
4068     (make-local-variable 'default-mime-charset)
4069     (setq default-mime-charset charset)
4070     (with-current-buffer (get-buffer-create gnus-article-buffer)
4071       (make-local-variable 'default-mime-charset)
4072       (setq default-mime-charset charset))
4073     (mime-display-message mime-message-structure
4074                           gnus-article-buffer nil gnus-article-mode-map)
4075     (when all-headers
4076       (gnus-article-hide-headers nil -1)))
4077   (run-hooks 'gnus-mime-article-prepare-hook))
4078
4079 (defun gnus-article-display-traditional-message ()
4080   "Article display method for traditional message."
4081   (set-buffer gnus-article-buffer)
4082   (let ((inhibit-read-only t))
4083     (erase-buffer)
4084     (insert-buffer-substring gnus-original-article-buffer)))
4085
4086 (defun gnus-article-make-full-mail-header (&optional number charset)
4087   "Create a new mail header structure in a raw article buffer."
4088   (unless (and number charset)
4089     (save-current-buffer
4090       (set-buffer gnus-summary-buffer)
4091       (unless number
4092         (setq number (or (cdr gnus-article-current) 0)))
4093       (unless charset
4094         (setq charset (or default-mime-charset 'x-ctext)))))
4095   (goto-char (point-min))
4096   (let ((header-end (if (search-forward "\n\n" nil t)
4097                         (1- (point))
4098                       (goto-char (point-max))))
4099         (chars (- (point-max) (point)))
4100         (lines (count-lines (point) (point-max)))
4101         (default-mime-charset charset)
4102         xref)
4103     (narrow-to-region (point-min) header-end)
4104     (setq xref (std11-fetch-field "xref"))
4105     (prog1
4106         (make-full-mail-header
4107          number
4108          (std11-fetch-field "subject")
4109          (std11-fetch-field "from")
4110          (std11-fetch-field "date")
4111          (std11-fetch-field "message-id")
4112          (std11-fetch-field "references")
4113          chars
4114          lines
4115          (when xref (concat "Xref: " xref)))
4116       (widen))))
4117
4118 (defun gnus-article-prepare (article &optional all-headers header)
4119   "Prepare ARTICLE in article mode buffer.
4120 ARTICLE should either be an article number or a Message-ID.
4121 If ARTICLE is an id, HEADER should be the article headers.
4122 If ALL-HEADERS is non-nil, no headers are hidden."
4123   (save-excursion
4124     ;; Make sure we start in a summary buffer.
4125     (unless (eq major-mode 'gnus-summary-mode)
4126       (set-buffer gnus-summary-buffer))
4127     (setq gnus-summary-buffer (current-buffer))
4128     (let* ((gnus-article (if header (mail-header-number header) article))
4129            (summary-buffer (current-buffer))
4130            (gnus-tmp-internal-hook gnus-article-internal-prepare-hook)
4131            (group gnus-newsgroup-name)
4132            result)
4133       (save-excursion
4134         (gnus-article-setup-buffer)
4135         (set-buffer gnus-article-buffer)
4136         ;; Deactivate active regions.
4137         (when (and (boundp 'transient-mark-mode)
4138                    transient-mark-mode)
4139           (setq mark-active nil))
4140         (if (not (setq result (let ((inhibit-read-only t))
4141                                 (gnus-request-article-this-buffer
4142                                  article group))))
4143             ;; There is no such article.
4144             (save-excursion
4145               (when (and (numberp article)
4146                          (not (memq article gnus-newsgroup-sparse)))
4147                 (setq gnus-article-current
4148                       (cons gnus-newsgroup-name article))
4149                 (set-buffer gnus-summary-buffer)
4150                 (setq gnus-current-article article)
4151                 (if (and (memq article gnus-newsgroup-undownloaded)
4152                          (not (gnus-online (gnus-find-method-for-group
4153                                             gnus-newsgroup-name))))
4154                     (progn
4155                       (gnus-summary-set-agent-mark article)
4156                       (message "Message marked for downloading"))
4157                   (gnus-summary-mark-article article gnus-canceled-mark)
4158                   (unless (memq article gnus-newsgroup-sparse)
4159                     (gnus-error 1 "No such article (may have expired or been canceled)")))))
4160           (if (or (eq result 'pseudo)
4161                   (eq result 'nneething))
4162               (progn
4163                 (save-excursion
4164                   (set-buffer summary-buffer)
4165                   (push article gnus-newsgroup-history)
4166                   (setq gnus-last-article gnus-current-article
4167                         gnus-current-article 0
4168                         gnus-current-headers nil
4169                         gnus-article-current nil)
4170                   (if (eq result 'nneething)
4171                       (gnus-configure-windows 'summary)
4172                     (gnus-configure-windows 'article))
4173                   (gnus-set-global-variables))
4174                 (let ((gnus-article-mime-handle-alist-1
4175                        gnus-article-mime-handle-alist))
4176                   (gnus-set-mode-line 'article)))
4177             ;; The result from the `request' was an actual article -
4178             ;; or at least some text that is now displayed in the
4179             ;; article buffer.
4180             (when (and (numberp article)
4181                        (not (eq article gnus-current-article)))
4182               ;; Seems like a new article has been selected.
4183               ;; `gnus-current-article' must be an article number.
4184               (save-excursion
4185                 (set-buffer summary-buffer)
4186                 (push article gnus-newsgroup-history)
4187                 (setq gnus-last-article gnus-current-article
4188                       gnus-current-article article
4189                       gnus-current-headers
4190                       (gnus-summary-article-header gnus-current-article)
4191                       gnus-article-current
4192                       (cons gnus-newsgroup-name gnus-current-article))
4193                 (unless (vectorp gnus-current-headers)
4194                   (setq gnus-current-headers nil))
4195                 (gnus-summary-goto-subject gnus-current-article)
4196                 (when (gnus-summary-show-thread)
4197                   ;; If the summary buffer really was folded, the
4198                   ;; previous goto may not actually have gone to
4199                   ;; the right article, but the thread root instead.
4200                   ;; So we go again.
4201                   (gnus-summary-goto-subject gnus-current-article))
4202                 (gnus-run-hooks 'gnus-mark-article-hook)
4203                 (gnus-set-mode-line 'summary)
4204                 (when (gnus-visual-p 'article-highlight 'highlight)
4205                   (gnus-run-hooks 'gnus-visual-mark-article-hook))
4206                 ;; Set the global newsgroup variables here.
4207                 (gnus-set-global-variables)
4208                 (setq gnus-have-all-headers
4209                       (or all-headers gnus-show-all-headers))))
4210             (save-excursion
4211               (gnus-configure-windows 'article))
4212             (when (or (numberp article)
4213                       (stringp article))
4214               (gnus-article-prepare-display)
4215               ;; Do page break.
4216               (goto-char (point-min))
4217               (when gnus-break-pages
4218                 (gnus-narrow-to-page)))
4219             (let ((gnus-article-mime-handle-alist-1
4220                    gnus-article-mime-handle-alist))
4221               (gnus-set-mode-line 'article))
4222             (article-goto-body)
4223             (unless (bobp)
4224               (forward-line -1))
4225             (set-window-point (get-buffer-window (current-buffer)) (point))
4226             (gnus-configure-windows 'article)
4227             t))))))
4228
4229 (defun gnus-article-prepare-mime-display (&optional number)
4230   (goto-char (point-min))
4231   (when (re-search-forward "^[^\t ]+:" nil t)
4232     (goto-char (match-beginning 0)))
4233   (let ((entity (if (eq 1 (point-min))
4234                     (get-text-property 1 'mime-view-entity)
4235                   (get-text-property (point) 'mime-view-entity)))
4236         last-entity child-entity next type)
4237     (setq child-entity (mime-entity-children entity))
4238     (if child-entity
4239         (setq last-entity (nth (1- (length child-entity))
4240                                child-entity))
4241       (setq last-entity entity))
4242     (save-restriction
4243       (narrow-to-region (point)
4244                         (if (search-forward "\n\n" nil t)
4245                             (point)
4246                           (point-max)))
4247       (gnus-article-save-original-date (gnus-treat-article 'head))
4248       (put-text-property (point-min) (point-max) 'article-treated-header t)
4249       (goto-char (point-max)))
4250     (while (and (not (eobp)) entity)
4251       (setq next (set-marker
4252                   (make-marker)
4253                   (next-single-property-change (point) 'mime-view-entity
4254                                                nil (point-max))))
4255       (let ((types (mime-entity-content-type entity)))
4256         (while (eq 'multipart (mime-content-type-primary-type types))
4257           (setq entity (car (mime-entity-children entity))
4258                 types (mime-entity-content-type entity)))
4259         (when types
4260           (setq type (format "%s/%s"
4261                              (mime-content-type-primary-type types)
4262                              (mime-content-type-subtype types)))))
4263       (if (string-equal type "message/rfc822")
4264           (progn
4265             (setq next (point))
4266             (let ((children (mime-entity-children entity))
4267                   last-children)
4268               (when children
4269                 (setq last-children (nth (1- (length children)) children))
4270                 (while
4271                     (and
4272                      (not (eq last-children
4273                               (get-text-property next 'mime-view-entity)))
4274                      (setq next
4275                            (next-single-property-change next
4276                                                         'mime-view-entity
4277                                                         nil (point-max)))))))
4278             (setq next (next-single-property-change next 'mime-view-entity
4279                                                     nil (point-max)))
4280             (save-restriction
4281               (narrow-to-region (point) next)
4282               (gnus-article-prepare-mime-display)
4283               (goto-char (point-max)))
4284             (setq entity (get-text-property (point) 'mime-view-entity)))
4285         (save-restriction
4286           (narrow-to-region (point) next)
4287           ;; Kludge. We have to count true number, but for now,
4288           ;; part number is here only to achieve `last'.
4289           (gnus-treat-article nil 1
4290                               (if (eq entity last-entity)
4291                                   1 2)
4292                               type)
4293           (goto-char (point-max)))
4294         (setq entity (get-text-property next 'mime-view-entity))))))
4295
4296 ;;;###autoload
4297 (defun gnus-article-prepare-display ()
4298   "Make the current buffer look like a nice article."
4299   (let ((gnus-article-buffer (current-buffer))
4300         buffer-read-only
4301         (inhibit-read-only t))
4302     (unless (eq major-mode 'gnus-article-mode)
4303       (gnus-article-mode))
4304     (setq buffer-read-only nil
4305           gnus-button-marker-list nil
4306           gnus-article-wash-types nil
4307           gnus-article-image-alist nil)
4308     (save-restriction
4309       (widen)
4310       (static-if (featurep 'xemacs)
4311           (map-extents (lambda (extent maparg) (delete-extent extent)))
4312         (let ((lists (overlay-lists)))
4313           (dolist (overlay (nconc (car lists) (cdr lists)))
4314             (delete-overlay overlay)))))
4315     (gnus-run-hooks 'gnus-tmp-internal-hook))
4316   (let ((show-mime (unless (member gnus-newsgroup-name '("nndraft:delayed"
4317                                                          "nndraft:drafts"))
4318                      gnus-show-mime))
4319         (inhibit-read-only t))
4320     (set-buffer gnus-original-article-buffer)
4321     ;; Display message.
4322     (setq mime-message-structure gnus-current-headers)
4323     (mime-buffer-entity-set-buffer-internal mime-message-structure
4324                                             gnus-original-article-buffer)
4325     (mime-entity-set-representation-type-internal mime-message-structure
4326                                                   'mime-buffer-entity)
4327     (luna-send mime-message-structure 'initialize-instance
4328                mime-message-structure)
4329     (if show-mime
4330         (let (mime-display-header-hook mime-display-text/plain-hook)
4331           (funcall gnus-article-display-method-for-mime))
4332       (funcall gnus-article-display-method-for-traditional))
4333     ;; Call the treatment functions.
4334     (save-restriction
4335       (widen)
4336       (if show-mime
4337           (gnus-article-prepare-mime-display)
4338         (narrow-to-region (goto-char (point-min))
4339                           (if (search-forward "\n\n" nil t)
4340                               (point)
4341                             (point-max)))
4342         (gnus-article-save-original-date (gnus-treat-article 'head))
4343         (put-text-property (point-min) (point-max) 'article-treated-header t)
4344         (goto-char (point-max))
4345         (widen)
4346         (narrow-to-region (point) (point-max))
4347         (gnus-treat-article nil))
4348       (put-text-property (point-min) (point-max) 'read-only nil)))
4349   (gnus-run-hooks 'gnus-article-prepare-hook))
4350
4351 (defun gnus-article-decode-article-as-default-mime-charset ()
4352   "Decode an article as `default-mime-charset'.  It won't work if the
4353 value of the variable `gnus-show-mime' is non-nil."
4354   (unless (or gnus-show-mime
4355               (member gnus-newsgroup-name '("nndraft:delayed"
4356                                             "nndraft:drafts")))
4357     (set (make-local-variable 'default-mime-charset)
4358          (with-current-buffer gnus-summary-buffer
4359            default-mime-charset))
4360     (decode-mime-charset-region (point-min) (point-max)
4361                                 default-mime-charset)))
4362
4363 ;;;
4364 ;;; Gnus MIME viewing functions
4365 ;;;
4366
4367 (defvar gnus-mime-button-line-format "%{%([%p. %d%T]%)%}%e\n"
4368   "Format of the MIME buttons.
4369
4370 Valid specifiers include:
4371 %t  The MIME type
4372 %T  MIME type, along with additional info
4373 %n  The `name' parameter
4374 %d  The description, if any
4375 %l  The length of the encoded part
4376 %p  The part identifier number
4377 %e  Dots if the part isn't displayed
4378
4379 General format specifiers can also be used.  See Info node
4380 `(gnus)Formatting Variables'.")
4381
4382 (defvar gnus-mime-button-line-format-alist
4383   '((?t gnus-tmp-type ?s)
4384     (?T gnus-tmp-type-long ?s)
4385     (?n gnus-tmp-name ?s)
4386     (?d gnus-tmp-description ?s)
4387     (?p gnus-tmp-id ?s)
4388     (?l gnus-tmp-length ?d)
4389     (?e gnus-tmp-dots ?s)))
4390
4391 (defvar gnus-mime-button-commands
4392   '((gnus-article-press-button "\r" "Toggle Display")
4393     (gnus-mime-view-part "v" "View Interactively...")
4394     (gnus-mime-view-part-as-type "t" "View As Type...")
4395     (gnus-mime-view-part-as-charset "C" "View As charset...")
4396     (gnus-mime-save-part "o" "Save...")
4397     (gnus-mime-save-part-and-strip "\C-o" "Save and Strip")
4398     (gnus-mime-delete-part "d" "Delete part")
4399     (gnus-mime-copy-part "c" "View As Text, In Other Buffer")
4400     (gnus-mime-inline-part "i" "View As Text, In This Buffer")
4401     (gnus-mime-view-part-internally "E" "View Internally")
4402     (gnus-mime-view-part-externally "e" "View Externally")
4403     (gnus-mime-print-part "p" "Print")
4404     (gnus-mime-pipe-part "|" "Pipe To Command...")
4405     (gnus-mime-action-on-part "." "Take action on the part...")))
4406
4407 (defun gnus-article-mime-part-status ()
4408   (with-current-buffer gnus-article-buffer
4409     (let ((entity (get-text-property (point-min) 'mime-view-entity))
4410           children)
4411       (if (and entity
4412                (setq children (mime-entity-children entity))
4413                (setq children (length children)))
4414           (if (eq 1 children)
4415               " (1 part)"
4416             (format " (%d parts)" children))
4417         ""))))
4418
4419 (defvar gnus-mime-button-map
4420   (let ((map (make-sparse-keymap)))
4421     (define-key map gnus-mouse-2 'gnus-article-push-button)
4422     (define-key map gnus-down-mouse-3 'gnus-mime-button-menu)
4423     (dolist (c gnus-mime-button-commands)
4424       (define-key map (cadr c) (car c)))
4425     map))
4426
4427 (easy-menu-define
4428   gnus-mime-button-menu gnus-mime-button-map "MIME button menu."
4429   `("MIME Part"
4430     ,@(mapcar (lambda (c)
4431                 (vector (caddr c) (car c) :enable t))
4432               gnus-mime-button-commands)))
4433
4434 (defun gnus-mime-button-menu (event prefix)
4435  "Construct a context-sensitive menu of MIME commands."
4436  (interactive "e\nP")
4437  (save-window-excursion
4438    (let ((pos (event-start event)))
4439      (select-window (posn-window pos))
4440      (goto-char (posn-point pos))
4441      (gnus-article-check-buffer)
4442      (popup-menu gnus-mime-button-menu nil prefix))))
4443
4444 (defun gnus-mime-view-all-parts (&optional handles)
4445   "View all the MIME parts."
4446   (interactive)
4447   (with-current-buffer gnus-article-buffer
4448     (let ((handles (or handles gnus-article-mime-handles))
4449           (mail-parse-charset gnus-newsgroup-charset)
4450           (mail-parse-ignored-charsets
4451            (with-current-buffer gnus-summary-buffer
4452              gnus-newsgroup-ignored-charsets)))
4453       (when handles
4454         (mm-remove-parts handles)
4455         (goto-char (point-min))
4456         (or (search-forward "\n\n") (goto-char (point-max)))
4457         (let ((inhibit-read-only t))
4458           (delete-region (point) (point-max))
4459           (mm-display-parts handles))))))
4460
4461 (defun gnus-article-jump-to-part (n)
4462   "Jump to MIME part N."
4463   (interactive "P")
4464   (pop-to-buffer gnus-article-buffer)
4465   ;; FIXME: why is it necessary?
4466   (sit-for 0)
4467   (let ((parts (length gnus-article-mime-handle-alist)))
4468     (or n (setq n
4469                 (string-to-number
4470                  (read-string ;; Emacs 21 doesn't have `read-number'.
4471                   (format "Jump to part (2..%s): " parts)))))
4472     (unless (and (integerp n) (<= n parts) (>= n 1))
4473       (setq n
4474             (progn
4475               (gnus-message 7 "Invalid part `%s', using %s instead."
4476                             n parts)
4477               parts)))
4478     (gnus-message 9 "Jumping to part %s." n)
4479     (cond ((>= gnus-auto-select-part 1)
4480            (while (and (<= n parts)
4481                        (not (gnus-article-goto-part n)))
4482              (setq n (1+ n))))
4483           ((< gnus-auto-select-part 0)
4484            (while (and (>= n 1)
4485                        (not (gnus-article-goto-part n)))
4486              (setq n (1- n))))
4487           (t
4488            (gnus-article-goto-part n)))))
4489
4490 (eval-when-compile
4491   (defsubst gnus-article-edit-part (handles &optional current-id)
4492     "Edit an article in order to delete a mime part.
4493 This function is exclusively used by `gnus-mime-save-part-and-strip'
4494 and `gnus-mime-delete-part', and not provided at run-time normally."
4495     (gnus-article-edit-article
4496      `(lambda ()
4497         (buffer-disable-undo)
4498         (erase-buffer)
4499         (let ((mail-parse-charset (or gnus-article-charset
4500                                       ',gnus-newsgroup-charset))
4501               (mail-parse-ignored-charsets
4502                (or gnus-article-ignored-charsets
4503                    ',gnus-newsgroup-ignored-charsets))
4504               (mbl mml-buffer-list))
4505           (setq mml-buffer-list nil)
4506           (insert-buffer-substring gnus-original-article-buffer)
4507           (mime-to-mml ',handles)
4508           (setq gnus-article-mime-handles nil)
4509           (let ((mbl1 mml-buffer-list))
4510             (setq mml-buffer-list mbl)
4511             (set (make-local-variable 'mml-buffer-list) mbl1))
4512           (gnus-make-local-hook 'kill-buffer-hook)
4513           (add-hook 'kill-buffer-hook 'mml-destroy-buffers t t)))
4514      `(lambda (no-highlight)
4515         (let ((mail-parse-charset (or gnus-article-charset
4516                                       ',gnus-newsgroup-charset))
4517               (message-options message-options)
4518               (message-options-set-recipient)
4519               (mail-parse-ignored-charsets
4520                (or gnus-article-ignored-charsets
4521                    ',gnus-newsgroup-ignored-charsets)))
4522           (mml-to-mime)
4523           (mml-destroy-buffers)
4524           (remove-hook 'kill-buffer-hook
4525                        'mml-destroy-buffers t)
4526           (kill-local-variable 'mml-buffer-list))
4527         (gnus-summary-edit-article-done
4528          ,(or (mail-header-references gnus-current-headers) "")
4529          ,(gnus-group-read-only-p)
4530          ,gnus-summary-buffer no-highlight))
4531      t)
4532     (gnus-article-edit-done)
4533     (gnus-summary-expand-window)
4534     (gnus-summary-show-article)
4535     (when (and current-id (integerp gnus-auto-select-part))
4536       (gnus-article-jump-to-part
4537        (+ current-id gnus-auto-select-part)))))
4538
4539 (defun gnus-mime-save-part-and-strip ()
4540   "Save the MIME part under point then replace it with an external body."
4541   (interactive)
4542   (gnus-article-check-buffer)
4543   (when (gnus-group-read-only-p)
4544     (error "The current group does not support deleting of parts"))
4545   (when (mm-complicated-handles gnus-article-mime-handles)
4546     (error "\
4547 The current article has a complicated MIME structure, giving up..."))
4548   (let* ((data (get-text-property (point) 'gnus-data))
4549          (id (get-text-property (point) 'gnus-part))
4550          file param
4551          (handles gnus-article-mime-handles))
4552     (setq file (and data (mm-save-part data "Delete MIME part and save to: ")))
4553     (when file
4554       (with-current-buffer (mm-handle-buffer data)
4555         (erase-buffer)
4556         (insert "Content-Type: " (mm-handle-media-type data))
4557         (mml-insert-parameter-string (cdr (mm-handle-type data))
4558                                      '(charset))
4559         (insert "\n")
4560         (insert "Content-ID: " (message-make-message-id) "\n")
4561         (insert "Content-Transfer-Encoding: binary\n")
4562         (insert "\n"))
4563       (setcdr data
4564               (cdr (mm-make-handle nil
4565                                    `("message/external-body"
4566                                      (access-type . "LOCAL-FILE")
4567                                      (name . ,file)))))
4568       ;; (set-buffer gnus-summary-buffer)
4569       (gnus-article-edit-part handles id))))
4570
4571 (defun gnus-mime-delete-part ()
4572   "Delete the MIME part under point.
4573 Replace it with some information about the removed part."
4574   (interactive)
4575   (gnus-article-check-buffer)
4576   (when (gnus-group-read-only-p)
4577     (error "The current group does not support deleting of parts"))
4578   (when (mm-complicated-handles gnus-article-mime-handles)
4579     (error "\
4580 The current article has a complicated MIME structure, giving up..."))
4581   (when (or gnus-expert-user
4582             (gnus-yes-or-no-p "\
4583 Deleting parts may malfunction or destroy the article; continue? "))
4584     (let* ((data (get-text-property (point) 'gnus-data))
4585            (id (get-text-property (point) 'gnus-part))
4586            (handles gnus-article-mime-handles)
4587            (none "(none)")
4588            (description
4589             (or
4590              (mail-decode-encoded-word-string (or (mm-handle-description data)
4591                                                   none))))
4592            (filename
4593             (or (mail-content-type-get (mm-handle-disposition data) 'filename)
4594                 none))
4595            (type (mm-handle-media-type data)))
4596       (unless data
4597         (error "No MIME part under point"))
4598       (with-current-buffer (mm-handle-buffer data)
4599         (let ((bsize (format "%s" (buffer-size))))
4600           (erase-buffer)
4601           (insert
4602            (concat
4603             ",----\n"
4604             "| The following attachment has been deleted:\n"
4605             "|\n"
4606             "| Type:           " type "\n"
4607             "| Filename:       " filename "\n"
4608             "| Size (encoded): " bsize " Byte\n"
4609             "| Description:    " description "\n"
4610             "`----\n"))
4611           (setcdr data
4612                   (cdr (mm-make-handle
4613                         nil `("text/plain") nil nil
4614                         (list "attachment")
4615                         (format "Deleted attachment (%s bytes)" bsize))))))
4616       ;; (set-buffer gnus-summary-buffer)
4617       (gnus-article-edit-part handles id))))
4618
4619 (defun gnus-mime-save-part ()
4620   "Save the MIME part under point."
4621   (interactive)
4622   (gnus-article-check-buffer)
4623   (let ((data (get-text-property (point) 'gnus-data)))
4624     (when data
4625       (mm-save-part data))))
4626
4627 (defun gnus-mime-pipe-part ()
4628   "Pipe the MIME part under point to a process."
4629   (interactive)
4630   (gnus-article-check-buffer)
4631   (let ((data (get-text-property (point) 'gnus-data)))
4632     (when data
4633       (mm-pipe-part data))))
4634
4635 (defun gnus-mime-view-part ()
4636   "Interactively choose a viewing method for the MIME part under point."
4637   (interactive)
4638   (gnus-article-check-buffer)
4639   (let ((data (get-text-property (point) 'gnus-data)))
4640     (when data
4641       (setq gnus-article-mime-handles
4642             (mm-merge-handles
4643              gnus-article-mime-handles (setq data (copy-sequence data))))
4644       (mm-interactively-view-part data))))
4645
4646 (defun gnus-mime-view-part-as-type-internal ()
4647   (gnus-article-check-buffer)
4648   (let* ((name (mail-content-type-get
4649                 (mm-handle-type (get-text-property (point) 'gnus-data))
4650                 'name))
4651          (def-type (and name (mm-default-file-encoding name))))
4652     (and def-type (cons def-type 0))))
4653
4654 (defun gnus-mime-view-part-as-type (&optional mime-type)
4655   "Choose a MIME media type, and view the part as such."
4656   (interactive)
4657   (unless mime-type
4658     (setq mime-type (completing-read
4659                      "View as MIME type: "
4660                      (mapcar #'list (mailcap-mime-types))
4661                      nil nil
4662                      (gnus-mime-view-part-as-type-internal))))
4663   (gnus-article-check-buffer)
4664   (let ((handle (get-text-property (point) 'gnus-data)))
4665     (when handle
4666       (setq handle
4667             (mm-make-handle (mm-handle-buffer handle)
4668                             (cons mime-type (cdr (mm-handle-type handle)))
4669                             (mm-handle-encoding handle)
4670                             (mm-handle-undisplayer handle)
4671                             (mm-handle-disposition handle)
4672                             (mm-handle-description handle)
4673                             nil
4674                             (mm-handle-id handle)))
4675       (setq gnus-article-mime-handles
4676             (mm-merge-handles gnus-article-mime-handles handle))
4677       (gnus-mm-display-part handle))))
4678
4679 (defun gnus-mime-copy-part (&optional handle arg)
4680   "Put the MIME part under point into a new buffer.
4681 If `auto-compression-mode' is enabled, compressed files like .gz and .bz2
4682 are decompressed."
4683   (interactive (list nil current-prefix-arg))
4684   (gnus-article-check-buffer)
4685   (unless handle
4686     (setq handle (get-text-property (point) 'gnus-data)))
4687   (when handle
4688     (let ((filename (or (mail-content-type-get (mm-handle-disposition handle)
4689                                                'name)
4690                         (mail-content-type-get (mm-handle-disposition handle)
4691                                                'filename)))
4692           contents dont-decode charset coding-system)
4693       (mm-with-unibyte-buffer
4694         (mm-insert-part handle)
4695         (setq contents (or (condition-case nil
4696                                (mm-decompress-buffer filename nil 'sig)
4697                              (error
4698                               (setq dont-decode t)
4699                               nil))
4700                            (buffer-string))))
4701       (setq filename (cond (filename (file-name-nondirectory filename))
4702                            (dont-decode "*raw data*")
4703                            (t "*decoded*")))
4704       (cond
4705        (dont-decode)
4706        ((not arg)
4707         (unless (setq charset (mail-content-type-get
4708                                (mm-handle-type handle) 'charset))
4709           (unless (setq coding-system (mm-with-unibyte-buffer
4710                                         (insert contents)
4711                                         (mm-find-buffer-file-coding-system)))
4712             (setq charset gnus-newsgroup-charset))))
4713        ((numberp arg)
4714         (setq charset (or (cdr (assq arg
4715                                      gnus-summary-show-article-charset-alist))
4716                           (mm-read-coding-system "Charset: ")))))
4717       (switch-to-buffer (generate-new-buffer filename))
4718       (if (or coding-system
4719               (and charset
4720                    (setq coding-system (mm-charset-to-coding-system charset))
4721                    (not (eq charset 'ascii))))
4722           (progn
4723             (mm-enable-multibyte)
4724             (insert (mm-decode-coding-string contents coding-system))
4725             (setq buffer-file-coding-system
4726                   (if (boundp 'last-coding-system-used)
4727                       (symbol-value 'last-coding-system-used)
4728                     coding-system)))
4729         (mm-disable-multibyte)
4730         (insert contents)
4731         (setq buffer-file-coding-system mm-binary-coding-system))
4732       ;; We do it this way to make `normal-mode' set the appropriate mode.
4733       (unwind-protect
4734           (progn
4735             (setq buffer-file-name (expand-file-name filename))
4736             (normal-mode))
4737         (setq buffer-file-name nil))
4738       (goto-char (point-min)))))
4739
4740 (defun gnus-mime-print-part (&optional handle filename)
4741   "Print the MIME part under point."
4742   (interactive (list nil (ps-print-preprint current-prefix-arg)))
4743   (gnus-article-check-buffer)
4744   (let* ((handle (or handle (get-text-property (point) 'gnus-data)))
4745          (contents (and handle (mm-get-part handle)))
4746          (file (mm-make-temp-file (expand-file-name "mm." mm-tmp-directory)))
4747          (printer (mailcap-mime-info (mm-handle-media-type handle) "print")))
4748     (when contents
4749         (if printer
4750             (unwind-protect
4751                 (progn
4752                   (mm-save-part-to-file handle file)
4753                   (call-process shell-file-name nil
4754                                 (generate-new-buffer " *mm*")
4755                                 nil
4756                                 shell-command-switch
4757                                 (mm-mailcap-command
4758                                  printer file (mm-handle-type handle))))
4759               (delete-file file))
4760           (with-temp-buffer
4761             (insert contents)
4762             (gnus-print-buffer))
4763           (ps-despool filename)))))
4764
4765 (defun gnus-mime-inline-part (&optional handle arg)
4766   "Insert the MIME part under point into the current buffer.
4767 Compressed files like .gz and .bz2 are decompressed."
4768   (interactive (list nil current-prefix-arg))
4769   (gnus-article-check-buffer)
4770   (unless handle
4771     (setq handle (get-text-property (point) 'gnus-data)))
4772   (when handle
4773     (let ((b (point))
4774           (inhibit-read-only t)
4775           contents charset coding-system)
4776       (if (and (not arg) (mm-handle-undisplayer handle))
4777           (mm-remove-part handle)
4778         (mm-with-unibyte-buffer
4779           (mm-insert-part handle)
4780           (setq contents
4781                 (or (mm-decompress-buffer
4782                      (or (mail-content-type-get (mm-handle-disposition handle)
4783                                                 'name)
4784                          (mail-content-type-get (mm-handle-disposition handle)
4785                                                 'filename))
4786                      nil t)
4787                     (buffer-string))))
4788         (cond
4789          ((not arg)
4790           (unless (setq charset (mail-content-type-get
4791                                  (mm-handle-type handle) 'charset))
4792             (unless (setq coding-system
4793                           (mm-with-unibyte-buffer
4794                             (insert contents)
4795                             (mm-find-buffer-file-coding-system)))
4796               (setq charset gnus-newsgroup-charset))))
4797          ((numberp arg)
4798           (if (mm-handle-undisplayer handle)
4799               (mm-remove-part handle))
4800           (setq charset
4801                 (or (cdr (assq arg
4802                                gnus-summary-show-article-charset-alist))
4803                     (mm-read-coding-system "Charset: "))))
4804          (t
4805           (if (mm-handle-undisplayer handle)
4806               (mm-remove-part handle))))
4807         (forward-line 2)
4808         (mm-insert-inline
4809          handle
4810          (if (or coding-system
4811                  (and charset
4812                       (setq coding-system
4813                             (mm-charset-to-coding-system charset))
4814                       (not (eq coding-system 'ascii))))
4815              (mm-decode-coding-string contents coding-system)
4816            (mm-string-to-multibyte contents)))
4817         (goto-char b)))))
4818
4819 (defun gnus-mime-view-part-as-charset (&optional handle arg)
4820   "Insert the MIME part under point into the current buffer using the
4821 specified charset."
4822   (interactive (list nil current-prefix-arg))
4823   (gnus-article-check-buffer)
4824   (let* ((handle (or handle (get-text-property (point) 'gnus-data)))
4825          contents charset
4826          (b (point))
4827          (inhibit-read-only t))
4828     (when handle
4829       (if (mm-handle-undisplayer handle)
4830           (mm-remove-part handle))
4831       (let ((gnus-newsgroup-charset
4832              (or (cdr (assq arg
4833                             gnus-summary-show-article-charset-alist))
4834                  (mm-read-coding-system "Charset: ")))
4835             (gnus-newsgroup-ignored-charsets 'gnus-all))
4836         (gnus-article-press-button)))))
4837
4838 (defun gnus-mime-view-part-externally (&optional handle)
4839   "View the MIME part under point with an external viewer."
4840   (interactive)
4841   (gnus-article-check-buffer)
4842   (let* ((handle (or handle (get-text-property (point) 'gnus-data)))
4843          (mm-user-display-methods nil)
4844          (mm-inlined-types nil)
4845          (mail-parse-charset gnus-newsgroup-charset)
4846          (mail-parse-ignored-charsets
4847           (with-current-buffer gnus-summary-buffer
4848             gnus-newsgroup-ignored-charsets)))
4849     (when handle
4850       (if (mm-handle-undisplayer handle)
4851           (mm-remove-part handle)
4852         (mm-display-part handle)))))
4853
4854 (defun gnus-mime-view-part-internally (&optional handle)
4855   "View the MIME part under point with an internal viewer.
4856 If no internal viewer is available, use an external viewer."
4857   (interactive)
4858   (gnus-article-check-buffer)
4859   (let* ((handle (or handle (get-text-property (point) 'gnus-data)))
4860          (mm-inlined-types '(".*"))
4861          (mm-inline-large-images t)
4862          (mail-parse-charset gnus-newsgroup-charset)
4863          (mail-parse-ignored-charsets
4864           (with-current-buffer gnus-summary-buffer
4865             gnus-newsgroup-ignored-charsets))
4866          (inhibit-read-only t))
4867     (when handle
4868       (if (mm-handle-undisplayer handle)
4869           (mm-remove-part handle)
4870         (mm-display-part handle)))))
4871
4872 (defun gnus-mime-action-on-part (&optional action)
4873   "Do something with the MIME attachment at \(point\)."
4874   (interactive
4875    (list (completing-read "Action: " gnus-mime-action-alist nil t)))
4876   (gnus-article-check-buffer)
4877   (let ((action-pair (assoc action gnus-mime-action-alist)))
4878     (if action-pair
4879         (funcall (cdr action-pair)))))
4880
4881 (defun gnus-article-part-wrapper (n function &optional no-handle)
4882   (let (window frame)
4883     ;; Check whether the article is displayed.
4884     (unless (and (gnus-buffer-live-p gnus-article-buffer)
4885                  (setq window (get-buffer-window gnus-article-buffer t))
4886                  (frame-visible-p (setq frame (window-frame window))))
4887       (error "No article is displayed"))
4888     (with-current-buffer gnus-article-buffer
4889       ;; Check whether the article displays the right contents.
4890       (unless (with-current-buffer gnus-summary-buffer
4891                 (eq gnus-current-article (gnus-summary-article-number)))
4892         (error "You should select the right article first"))
4893       ;; Check whether the specified part exists.
4894       (when (> n (length gnus-article-mime-handle-alist))
4895         (error "No such part")))
4896     (unless
4897         (progn
4898           ;; To select the window is needed so that the cursor
4899           ;; might be visible on the MIME button.
4900           (select-window (prog1
4901                              window
4902                            (setq window (selected-window))
4903                            ;; Article may be displayed in the other frame.
4904                            (gnus-select-frame-set-input-focus
4905                             (prog1
4906                                 frame
4907                               (setq frame (selected-frame))))))
4908           (when (gnus-article-goto-part n)
4909             ;; We point the cursor and the arrow at the MIME button
4910             ;; when the `function' prompt the user for something.
4911             (let ((cursor-in-non-selected-windows t)
4912                   (overlay-arrow-string "=>")
4913                   (overlay-arrow-position (point-marker)))
4914               (unwind-protect
4915                   (if no-handle
4916                       (funcall function)
4917                     (funcall function
4918                              (cdr (assq n gnus-article-mime-handle-alist))))
4919                 (set-marker overlay-arrow-position nil)
4920                 (unless gnus-auto-select-part
4921                   (gnus-select-frame-set-input-focus frame)
4922                   (select-window window))))
4923             t))
4924       (if gnus-inhibit-mime-unbuttonizing
4925           ;; This is the default though the program shouldn't reach here.
4926           (error "No such part")
4927         ;; The part which doesn't have the MIME button is selected.
4928         ;; So, we display all the buttons and redo it.
4929         (let ((gnus-inhibit-mime-unbuttonizing t))
4930           (gnus-summary-show-article)
4931           (gnus-article-part-wrapper n function no-handle))))))
4932
4933 (defun gnus-article-pipe-part (n)
4934   "Pipe MIME part N, which is the numerical prefix."
4935   (interactive "p")
4936   (gnus-article-part-wrapper n 'mm-pipe-part))
4937
4938 (defun gnus-article-save-part (n)
4939   "Save MIME part N, which is the numerical prefix."
4940   (interactive "p")
4941   (gnus-article-part-wrapper n 'mm-save-part))
4942
4943 (defun gnus-article-interactively-view-part (n)
4944   "View MIME part N interactively, which is the numerical prefix."
4945   (interactive "p")
4946   (gnus-article-part-wrapper n 'mm-interactively-view-part))
4947
4948 (defun gnus-article-copy-part (n)
4949   "Copy MIME part N, which is the numerical prefix."
4950   (interactive "p")
4951   (gnus-article-part-wrapper n 'gnus-mime-copy-part))
4952
4953 (defun gnus-article-view-part-as-charset (n)
4954   "View MIME part N using a specified charset.
4955 N is the numerical prefix."
4956   (interactive "p")
4957   (gnus-article-part-wrapper n 'gnus-mime-view-part-as-charset))
4958
4959 (defun gnus-article-view-part-externally (n)
4960   "View MIME part N externally, which is the numerical prefix."
4961   (interactive "p")
4962   (gnus-article-part-wrapper n 'gnus-mime-view-part-externally))
4963
4964 (defun gnus-article-inline-part (n)
4965   "Inline MIME part N, which is the numerical prefix."
4966   (interactive "p")
4967   (gnus-article-part-wrapper n 'gnus-mime-inline-part))
4968
4969 (defun gnus-article-save-part-and-strip (n)
4970   "Save MIME part N and replace it with an external body.
4971 N is the numerical prefix."
4972   (interactive "p")
4973   (gnus-article-part-wrapper n 'gnus-mime-save-part-and-strip t))
4974
4975 (defun gnus-article-delete-part (n)
4976   "Delete MIME part N and add some information about the removed part.
4977 N is the numerical prefix."
4978   (interactive "p")
4979   (gnus-article-part-wrapper n 'gnus-mime-delete-part t))
4980
4981 (defun gnus-article-mime-match-handle-first (condition)
4982   (if condition
4983       (let (n)
4984         (dolist (ihandle gnus-article-mime-handle-alist)
4985           (if (and (cond
4986                     ((functionp condition)
4987                      (funcall condition (cdr ihandle)))
4988                     ((eq condition 'undisplayed)
4989                      (not (or (mm-handle-undisplayer (cdr ihandle))
4990                               (equal (mm-handle-media-type (cdr ihandle))
4991                                      "multipart/alternative"))))
4992                     ((eq condition 'undisplayed-alternative)
4993                      (not (mm-handle-undisplayer (cdr ihandle))))
4994                     (t t))
4995                    (gnus-article-goto-part (car ihandle))
4996                    (or (not n) (< (car ihandle) n)))
4997               (setq n (car ihandle))))
4998         (or n 1))
4999     1))
5000
5001 (defun gnus-article-view-part (&optional n)
5002   "View MIME part N, which is the numerical prefix."
5003   (interactive "P")
5004   (with-current-buffer gnus-article-buffer
5005     (or (numberp n) (setq n (gnus-article-mime-match-handle-first
5006                              gnus-article-mime-match-handle-function)))
5007     (when (> n (length gnus-article-mime-handle-alist))
5008       (error "No such part"))
5009     (let ((handle (cdr (assq n gnus-article-mime-handle-alist))))
5010       (when (gnus-article-goto-part n)
5011         (if (equal (car handle) "multipart/alternative")
5012             (gnus-article-press-button)
5013           (when (eq (gnus-mm-display-part handle) 'internal)
5014             (gnus-set-window-start)))))))
5015
5016 (defsubst gnus-article-mime-total-parts ()
5017   (if (bufferp (car gnus-article-mime-handles))
5018       1 ;; single part
5019     (1- (length gnus-article-mime-handles))))
5020
5021 (defun gnus-mm-display-part (handle)
5022   "Display HANDLE and fix MIME button."
5023   (let ((id (get-text-property (point) 'gnus-part))
5024         (point (point))
5025         (inhibit-read-only t))
5026     (forward-line 1)
5027     (prog1
5028         (let ((window (selected-window))
5029               (mail-parse-charset gnus-newsgroup-charset)
5030               (mail-parse-ignored-charsets
5031                (if (gnus-buffer-live-p gnus-summary-buffer)
5032                    (with-current-buffer gnus-summary-buffer
5033                      gnus-newsgroup-ignored-charsets)
5034                  nil)))
5035           (save-excursion
5036             (unwind-protect
5037                 (let ((win (gnus-get-buffer-window (current-buffer) t))
5038                       (beg (point)))
5039                   (when win
5040                     (select-window win))
5041                   (goto-char point)
5042                   (forward-line)
5043                   (if (mm-handle-displayed-p handle)
5044                       ;; This will remove the part.
5045                       (mm-display-part handle)
5046                     (save-restriction
5047                       (narrow-to-region (point)
5048                                         (if (eobp) (point) (1+ (point))))
5049                       (mm-display-part handle)
5050                       ;; We narrow to the part itself and
5051                       ;; then call the treatment functions.
5052                       (goto-char (point-min))
5053                       (forward-line 1)
5054                       (narrow-to-region (point) (point-max))
5055                       (gnus-treat-article
5056                        nil id
5057                        (gnus-article-mime-total-parts)
5058                        (mm-handle-media-type handle)))))
5059               (if (window-live-p window)
5060                   (select-window window)))))
5061       (goto-char point)
5062       (gnus-delete-line)
5063       (gnus-insert-mime-button
5064        handle id (list (mm-handle-displayed-p handle)))
5065       (goto-char point))))
5066
5067 (defun gnus-article-goto-part (n)
5068   "Go to MIME part N."
5069   (gnus-goto-char (text-property-any (point-min) (point-max) 'gnus-part n)))
5070
5071 (defun gnus-insert-mime-button (handle gnus-tmp-id &optional displayed)
5072   (let ((gnus-tmp-name
5073          (or (mail-content-type-get (mm-handle-type handle) 'name)
5074              (mail-content-type-get (mm-handle-disposition handle) 'filename)
5075              (mail-content-type-get (mm-handle-type handle) 'url)
5076              ""))
5077         (gnus-tmp-type (mm-handle-media-type handle))
5078         (gnus-tmp-description
5079          (mail-decode-encoded-word-string (or (mm-handle-description handle)
5080                                               "")))
5081         (gnus-tmp-dots
5082          (if (if displayed (car displayed)
5083                (mm-handle-displayed-p handle))
5084              "" "..."))
5085         (gnus-tmp-length (with-current-buffer (mm-handle-buffer handle)
5086                            (buffer-size)))
5087         gnus-tmp-type-long b e)
5088     (when (string-match ".*/" gnus-tmp-name)
5089       (setq gnus-tmp-name (replace-match "" t t gnus-tmp-name)))
5090     (setq gnus-tmp-type-long (concat gnus-tmp-type
5091                                      (and (not (equal gnus-tmp-name ""))
5092                                           (concat "; " gnus-tmp-name))))
5093     (unless (equal gnus-tmp-description "")
5094       (setq gnus-tmp-type-long (concat " --- " gnus-tmp-type-long)))
5095     (unless (bolp)
5096       (insert "\n"))
5097     (setq b (point))
5098     (gnus-eval-format
5099      gnus-mime-button-line-format gnus-mime-button-line-format-alist
5100      `(keymap ,gnus-mime-button-map
5101               gnus-callback gnus-mm-display-part
5102               gnus-part ,gnus-tmp-id
5103               article-type annotation
5104               gnus-data ,handle))
5105     (setq e (if (bolp)
5106                 ;; Exclude a newline.
5107                 (1- (point))
5108               (point)))
5109     (widget-convert-button
5110      'link b e
5111      :mime-handle handle
5112      :action 'gnus-widget-press-button
5113      :button-keymap gnus-mime-button-map
5114      :help-echo
5115      (lambda (widget/window &optional overlay pos)
5116        ;; Needed to properly clear the message due to a bug in
5117        ;; wid-edit (XEmacs only).
5118        (if (boundp 'help-echo-owns-message)
5119            (setq help-echo-owns-message t))
5120        (format
5121         "%S: %s the MIME part; %S: more options"
5122         (aref gnus-mouse-2 0)
5123         ;; XEmacs will get a single widget arg; Emacs 21 will get
5124         ;; window, overlay, position.
5125         (if (mm-handle-displayed-p
5126              (if overlay
5127                  (with-current-buffer (gnus-overlay-buffer overlay)
5128                    (widget-get (widget-at (gnus-overlay-start overlay))
5129                                :mime-handle))
5130                (widget-get widget/window :mime-handle)))
5131             "hide" "show")
5132         (aref gnus-down-mouse-3 0))))))
5133
5134 (defun gnus-widget-press-button (elems el)
5135   (goto-char (widget-get elems :from))
5136   (gnus-article-press-button))
5137
5138 (defvar gnus-displaying-mime nil)
5139
5140 (defun gnus-display-mime (&optional ihandles)
5141   "Display the MIME parts."
5142   (save-excursion
5143     (save-selected-window
5144       (let ((window (get-buffer-window gnus-article-buffer))
5145             (point (point)))
5146         (when window
5147           (select-window window)
5148           ;; We have to do this since selecting the window
5149           ;; may change the point.  So we set the window point.
5150           (set-window-point window point)))
5151       (let ((handles ihandles)
5152             (inhibit-read-only t)
5153             handle)
5154         (cond (handles)
5155               ((setq handles (mm-dissect-buffer nil gnus-article-loose-mime))
5156                (when gnus-article-emulate-mime
5157                  (mm-uu-dissect-text-parts handles)))
5158               (gnus-article-emulate-mime
5159                (setq handles (mm-uu-dissect))))
5160         (when (and (not ihandles)
5161                    (not gnus-displaying-mime))
5162           ;; Top-level call; we clean up.
5163           (when gnus-article-mime-handles
5164             (mm-destroy-parts gnus-article-mime-handles)
5165             (setq gnus-article-mime-handle-alist nil));; A trick.
5166           (setq gnus-article-mime-handles handles)
5167           ;; We allow users to glean info from the handles.
5168           (when gnus-article-mime-part-function
5169             (gnus-mime-part-function handles)))
5170         (if (and handles
5171                  (or (not (stringp (car handles)))
5172                      (cdr handles)))
5173             (progn
5174               (when (and (not ihandles)
5175                          (not gnus-displaying-mime))
5176                 ;; Clean up for mime parts.
5177                 (article-goto-body)
5178                 (delete-region (point) (point-max)))
5179               (let ((gnus-displaying-mime t))
5180                 (gnus-mime-display-part handles)))
5181           (save-restriction
5182             (article-goto-body)
5183             (narrow-to-region (point) (point-max))
5184             (gnus-treat-article nil 1 1)
5185             (widen)))
5186         (unless ihandles
5187           ;; Highlight the headers.
5188           (save-excursion
5189             (save-restriction
5190               (article-goto-body)
5191               (narrow-to-region (point-min) (point))
5192               (gnus-article-save-original-date
5193                (gnus-treat-article 'head)))))))))
5194
5195 (defcustom gnus-mime-display-multipart-as-mixed nil
5196   "Display \"multipart\" parts as  \"multipart/mixed\".
5197
5198 If t, it overrides nil values of
5199 `gnus-mime-display-multipart-alternative-as-mixed' and
5200 `gnus-mime-display-multipart-related-as-mixed'."
5201   :group 'gnus-article-mime
5202   :type 'boolean)
5203
5204 (defcustom gnus-mime-display-multipart-alternative-as-mixed nil
5205   "Display \"multipart/alternative\" parts as  \"multipart/mixed\"."
5206   :version "22.1"
5207   :group 'gnus-article-mime
5208   :type 'boolean)
5209
5210 (defcustom gnus-mime-display-multipart-related-as-mixed nil
5211   "Display \"multipart/related\" parts as  \"multipart/mixed\".
5212
5213 If displaying \"text/html\" is discouraged \(see
5214 `mm-discouraged-alternatives'\) images or other material inside a
5215 \"multipart/related\" part might be overlooked when this variable is nil."
5216   :version "22.1"
5217   :group 'gnus-article-mime
5218   :type 'boolean)
5219
5220 (defun gnus-mime-display-part (handle)
5221   (cond
5222    ;; Maybe a broken MIME message.
5223    ((null handle))
5224    ;; Single part.
5225    ((not (stringp (car handle)))
5226     (gnus-mime-display-single handle))
5227    ;; User-defined multipart
5228    ((cdr (assoc (car handle) gnus-mime-multipart-functions))
5229     (funcall (cdr (assoc (car handle) gnus-mime-multipart-functions))
5230              handle))
5231    ;; multipart/alternative
5232    ((and (equal (car handle) "multipart/alternative")
5233          (not (or gnus-mime-display-multipart-as-mixed
5234                   gnus-mime-display-multipart-alternative-as-mixed)))
5235     (let ((id (1+ (length gnus-article-mime-handle-alist))))
5236       (push (cons id handle) gnus-article-mime-handle-alist)
5237       (gnus-mime-display-alternative (cdr handle) nil nil id)))
5238    ;; multipart/related
5239    ((and (equal (car handle) "multipart/related")
5240          (not (or gnus-mime-display-multipart-as-mixed
5241                   gnus-mime-display-multipart-related-as-mixed)))
5242     ;;;!!!We should find the start part, but we just default
5243     ;;;!!!to the first part.
5244     ;;(gnus-mime-display-part (cadr handle))
5245     ;;;!!! Most multipart/related is an HTML message plus images.
5246     ;;;!!! Unfortunately we are unable to let W3 display those
5247     ;;;!!! included images, so we just display it as a mixed multipart.
5248     ;;(gnus-mime-display-mixed (cdr handle))
5249     ;;;!!! No, w3 can display everything just fine.
5250     (gnus-mime-display-part (cadr handle)))
5251    ((equal (car handle) "multipart/signed")
5252     (gnus-add-wash-type 'signed)
5253     (gnus-mime-display-security handle))
5254    ((equal (car handle) "multipart/encrypted")
5255     (gnus-add-wash-type 'encrypted)
5256     (gnus-mime-display-security handle))
5257    ;; Other multiparts are handled like multipart/mixed.
5258    (t
5259     (gnus-mime-display-mixed (cdr handle)))))
5260
5261 (defun gnus-mime-part-function (handles)
5262   (if (stringp (car handles))
5263       (mapcar 'gnus-mime-part-function (cdr handles))
5264     (funcall gnus-article-mime-part-function handles)))
5265
5266 (defun gnus-mime-display-mixed (handles)
5267   (mapcar 'gnus-mime-display-part handles))
5268
5269 (defun gnus-mime-display-single (handle)
5270   (let ((type (mm-handle-media-type handle))
5271         (ignored gnus-ignored-mime-types)
5272         (not-attachment t)
5273         (move nil)
5274         display text)
5275     (catch 'ignored
5276       (progn
5277         (while ignored
5278           (when (string-match (pop ignored) type)
5279             (throw 'ignored nil)))
5280         (if (and (setq not-attachment
5281                        (and (not (mm-inline-override-p handle))
5282                             (or (not (mm-handle-disposition handle))
5283                                 (equal (car (mm-handle-disposition handle))
5284                                        "inline")
5285                                 (mm-attachment-override-p handle))))
5286                  (mm-automatic-display-p handle)
5287                  (or (and
5288                       (mm-inlinable-p handle)
5289                       (mm-inlined-p handle))
5290                      (mm-automatic-external-display-p type)))
5291             (setq display t)
5292           (when (equal (mm-handle-media-supertype handle) "text")
5293             (setq text t)))
5294         (let ((id (1+ (length gnus-article-mime-handle-alist)))
5295               beg)
5296           (push (cons id handle) gnus-article-mime-handle-alist)
5297           (when (or (not display)
5298                     (not (gnus-unbuttonized-mime-type-p type)))
5299             (gnus-insert-mime-button
5300              handle id (list (or display (and not-attachment text))))
5301             (gnus-article-insert-newline)
5302             ;; Remember modify the number of forward lines.
5303             (setq move t))
5304           (setq beg (point))
5305           (cond
5306            (display
5307             (when move
5308               (forward-line -1)
5309               (setq beg (point)))
5310             (let ((mail-parse-charset gnus-newsgroup-charset)
5311                   (mail-parse-ignored-charsets
5312                    (save-excursion (condition-case ()
5313                                        (set-buffer gnus-summary-buffer)
5314                                      (error))
5315                                    gnus-newsgroup-ignored-charsets)))
5316               (mm-display-part handle t))
5317             (goto-char (point-max)))
5318            ((and text not-attachment)
5319             (when move
5320               (forward-line -1)
5321               (setq beg (point)))
5322             (gnus-article-insert-newline)
5323             (mm-insert-inline handle
5324                               (let ((charset
5325                                      (mail-content-type-get
5326                                       (mm-handle-type handle) 'charset)))
5327                                 (if (eq charset 'gnus-decoded)
5328                                     (mm-get-part handle)
5329                                   (mm-decode-string (mm-get-part handle)
5330                                                     charset))))
5331             (goto-char (point-max))))
5332           ;; Do highlighting.
5333           (save-excursion
5334             (save-restriction
5335               (narrow-to-region beg (point))
5336               (gnus-treat-article
5337                nil id
5338                (gnus-article-mime-total-parts)
5339                (mm-handle-media-type handle)))))))))
5340
5341 (defun gnus-unbuttonized-mime-type-p (type)
5342   "Say whether TYPE is to be unbuttonized."
5343   (unless gnus-inhibit-mime-unbuttonizing
5344     (when (catch 'found
5345             (let ((types gnus-unbuttonized-mime-types))
5346               (while types
5347                 (when (string-match (pop types) type)
5348                   (throw 'found t)))))
5349       (not (catch 'found
5350              (let ((types gnus-buttonized-mime-types))
5351                (while types
5352                  (when (string-match (pop types) type)
5353                    (throw 'found t)))))))))
5354
5355 (defun gnus-article-insert-newline ()
5356   "Insert a newline, but mark it as undeletable."
5357   (gnus-put-text-property
5358    (point) (progn (insert "\n") (point)) 'gnus-undeletable t))
5359
5360 (defun gnus-mime-display-alternative (handles &optional preferred ibegend id)
5361   (let* ((preferred (or preferred (mm-preferred-alternative handles)))
5362          (ihandles handles)
5363          (point (point))
5364          handle (inhibit-read-only t) from props begend not-pref)
5365     (save-window-excursion
5366       (save-restriction
5367         (when ibegend
5368           (narrow-to-region (car ibegend)
5369                             (or (cdr ibegend)
5370                                 (progn
5371                                   (goto-char (car ibegend))
5372                                   (forward-line 2)
5373                                   (point))))
5374           (delete-region (point-min) (point-max))
5375           (mm-remove-parts handles))
5376         (setq begend (list (point-marker)))
5377         ;; Do the toggle.
5378         (unless (setq not-pref (cadr (member preferred ihandles)))
5379           (setq not-pref (car ihandles)))
5380         (when (or ibegend
5381                   (not preferred)
5382                   (not (gnus-unbuttonized-mime-type-p
5383                         "multipart/alternative")))
5384           (gnus-add-text-properties
5385            (setq from (point))
5386            (progn
5387              (insert (format "%d.  " id))
5388              (point))
5389            `(gnus-callback
5390              (lambda (handles)
5391                (unless ,(not ibegend)
5392                  (setq gnus-article-mime-handle-alist
5393                        ',gnus-article-mime-handle-alist))
5394                (gnus-mime-display-alternative
5395                 ',ihandles ',not-pref ',begend ,id))
5396              keymap ,gnus-mime-button-map
5397              ,gnus-mouse-face-prop ,gnus-article-mouse-face
5398              face ,gnus-article-button-face
5399              gnus-part ,id
5400              gnus-data ,handle))
5401           (widget-convert-button 'link from (point)
5402                                  :action 'gnus-widget-press-button
5403                                  :button-keymap gnus-widget-button-keymap)
5404           ;; Do the handles
5405           (while (setq handle (pop handles))
5406             (gnus-add-text-properties
5407              (setq from (point))
5408              (progn
5409                (insert (format "(%c) %-18s"
5410                                (if (equal handle preferred) ?* ? )
5411                                (mm-handle-media-type handle)))
5412                (point))
5413              `(gnus-callback
5414                (lambda (handles)
5415                  (unless ,(not ibegend)
5416                    (setq gnus-article-mime-handle-alist
5417                          ',gnus-article-mime-handle-alist))
5418                  (gnus-mime-display-alternative
5419                   ',ihandles ',handle ',begend ,id))
5420                keymap ,gnus-mime-button-map
5421                ,gnus-mouse-face-prop ,gnus-article-mouse-face
5422                face ,gnus-article-button-face
5423                gnus-part ,id
5424                gnus-data ,handle))
5425             (widget-convert-button 'link from (point)
5426                                    :action 'gnus-widget-press-button
5427                                    :button-keymap gnus-widget-button-keymap)
5428             (insert "  "))
5429           (insert "\n\n"))
5430         (when preferred
5431           (if (stringp (car preferred))
5432               (gnus-display-mime preferred)
5433             (let ((mail-parse-charset gnus-newsgroup-charset)
5434                   (mail-parse-ignored-charsets
5435                    (with-current-buffer gnus-summary-buffer
5436                      gnus-newsgroup-ignored-charsets)))
5437               (mm-display-part preferred)
5438               ;; Do highlighting.
5439               (save-excursion
5440                 (save-restriction
5441                   (narrow-to-region (car begend) (point-max))
5442                   (gnus-treat-article
5443                    nil (length gnus-article-mime-handle-alist)
5444                    (gnus-article-mime-total-parts)
5445                    (mm-handle-media-type handle))))))
5446           (goto-char (point-max))
5447           (setcdr begend (point-marker)))))
5448     (when ibegend
5449       (goto-char point))))
5450
5451 (defconst gnus-article-wash-status-strings
5452   (let ((alist '((cite "c" "Possible hidden citation text"
5453                        " " "All citation text visible")
5454                  (headers "h" "Hidden headers"
5455                           " " "All headers visible.")
5456                  (pgp "p" "Encrypted or signed message status hidden"
5457                       " " "No hidden encryption nor digital signature status")
5458                  (signature "s" "Signature has been hidden"
5459                             " " "Signature is visible")
5460                  (overstrike "o" "Overstrike (^H) characters applied"
5461                              " " "No overstrike characters applied")
5462                  (gnus-show-mime "m" "Mime processing is activated"
5463                                  " " "Mime processing is not activated")
5464                  (emphasis "e" "/*_Emphasis_*/ characters applied"
5465                            " " "No /*_emphasis_*/ characters applied")))
5466         result)
5467     (dolist (entry alist result)
5468       (let ((key (nth 0 entry))
5469             (on (copy-sequence (nth 1 entry)))
5470             (on-help (nth 2 entry))
5471             (off (copy-sequence (nth 3 entry)))
5472             (off-help (nth 4 entry)))
5473         (put-text-property 0 1 'help-echo on-help on)
5474         (put-text-property 0 1 'help-echo off-help off)
5475         (push (list key on off) result))))
5476   "Alist of strings describing wash status in the mode line.
5477 Each entry has the form (KEY ON OF), where the KEY is a symbol
5478 representing the particular washing function, ON is the string to use
5479 in the article mode line when the washing function is active, and OFF
5480 is the string to use when it is inactive.")
5481
5482 (defun gnus-article-wash-status-entry (key value)
5483   (let ((entry (assoc key gnus-article-wash-status-strings)))
5484     (if value (nth 1 entry) (nth 2 entry))))
5485
5486 (defun gnus-article-wash-status ()
5487   "Return a string which display status of article washing."
5488   (with-current-buffer gnus-article-buffer
5489     (let ((cite (memq 'cite gnus-article-wash-types))
5490           (headers (memq 'headers gnus-article-wash-types))
5491           (boring (memq 'boring-headers gnus-article-wash-types))
5492           (pgp (memq 'pgp gnus-article-wash-types))
5493           (pem (memq 'pem gnus-article-wash-types))
5494           (signed (memq 'signed gnus-article-wash-types))
5495           (encrypted (memq 'encrypted gnus-article-wash-types))
5496           (signature (memq 'signature gnus-article-wash-types))
5497           (overstrike (memq 'overstrike gnus-article-wash-types))
5498           (emphasis (memq 'emphasis gnus-article-wash-types)))
5499       (concat
5500        (gnus-article-wash-status-entry 'cite cite)
5501        (gnus-article-wash-status-entry 'headers (or headers boring))
5502        (gnus-article-wash-status-entry 'pgp (or pgp pem signed encrypted))
5503        (gnus-article-wash-status-entry 'signature signature)
5504        (gnus-article-wash-status-entry 'overstrike overstrike)
5505        (gnus-article-wash-status-entry 'gnus-show-mime gnus-show-mime)
5506        (gnus-article-wash-status-entry 'emphasis emphasis)))))
5507
5508 (defun gnus-add-wash-type (type)
5509   "Add a washing of TYPE to the current status."
5510   (add-to-list 'gnus-article-wash-types type))
5511
5512 (defun gnus-delete-wash-type (type)
5513   "Add a washing of TYPE to the current status."
5514   (setq gnus-article-wash-types (delq type gnus-article-wash-types)))
5515
5516 (defun gnus-add-image (category image)
5517   "Add IMAGE of CATEGORY to the list of displayed images."
5518   (let ((entry (assq category gnus-article-image-alist)))
5519     (unless entry
5520       (setq entry (list category))
5521       (push entry gnus-article-image-alist))
5522     (nconc entry (list image))))
5523
5524 (defun gnus-delete-images (category)
5525   "Delete all images in CATEGORY."
5526   (let ((entry (assq category gnus-article-image-alist)))
5527     (dolist (image (cdr entry))
5528       (gnus-remove-image image category))
5529     (setq gnus-article-image-alist (delq entry gnus-article-image-alist))
5530     (gnus-delete-wash-type category)))
5531
5532 (defalias 'gnus-article-hide-headers-if-wanted 'gnus-article-maybe-hide-headers)
5533
5534 (defun gnus-article-maybe-hide-headers ()
5535   "Hide unwanted headers if `gnus-have-all-headers' is nil.
5536 Provided for backwards compatibility."
5537   (when (and (or (not (gnus-buffer-live-p gnus-summary-buffer))
5538                  (not (with-current-buffer gnus-summary-buffer
5539                         gnus-have-all-headers)))
5540              (not gnus-inhibit-hiding))
5541     (gnus-article-hide-headers)))
5542
5543 ;;; Article savers.
5544
5545 (defun gnus-output-to-file (file-name)
5546   "Append the current article to a file named FILE-NAME."
5547   (let ((artbuf (current-buffer)))
5548     (with-temp-buffer
5549       (insert-buffer-substring artbuf)
5550       ;; Append newline at end of the buffer as separator, and then
5551       ;; save it to file.
5552       (goto-char (point-max))
5553       (insert "\n")
5554       (let ((file-name-coding-system nnmail-pathname-coding-system))
5555         (write-region-as-binary (point-min) (point-max) file-name 'append))
5556       t)))
5557
5558 (defun gnus-narrow-to-page (&optional arg)
5559   "Narrow the article buffer to a page.
5560 If given a numerical ARG, move forward ARG pages."
5561   (interactive "P")
5562   (setq arg (if arg (prefix-numeric-value arg) 0))
5563   (save-excursion
5564     (set-buffer gnus-article-buffer)
5565     (goto-char (point-min))
5566     (widen)
5567     ;; Remove any old next/prev buttons.
5568     (when (gnus-visual-p 'page-marker)
5569       (let ((inhibit-read-only t))
5570         (gnus-remove-text-with-property 'gnus-prev)
5571         (gnus-remove-text-with-property 'gnus-next)))
5572     (if
5573         (cond ((< arg 0)
5574                (re-search-backward page-delimiter nil 'move (1+ (abs arg))))
5575               ((> arg 0)
5576                (re-search-forward page-delimiter nil 'move arg)))
5577         (goto-char (match-end 0))
5578       (save-excursion
5579         (goto-char (point-min))
5580         (setq gnus-page-broken
5581               (and (re-search-forward page-delimiter nil t) t))))
5582     (when gnus-page-broken
5583       (narrow-to-region
5584        (point)
5585        (if (re-search-forward page-delimiter nil 'move)
5586            (match-beginning 0)
5587          (point)))
5588       (when (and (gnus-visual-p 'page-marker)
5589                  (> (point-min) (save-restriction (widen) (point-min))))
5590         (save-excursion
5591           (goto-char (point-min))
5592           (gnus-insert-prev-page-button)))
5593       (when (and (gnus-visual-p 'page-marker)
5594                  (< (point-max) (save-restriction (widen) (point-max))))
5595         (save-excursion
5596           (goto-char (point-max))
5597           (gnus-insert-next-page-button))))))
5598
5599 ;; Article mode commands
5600
5601 (defun gnus-article-goto-next-page ()
5602   "Show the next page of the article."
5603   (interactive)
5604   (when (gnus-article-next-page)
5605     (goto-char (point-min))
5606     (gnus-article-read-summary-keys nil (gnus-character-to-event ?n))))
5607
5608
5609 (defun gnus-article-goto-prev-page ()
5610   "Show the previous page of the article."
5611   (interactive)
5612   (if (bobp)
5613       (gnus-article-read-summary-keys nil (gnus-character-to-event ?p))
5614     (gnus-article-prev-page nil)))
5615
5616 ;; This is cleaner but currently breaks `gnus-pick-mode':
5617 ;;
5618 ;; (defun gnus-article-goto-next-page ()
5619 ;;   "Show the next page of the article."
5620 ;;   (interactive)
5621 ;;   (gnus-eval-in-buffer-window gnus-summary-buffer
5622 ;;     (gnus-summary-next-page)))
5623 ;;
5624 ;; (defun gnus-article-goto-prev-page ()
5625 ;;   "Show the next page of the article."
5626 ;;   (interactive)
5627 ;;   (gnus-eval-in-buffer-window gnus-summary-buffer
5628 ;;     (gnus-summary-prev-page)))
5629
5630 (defun gnus-article-next-page (&optional lines)
5631   "Show the next page of the current article.
5632 If end of article, return non-nil.  Otherwise return nil.
5633 Argument LINES specifies lines to be scrolled up."
5634   (interactive "p")
5635   (move-to-window-line -1)
5636   (if (save-excursion
5637         (end-of-line)
5638         (and (pos-visible-in-window-p)  ;Not continuation line.
5639              (>= (1+ (point)) (point-max)))) ;Allow for trailing newline.
5640       ;; Nothing in this page.
5641       (if (or (not gnus-page-broken)
5642               (save-excursion
5643                 (save-restriction
5644                   (widen)
5645                   (forward-line)
5646                   (eobp)))) ;Real end-of-buffer?
5647           (progn
5648             (when gnus-article-over-scroll
5649               (gnus-article-next-page-1 lines))
5650             t)                  ;Nothing more.
5651         (gnus-narrow-to-page 1)         ;Go to next page.
5652         nil)
5653     ;; More in this page.
5654     (gnus-article-next-page-1 lines)
5655     nil))
5656
5657 (defmacro gnus-article-beginning-of-window ()
5658   "Move point to the beginning of the window.
5659 In Emacs, the point is placed at the line number which `scroll-margin'
5660 specifies."
5661   (if (featurep 'xemacs)
5662       '(move-to-window-line 0)
5663     '(move-to-window-line
5664       (min (max 0 scroll-margin)
5665            (max 1 (- (window-height)
5666                      (if mode-line-format 1 0)
5667                      (if header-line-format 1 0)))))))
5668
5669 (defun gnus-article-next-page-1 (lines)
5670   (when (and (not (featurep 'xemacs))
5671              (numberp lines)
5672              (> lines 0)
5673              (numberp (symbol-value 'scroll-margin))
5674              (> (symbol-value 'scroll-margin) 0))
5675     ;; Protect against the bug that Emacs 21.x hangs up when scrolling up for
5676     ;; too many number of lines if `scroll-margin' is set as two or greater.
5677     (setq lines (min lines
5678                      (max 0 (- (count-lines (window-start) (point-max))
5679                                (symbol-value 'scroll-margin))))))
5680   (condition-case ()
5681       (let ((scroll-in-place nil))
5682         (scroll-up lines))
5683     (end-of-buffer
5684      ;; Long lines may cause an end-of-buffer error.
5685      (goto-char (point-max))))
5686   (gnus-article-beginning-of-window))
5687
5688 (defun gnus-article-prev-page (&optional lines)
5689   "Show previous page of current article.
5690 Argument LINES specifies lines to be scrolled down."
5691   (interactive "p")
5692   (move-to-window-line 0)
5693   (if (and gnus-page-broken
5694            (bobp)
5695            (not (save-restriction (widen) (bobp)))) ;Real beginning-of-buffer?
5696       (progn
5697         (gnus-narrow-to-page -1)        ;Go to previous page.
5698         (goto-char (point-max))
5699         (recenter -1))
5700     (prog1
5701         (condition-case ()
5702             (let ((scroll-in-place nil))
5703               (scroll-down lines))
5704           (beginning-of-buffer
5705            (goto-char (point-min))))
5706       (gnus-article-beginning-of-window))))
5707
5708 (defun gnus-article-only-boring-p ()
5709   "Decide whether there is only boring text remaining in the article.
5710 Something \"interesting\" is a word of at least two letters that does
5711 not have a face in `gnus-article-boring-faces'."
5712   (when (and gnus-article-skip-boring
5713              (boundp 'gnus-article-boring-faces)
5714              (symbol-value 'gnus-article-boring-faces))
5715     (save-excursion
5716       (catch 'only-boring
5717         (while (re-search-forward "\\b\\w\\w" nil t)
5718           (forward-char -1)
5719           (when (not (gnus-intersection
5720                       (gnus-faces-at (point))
5721                       (symbol-value 'gnus-article-boring-faces)))
5722             (throw 'only-boring nil)))
5723         (throw 'only-boring t)))))
5724
5725 (defun gnus-article-refer-article ()
5726   "Read article specified by message-id around point."
5727   (interactive)
5728   (save-excursion
5729     (re-search-backward "[ \t]\\|^" (point-at-bol) t)
5730     (re-search-forward "<?news:<?\\|<" (point-at-eol) t)
5731     (if (re-search-forward "[^@ ]+@[^ \t>]+" (point-at-eol) t)
5732         (let ((msg-id (concat "<" (match-string 0) ">")))
5733           (set-buffer gnus-summary-buffer)
5734           (gnus-summary-refer-article msg-id))
5735       (error "No references around point"))))
5736
5737 (defun gnus-article-show-summary ()
5738   "Reconfigure windows to show summary buffer."
5739   (interactive)
5740   (if (not (gnus-buffer-live-p gnus-summary-buffer))
5741       (error "There is no summary buffer for this article buffer")
5742     (gnus-article-set-globals)
5743     (gnus-configure-windows 'article)
5744     (gnus-summary-goto-subject gnus-current-article)
5745     (gnus-summary-position-point)))
5746
5747 (defun gnus-article-describe-briefly ()
5748   "Describe article mode commands briefly."
5749   (interactive)
5750   (gnus-message 6 (substitute-command-keys "\\<gnus-article-mode-map>\\[gnus-article-goto-next-page]:Next page   \\[gnus-article-goto-prev-page]:Prev page  \\[gnus-article-show-summary]:Show summary  \\[gnus-info-find-node]:Run Info  \\[gnus-article-describe-briefly]:This help")))
5751
5752 (defun gnus-article-summary-command ()
5753   "Execute the last keystroke in the summary buffer."
5754   (interactive)
5755   (let ((obuf (current-buffer))
5756         (owin (current-window-configuration))
5757         func)
5758     (switch-to-buffer gnus-article-current-summary 'norecord)
5759     (setq func (lookup-key (current-local-map) (this-command-keys)))
5760     (call-interactively func)
5761     (set-buffer obuf)
5762     (set-window-configuration owin)
5763     (set-window-point (get-buffer-window (current-buffer)) (point))))
5764
5765 (defun gnus-article-summary-command-nosave ()
5766   "Execute the last keystroke in the summary buffer."
5767   (interactive)
5768   (let (func)
5769     (pop-to-buffer gnus-article-current-summary 'norecord)
5770     (setq func (lookup-key (current-local-map) (this-command-keys)))
5771     (call-interactively func)))
5772
5773 (defun gnus-article-check-buffer ()
5774   "Beep if not in an article buffer."
5775   (unless (eq (get-buffer gnus-article-buffer) (current-buffer))
5776     (error "Command invoked outside of a Gnus article buffer")))
5777
5778 (defun gnus-article-read-summary-keys (&optional arg key not-restore-window)
5779   "Read a summary buffer key sequence and execute it from the article buffer."
5780   (interactive "P")
5781   (gnus-article-check-buffer)
5782   (let ((nosaves
5783          '("q" "Q"  "c" "r" "\C-c\C-f" "m"  "a" "f"
5784            "Zc" "ZC" "ZE" "ZJ" "ZQ" "ZZ" "Zn" "ZR" "ZG" "ZN" "ZP"
5785            "=" "^" "\M-^" "|"))
5786         (nosave-but-article
5787          '("A\r"))
5788         (nosave-in-article
5789          '("\C-d"))
5790         (up-to-top
5791          '("n" "Gn" "p" "Gp"))
5792         keys new-sum-point)
5793     (save-excursion
5794       (set-buffer gnus-article-current-summary)
5795       (let (gnus-pick-mode)
5796         (push (or key last-command-event) unread-command-events)
5797         (setq keys (static-if (featurep 'xemacs)
5798                        (events-to-keys (read-key-sequence nil))
5799                      (read-key-sequence nil)))))
5800     (message "")
5801
5802     (if (or (member keys nosaves)
5803             (member keys nosave-but-article)
5804             (member keys nosave-in-article))
5805         (let (func)
5806           (save-window-excursion
5807             (pop-to-buffer gnus-article-current-summary 'norecord)
5808             ;; We disable the pick minor mode commands.
5809             (let (gnus-pick-mode)
5810               (setq func (lookup-key (current-local-map) keys))))
5811           (if (or (not func)
5812                   (numberp func))
5813               (ding)
5814             (unless (member keys nosave-in-article)
5815               (set-buffer gnus-article-current-summary))
5816             (call-interactively func)
5817             (setq new-sum-point (point)))
5818           (when (member keys nosave-but-article)
5819             (pop-to-buffer gnus-article-buffer 'norecord)))
5820       ;; These commands should restore window configuration.
5821       (let ((obuf (current-buffer))
5822             (owin (current-window-configuration))
5823             (opoint (point))
5824             win func in-buffer selected new-sum-start new-sum-hscroll)
5825         (cond (not-restore-window
5826                (pop-to-buffer gnus-article-current-summary 'norecord))
5827               ((setq win (get-buffer-window gnus-article-current-summary))
5828                (select-window win))
5829               (t
5830                (switch-to-buffer gnus-article-current-summary 'norecord)))
5831         (setq in-buffer (current-buffer))
5832         ;; We disable the pick minor mode commands.
5833         (if (and (setq func (let (gnus-pick-mode)
5834                               (lookup-key (current-local-map) keys)))
5835                  (functionp func))
5836             (progn
5837               (call-interactively func)
5838               (when (eq win (selected-window))
5839                 (setq new-sum-point (point)
5840                       new-sum-start (window-start win)
5841                       new-sum-hscroll (window-hscroll win)))
5842               (when (eq in-buffer (current-buffer))
5843                 (setq selected (gnus-summary-select-article))
5844                 (set-buffer obuf)
5845                 (unless not-restore-window
5846                   (set-window-configuration owin))
5847                 (when (eq selected 'old)
5848                   (article-goto-body)
5849                   (set-window-start (get-buffer-window (current-buffer))
5850                                     1)
5851                   (set-window-point (get-buffer-window (current-buffer))
5852                                     (point)))
5853                 (when (and (not not-restore-window)
5854                            new-sum-point)
5855                   (set-window-point win new-sum-point)
5856                   (set-window-start win new-sum-start)
5857                   (set-window-hscroll win new-sum-hscroll))))
5858           (set-window-configuration owin)
5859           (ding))))))
5860
5861 (defun gnus-article-describe-key (key)
5862   "Display documentation of the function invoked by KEY.  KEY is a string."
5863   (interactive "kDescribe key: ")
5864   (gnus-article-check-buffer)
5865   (if (eq (key-binding key) 'gnus-article-read-summary-keys)
5866       (save-excursion
5867         (set-buffer gnus-article-current-summary)
5868         (let (gnus-pick-mode)
5869           (if (featurep 'xemacs)
5870               (progn
5871                 (push (elt key 0) unread-command-events)
5872                 (setq key (events-to-keys
5873                            (read-key-sequence "Describe key: "))))
5874             (setq unread-command-events
5875                   (mapcar
5876                    (lambda (x) (if (>= x 128) (list 'meta (- x 128)) x))
5877                    key))
5878             (setq key (read-key-sequence "Describe key: "))))
5879         (describe-key key))
5880     (describe-key key)))
5881
5882 (defun gnus-article-describe-key-briefly (key &optional insert)
5883   "Display documentation of the function invoked by KEY.  KEY is a string."
5884   (interactive "kDescribe key: \nP")
5885   (gnus-article-check-buffer)
5886   (if (eq (key-binding key) 'gnus-article-read-summary-keys)
5887       (save-excursion
5888         (set-buffer gnus-article-current-summary)
5889         (let (gnus-pick-mode)
5890           (if (featurep 'xemacs)
5891               (progn
5892                 (push (elt key 0) unread-command-events)
5893                 (setq key (events-to-keys
5894                            (read-key-sequence "Describe key: "))))
5895             (setq unread-command-events
5896                   (mapcar
5897                    (lambda (x) (if (>= x 128) (list 'meta (- x 128)) x))
5898                    key))
5899             (setq key (read-key-sequence "Describe key: "))))
5900         (describe-key-briefly key insert))
5901     (describe-key-briefly key insert)))
5902
5903 (defun gnus-article-reply-with-original (&optional wide)
5904   "Start composing a reply mail to the current message.
5905 The text in the region will be yanked.  If the region isn't active,
5906 the entire article will be yanked."
5907   (interactive "P")
5908   (let ((article (cdr gnus-article-current))
5909         contents)
5910     (if (not (gnus-region-active-p))
5911         (with-current-buffer gnus-summary-buffer
5912           (gnus-summary-reply (list (list article)) wide))
5913       (setq contents (buffer-substring (point) (mark t)))
5914       ;; Deactivate active regions.
5915       (when (and (boundp 'transient-mark-mode)
5916                  transient-mark-mode)
5917         (setq mark-active nil))
5918       (with-current-buffer gnus-summary-buffer
5919         (gnus-summary-reply
5920          (list (list article contents)) wide)))))
5921
5922 (defun gnus-article-followup-with-original ()
5923   "Compose a followup to the current article.
5924 The text in the region will be yanked.  If the region isn't active,
5925 the entire article will be yanked."
5926   (interactive)
5927   (let ((article (cdr gnus-article-current))
5928         contents)
5929       (if (not (gnus-region-active-p))
5930           (with-current-buffer gnus-summary-buffer
5931             (gnus-summary-followup (list (list article))))
5932         (setq contents (buffer-substring (point) (mark t)))
5933         ;; Deactivate active regions.
5934         (when (and (boundp 'transient-mark-mode)
5935                    transient-mark-mode)
5936           (setq mark-active nil))
5937         (with-current-buffer gnus-summary-buffer
5938           (gnus-summary-followup
5939            (list (list article contents)))))))
5940
5941 (defun gnus-article-hide (&optional arg force)
5942   "Hide all the gruft in the current article.
5943 This means that signatures, cited text and (some) headers will be
5944 hidden.
5945 If given a prefix, show the hidden text instead."
5946   (interactive (append (gnus-article-hidden-arg) (list 'force)))
5947   (gnus-article-hide-headers arg)
5948   (gnus-article-hide-list-identifiers arg)
5949   (gnus-article-hide-citation-maybe arg force)
5950   (gnus-article-hide-signature arg))
5951
5952 (defun gnus-article-maybe-highlight ()
5953   "Do some article highlighting if article highlighting is requested."
5954   (when (gnus-visual-p 'article-highlight 'highlight)
5955     (gnus-article-highlight-some)))
5956
5957 (defun gnus-check-group-server ()
5958   ;; Make sure the connection to the server is alive.
5959   (unless (gnus-server-opened
5960            (gnus-find-method-for-group gnus-newsgroup-name))
5961     (gnus-check-server (gnus-find-method-for-group gnus-newsgroup-name))
5962     (gnus-request-group gnus-newsgroup-name t)))
5963
5964 (eval-when-compile
5965   (autoload 'nneething-get-file-name "nneething"))
5966
5967 (defun gnus-request-article-this-buffer (article group)
5968   "Get an article and insert it into this buffer."
5969   (let (do-update-line sparse-header)
5970     (prog1
5971         (save-excursion
5972           (erase-buffer)
5973           (gnus-kill-all-overlays)
5974           (setq group (or group gnus-newsgroup-name))
5975
5976           ;; Using `gnus-request-article' directly will insert the article into
5977           ;; `nntp-server-buffer' - so we'll save some time by not having to
5978           ;; copy it from the server buffer into the article buffer.
5979
5980           ;; We only request an article by message-id when we do not have the
5981           ;; headers for it, so we'll have to get those.
5982           (when (stringp article)
5983             (gnus-read-header article))
5984
5985           ;; If the article number is negative, that means that this article
5986           ;; doesn't belong in this newsgroup (possibly), so we find its
5987           ;; message-id and request it by id instead of number.
5988           (when (and (numberp article)
5989                      gnus-summary-buffer
5990                      (get-buffer gnus-summary-buffer)
5991                      (gnus-buffer-exists-p gnus-summary-buffer))
5992             (save-excursion
5993               (set-buffer gnus-summary-buffer)
5994               (let ((header (gnus-summary-article-header article)))
5995                 (when (< article 0)
5996                   (cond
5997                    ((memq article gnus-newsgroup-sparse)
5998                     ;; This is a sparse gap article.
5999                     (setq do-update-line article)
6000                     (setq article (mail-header-id header))
6001                     (setq sparse-header (gnus-read-header article))
6002                     (setq gnus-newsgroup-sparse
6003                           (delq article gnus-newsgroup-sparse)))
6004                    ((vectorp header)
6005                     ;; It's a real article.
6006                     (setq article (mail-header-id header)))
6007                    (t
6008                     ;; It is an extracted pseudo-article.
6009                     (setq article 'pseudo)
6010                     (gnus-request-pseudo-article header))))
6011
6012                 (let ((method (gnus-find-method-for-group
6013                                gnus-newsgroup-name)))
6014                   (when (and (eq (car method) 'nneething)
6015                              (vectorp header))
6016                     (let ((dir (nneething-get-file-name
6017                                 (mail-header-id header))))
6018                       (when (and (stringp dir)
6019                                  (file-directory-p dir))
6020                         (setq article 'nneething)
6021                         (gnus-group-enter-directory dir))))))))
6022
6023           (cond
6024            ;; Refuse to select canceled articles.
6025            ((and (numberp article)
6026                  gnus-summary-buffer
6027                  (get-buffer gnus-summary-buffer)
6028                  (gnus-buffer-exists-p gnus-summary-buffer)
6029                  (eq (cdr (with-current-buffer gnus-summary-buffer
6030                             (assq article gnus-newsgroup-reads)))
6031                      gnus-canceled-mark))
6032             nil)
6033            ;; We first check `gnus-original-article-buffer'.
6034            ((and (get-buffer gnus-original-article-buffer)
6035                  (numberp article)
6036                  (with-current-buffer gnus-original-article-buffer
6037                    (and (equal (car gnus-original-article) group)
6038                         (eq (cdr gnus-original-article) article))))
6039             (insert-buffer-substring gnus-original-article-buffer)
6040             'article)
6041            ;; Check the backlog.
6042            ((and gnus-keep-backlog
6043                  (gnus-backlog-request-article group article (current-buffer)))
6044             'article)
6045            ;; Check asynchronous pre-fetch.
6046            ((gnus-async-request-fetched-article group article (current-buffer))
6047             (gnus-async-prefetch-next group article gnus-summary-buffer)
6048             (when (and (numberp article) gnus-keep-backlog)
6049               (gnus-backlog-enter-article group article (current-buffer)))
6050             'article)
6051            ;; Check the cache.
6052            ((and gnus-use-cache
6053                  (numberp article)
6054                  (gnus-cache-request-article article group))
6055             'article)
6056            ;; Check the agent cache.
6057            ((gnus-agent-request-article article group)
6058             'article)
6059            ;; Get the article and put into the article buffer.
6060            ((or (stringp article)
6061                 (numberp article))
6062             (let ((gnus-override-method gnus-override-method)
6063                   (methods (and (stringp article)
6064                                 gnus-refer-article-method))
6065                   (backend (car (gnus-find-method-for-group
6066                                  gnus-newsgroup-name)))
6067                   result
6068                   (inhibit-read-only t))
6069               (if (or (not (listp methods))
6070                       (and (symbolp (car methods))
6071                            (assq (car methods) nnoo-definition-alist)))
6072                   (setq methods (list methods)))
6073               (when (and (null gnus-override-method)
6074                          methods)
6075                 (setq gnus-override-method (pop methods)))
6076               (while (not result)
6077                 (when (eq gnus-override-method 'current)
6078                   (setq gnus-override-method
6079                         (with-current-buffer gnus-summary-buffer
6080                           gnus-current-select-method)))
6081                 (erase-buffer)
6082                 (gnus-kill-all-overlays)
6083                 (let ((gnus-newsgroup-name group))
6084                   (gnus-check-group-server))
6085                 (cond
6086                  ((gnus-request-article article group (current-buffer))
6087                   (when (numberp article)
6088                     (gnus-async-prefetch-next group article
6089                                               gnus-summary-buffer)
6090                     (when gnus-keep-backlog
6091                       (gnus-backlog-enter-article
6092                        group article (current-buffer))))
6093                   (setq result 'article))
6094                  (methods
6095                   (setq gnus-override-method (pop methods)))
6096                  ((not (string-match "^400 "
6097                                      (nnheader-get-report backend)))
6098                   ;; If we get 400 server disconnect, reconnect and
6099                   ;; retry; otherwise, assume the article has expired.
6100                   (setq result 'done))))
6101               (and (eq result 'article) 'article)))
6102            ;; It was a pseudo.
6103            (t article)))
6104
6105       ;; Associate this article with the current summary buffer.
6106       (setq gnus-article-current-summary gnus-summary-buffer)
6107
6108       ;; Take the article from the original article buffer
6109       ;; and place it in the buffer it's supposed to be in.
6110       (when (and (get-buffer gnus-article-buffer)
6111                  (equal (buffer-name (current-buffer))
6112                         (buffer-name (get-buffer gnus-article-buffer))))
6113         (save-excursion
6114           (if (get-buffer gnus-original-article-buffer)
6115               (set-buffer gnus-original-article-buffer)
6116             (set-buffer (gnus-get-buffer-create gnus-original-article-buffer))
6117             (buffer-disable-undo)
6118             (setq major-mode 'gnus-original-article-mode)
6119             (setq buffer-read-only t))
6120           (let ((inhibit-read-only t))
6121             (erase-buffer)
6122             (insert-buffer-substring gnus-article-buffer))
6123           (setq gnus-original-article (cons group article)))
6124
6125         ;; Decode charsets.
6126         (run-hooks 'gnus-article-decode-hook)
6127         ;; Mark article as decoded or not.
6128         (setq gnus-article-decoded-p gnus-article-decode-hook))
6129
6130       ;; Update sparse articles.
6131       (when (and do-update-line
6132                  (or (numberp article)
6133                      (stringp article)))
6134         (let ((buf (current-buffer)))
6135           (set-buffer gnus-summary-buffer)
6136           (gnus-summary-update-article do-update-line sparse-header)
6137           (gnus-summary-goto-subject do-update-line nil t)
6138           (set-window-point (gnus-get-buffer-window (current-buffer) t)
6139                             (point))
6140           (set-buffer buf))))))
6141
6142 ;;;
6143 ;;; Article editing
6144 ;;;
6145
6146 (defcustom gnus-article-edit-mode-hook nil
6147   "Hook run in article edit mode buffers."
6148   :group 'gnus-article-various
6149   :type 'hook)
6150
6151 (defcustom gnus-article-edit-article-setup-function
6152   'gnus-article-mime-edit-article-setup
6153   "Function called to setup an editing article buffer."
6154   :group 'gnus-article-various
6155   :type 'function)
6156
6157 (defvar gnus-article-edit-done-function nil)
6158
6159 (defvar gnus-article-edit-mode-map nil)
6160
6161 ;; Should we be using derived.el for this?
6162 (unless gnus-article-edit-mode-map
6163   (setq gnus-article-edit-mode-map (make-keymap))
6164   (set-keymap-parent gnus-article-edit-mode-map text-mode-map)
6165
6166   (gnus-define-keys gnus-article-edit-mode-map
6167     "\C-c?"    describe-mode
6168     "\C-c\C-c" gnus-article-edit-done
6169     "\C-c\C-k" gnus-article-edit-exit
6170     "\C-c\C-f\C-t" message-goto-to
6171     "\C-c\C-f\C-o" message-goto-from
6172     "\C-c\C-f\C-b" message-goto-bcc
6173     ;;"\C-c\C-f\C-w" message-goto-fcc
6174     "\C-c\C-f\C-c" message-goto-cc
6175     "\C-c\C-f\C-s" message-goto-subject
6176     "\C-c\C-f\C-r" message-goto-reply-to
6177     "\C-c\C-f\C-n" message-goto-newsgroups
6178     "\C-c\C-f\C-d" message-goto-distribution
6179     "\C-c\C-f\C-f" message-goto-followup-to
6180     "\C-c\C-f\C-m" message-goto-mail-followup-to
6181     "\C-c\C-f\C-k" message-goto-keywords
6182     "\C-c\C-f\C-u" message-goto-summary
6183     "\C-c\C-f\C-i" message-insert-or-toggle-importance
6184     "\C-c\C-f\C-a" message-generate-unsubscribed-mail-followup-to
6185     "\C-c\C-b" message-goto-body
6186     "\C-c\C-i" message-goto-signature
6187
6188     "\C-c\C-t" message-insert-to
6189     "\C-c\C-n" message-insert-newsgroups
6190     "\C-c\C-o" message-sort-headers
6191     "\C-c\C-e" message-elide-region
6192     "\C-c\C-v" message-delete-not-region
6193     "\C-c\C-z" message-kill-to-signature
6194     "\M-\r" message-newline-and-reformat
6195     "\C-c\C-a" mml-attach-file
6196     "\C-a" message-beginning-of-line
6197     "\t" message-tab
6198     "\M-;" comment-region)
6199
6200   (gnus-define-keys (gnus-article-edit-wash-map
6201                      "\C-c\C-w" gnus-article-edit-mode-map)
6202     "f" gnus-article-edit-full-stops))
6203
6204 (easy-menu-define
6205   gnus-article-edit-mode-field-menu gnus-article-edit-mode-map ""
6206   '("Field"
6207     ["Fetch To" message-insert-to t]
6208     ["Fetch Newsgroups" message-insert-newsgroups t]
6209     "----"
6210     ["To" message-goto-to t]
6211     ["From" message-goto-from t]
6212     ["Subject" message-goto-subject t]
6213     ["Cc" message-goto-cc t]
6214     ["Reply-To" message-goto-reply-to t]
6215     ["Summary" message-goto-summary t]
6216     ["Keywords" message-goto-keywords t]
6217     ["Newsgroups" message-goto-newsgroups t]
6218     ["Followup-To" message-goto-followup-to t]
6219     ["Mail-Followup-To" message-goto-mail-followup-to t]
6220     ["Distribution" message-goto-distribution t]
6221     ["Body" message-goto-body t]
6222     ["Signature" message-goto-signature t]))
6223
6224 (define-derived-mode gnus-article-edit-mode message-mode "Article Edit"
6225   "Major mode for editing articles.
6226 This is an extended text-mode.
6227
6228 \\{gnus-article-edit-mode-map}"
6229   (make-local-variable 'gnus-article-edit-done-function)
6230   (make-local-variable 'gnus-prev-winconf)
6231   (set (make-local-variable 'font-lock-defaults)
6232        '(message-font-lock-keywords t))
6233   (set (make-local-variable 'mail-header-separator) "")
6234   (set (make-local-variable 'gnus-article-edit-mode) t)
6235   (easy-menu-add message-mode-field-menu message-mode-map)
6236   (setq buffer-read-only nil)
6237   (buffer-enable-undo)
6238   (widen))
6239
6240 (defun gnus-article-edit (&optional force)
6241   "Edit the current article.
6242 This will have permanent effect only in mail groups.
6243 If FORCE is non-nil, allow editing of articles even in read-only
6244 groups."
6245   (interactive "P")
6246   (when (and (not force)
6247              (gnus-group-read-only-p))
6248     (error "The current newsgroup does not support article editing"))
6249   (gnus-article-date-original)
6250   (gnus-article-edit-article
6251    'ignore
6252    `(lambda (no-highlight)
6253       'ignore
6254       (gnus-summary-edit-article-done
6255        ,(or (mail-header-references gnus-current-headers) "")
6256        ,(gnus-group-read-only-p) ,gnus-summary-buffer no-highlight))))
6257
6258 (defun gnus-article-edit-article (start-func exit-func &optional quiet)
6259   "Start editing the contents of the current article buffer."
6260   (let ((winconf (current-window-configuration)))
6261     (set-buffer gnus-article-buffer)
6262     (let ((message-auto-save-directory
6263            ;; Don't associate the article buffer with a draft file.
6264            nil))
6265       (gnus-article-edit-mode))
6266     (funcall start-func)
6267     (set-buffer-modified-p nil)
6268     (gnus-configure-windows 'edit-article)
6269     (setq gnus-article-edit-done-function exit-func)
6270     (setq gnus-prev-winconf winconf)
6271     (when gnus-article-edit-article-setup-function
6272       (funcall gnus-article-edit-article-setup-function))
6273     (unless quiet
6274       (gnus-message 6 "C-c C-c to end edits; C-c C-k to exit"))))
6275
6276 (defun gnus-article-edit-done (&optional arg)
6277   "Update the article edits and exit."
6278   (interactive "P")
6279   (let ((func gnus-article-edit-done-function)
6280         (buf (current-buffer))
6281         (start (window-start))
6282         (p (point))
6283         (winconf gnus-prev-winconf))
6284     (remove-hook 'gnus-article-mode-hook
6285                  'gnus-article-mime-edit-article-unwind)
6286     (widen) ;; Widen it in case that users narrowed the buffer.
6287     (funcall func arg)
6288     (set-buffer buf)
6289     ;; The cache and backlog have to be flushed somewhat.
6290     (when gnus-keep-backlog
6291       (gnus-backlog-remove-article
6292        (car gnus-article-current) (cdr gnus-article-current)))
6293     ;; Flush original article as well.
6294     (save-excursion
6295       (when (get-buffer gnus-original-article-buffer)
6296         (set-buffer gnus-original-article-buffer)
6297         (setq gnus-original-article nil)))
6298     (when gnus-use-cache
6299       (gnus-cache-update-article
6300        (car gnus-article-current) (cdr gnus-article-current)))
6301     ;; We remove all text props from the article buffer.
6302     (kill-all-local-variables)
6303     (set-text-properties (point-min) (point-max) nil)
6304     (gnus-article-mode)
6305     (set-window-configuration winconf)
6306     (set-buffer buf)
6307     (set-window-start (get-buffer-window buf) start)
6308     (set-window-point (get-buffer-window buf) (point)))
6309   (gnus-summary-show-article))
6310
6311 (defun gnus-article-edit-exit ()
6312   "Exit the article editing without updating."
6313   (interactive)
6314   (when (or (not (buffer-modified-p))
6315             (yes-or-no-p "Article modified; kill anyway? "))
6316     (let ((curbuf (current-buffer))
6317           (p (point))
6318           (window-start (window-start)))
6319       (erase-buffer)
6320       (if (gnus-buffer-live-p gnus-original-article-buffer)
6321           (insert-buffer-substring gnus-original-article-buffer))
6322       (let ((winconf gnus-prev-winconf))
6323         (kill-all-local-variables)
6324         (gnus-article-mode)
6325         (set-window-configuration winconf)
6326         ;; Tippy-toe some to make sure that point remains where it was.
6327         (save-current-buffer
6328           (set-buffer curbuf)
6329           (set-window-start (get-buffer-window (current-buffer)) window-start)
6330           (goto-char p))))
6331     (gnus-summary-show-article)))
6332
6333 (defun gnus-article-edit-full-stops ()
6334   "Interactively repair spacing at end of sentences."
6335   (interactive)
6336   (save-excursion
6337     (goto-char (point-min))
6338     (search-forward-regexp "^$" nil t)
6339     (let ((case-fold-search nil))
6340       (query-replace-regexp "\\([.!?][])}]* \\)\\([[({A-Z]\\)" "\\1 \\2"))))
6341
6342 ;;;
6343 ;;; Article editing with MIME-Edit
6344 ;;;
6345
6346 (defcustom gnus-article-mime-edit-article-setup-hook nil
6347   "Hook run after setting up a MIME editing article buffer."
6348   :group 'gnus-article-various
6349   :type 'hook)
6350
6351 (defun gnus-article-mime-edit-article-unwind ()
6352   "Unwind `gnus-article-buffer' if article editing was given up."
6353   (remove-hook 'gnus-article-mode-hook 'gnus-article-mime-edit-article-unwind)
6354   (when (featurep 'font-lock)
6355     (setq font-lock-defaults nil)
6356     (font-lock-mode -1))
6357   (when mime-edit-mode-flag
6358     (mime-edit-exit 'nomime 'no-error)
6359     (message "")))
6360
6361 (defun gnus-article-mime-edit-article-setup ()
6362   "Convert current buffer to MIME-Edit buffer and turn on MIME-Edit mode
6363 after replacing with the original article."
6364   (setq gnus-show-mime t)
6365   (setq gnus-article-edit-done-function
6366         `(lambda (&rest args)
6367            (when mime-edit-mode-flag
6368              (let (mime-edit-insert-user-agent-field)
6369                (mime-edit-exit))
6370              (message ""))
6371            (goto-char (point-min))
6372            (let (case-fold-search)
6373              (when (re-search-forward
6374                     (format "^%s$" (regexp-quote mail-header-separator))
6375                     nil t)
6376                (replace-match "")))
6377            (apply ,gnus-article-edit-done-function args)
6378            (insert
6379             (prog1
6380                 (buffer-substring-no-properties (point-min) (point-max))
6381               (set-buffer (get-buffer-create gnus-original-article-buffer))
6382               (erase-buffer)))
6383            (setq gnus-current-headers (gnus-article-make-full-mail-header))
6384            (set-buffer gnus-article-buffer)
6385            (gnus-article-prepare-display)))
6386   (substitute-key-definition 'gnus-article-edit-done
6387                              'gnus-article-mime-edit-done
6388                              gnus-article-edit-mode-map)
6389   (substitute-key-definition 'gnus-article-edit-exit
6390                              'gnus-article-mime-edit-exit
6391                              gnus-article-edit-mode-map)
6392   (erase-buffer)
6393   (insert-buffer-substring gnus-original-article-buffer)
6394   (unless (member (with-current-buffer gnus-summary-buffer
6395                     gnus-newsgroup-name)
6396                   '("nndraft:delayed" "nndraft:drafts"))
6397     (let ((ofn (symbol-function 'mime-edit-decode-single-part-in-buffer)))
6398       (fset 'mime-edit-decode-single-part-in-buffer
6399             (lambda (&rest args)
6400               (if (let ((content-type (car args)))
6401                     (and (eq 'message (mime-content-type-primary-type
6402                                        content-type))
6403                          (eq 'rfc822 (mime-content-type-subtype
6404                                       content-type))))
6405                   (setcar (cdr args) 'not-decode-text))
6406               (apply ofn args)))
6407       (unwind-protect
6408           (mime-edit-again)
6409         (fset 'mime-edit-decode-single-part-in-buffer ofn))))
6410   (when (featurep 'font-lock)
6411     (set (make-local-variable 'font-lock-defaults)
6412          '(message-font-lock-keywords t))
6413     (font-lock-set-defaults)
6414     (turn-on-font-lock))
6415   (set-buffer-modified-p nil)
6416   (delete-other-windows)
6417   (add-hook 'gnus-article-mode-hook 'gnus-article-mime-edit-article-unwind)
6418   (gnus-run-hooks 'gnus-article-mime-edit-article-setup-hook))
6419
6420 (defun gnus-article-mime-edit-done (&optional arg)
6421   "Update the article MIME edits and exit."
6422   (interactive "P")
6423   (when (featurep 'font-lock)
6424     (setq font-lock-defaults nil)
6425     (font-lock-mode -1))
6426   (let ((inhibit-read-only t))
6427     (gnus-article-edit-done arg)))
6428
6429 (defun gnus-article-mime-edit-exit ()
6430   "Exit the article MIME editing without updating."
6431   (interactive)
6432   (when (or (not (buffer-modified-p))
6433             (yes-or-no-p "Article modified; kill anyway? "))
6434     (when (featurep 'font-lock)
6435       (setq font-lock-defaults nil)
6436       (font-lock-mode -1))
6437     (when mime-edit-mode-flag
6438       (let (mime-edit-insert-user-agent-field)
6439         (mime-edit-exit))
6440       (message ""))
6441     (goto-char (point-min))
6442     (let (case-fold-search)
6443       (when (re-search-forward
6444              (format "^%s$" (regexp-quote mail-header-separator)) nil t)
6445         (replace-match "")))
6446     (let ((winconf gnus-prev-winconf))
6447       (insert (prog1
6448                   (buffer-substring-no-properties (point-min) (point-max))
6449                 (set-buffer (get-buffer-create gnus-original-article-buffer))
6450                 (erase-buffer)))
6451       (setq gnus-current-headers (gnus-article-make-full-mail-header))
6452       (set-buffer gnus-article-buffer)
6453       (gnus-article-prepare-display)
6454       (set-window-configuration winconf))))
6455
6456 ;;;
6457 ;;; Article highlights
6458 ;;;
6459
6460 ;; Written by Per Abrahamsen <abraham@iesd.auc.dk>.
6461
6462 ;;; Internal Variables:
6463
6464 (defcustom gnus-button-url-regexp
6465   (if (string-match "[[:digit:]]" "1") ;; support POSIX?
6466       "\\b\\(\\(www\\.\\|\\(s?https?\\|ftp\\|file\\|gopher\\|nntp\\|news\\|telnet\\|wais\\|mailto\\|info\\):\\)\\(//[-a-z0-9_.]+:[0-9]*\\)?[-a-z0-9_=!?#$@~%&*+\\/:;.,[:word:]]+[-a-z0-9_=#$@~%&*+\\/[:word:]]\\)"
6467     "\\b\\(\\(www\\.\\|\\(s?https?\\|ftp\\|file\\|gopher\\|nntp\\|news\\|telnet\\|wais\\|mailto\\|info\\):\\)\\(//[-a-z0-9_.]+:[0-9]*\\)?\\([-a-z0-9_=!?#$@~%&*+\\/:;.,]\\|\\w\\)+\\([-a-z0-9_=#$@~%&*+\\/]\\|\\w\\)\\)")
6468   "Regular expression that matches URLs."
6469   :group 'gnus-article-buttons
6470   :type 'regexp)
6471
6472 (defcustom gnus-button-valid-fqdn-regexp
6473   message-valid-fqdn-regexp
6474   "Regular expression that matches a valid FQDN."
6475   :version "22.1"
6476   :group 'gnus-article-buttons
6477   :type 'regexp)
6478
6479 ;; Regexp suggested by Felix Wiemann in <87oeuomcz9.fsf@news2.ososo.de>
6480 (defcustom gnus-button-valid-localpart-regexp
6481   "[a-z0-9$%(*-=?[_][^<>\")!;:,{}\n\t ]*"
6482   "Regular expression that matches a localpart of mail addresses or MIDs."
6483   :version "22.1"
6484   :group 'gnus-article-buttons
6485   :type 'regexp)
6486
6487 (defcustom gnus-button-man-handler 'manual-entry
6488   "Function to use for displaying man pages.
6489 The function must take at least one argument with a string naming the
6490 man page."
6491   :version "22.1"
6492   :type '(choice (function-item :tag "Man" manual-entry)
6493                  (function-item :tag "Woman" woman)
6494                  (function :tag "Other"))
6495   :group 'gnus-article-buttons)
6496
6497 (defcustom gnus-ctan-url "http://tug.ctan.org/tex-archive/"
6498   "Top directory of a CTAN \(Comprehensive TeX Archive Network\) archive.
6499 If the default site is too slow, try to find a CTAN mirror, see
6500 <URL:http://tug.ctan.org/tex-archive/CTAN.sites?action=/index.html>.  See also
6501 the variable `gnus-button-handle-ctan'."
6502   :version "22.1"
6503   :group 'gnus-article-buttons
6504   :link '(custom-manual "(gnus)Group Parameters")
6505   :type '(choice (const "http://www.tex.ac.uk/tex-archive/")
6506                  (const "http://tug.ctan.org/tex-archive/")
6507                  (const "http://www.dante.de/CTAN/")
6508                  (string :tag "Other")))
6509
6510 (defcustom gnus-button-ctan-handler 'browse-url
6511   "Function to use for displaying CTAN links.
6512 The function must take one argument, the string naming the URL."
6513   :version "22.1"
6514   :type '(choice (function-item :tag "Browse Url" browse-url)
6515                  (function :tag "Other"))
6516   :group 'gnus-article-buttons)
6517
6518 (defcustom gnus-button-handle-ctan-bogus-regexp "^/?tex-archive/\\|^/"
6519   "Bogus strings removed from CTAN URLs."
6520   :version "22.1"
6521   :group 'gnus-article-buttons
6522   :type '(choice (const "^/?tex-archive/\\|/")
6523                  (regexp :tag "Other")))
6524
6525 (defcustom gnus-button-ctan-directory-regexp
6526   (regexp-opt
6527    (list "archive-tools" "biblio" "bibliography" "digests" "documentation"
6528          "dviware" "fonts" "graphics" "help" "indexing" "info" "language"
6529          "languages" "macros" "nonfree" "obsolete" "support" "systems"
6530          "tds" "tools" "usergrps" "web") t)
6531   "Regular expression for ctan directories.
6532 It should match all directories in the top level of `gnus-ctan-url'."
6533   :version "22.1"
6534   :group 'gnus-article-buttons
6535   :type 'regexp)
6536
6537 (defcustom gnus-button-mid-or-mail-regexp
6538   (concat "\\b\\(<?" gnus-button-valid-localpart-regexp "@"
6539           gnus-button-valid-fqdn-regexp
6540           ">?\\)\\b")
6541   "Regular expression that matches a message ID or a mail address."
6542   :version "22.1"
6543   :group 'gnus-article-buttons
6544   :type 'regexp)
6545
6546 (defcustom gnus-button-prefer-mid-or-mail 'gnus-button-mid-or-mail-heuristic
6547   "What to do when the button on a string as \"foo123@bar.invalid\" is pushed.
6548 Strings like this can be either a message ID or a mail address.  If it is one
6549 of the symbols `mid' or `mail', Gnus will always assume that the string is a
6550 message ID or a mail address, respectively.  If this variable is set to the
6551 symbol `ask', always query the user what do do.  If it is a function, this
6552 function will be called with the string as it's only argument.  The function
6553 must return `mid', `mail', `invalid' or `ask'."
6554   :version "22.1"
6555   :group 'gnus-article-buttons
6556   :type '(choice (function-item :tag "Heuristic function"
6557                                 gnus-button-mid-or-mail-heuristic)
6558                  (const ask)
6559                  (const mid)
6560                  (const mail)))
6561
6562 (defcustom gnus-button-mid-or-mail-heuristic-alist
6563   '((-10.0 . ".+\\$.+@")
6564     (-10.0 . "#")
6565     (-10.0 . "\\*")
6566     (-5.0  . "\\+[^+]*\\+.*@") ;; # two plus signs
6567     (-5.0  . "@[Nn][Ee][Ww][Ss]") ;; /\@news/i
6568     (-5.0  . "@.*[Dd][Ii][Aa][Ll][Uu][Pp]") ;; /\@.*dialup/i;
6569     (-1.0  . "^[^a-z]+@")
6570     ;;
6571     (-5.0  . "\\.[0-9][0-9]+.*@") ;; "\.[0-9]{2,}.*\@"
6572     (-5.0  . "[a-z].*[A-Z].*[a-z].*[A-Z].*@") ;; "([a-z].*[A-Z].*){2,}\@"
6573     (-3.0  . "[A-Z][A-Z][a-z][a-z].*@")
6574     (-5.0  . "\\...?.?@") ;; (-5.0 . "\..{1,3}\@")
6575     ;;
6576     (-2.0  . "^[0-9]")
6577     (-1.0  . "^[0-9][0-9]")
6578     ;;
6579     ;; -3.0 /^[0-9][0-9a-fA-F]{2,2}/;
6580     (-3.0  . "^[0-9][0-9a-fA-F][0-9a-fA-F][^0-9a-fA-F]")
6581     ;; -5.0 /^[0-9][0-9a-fA-F]{3,3}/;
6582     (-5.0  . "^[0-9][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][^0-9a-fA-F]")
6583     ;;
6584     (-3.0  .  "[0-9][0-9][0-9][0-9][0-9][^0-9].*@") ;; "[0-9]{5,}.*\@"
6585     (-3.0  .  "[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][^0-9].*@")
6586     ;;       "[0-9]{8,}.*\@"
6587     (-3.0
6588      . "[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9].*@")
6589     ;; "[0-9]{12,}.*\@"
6590     ;; compensation for TDMA dated mail addresses:
6591     (25.0  . "-dated-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]+.*@")
6592     ;;
6593     (-20.0 . "\\.fsf@") ;; Gnus
6594     (-20.0 . "^slrn")
6595     (-20.0 . "^Pine")
6596     (-20.0 . "_-_") ;; Subject change in thread
6597     ;;
6598     (-20.0 . "\\.ln@") ;; leafnode
6599     (-30.0 . "@ID-[0-9]+\\.[a-zA-Z]+\\.dfncis\\.de")
6600     (-30.0 . "@4[Aa][Xx]\\.com") ;; Forte Agent
6601     ;;
6602     ;; (5.0 . "") ;; $local_part_len <= 7
6603     (10.0  . "^[^0-9]+@")
6604     (3.0   . "^[^0-9]+[0-9][0-9]?[0-9]?@")
6605     ;;      ^[^0-9]+[0-9]{1,3}\@ digits only at end of local part
6606     (3.0   . "\@stud")
6607     ;;
6608     (2.0   . "[a-z][a-z][._-][A-Z][a-z].*@")
6609     ;;
6610     (0.5   . "^[A-Z][a-z]")
6611     (0.5   . "^[A-Z][a-z][a-z]")
6612     (1.5   . "^[A-Z][a-z][A-Z][a-z][^a-z]") ;; ^[A-Z][a-z]{3,3}
6613     (2.0   . "^[A-Z][a-z][A-Z][a-z][a-z][^a-z]")) ;; ^[A-Z][a-z]{4,4}
6614   "An alist of \(RATE . REGEXP\) pairs for `gnus-button-mid-or-mail-heuristic'.
6615
6616 A negative RATE indicates a message IDs, whereas a positive indicates a mail
6617 address.  The REGEXP is processed with `case-fold-search' set to nil."
6618   :version "22.1"
6619   :group 'gnus-article-buttons
6620   :type '(repeat (cons (number :tag "Rate")
6621                        (regexp :tag "Regexp"))))
6622
6623 (defun gnus-button-mid-or-mail-heuristic (mid-or-mail)
6624   "Guess whether MID-OR-MAIL is a message ID or a mail address.
6625 Returns `mid' if MID-OR-MAIL is a message IDs, `mail' if it's a mail
6626 address, `ask' if unsure and `invalid' if the string is invalid."
6627   (let ((case-fold-search nil)
6628         (list gnus-button-mid-or-mail-heuristic-alist)
6629         (result 0) rate regexp lpartlen elem)
6630     (setq lpartlen
6631           (length (gnus-replace-in-string mid-or-mail "^\\(.*\\)@.*$" "\\1")))
6632     (gnus-message 8 "`%s', length of local part=`%s'." mid-or-mail lpartlen)
6633     ;; Certain special cases...
6634     (when (string-match
6635            (concat
6636             "^0[0-9]+-[0-9][0-9][0-9][0-9]@t-online\\.de$\\|"
6637             "^[0-9]+\\.[0-9]+@compuserve\\|"
6638             "@public\\.gmane\\.org")
6639            mid-or-mail)
6640       (gnus-message 8 "`%s' is a known mail address." mid-or-mail)
6641       (setq result 'mail))
6642     (when (string-match "@.*@\\| " mid-or-mail)
6643       (gnus-message 8 "`%s' is invalid." mid-or-mail)
6644       (setq result 'invalid))
6645     ;; Nothing more to do, if result is not a number here...
6646     (when (numberp result)
6647       (while list
6648         (setq elem (car list)
6649               rate (car elem)
6650               regexp (cdr elem)
6651               list (cdr list))
6652         (when (string-match regexp mid-or-mail)
6653           (setq result (+ result rate))
6654           (gnus-message
6655            9 "`%s' matched `%s', rate `%s', result `%s'."
6656            mid-or-mail regexp rate result)))
6657       (when (<= lpartlen 7)
6658         (setq result (+ result 5.0))
6659         (gnus-message 9 "`%s' matched (<= lpartlen 7), result `%s'."
6660                       mid-or-mail result))
6661       (when (>= lpartlen 12)
6662         (gnus-message 9 "`%s' matched (>= lpartlen 12)" mid-or-mail)
6663         (cond
6664          ((string-match "[0-9][^0-9]+[0-9].*@" mid-or-mail)
6665           ;; Long local part should contain realname if e-mail address,
6666           ;; too many digits: message-id.
6667           ;; $score -= 5.0 + 0.1 * $local_part_len;
6668           (setq rate (* -1.0 (+ 5.0 (* 0.1 lpartlen))))
6669           (setq result (+ result rate))
6670           (gnus-message
6671            9 "Many digits in `%s', rate `%s', result `%s'."
6672            mid-or-mail rate result))
6673          ((string-match "[^aeiouy][^aeiouy][^aeiouy][^aeiouy]+.*\@"
6674                         mid-or-mail)
6675           ;; Too few vowels [^aeiouy]{4,}.*\@
6676           (setq result (+ result -5.0))
6677           (gnus-message
6678            9 "Few vowels in `%s', rate `%s', result `%s'."
6679            mid-or-mail -5.0 result))
6680          (t
6681           (setq result (+ result 5.0))
6682           (gnus-message
6683            9 "`%s', rate `%s', result `%s'." mid-or-mail 5.0 result)))))
6684     (gnus-message 8 "`%s': Final rate is `%s'." mid-or-mail result)
6685     ;; Maybe we should make this a customizable alist: (condition . 'result)
6686     (cond
6687      ((symbolp result) result)
6688      ;; Now convert number into proper results:
6689      ((< result -10.0) 'mid)
6690      ((> result  10.0) 'mail)
6691      (t 'ask))))
6692
6693 (defun gnus-button-handle-mid-or-mail (mid-or-mail)
6694   (let* ((pref gnus-button-prefer-mid-or-mail) guessed
6695          (url-mid (concat "news" ":" mid-or-mail))
6696          (url-mailto (concat "mailto" ":" mid-or-mail)))
6697     (gnus-message 9 "mid-or-mail=%s" mid-or-mail)
6698     (when (fboundp pref)
6699       (setq guessed
6700             ;; get rid of surrounding angles...
6701             (funcall pref
6702                      (gnus-replace-in-string mid-or-mail "^<\\|>$" "")))
6703       (if (or (eq 'mid guessed) (eq 'mail guessed))
6704           (setq pref guessed)
6705         (setq pref 'ask)))
6706     (if (eq pref 'ask)
6707         (save-window-excursion
6708           (if (y-or-n-p (concat "Is <" mid-or-mail "> a mail address? "))
6709               (setq pref 'mail)
6710             (setq pref 'mid))))
6711     (cond ((eq pref 'mid)
6712            (gnus-message 8 "calling `gnus-button-handle-news' %s" url-mid)
6713            (gnus-button-handle-news url-mid))
6714           ((eq pref 'mail)
6715            (gnus-message 8 "calling `gnus-url-mailto'  %s" url-mailto)
6716            (gnus-url-mailto url-mailto))
6717           (t (gnus-message 3 "Invalid string.")))))
6718
6719 (defun gnus-button-handle-custom (url)
6720   "Follow a Custom URL."
6721   (customize-apropos (gnus-url-unhex-string url)))
6722
6723 (defvar gnus-button-handle-describe-prefix "^\\(C-h\\|<?[Ff]1>?\\)")
6724
6725 ;; FIXME: Maybe we should merge some of the functions that do quite similar
6726 ;; stuff?
6727
6728 (defun gnus-button-handle-describe-function (url)
6729   "Call `describe-function' when pushing the corresponding URL button."
6730   (describe-function
6731    (intern
6732     (gnus-replace-in-string url gnus-button-handle-describe-prefix ""))))
6733
6734 (defun gnus-button-handle-describe-variable (url)
6735   "Call `describe-variable' when pushing the corresponding URL button."
6736   (describe-variable
6737    (intern
6738     (gnus-replace-in-string url gnus-button-handle-describe-prefix ""))))
6739
6740 (defun gnus-button-handle-symbol (url)
6741 "Display help on variable or function.
6742 Calls `describe-variable' or `describe-function'."
6743   (let ((sym (intern url)))
6744     (cond
6745      ((fboundp sym) (describe-function sym))
6746      ((boundp sym) (describe-variable sym))
6747      (t (gnus-message 3 "`%s' is not a known function of variable." url)))))
6748
6749 (defun gnus-button-handle-describe-key (url)
6750   "Call `describe-key' when pushing the corresponding URL button."
6751   (let* ((key-string
6752           (gnus-replace-in-string url gnus-button-handle-describe-prefix ""))
6753          (keys (ignore-errors (eval `(kbd ,key-string)))))
6754     (if keys
6755         (describe-key keys)
6756       (gnus-message 3 "Invalid key sequence in button: %s" key-string))))
6757
6758 (defun gnus-button-handle-apropos (url)
6759   "Call `apropos' when pushing the corresponding URL button."
6760   (apropos (gnus-replace-in-string url gnus-button-handle-describe-prefix "")))
6761
6762 (defun gnus-button-handle-apropos-command (url)
6763   "Call `apropos' when pushing the corresponding URL button."
6764   (apropos-command
6765    (gnus-replace-in-string url gnus-button-handle-describe-prefix "")))
6766
6767 (defun gnus-button-handle-apropos-variable (url)
6768   "Call `apropos' when pushing the corresponding URL button."
6769   (funcall
6770    (if (fboundp 'apropos-variable) 'apropos-variable 'apropos)
6771    (gnus-replace-in-string url gnus-button-handle-describe-prefix "")))
6772
6773 (defun gnus-button-handle-apropos-documentation (url)
6774   "Call `apropos' when pushing the corresponding URL button."
6775   (funcall
6776    (if (fboundp 'apropos-documentation) 'apropos-documentation 'apropos)
6777    (gnus-replace-in-string url gnus-button-handle-describe-prefix "")))
6778
6779 (defun gnus-button-handle-library (url)
6780   "Call `locate-library' when pushing the corresponding URL button."
6781   (gnus-message 9 "url=`%s'" url)
6782   (let* ((lib (locate-library url))
6783          (file (gnus-replace-in-string (or lib "") "\.elc" ".el")))
6784     (if (not lib)
6785         (gnus-message 1 "Cannot locale library `%s'." url)
6786       (find-file-read-only file))))
6787
6788 (defun gnus-button-handle-ctan (url)
6789   "Call `browse-url' when pushing a CTAN URL button."
6790   (funcall
6791    gnus-button-ctan-handler
6792    (concat
6793     gnus-ctan-url
6794     (gnus-replace-in-string url gnus-button-handle-ctan-bogus-regexp ""))))
6795
6796 (defcustom gnus-button-tex-level 5
6797   "*Integer that says how many TeX-related buttons Gnus will show.
6798 The higher the number, the more buttons will appear and the more false
6799 positives are possible.  Note that you can set this variable local to
6800 specific groups.  Setting it higher in TeX groups is probably a good idea.
6801 See Info node `(gnus)Group Parameters' and the variable `gnus-parameters' on
6802 how to set variables in specific groups."
6803   :version "22.1"
6804   :group 'gnus-article-buttons
6805   :link '(custom-manual "(gnus)Group Parameters")
6806   :type 'integer)
6807
6808 (defcustom gnus-button-man-level 5
6809   "*Integer that says how many man-related buttons Gnus will show.
6810 The higher the number, the more buttons will appear and the more false
6811 positives are possible.  Note that you can set this variable local to
6812 specific groups.  Setting it higher in Unix groups is probably a good idea.
6813 See Info node `(gnus)Group Parameters' and the variable `gnus-parameters' on
6814 how to set variables in specific groups."
6815   :version "22.1"
6816   :group 'gnus-article-buttons
6817   :link '(custom-manual "(gnus)Group Parameters")
6818   :type 'integer)
6819
6820 (defcustom gnus-button-emacs-level 5
6821   "*Integer that says how many emacs-related buttons Gnus will show.
6822 The higher the number, the more buttons will appear and the more false
6823 positives are possible.  Note that you can set this variable local to
6824 specific groups.  Setting it higher in Emacs or Gnus related groups is
6825 probably a good idea.  See Info node `(gnus)Group Parameters' and the variable
6826 `gnus-parameters' on how to set variables in specific groups."
6827   :version "22.1"
6828   :group 'gnus-article-buttons
6829   :link '(custom-manual "(gnus)Group Parameters")
6830   :type 'integer)
6831
6832 (defcustom gnus-button-message-level 5
6833   "*Integer that says how many buttons for news or mail messages will appear.
6834 The higher the number, the more buttons will appear and the more false
6835 positives are possible."
6836   ;; mail addresses, MIDs, URLs for news, ...
6837   :version "22.1"
6838   :group 'gnus-article-buttons
6839   :type 'integer)
6840
6841 (defcustom gnus-button-browse-level 5
6842   "*Integer that says how many buttons for browsing will appear.
6843 The higher the number, the more buttons will appear and the more false
6844 positives are possible."
6845   ;; stuff handled by `browse-url' or `gnus-button-embedded-url'
6846   :version "22.1"
6847   :group 'gnus-article-buttons
6848   :type 'integer)
6849
6850 (defcustom gnus-button-alist
6851   '(("<\\(url:[>\n\t ]*?\\)?\\(nntp\\|news\\):[>\n\t ]*\\([^>\n\t ]*@[^>\n\t ]*\\)>"
6852      0 (>= gnus-button-message-level 0) gnus-button-handle-news 3)
6853     ((concat "\\b\\(nntp\\|news\\):\\("
6854              gnus-button-valid-localpart-regexp "@[a-z0-9.-]+[a-z]\\)")
6855      0 t gnus-button-handle-news 2)
6856     ("\\(\\b<\\(url:[>\n\t ]*\\)?\\(nntp\\|news\\):[>\n\t ]*\\(//\\)?\\([^>\n\t ]*\\)>\\)"
6857      1 (>= gnus-button-message-level 0) gnus-button-fetch-group 5)
6858     ("\\b\\(nntp\\|news\\):\\(//\\)?\\([^'\">\n\t ]+\\)"
6859      0 (>= gnus-button-message-level 0) gnus-button-fetch-group 3)
6860     ;; RFC 2392 (Don't allow `/' in domain part --> CID)
6861     ("\\bmid:\\(//\\)?\\([^'\">\n\t ]+@[^'\">\n\t /]+\\)"
6862      0 (>= gnus-button-message-level 0) gnus-button-message-id 2)
6863     ("\\bin\\( +article\\| +message\\)? +\\(<\\([^\n @<>]+@[^\n @<>]+\\)>\\)"
6864      2 (>= gnus-button-message-level 0) gnus-button-message-id 3)
6865     ("\\(<URL: *\\)mailto: *\\([^> \n\t]+\\)>"
6866      0 (>= gnus-button-message-level 0) gnus-url-mailto 2)
6867     ;; RFC 2368 (The mailto URL scheme)
6868     ("\\bmailto:\\([-a-z.@_+0-9%=?&/]+\\)"
6869      0 (>= gnus-button-message-level 0) gnus-url-mailto 1)
6870     ("\\bmailto:\\([^ \n\t]+\\)"
6871      0 (>= gnus-button-message-level 0) gnus-url-mailto 1)
6872     ;; CTAN
6873     ((concat "\\bCTAN:[ \t\n]?[^>)!;:,'\n\t ]*\\("
6874              gnus-button-ctan-directory-regexp
6875              "[^][>)!;:,'\n\t ]+\\)")
6876      0 (>= gnus-button-tex-level 1) gnus-button-handle-ctan 1)
6877     ((concat "\\btex-archive/\\("
6878              gnus-button-ctan-directory-regexp
6879              "/[-_.a-z0-9/]+[-_./a-z0-9]+[/a-z0-9]\\)")
6880      1 (>= gnus-button-tex-level 6) gnus-button-handle-ctan 1)
6881     ((concat
6882       "\\b\\("
6883       gnus-button-ctan-directory-regexp
6884       "/[-_.a-z0-9]+/[-_./a-z0-9]+[/a-z0-9]\\)")
6885      1 (>= gnus-button-tex-level 8) gnus-button-handle-ctan 1)
6886     ;; This is info (home-grown style) <info://foo/bar+baz>
6887     ("\\binfo://\\([^'\">\n\t ]+\\)"
6888      0 (>= gnus-button-emacs-level 1) gnus-button-handle-info-url 1)
6889     ;; Info GNOME style <info:foo#bar_baz>
6890     ("\\binfo:\\([^('\n\t\r \"><][^'\n\t\r \"><]*\\)"
6891      0 (>= gnus-button-emacs-level 1) gnus-button-handle-info-url-gnome 1)
6892     ;; Info KDE style <info:(foo)bar baz>
6893     ("<\\(info:\\(([^)]+)[^>\n\r]*\\)\\)>"
6894      1 (>= gnus-button-emacs-level 1) gnus-button-handle-info-url-kde 2)
6895     ("\\((Info-goto-node\\|(info\\)[ \t\n]*\\(\"[^\"]*\"\\))" 0
6896      (>= gnus-button-emacs-level 1) gnus-button-handle-info-url 2)
6897     ("\\b\\(C-h\\|<?[Ff]1>?\\)[ \t\n]+i[ \t\n]+d?[ \t\n]?m[ \t\n]+\\([^ ]+ ?[^ ]+\\)[ \t\n]+RET"
6898      ;; Info links like `C-h i d m CC Mode RET'
6899      0 (>= gnus-button-emacs-level 1) gnus-button-handle-info-keystrokes 2)
6900     ;; This is custom
6901     ("\\bcustom:\\(//\\)?\\([^'\">\n\t ]+\\)"
6902      0 (>= gnus-button-emacs-level 5) gnus-button-handle-custom 2)
6903     ("M-x[ \t\n]customize-[^ ]+[ \t\n]RET[ \t\n]\\([^ ]+\\)[ \t\n]RET" 0
6904      (>= gnus-button-emacs-level 1) gnus-button-handle-custom 1)
6905     ;; Emacs help commands
6906     ("M-x[ \t\n]+apropos[ \t\n]+RET[ \t\n]+\\([^ \t\n]+\\)[ \t\n]+RET"
6907      ;; regexp doesn't match arguments containing ` '.
6908      0 (>= gnus-button-emacs-level 1) gnus-button-handle-apropos 1)
6909     ("M-x[ \t\n]+apropos-command[ \t\n]+RET[ \t\n]+\\([^ \t\n]+\\)[ \t\n]+RET"
6910      0 (>= gnus-button-emacs-level 1) gnus-button-handle-apropos-command 1)
6911     ("M-x[ \t\n]+apropos-variable[ \t\n]+RET[ \t\n]+\\([^ \t\n]+\\)[ \t\n]+RET"
6912      0 (>= gnus-button-emacs-level 1) gnus-button-handle-apropos-variable 1)
6913     ("M-x[ \t\n]+apropos-documentation[ \t\n]+RET[ \t\n]+\\([^ \t\n]+\\)[ \t\n]+RET"
6914      0 (>= gnus-button-emacs-level 1) gnus-button-handle-apropos-documentation 1)
6915     ;; The following entries may lead to many false positives so don't enable
6916     ;; them by default (use a high button level).
6917     ("/\\([a-z][-a-z0-9]+\\.el\\)\\>[^.?]"
6918      ;; Exclude [.?] for URLs in gmane.emacs.cvs
6919      1 (>= gnus-button-emacs-level 8) gnus-button-handle-library 1)
6920     ("`\\([a-z][-a-z0-9]+\\.el\\)'"
6921      1 (>= gnus-button-emacs-level 8) gnus-button-handle-library 1)
6922     ("`\\([a-z][a-z0-9]+-[a-z]+-[-a-z]+\\|\\(gnus\\|message\\)-[-a-z]+\\)'"
6923      0 (>= gnus-button-emacs-level 8) gnus-button-handle-symbol 1)
6924     ("`\\([a-z][a-z0-9]+-[a-z]+\\)'"
6925      0 (>= gnus-button-emacs-level 9) gnus-button-handle-symbol 1)
6926     ("(setq[ \t\n]+\\([a-z][a-z0-9]+-[-a-z0-9]+\\)[ \t\n]+.+)"
6927      1 (>= gnus-button-emacs-level 7) gnus-button-handle-describe-variable 1)
6928     ("\\bM-x[ \t\n]+\\([^ \t\n]+\\)[ \t\n]+RET"
6929      1 (>= gnus-button-emacs-level 7) gnus-button-handle-describe-function 1)
6930     ("\\b\\(C-h\\|<?[Ff]1>?\\)[ \t\n]+f[ \t\n]+\\([^ \t\n]+\\)[ \t\n]+RET"
6931      0 (>= gnus-button-emacs-level 1) gnus-button-handle-describe-function 2)
6932     ("\\b\\(C-h\\|<?[Ff]1>?\\)[ \t\n]+v[ \t\n]+\\([^ \t\n]+\\)[ \t\n]+RET"
6933      0 (>= gnus-button-emacs-level 1) gnus-button-handle-describe-variable 2)
6934     ("`\\(\\b\\(C-h\\|<?[Ff]1>?\\)[ \t\n]+k[ \t\n]+\\([^']+\\)\\)'"
6935      ;; Unlike the other regexps we really have to require quoting
6936      ;; here to determine where it ends.
6937      1 (>= gnus-button-emacs-level 1) gnus-button-handle-describe-key 3)
6938     ;; This is how URLs _should_ be embedded in text (RFC 1738, RFC 2396)...
6939     ("<URL: *\\([^<>]*\\)>"
6940      1 (>= gnus-button-browse-level 0) gnus-button-embedded-url 1)
6941     ;; RFC 2396 (2.4.3., delims) ...
6942     ("\"URL: *\\([^\"]*\\)\""
6943      1 (>= gnus-button-browse-level 0) gnus-button-embedded-url 1)
6944     ;; RFC 2396 (2.4.3., delims) ...
6945     ("\"URL: *\\([^\"]*\\)\""
6946      1 (>= gnus-button-browse-level 0) gnus-button-embedded-url 1)
6947     ;; Raw URLs.
6948     (gnus-button-url-regexp
6949      0 (>= gnus-button-browse-level 0) browse-url 0)
6950     ;; man pages
6951     ("\\b\\([a-z][a-z]+([1-9])\\)\\W"
6952      0 (and (>= gnus-button-man-level 1) (< gnus-button-man-level 3))
6953      gnus-button-handle-man 1)
6954     ;; more man pages: resolv.conf(5), iso_8859-1(7), xterm(1x)
6955     ("\\b\\([a-z][-_.a-z0-9]+([1-9])\\)\\W"
6956      0 (and (>= gnus-button-man-level 3) (< gnus-button-man-level 5))
6957      gnus-button-handle-man 1)
6958     ;; even more: Apache::PerlRun(3pm), PDL::IO::FastRaw(3pm),
6959     ;; SoWWWAnchor(3iv), XSelectInput(3X11), X(1), X(7)
6960     ("\\b\\(\\(?:[a-z][-+_.:a-z0-9]+([1-9][X1a-z]*)\\)\\|\\b\\(?:X([1-9])\\)\\)\\W"
6961      0 (>= gnus-button-man-level 5) gnus-button-handle-man 1)
6962     ;; MID or mail: To avoid too many false positives we don't try to catch
6963     ;; all kind of allowed MIDs or mail addresses.  Domain part must contain
6964     ;; at least one dot.  TLD must contain two or three chars or be a know TLD
6965     ;; (info|name|...).  Put this entry near the _end_ of `gnus-button-alist'
6966     ;; so that non-ambiguous entries (see above) match first.
6967     (gnus-button-mid-or-mail-regexp
6968      0 (>= gnus-button-message-level 5) gnus-button-handle-mid-or-mail 1))
6969   "*Alist of regexps matching buttons in article bodies.
6970
6971 Each entry has the form (REGEXP BUTTON FORM CALLBACK PAR...), where
6972 REGEXP: is the string (case insensitive) matching text around the button (can
6973 also be Lisp expression evaluating to a string),
6974 BUTTON: is the number of the regexp grouping actually matching the button,
6975 FORM: is a Lisp expression which must eval to true for the button to
6976 be added,
6977 CALLBACK: is the function to call when the user push this button, and each
6978 PAR: is a number of a regexp grouping whose text will be passed to CALLBACK.
6979
6980 CALLBACK can also be a variable, in that case the value of that
6981 variable it the real callback function."
6982   :group 'gnus-article-buttons
6983   :type '(repeat (list (choice regexp variable sexp)
6984                        (integer :tag "Button")
6985                        (sexp :tag "Form")
6986                        (function :tag "Callback")
6987                        (repeat :tag "Par"
6988                                :inline t
6989                                (integer :tag "Regexp group")))))
6990
6991 (defcustom gnus-header-button-alist
6992   '(("^\\(References\\|Message-I[Dd]\\|^In-Reply-To\\):" "<[^<>]+>"
6993      0 (>= gnus-button-message-level 0) gnus-button-message-id 0)
6994     ("^\\(From\\|Reply-To\\):" ": *\\(.+\\)$"
6995      1 (>= gnus-button-message-level 0) gnus-button-reply 1)
6996     ("^\\(Cc\\|To\\):" "[^ \t\n<>,()\"]+@[^ \t\n<>,()\"]+"
6997      0 (>= gnus-button-message-level 0) gnus-msg-mail 0)
6998     ("^X-[Uu][Rr][Ll]:" gnus-button-url-regexp
6999      0 (>= gnus-button-browse-level 0) browse-url 0)
7000     ("^Subject:" gnus-button-url-regexp
7001      0 (>= gnus-button-browse-level 0) browse-url 0)
7002     ("^[^:]+:" gnus-button-url-regexp
7003      0 (>= gnus-button-browse-level 0) browse-url 0)
7004     ("^OpenPGP:.*url=" gnus-button-url-regexp
7005      0 (>= gnus-button-browse-level 0) gnus-button-openpgp 0)
7006     ("^[^:]+:" "\\bmailto:\\([-a-z.@_+0-9%=?&/]+\\)"
7007      0 (>= gnus-button-message-level 0) gnus-url-mailto 1)
7008     ("^[^:]+:" "\\(<\\(url: \\)?\\(nntp\\|news\\):\\([^>\n ]*\\)>\\)"
7009      1 (>= gnus-button-message-level 0) gnus-button-message-id 4))
7010   "*Alist of headers and regexps to match buttons in article heads.
7011
7012 This alist is very similar to `gnus-button-alist', except that each
7013 alist has an additional HEADER element first in each entry:
7014
7015 \(HEADER REGEXP BUTTON FORM CALLBACK PAR)
7016
7017 HEADER is a regexp to match a header.  For a fuller explanation, see
7018 `gnus-button-alist'."
7019   :group 'gnus-article-buttons
7020   :group 'gnus-article-headers
7021   :type '(repeat (list (regexp :tag "Header")
7022                        (choice regexp variable)
7023                        (integer :tag "Button")
7024                        (sexp :tag "Form")
7025                        (function :tag "Callback")
7026                        (repeat :tag "Par"
7027                                :inline t
7028                                (integer :tag "Regexp group")))))
7029
7030 ;;; Commands:
7031
7032 (defun gnus-article-push-button (event)
7033   "Check text under the mouse pointer for a callback function.
7034 If the text under the mouse pointer has a `gnus-callback' property,
7035 call it with the value of the `gnus-data' text property."
7036   (interactive "e")
7037   (set-buffer (window-buffer (posn-window (event-start event))))
7038   (let* ((pos (posn-point (event-start event)))
7039          (data (get-text-property pos 'gnus-data))
7040          (fun (get-text-property pos 'gnus-callback)))
7041     (goto-char pos)
7042     (when fun
7043       (funcall fun data))))
7044
7045 (defun gnus-article-press-button ()
7046   "Check text at point for a callback function.
7047 If the text at point has a `gnus-callback' property,
7048 call it with the value of the `gnus-data' text property."
7049   (interactive)
7050   (let ((data (get-text-property (point) 'gnus-data))
7051         (fun (get-text-property (point) 'gnus-callback)))
7052     (when fun
7053       (funcall fun data))))
7054
7055 (defun gnus-article-prev-button (n)
7056   "Move point to N buttons backward.
7057 If N is negative, move forward instead."
7058   (interactive "p")
7059   (gnus-article-next-button (- n)))
7060
7061 (defun gnus-article-next-button (n)
7062   "Move point to N buttons forward.
7063 If N is negative, move backward instead."
7064   (interactive "p")
7065   (let ((function (if (< n 0) 'previous-single-property-change
7066                     'next-single-property-change))
7067         (inhibit-point-motion-hooks t)
7068         (backward (< n 0))
7069         (limit (if (< n 0) (point-min) (point-max))))
7070     (setq n (abs n))
7071     (while (and (not (= limit (point)))
7072                 (> n 0))
7073       ;; Skip past the current button.
7074       (when (get-text-property (point) 'gnus-callback)
7075         (goto-char (funcall function (point) 'gnus-callback nil limit)))
7076       ;; Go to the next (or previous) button.
7077       (gnus-goto-char (funcall function (point) 'gnus-callback nil limit))
7078       ;; Put point at the start of the button.
7079       (when (and backward (not (get-text-property (point) 'gnus-callback)))
7080         (goto-char (funcall function (point) 'gnus-callback nil limit)))
7081       ;; Skip past intangible buttons.
7082       (when (get-text-property (point) 'intangible)
7083         (incf n))
7084       (decf n))
7085     (unless (zerop n)
7086       (gnus-message 5 "No more buttons"))
7087     n))
7088
7089 (defun gnus-article-highlight (&optional force)
7090   "Highlight current article.
7091 This function calls `gnus-article-highlight-headers',
7092 `gnus-article-highlight-citation',
7093 `gnus-article-highlight-signature', and `gnus-article-add-buttons' to
7094 do the highlighting.  See the documentation for those functions."
7095   (interactive (list 'force))
7096   (gnus-article-highlight-headers)
7097   (gnus-article-highlight-citation force)
7098   (gnus-article-highlight-signature)
7099   (gnus-article-add-buttons force)
7100   (gnus-article-add-buttons-to-head))
7101
7102 (defun gnus-article-highlight-some (&optional force)
7103   "Highlight current article.
7104 This function calls `gnus-article-highlight-headers',
7105 `gnus-article-highlight-signature', and `gnus-article-add-buttons' to
7106 do the highlighting.  See the documentation for those functions."
7107   (interactive (list 'force))
7108   (gnus-article-highlight-headers)
7109   (gnus-article-highlight-signature)
7110   (gnus-article-add-buttons))
7111
7112 (defun gnus-article-highlight-headers ()
7113   "Highlight article headers as specified by `gnus-header-face-alist'."
7114   (interactive)
7115   (gnus-with-article-headers
7116     (let (regexp header-face field-face from hpoints fpoints)
7117       (dolist (entry gnus-header-face-alist)
7118         (goto-char (point-min))
7119         (setq regexp (concat "^\\("
7120                              (if (string-equal "" (nth 0 entry))
7121                                  "[^\t ]"
7122                                (nth 0 entry))
7123                              "\\)")
7124               header-face (nth 1 entry)
7125               field-face (nth 2 entry))
7126         (while (and (re-search-forward regexp nil t)
7127                     (not (eobp)))
7128           (beginning-of-line)
7129           (setq from (point))
7130           (unless (search-forward ":" nil t)
7131             (forward-char 1))
7132           (when (and header-face
7133                      (not (memq (point) hpoints)))
7134             (push (point) hpoints)
7135             (gnus-put-text-property from (point) 'face header-face))
7136           (when (and field-face
7137                      (not (memq (setq from (point)) fpoints)))
7138             (push from fpoints)
7139             (if (re-search-forward "^[^ \t]" nil t)
7140                 (forward-char -2)
7141               (goto-char (point-max)))
7142             (gnus-put-text-property from (point) 'face field-face)))))))
7143
7144 (defun gnus-article-highlight-signature ()
7145   "Highlight the signature in an article.
7146 It does this by highlighting everything after
7147 `gnus-signature-separator' using the face `gnus-signature'."
7148   (interactive)
7149   (when gnus-signature-face
7150     (gnus-with-article-buffer
7151       (let ((inhibit-point-motion-hooks t))
7152         (save-restriction
7153           (when (gnus-article-narrow-to-signature)
7154             (gnus-overlay-put (gnus-make-overlay (point-min) (point-max))
7155                               'face gnus-signature-face)))))))
7156
7157 (defun gnus-article-buttonize-signature ()
7158   "Add button to the signature."
7159   (interactive)
7160   (save-excursion
7161     (set-buffer gnus-article-buffer)
7162     (let ((inhibit-read-only t)
7163           (inhibit-point-motion-hooks t))
7164       (when (gnus-article-search-signature)
7165         (gnus-article-add-button (match-beginning 0) (match-end 0)
7166                                  'gnus-signature-toggle
7167                                  (set-marker (make-marker)
7168                                              (1+ (match-end 0))))))))
7169
7170 (defun gnus-button-in-region-p (b e prop)
7171   "Say whether PROP exists in the region."
7172   (text-property-not-all b e prop nil))
7173
7174 (defun gnus-article-add-buttons (&optional force)
7175   "Find external references in the article and make buttons of them.
7176 \"External references\" are things like Message-IDs and URLs, as
7177 specified by `gnus-button-alist'."
7178   (interactive (list 'force))
7179   (gnus-with-article-buffer
7180     (let ((inhibit-point-motion-hooks t)
7181           (case-fold-search t)
7182           (alist gnus-button-alist)
7183           beg entry regexp)
7184       ;; Remove all old markers.
7185       (let (marker entry new-list)
7186         (while (setq marker (pop gnus-button-marker-list))
7187           (if (or (< marker (point-min)) (>= marker (point-max)))
7188               (push marker new-list)
7189             (goto-char marker)
7190             (when (setq entry (gnus-button-entry))
7191               (put-text-property (match-beginning (nth 1 entry))
7192                                  (match-end (nth 1 entry))
7193                                  'gnus-callback nil))
7194             (set-marker marker nil)))
7195         (setq gnus-button-marker-list new-list))
7196       ;; We skip the headers.
7197       (article-goto-body)
7198       (setq beg (point))
7199       (while (setq entry (pop alist))
7200         (setq regexp (eval (car entry)))
7201         (goto-char beg)
7202         (while (re-search-forward regexp nil t)
7203           (let* ((start (and entry (match-beginning (nth 1 entry))))
7204                  (end (and entry (match-end (nth 1 entry))))
7205                  (from (match-beginning 0)))
7206             (when (and (or (eq t (nth 2 entry))
7207                            (eval (nth 2 entry)))
7208                        (not (gnus-button-in-region-p
7209                              start end 'gnus-callback)))
7210               ;; That optional form returned non-nil, so we add the
7211               ;; button.
7212               (gnus-article-add-button
7213                start end 'gnus-button-push
7214                (car (push (set-marker (make-marker) from)
7215                           gnus-button-marker-list))))))))))
7216
7217 ;; Add buttons to the head of an article.
7218 (defun gnus-article-add-buttons-to-head ()
7219   "Add buttons to the head of the article."
7220   (interactive)
7221   (gnus-with-article-headers
7222     (let (beg end)
7223       (dolist (entry gnus-header-button-alist)
7224         ;; Each alist entry.
7225         (goto-char (point-min))
7226         (while (re-search-forward (car entry) nil t)
7227           ;; Each header matching the entry.
7228           (setq beg (match-beginning 0))
7229           (setq end (or (and (re-search-forward "^[^ \t]" nil t)
7230                              (match-beginning 0))
7231                         (point-max)))
7232           (goto-char beg)
7233           (while (re-search-forward (eval (nth 1 entry)) end t)
7234             ;; Each match within a header.
7235             (let* ((entry (cdr entry))
7236                    (start (match-beginning (nth 1 entry)))
7237                    (end (match-end (nth 1 entry)))
7238                    (form (nth 2 entry)))
7239               (goto-char (match-end 0))
7240               (when (eval form)
7241                 (gnus-article-add-button
7242                  start end (nth 3 entry)
7243                  (buffer-substring (match-beginning (nth 4 entry))
7244                                    (match-end (nth 4 entry)))))))
7245           (goto-char end))))))
7246
7247 ;;; External functions:
7248
7249 (defun gnus-article-add-button (from to fun &optional data)
7250   "Create a button between FROM and TO with callback FUN and data DATA."
7251   (when gnus-article-button-face
7252     (gnus-overlay-put (gnus-make-overlay from to)
7253                       'face gnus-article-button-face))
7254   (gnus-add-text-properties
7255    from to
7256    (nconc (and gnus-article-mouse-face
7257                (list gnus-mouse-face-prop gnus-article-mouse-face))
7258           (list 'gnus-callback fun)
7259           (and data (list 'gnus-data data))))
7260   (widget-convert-button 'link from to :action 'gnus-widget-press-button
7261                          :button-keymap gnus-widget-button-keymap))
7262
7263 ;;; Internal functions:
7264
7265 (defun gnus-article-set-globals ()
7266   (with-current-buffer gnus-summary-buffer
7267     (gnus-set-global-variables)))
7268
7269 (defun gnus-signature-toggle (end)
7270   (gnus-with-article-buffer
7271     (let ((inhibit-point-motion-hooks t)
7272           (limit (next-single-property-change end 'mime-view-entity
7273                                               nil (point-max))))
7274       (if (text-property-any end limit 'article-type 'signature)
7275           (progn
7276             (gnus-delete-wash-type 'signature)
7277             (gnus-remove-text-properties-when
7278              'article-type 'signature end limit
7279              (cons 'article-type (cons 'signature
7280                                        gnus-hidden-properties))))
7281         (gnus-add-wash-type 'signature)
7282         (gnus-add-text-properties-when
7283          'article-type nil end limit
7284          (cons 'article-type (cons 'signature
7285                                    gnus-hidden-properties)))))
7286     (let ((gnus-article-mime-handle-alist-1 gnus-article-mime-handle-alist))
7287       (gnus-set-mode-line 'article))))
7288
7289 (defun gnus-button-entry ()
7290   ;; Return the first entry in `gnus-button-alist' matching this place.
7291   (let ((alist gnus-button-alist)
7292         (entry nil))
7293     (while alist
7294       (setq entry (pop alist))
7295       (if (looking-at (eval (car entry)))
7296           (setq alist nil)
7297         (setq entry nil)))
7298     entry))
7299
7300 (defun gnus-button-push (marker)
7301   ;; Push button starting at MARKER.
7302   (save-excursion
7303     (goto-char marker)
7304     (let* ((entry (gnus-button-entry))
7305            (inhibit-point-motion-hooks t)
7306            (fun (nth 3 entry))
7307            (args (mapcar (lambda (group)
7308                            (let ((string (match-string group)))
7309                              (set-text-properties
7310                               0 (length string) nil string)
7311                              string))
7312                          (nthcdr 4 entry))))
7313       (cond
7314        ((fboundp fun)
7315         (apply fun args))
7316        ((and (boundp fun)
7317              (fboundp (symbol-value fun)))
7318         (apply (symbol-value fun) args))
7319        (t
7320         (gnus-message 1 "You must define `%S' to use this button"
7321                       (cons fun args)))))))
7322
7323 (defun gnus-parse-news-url (url)
7324   (let (scheme server port group message-id articles)
7325     (with-temp-buffer
7326       (insert url)
7327       (goto-char (point-min))
7328       (when (looking-at "\\([A-Za-z]+\\):")
7329         (setq scheme (match-string 1))
7330         (goto-char (match-end 0)))
7331       (when (looking-at "//\\([^:/]+\\)\\(:?\\)\\([0-9]+\\)?/")
7332         (setq server (match-string 1))
7333         (setq port (if (stringp (match-string 3))
7334                        (string-to-number (match-string 3))
7335                      (match-string 3)))
7336         (goto-char (match-end 0)))
7337
7338       (cond
7339        ((looking-at "\\(.*@.*\\)")
7340         (setq message-id (match-string 1)))
7341        ((looking-at "\\([^/]+\\)/\\([-0-9]+\\)")
7342         (setq group (match-string 1)
7343               articles (split-string (match-string 2) "-")))
7344        ((looking-at "\\([^/]+\\)/?")
7345         (setq group (match-string 1)))
7346        (t
7347         (error "Unknown news URL syntax"))))
7348     (list scheme server port group message-id articles)))
7349
7350 (defun gnus-button-handle-news (url)
7351   "Fetch a news URL."
7352   (destructuring-bind (scheme server port group message-id articles)
7353       (gnus-parse-news-url url)
7354     (cond
7355      (message-id
7356       (save-excursion
7357         (set-buffer gnus-summary-buffer)
7358         (if server
7359             (let ((gnus-refer-article-method
7360                    (nconc (list (list 'nntp server))
7361                           gnus-refer-article-method))
7362                   (nntp-port-number (or port "nntp")))
7363               (gnus-message 7 "Fetching %s with %s"
7364                             message-id gnus-refer-article-method)
7365               (gnus-summary-refer-article message-id))
7366           (gnus-summary-refer-article message-id))))
7367      (group
7368       (gnus-button-fetch-group url)))))
7369
7370 (defun gnus-button-handle-man (url)
7371   "Fetch a man page."
7372   (gnus-message 9 "`%s' `%s'" gnus-button-man-handler url)
7373   (when (eq gnus-button-man-handler 'woman)
7374     (setq url (gnus-replace-in-string url "([1-9][X1a-z]*).*\\'" "")))
7375   (gnus-message 9 "`%s' `%s'" gnus-button-man-handler url)
7376   (funcall gnus-button-man-handler url))
7377
7378 (defun gnus-button-handle-info-url (url)
7379   "Fetch an info URL."
7380   (setq url (mm-subst-char-in-string ?+ ?\  url))
7381   (cond
7382    ((string-match "^\\([^:/]+\\)?/\\(.*\\)" url)
7383     (gnus-info-find-node
7384      (concat "(" (or (gnus-url-unhex-string (match-string 1 url))
7385                      "Gnus")
7386              ")" (gnus-url-unhex-string (match-string 2 url)))))
7387    ((string-match "([^)\"]+)[^\"]+" url)
7388     (setq url
7389           (gnus-replace-in-string
7390            (gnus-replace-in-string url "[\n\t ]+" " ") "\"" ""))
7391     (gnus-info-find-node url))
7392    (t (error "Can't parse %s" url))))
7393
7394 (defun gnus-button-handle-info-url-gnome (url)
7395   "Fetch GNOME style info URL."
7396   (setq url (mm-subst-char-in-string ?_ ?\  url))
7397   (if (string-match "\\([^#]+\\)#?\\(.*\\)" url)
7398       (gnus-info-find-node
7399        (concat "("
7400                (gnus-url-unhex-string
7401                  (match-string 1 url))
7402                ")"
7403                (or (gnus-url-unhex-string
7404                     (match-string 2 url))
7405                    "Top")))
7406     (error "Can't parse %s" url)))
7407
7408 (defun gnus-button-handle-info-url-kde (url)
7409   "Fetch KDE style info URL."
7410   (gnus-info-find-node (gnus-url-unhex-string url)))
7411
7412 (defun gnus-button-handle-info-keystrokes (url)
7413   "Call `info' when pushing the corresponding URL button."
7414   ;; For links like `C-h i d m gnus RET', `C-h i d m CC Mode RET'.
7415   (info)
7416   (Info-directory)
7417   (Info-menu url))
7418
7419 (defun gnus-button-openpgp (url)
7420   "Retrieve and add an OpenPGP key given URL from an OpenPGP header."
7421   (with-temp-buffer
7422     (mm-url-insert-file-contents-external url)
7423     (pgg-snarf-keys-region (point-min) (point-max))
7424     (pgg-display-output-buffer nil nil nil)))
7425
7426 (defun gnus-button-message-id (message-id)
7427   "Fetch MESSAGE-ID."
7428   (with-current-buffer gnus-summary-buffer
7429     (gnus-summary-refer-article message-id)))
7430
7431 (defun gnus-button-fetch-group (address)
7432   "Fetch GROUP specified by ADDRESS."
7433   (if (not (string-match "[:/]" address))
7434       ;; This is just a simple group url.
7435       (gnus-group-read-ephemeral-group address gnus-select-method)
7436     (if (not
7437          (string-match
7438           "^\\([^:/]+\\)\\(:\\([^/]+\\)\\)?/\\([^/]+\\)\\(/\\([0-9]+\\)\\)?"
7439           address))
7440         (error "Can't parse %s" address)
7441       (gnus-group-read-ephemeral-group
7442        (match-string 4 address)
7443        `(nntp ,(match-string 1 address)
7444               (nntp-address ,(match-string 1 address))
7445               (nntp-port-number ,(if (match-end 3)
7446                                      (match-string 3 address)
7447                                    "nntp")))
7448        nil nil nil
7449        (and (match-end 6) (list (string-to-number (match-string 6 address))))))))
7450
7451 (defun gnus-url-parse-query-string (query &optional downcase)
7452   (let (retval pairs cur key val)
7453     (setq pairs (split-string query "&"))
7454     (while pairs
7455       (setq cur (car pairs)
7456             pairs (cdr pairs))
7457       (if (not (string-match "=" cur))
7458           nil                           ; Grace
7459         (setq key (gnus-url-unhex-string (substring cur 0 (match-beginning 0)))
7460               val (gnus-url-unhex-string (substring cur (match-end 0) nil) t))
7461         (if downcase
7462             (setq key (downcase key)))
7463         (setq cur (assoc key retval))
7464         (if cur
7465             (setcdr cur (cons val (cdr cur)))
7466           (setq retval (cons (list key val) retval)))))
7467     retval))
7468
7469 (defun gnus-url-mailto (url)
7470   ;; Send mail to someone
7471   (when (string-match "mailto:/*\\(.*\\)" url)
7472     (setq url (substring url (match-beginning 1) nil)))
7473   (let (to args subject func)
7474     (setq args (gnus-url-parse-query-string
7475                 (if (string-match "^\\?" url)
7476                     (substring url 1)
7477                   (if (string-match "^\\([^?]+\\)\\?\\(.*\\)" url)
7478                       (concat "to=" (match-string 1 url) "&"
7479                               (match-string 2 url))
7480                     (concat "to=" url)))
7481                 t)
7482           subject (cdr-safe (assoc "subject" args)))
7483     (gnus-msg-mail)
7484     (while args
7485       (setq func (intern-soft (concat "message-goto-" (downcase (caar args)))))
7486       (if (fboundp func)
7487           (funcall func)
7488         (message-position-on-field (caar args)))
7489       (insert (gnus-replace-in-string
7490                (mapconcat 'identity (reverse (cdar args)) ", ")
7491                "\r\n" "\n" t))
7492       (setq args (cdr args)))
7493     (if subject
7494         (message-goto-body)
7495       (message-goto-subject))))
7496
7497 (defun gnus-button-embedded-url (address)
7498   "Activate ADDRESS with `browse-url'."
7499   (browse-url (gnus-strip-whitespace address)))
7500
7501 ;;; Next/prev buttons in the article buffer.
7502
7503 (defvar gnus-next-page-line-format "%{%(Next page...%)%}\n")
7504 (defvar gnus-prev-page-line-format "%{%(Previous page...%)%}\n")
7505
7506 (defvar gnus-prev-page-map
7507   (let ((map (make-sparse-keymap)))
7508     (define-key map gnus-mouse-2 'gnus-button-prev-page)
7509     (define-key map "\r" 'gnus-button-prev-page)
7510     map))
7511
7512 (defvar gnus-next-page-map
7513   (let ((map (make-sparse-keymap)))
7514     (unless (>= emacs-major-version 21)
7515       ;; XEmacs doesn't care.
7516       (set-keymap-parent map gnus-article-mode-map))
7517     (define-key map gnus-mouse-2 'gnus-button-next-page)
7518     (define-key map "\r" 'gnus-button-next-page)
7519     map))
7520
7521 (defun gnus-insert-prev-page-button ()
7522   (let ((b (point))
7523         (inhibit-read-only t)
7524         (situation (get-text-property (point-min) 'mime-view-situation)))
7525     (gnus-eval-format
7526      gnus-prev-page-line-format nil
7527      `(keymap ,gnus-prev-page-map
7528          gnus-prev t
7529          gnus-callback gnus-article-button-prev-page
7530          article-type annotation
7531          mime-view-situation ,situation))
7532     (widget-convert-button
7533      'link b (if (bolp)
7534                  ;; Exclude a newline.
7535                  (1- (point))
7536                (point))
7537      :action 'gnus-button-prev-page
7538      :button-keymap gnus-prev-page-map)))
7539
7540 (defun gnus-button-next-page (&optional args more-args)
7541   "Go to the next page."
7542   (interactive)
7543   (let ((win (selected-window)))
7544     (select-window (gnus-get-buffer-window gnus-article-buffer t))
7545     (gnus-article-next-page)
7546     (select-window win)))
7547
7548 (defun gnus-button-prev-page (&optional args more-args)
7549   "Go to the prev page."
7550   (interactive)
7551   (let ((win (selected-window)))
7552     (select-window (gnus-get-buffer-window gnus-article-buffer t))
7553     (gnus-article-prev-page)
7554     (select-window win)))
7555
7556 (defun gnus-insert-next-page-button ()
7557   (let ((b (point))
7558         (inhibit-read-only t)
7559         (situation (get-text-property (point-min) 'mime-view-situation)))
7560     (gnus-eval-format gnus-next-page-line-format nil
7561                       `(keymap ,gnus-next-page-map
7562                           gnus-next t
7563                           gnus-callback gnus-article-button-next-page
7564                           article-type annotation
7565                           mime-view-situation ,situation))
7566     (widget-convert-button
7567      'link b (if (bolp)
7568                  ;; Exclude a newline.
7569                  (1- (point))
7570                (point))
7571      :action 'gnus-button-next-page
7572      :button-keymap gnus-next-page-map)))
7573
7574 (defun gnus-article-button-next-page (arg)
7575   "Go to the next page."
7576   (interactive "P")
7577   (let ((win (selected-window)))
7578     (select-window (gnus-get-buffer-window gnus-article-buffer t))
7579     (gnus-article-next-page)
7580     (select-window win)))
7581
7582 (defun gnus-article-button-prev-page (arg)
7583   "Go to the prev page."
7584   (interactive "P")
7585   (let ((win (selected-window)))
7586     (select-window (gnus-get-buffer-window gnus-article-buffer t))
7587     (gnus-article-prev-page)
7588     (select-window win)))
7589
7590 (defvar gnus-decode-header-methods
7591   '(mail-decode-encoded-word-region)
7592   "List of methods used to decode headers.
7593
7594 This variable is a list of FUNCTION or (REGEXP . FUNCTION).  If item
7595 is FUNCTION, FUNCTION will be applied to all newsgroups.  If item is a
7596 \(REGEXP . FUNCTION), FUNCTION will be only apply to the newsgroups
7597 whose names match REGEXP.
7598
7599 For example:
7600 \((\"chinese\" . gnus-decode-encoded-word-region-by-guess)
7601  mail-decode-encoded-word-region
7602  (\"chinese\" . rfc1843-decode-region))
7603 ")
7604
7605 (defvar gnus-decode-header-methods-cache nil)
7606
7607 (defun gnus-multi-decode-header (start end)
7608   "Apply the functions from `gnus-encoded-word-methods' that match."
7609   (unless (and gnus-decode-header-methods-cache
7610                (eq gnus-newsgroup-name
7611                    (car gnus-decode-header-methods-cache)))
7612     (setq gnus-decode-header-methods-cache (list gnus-newsgroup-name))
7613     (mapcar (lambda (x)
7614               (if (symbolp x)
7615                   (nconc gnus-decode-header-methods-cache (list x))
7616                 (if (and gnus-newsgroup-name
7617                          (string-match (car x) gnus-newsgroup-name))
7618                     (nconc gnus-decode-header-methods-cache
7619                            (list (cdr x))))))
7620             gnus-decode-header-methods))
7621   (let ((xlist gnus-decode-header-methods-cache))
7622     (pop xlist)
7623     (save-restriction
7624       (narrow-to-region start end)
7625       (while xlist
7626         (funcall (pop xlist) (point-min) (point-max))))))
7627
7628 ;;;
7629 ;;; Treatment top-level handling.
7630 ;;;
7631
7632 (defun gnus-treat-article (condition &optional part-number total-parts type)
7633   (let ((length (- (point-max) (point-min)))
7634         (alist gnus-treatment-function-alist)
7635         (article-goto-body-goes-to-point-min-p t)
7636         (treated-type
7637          (or (not type)
7638              (catch 'found
7639                (let ((list gnus-article-treat-types))
7640                  (while list
7641                    (when (string-match (pop list) type)
7642                      (throw 'found t)))))))
7643         (highlightp (gnus-visual-p 'article-highlight 'highlight))
7644         (entity (static-unless (featurep 'xemacs)
7645                   (when (eq 'head condition)
7646                     (get-text-property (point-min) 'mime-view-entity))))
7647         val elem buttonized)
7648     (gnus-run-hooks 'gnus-part-display-hook)
7649     (unless gnus-inhibit-treatment
7650       (dolist (elem alist)
7651         (setq val
7652               (save-excursion
7653                 (when (gnus-buffer-live-p gnus-summary-buffer)
7654                   (set-buffer gnus-summary-buffer))
7655                 (symbol-value (car elem))))
7656         (when (and (or (consp val)
7657                        treated-type)
7658                    (gnus-treat-predicate val)
7659                    (or (not (get (car elem) 'highlight))
7660                        highlightp))
7661           (when (and (not buttonized)
7662                      (memq (car elem)
7663                            '(gnus-treat-hide-signature
7664                              gnus-treat-highlight-signature)))
7665             (gnus-article-buttonize-signature)
7666             (setq buttonized t))
7667           (save-restriction
7668             (funcall (cadr elem)))))
7669       ;; FSF Emacsen does not inherit the existing text properties
7670       ;; in the new text, so we should do it for `mime-view-entity'.
7671       (static-unless (featurep 'xemacs)
7672         (when entity
7673           (put-text-property (point-min) (point-max)
7674                              'mime-view-entity entity))))))
7675
7676 ;; Dynamic variables.
7677 (eval-when-compile
7678   (defvar part-number)
7679   (defvar total-parts)
7680   (defvar type)
7681   (defvar condition)
7682   (defvar length))
7683
7684 (defun gnus-treat-predicate (val)
7685   (cond
7686    ((null val)
7687     nil)
7688    ((and (listp val)
7689          (stringp (car val)))
7690     (apply 'gnus-or (mapcar `(lambda (s)
7691                                (string-match s ,(or gnus-newsgroup-name "")))
7692                             val)))
7693    ((listp val)
7694     (let ((pred (pop val)))
7695       (cond
7696        ((eq pred 'or)
7697         (apply 'gnus-or (mapcar 'gnus-treat-predicate val)))
7698        ((eq pred 'and)
7699         (apply 'gnus-and (mapcar 'gnus-treat-predicate val)))
7700        ((eq pred 'not)
7701         (not (gnus-treat-predicate (car val))))
7702        ((eq pred 'typep)
7703         (equal (car val) type))
7704        (t
7705         (error "%S is not a valid predicate" pred)))))
7706    ((eq val 'mime)
7707     gnus-show-mime)
7708    (condition
7709     (eq condition val))
7710    ((eq val t)
7711     t)
7712    ((eq val 'head)
7713     nil)
7714    ((eq val 'last)
7715     (eq part-number total-parts))
7716    ((numberp val)
7717     (< length val))
7718    (t
7719     (error "%S is not a valid value" val))))
7720
7721 (defun gnus-article-encrypt-body (protocol &optional n)
7722   "Encrypt the article body."
7723   (interactive
7724    (list
7725     (or gnus-article-encrypt-protocol
7726         (completing-read "Encrypt protocol: "
7727                          gnus-article-encrypt-protocol-alist
7728                          nil t))
7729     current-prefix-arg))
7730   (let ((func (cdr (assoc protocol gnus-article-encrypt-protocol-alist))))
7731     (unless func
7732       (error "Can't find the encrypt protocol %s" protocol))
7733     (if (member gnus-newsgroup-name '("nndraft:delayed"
7734                                       "nndraft:drafts"
7735                                       "nndraft:queue"))
7736         (error "Can't encrypt the article in group %s"
7737                gnus-newsgroup-name))
7738     (gnus-summary-iterate n
7739       (save-excursion
7740         (set-buffer gnus-summary-buffer)
7741         (let ((mail-parse-charset gnus-newsgroup-charset)
7742               (mail-parse-ignored-charsets gnus-newsgroup-ignored-charsets)
7743               (summary-buffer gnus-summary-buffer)
7744               references point)
7745           (gnus-set-global-variables)
7746           (when (gnus-group-read-only-p)
7747             (error "The current newsgroup does not support article encrypt"))
7748           (gnus-summary-show-article t)
7749           (setq references
7750                 (or (mail-header-references gnus-current-headers) ""))
7751           (set-buffer gnus-article-buffer)
7752           (let* ((inhibit-read-only t)
7753                  (headers
7754                   (mapcar (lambda (field)
7755                             (and (save-restriction
7756                                    (message-narrow-to-head)
7757                                    (goto-char (point-min))
7758                                    (search-forward field nil t))
7759                                  (prog2
7760                                      (message-narrow-to-field)
7761                                      (buffer-string)
7762                                    (delete-region (point-min) (point-max))
7763                                    (widen))))
7764                           '("Content-Type:" "Content-Transfer-Encoding:"
7765                             "Content-Disposition:"))))
7766             (message-narrow-to-head)
7767             (message-remove-header "MIME-Version")
7768             (goto-char (point-max))
7769             (setq point (point))
7770             (insert (apply 'concat headers))
7771             (widen)
7772             (narrow-to-region point (point-max))
7773             (let ((message-options message-options))
7774               (message-options-set 'message-sender user-mail-address)
7775               (message-options-set 'message-recipients user-mail-address)
7776               (message-options-set 'message-sign-encrypt 'not)
7777               (funcall func))
7778             (goto-char (point-min))
7779             (insert "MIME-Version: 1.0\n")
7780             (widen)
7781             (gnus-summary-edit-article-done
7782              references nil summary-buffer t))
7783           (when gnus-keep-backlog
7784             (gnus-backlog-remove-article
7785              (car gnus-article-current) (cdr gnus-article-current)))
7786           (save-excursion
7787             (when (get-buffer gnus-original-article-buffer)
7788               (set-buffer gnus-original-article-buffer)
7789               (setq gnus-original-article nil)))
7790           (when gnus-use-cache
7791             (gnus-cache-update-article
7792              (car gnus-article-current) (cdr gnus-article-current))))))))
7793
7794 (defvar gnus-mime-security-button-line-format "%{%([[%t:%i]%D]%)%}\n"
7795   "The following specs can be used:
7796 %t  The security MIME type
7797 %i  Additional info
7798 %d  Details
7799 %D  Details if button is pressed")
7800
7801 (defvar gnus-mime-security-button-end-line-format "%{%([[End of %t]%D]%)%}\n"
7802   "The following specs can be used:
7803 %t  The security MIME type
7804 %i  Additional info
7805 %d  Details
7806 %D  Details if button is pressed")
7807
7808 (defvar gnus-mime-security-button-line-format-alist
7809   '((?t gnus-tmp-type ?s)
7810     (?i gnus-tmp-info ?s)
7811     (?d gnus-tmp-details ?s)
7812     (?D gnus-tmp-pressed-details ?s)))
7813
7814 (defvar gnus-mime-security-button-map
7815   (let ((map (make-sparse-keymap)))
7816     (define-key map gnus-mouse-2 'gnus-article-push-button)
7817     (define-key map "\r" 'gnus-article-press-button)
7818     map))
7819
7820 (defvar gnus-mime-security-details-buffer nil)
7821
7822 (defvar gnus-mime-security-button-pressed nil)
7823
7824 (defvar gnus-mime-security-show-details-inline t
7825   "If non-nil, show details in the article buffer.")
7826
7827 (defun gnus-mime-security-verify-or-decrypt (handle)
7828   (mm-remove-parts (cdr handle))
7829   (let ((region (mm-handle-multipart-ctl-parameter handle 'gnus-region))
7830         point (inhibit-read-only t))
7831     (if region
7832         (goto-char (car region)))
7833     (save-restriction
7834       (narrow-to-region (point) (point))
7835       (with-current-buffer (mm-handle-multipart-original-buffer handle)
7836         (let* ((mm-verify-option 'known)
7837                (mm-decrypt-option 'known)
7838                (nparts (mm-possibly-verify-or-decrypt (cdr handle) handle)))
7839           (unless (eq nparts (cdr handle))
7840             (mm-destroy-parts (cdr handle))
7841             (setcdr handle nparts))))
7842       (setq point (point))
7843       (gnus-mime-display-security handle)
7844       (goto-char (point-max)))
7845     (when region
7846       (delete-region (point) (cdr region))
7847       (set-marker (car region) nil)
7848       (set-marker (cdr region) nil))
7849     (goto-char point)))
7850
7851 (defun gnus-mime-security-show-details (handle)
7852   (let ((details (mm-handle-multipart-ctl-parameter handle 'gnus-details)))
7853     (if (not details)
7854         (gnus-message 5 "No details.")
7855       (if gnus-mime-security-show-details-inline
7856           (let ((gnus-mime-security-button-pressed
7857                  (not (get-text-property (point) 'gnus-mime-details)))
7858                 (gnus-mime-security-button-line-format
7859                  (get-text-property (point) 'gnus-line-format))
7860                 (inhibit-read-only t))
7861             (forward-char -1)
7862             (while (eq (get-text-property (point) 'gnus-line-format)
7863                        gnus-mime-security-button-line-format)
7864               (forward-char -1))
7865             (forward-char)
7866             (save-restriction
7867               (narrow-to-region (point) (point))
7868               (gnus-insert-mime-security-button handle))
7869             (delete-region (point)
7870                            (or (text-property-not-all
7871                                 (point) (point-max)
7872                                 'gnus-line-format
7873                                 gnus-mime-security-button-line-format)
7874                                (point-max))))
7875         ;; Not inlined.
7876         (if (gnus-buffer-live-p gnus-mime-security-details-buffer)
7877             (with-current-buffer gnus-mime-security-details-buffer
7878               (erase-buffer)
7879               t)
7880           (setq gnus-mime-security-details-buffer
7881                 (gnus-get-buffer-create "*MIME Security Details*")))
7882         (with-current-buffer gnus-mime-security-details-buffer
7883           (insert details)
7884           (goto-char (point-min)))
7885         (pop-to-buffer gnus-mime-security-details-buffer)))))
7886
7887 (defun gnus-mime-security-press-button (handle)
7888   (save-excursion
7889     (if (mm-handle-multipart-ctl-parameter handle 'gnus-info)
7890         (gnus-mime-security-show-details handle)
7891       (gnus-mime-security-verify-or-decrypt handle))))
7892
7893 (defun gnus-insert-mime-security-button (handle &optional displayed)
7894   (let* ((protocol (mm-handle-multipart-ctl-parameter handle 'protocol))
7895          (gnus-tmp-type
7896           (concat
7897            (or (nth 2 (assoc protocol mm-verify-function-alist))
7898                (nth 2 (assoc protocol mm-decrypt-function-alist))
7899                "Unknown")
7900            (if (equal (car handle) "multipart/signed")
7901                " Signed" " Encrypted")
7902            " Part"))
7903          (gnus-tmp-info
7904           (or (mm-handle-multipart-ctl-parameter handle 'gnus-info)
7905               "Undecided"))
7906          (gnus-tmp-details
7907           (mm-handle-multipart-ctl-parameter handle 'gnus-details))
7908          gnus-tmp-pressed-details
7909          b e)
7910     (setq gnus-tmp-details
7911           (if gnus-tmp-details
7912               (concat "\n" gnus-tmp-details)
7913             ""))
7914     (setq gnus-tmp-pressed-details
7915           (if gnus-mime-security-button-pressed gnus-tmp-details ""))
7916     (unless (bolp)
7917       (insert "\n"))
7918     (setq b (point))
7919     (gnus-eval-format
7920      gnus-mime-security-button-line-format
7921      gnus-mime-security-button-line-format-alist
7922      `(keymap ,gnus-mime-security-button-map
7923          gnus-callback gnus-mime-security-press-button
7924          gnus-line-format ,gnus-mime-security-button-line-format
7925          gnus-mime-details ,gnus-mime-security-button-pressed
7926          article-type annotation
7927          gnus-data ,handle))
7928     (setq e (if (bolp)
7929                 ;; Exclude a newline.
7930                 (1- (point))
7931               (point)))
7932     (widget-convert-button
7933      'link b e
7934      :mime-handle handle
7935      :action 'gnus-widget-press-button
7936      :button-keymap gnus-mime-security-button-map
7937      :help-echo
7938      (lambda (widget/window &optional overlay pos)
7939        ;; Needed to properly clear the message due to a bug in
7940        ;; wid-edit (XEmacs only).
7941        (when (boundp 'help-echo-owns-message)
7942          (setq help-echo-owns-message t))
7943        (format
7944         "%S: show detail"
7945         (aref gnus-mouse-2 0))))))
7946
7947 (defun gnus-mime-display-security (handle)
7948   (save-restriction
7949     (narrow-to-region (point) (point))
7950     (unless (gnus-unbuttonized-mime-type-p (car handle))
7951       (gnus-insert-mime-security-button handle))
7952     (gnus-mime-display-part (cadr handle))
7953     (unless (bolp)
7954       (insert "\n"))
7955     (unless (gnus-unbuttonized-mime-type-p (car handle))
7956       (let ((gnus-mime-security-button-line-format
7957              gnus-mime-security-button-end-line-format))
7958         (gnus-insert-mime-security-button handle)))
7959     (mm-set-handle-multipart-parameter
7960      handle 'gnus-region
7961      (cons (set-marker (make-marker) (point-min))
7962            (set-marker (make-marker) (point-max))))))
7963
7964
7965 ;;; @ for mime-view
7966 ;;;
7967
7968 (defun gnus-article-header-presentation-method (entity situation)
7969   (mime-insert-header entity)
7970   (article-decode-group-name))
7971
7972 (set-alist 'mime-header-presentation-method-alist
7973            'gnus-original-article-mode
7974            #'gnus-article-header-presentation-method)
7975
7976 (defun gnus-mime-preview-quitting-method ()
7977   (mime-preview-kill-buffer)
7978   (delete-other-windows)
7979   (gnus-article-show-summary)
7980   (gnus-summary-select-article gnus-show-all-headers t))
7981
7982 (set-alist 'mime-preview-quitting-method-alist
7983            'gnus-original-article-mode #'gnus-mime-preview-quitting-method)
7984
7985 (set-alist 'mime-preview-following-method-alist
7986            'gnus-original-article-mode #'gnus-following-method)
7987
7988 (set-alist 'mime-preview-over-to-previous-method-alist
7989            'gnus-original-article-mode
7990            (lambda ()
7991              (if (> (point-min) 1)
7992                  (gnus-article-prev-page)
7993                (gnus-article-read-summary-keys
7994                 nil (gnus-character-to-event ?P)))))
7995
7996 (set-alist 'mime-preview-over-to-next-method-alist
7997            'gnus-original-article-mode'
7998            (lambda ()
7999              (if (< (point-max) (buffer-size))
8000                  (gnus-article-next-page)
8001                (gnus-article-read-summary-keys
8002                 nil (gnus-character-to-event ?N)))))
8003
8004
8005 ;;; @ end
8006 ;;;
8007
8008 (gnus-ems-redefine)
8009
8010 (provide 'gnus-art)
8011
8012 (run-hooks 'gnus-art-load-hook)
8013
8014 ;;; gnus-art.el ends here