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