6dfc62bdee765a222433810e7054394c7e32d3a8
[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 Free Software Foundation, Inc.
3
4 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
5 ;;      MORIOKA Tomohiko <morioka@jaist.ac.jp>
6 ;;      Katsumi Yamaoka <yamaoka@jpl.org>
7 ;; Keywords: mail, news, MIME
8
9 ;; This file is part of GNU Emacs.
10
11 ;; GNU Emacs is free software; you can redistribute it and/or modify
12 ;; it under the terms of the GNU General Public License as published by
13 ;; the Free Software Foundation; either version 2, or (at your option)
14 ;; any later version.
15
16 ;; GNU Emacs is distributed in the hope that it will be useful,
17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 ;; GNU General Public License for more details.
20
21 ;; You should have received a copy of the GNU General Public License
22 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
23 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24 ;; Boston, MA 02111-1307, USA.
25
26 ;;; Commentary:
27
28 ;;; Code:
29
30 (eval-when-compile (require 'cl))
31 (eval-when-compile (require 'static))
32
33 (require 'path-util)
34 (require 'gnus)
35 (require 'gnus-sum)
36 (require 'gnus-spec)
37 (require 'gnus-int)
38 (require 'alist)
39 (require 'mime-view)
40 (require 'wid-edit)
41
42 ;; Avoid byte-compile warnings.
43 (eval-when-compile
44   (require 'mm-bodies)
45   (require 'mail-parse)
46   (require 'mm-decode)
47   (require 'mm-view)
48   (require 'mm-uu)
49   )
50
51 (defgroup gnus-article nil
52   "Article display."
53   :link '(custom-manual "(gnus)The Article Buffer")
54   :group 'gnus)
55
56 (defgroup gnus-article-treat nil
57   "Treating article parts."
58   :link '(custom-manual "(gnus)Article Hiding")
59   :group 'gnus-article)
60
61 (defgroup gnus-article-hiding nil
62   "Hiding article parts."
63   :link '(custom-manual "(gnus)Article Hiding")
64   :group 'gnus-article)
65
66 (defgroup gnus-article-highlight nil
67   "Article highlighting."
68   :link '(custom-manual "(gnus)Article Highlighting")
69   :group 'gnus-article
70   :group 'gnus-visual)
71
72 (defgroup gnus-article-signature nil
73   "Article signatures."
74   :link '(custom-manual "(gnus)Article Signature")
75   :group 'gnus-article)
76
77 (defgroup gnus-article-headers nil
78   "Article headers."
79   :link '(custom-manual "(gnus)Hiding Headers")
80   :group 'gnus-article)
81
82 (defgroup gnus-article-washing nil
83   "Special commands on articles."
84   :link '(custom-manual "(gnus)Article Washing")
85   :group 'gnus-article)
86
87 (defgroup gnus-article-emphasis nil
88   "Fontisizing articles."
89   :link '(custom-manual "(gnus)Article Fontisizing")
90   :group 'gnus-article)
91
92 (defgroup gnus-article-saving nil
93   "Saving articles."
94   :link '(custom-manual "(gnus)Saving Articles")
95   :group 'gnus-article)
96
97 (defgroup gnus-article-mime nil
98   "Worshiping the MIME wonder."
99   :link '(custom-manual "(gnus)Using MIME")
100   :group 'gnus-article)
101
102 (defgroup gnus-article-buttons nil
103   "Pushable buttons in the article buffer."
104   :link '(custom-manual "(gnus)Article Buttons")
105   :group 'gnus-article)
106
107 (defgroup gnus-article-various nil
108   "Other article options."
109   :link '(custom-manual "(gnus)Misc Article")
110   :group 'gnus-article)
111
112 (defcustom gnus-ignored-headers
113   '("^Path:" "^Expires:" "^Date-Received:" "^References:" "^Xref:" "^Lines:"
114     "^Relay-Version:" "^Message-ID:" "^Approved:" "^Sender:" "^Received:"
115     "^X-UIDL:" "^MIME-Version:" "^Return-Path:" "^In-Reply-To:"
116     "^Content-Type:" "^Content-Transfer-Encoding:" "^X-WebTV-Signature:"
117     "^X-MimeOLE:" "^X-MSMail-Priority:" "^X-Priority:" "^X-Loop:"
118     "^X-Authentication-Warning:" "^X-MIME-Autoconverted:" "^X-Face:"
119     "^X-Attribution:" "^X-Originating-IP:" "^Delivered-To:"
120     "^NNTP-[-A-Za-z]+:" "^Distribution:" "^X-no-archive:" "^X-Trace:"
121     "^X-Complaints-To:" "^X-NNTP-Posting-Host:" "^X-Orig.*:"
122     "^Abuse-Reports-To:" "^Cache-Post-Path:" "^X-Article-Creation-Date:"
123     "^X-Poster:" "^X-Mail2News-Path:" "^X-Server-Date:" "^X-Cache:"
124     "^Originator:" "^X-Problems-To:" "^X-Auth-User:" "^X-Post-Time:"
125     "^X-Admin:" "^X-UID:" "^Resent-[-A-Za-z]+:" "^X-Mailing-List:"
126     "^Precedence:" "^Original-[-A-Za-z]+:" "^X-filename:" "^X-Orcpt:"
127     "^Old-Received:" "^X-Pgp" "^X-Auth:" "^X-From-Line:"
128     "^X-Gnus-Article-Number:" "^X-Majordomo:" "^X-Url:" "^X-Sender:"
129     "^MBOX-Line" "^Priority:" "^X-Pgp" "^X400-[-A-Za-z]+:"
130     "^Status:" "^X-Gnus-Mail-Source:" "^Cancel-Lock:"
131     "^X-FTN" "^X-EXP32-SerialNo:" "^Encoding:" "^Importance:"
132     "^Autoforwarded:" "^Original-Encoded-Information-Types:" "^X-Ya-Pop3:"
133     "^X-Face-Version:" "^X-Vms-To:" "^X-ML-NAME:" "^X-ML-COUNT:"
134     "^Mailing-List:" "^X-finfo:" "^X-md5sum:" "^X-md5sum-Origin:"
135     "^X-Sun-Charset:" "^X-Accept-Language:" "^X-Envelope-Sender:"
136     "^List-[A-Za-z]+:" "^X-Listprocessor-Version:"
137     "^X-Received:" "^X-Distribute:" "^X-Sequence:" "^X-Juno-Line-Breaks:"
138     "^X-Notes-Item:" "^X-MS-TNEF-Correlator:" "^x-uunet-gateway:"
139     "^X-Received:" "^Content-length:" "X-precedence:")
140   "*All headers that start with this regexp will be hidden.
141 This variable can also be a list of regexps of headers to be ignored.
142 If `gnus-visible-headers' is non-nil, this variable will be ignored."
143   :type '(choice :custom-show nil
144                  regexp
145                  (repeat regexp))
146   :group 'gnus-article-hiding)
147
148 (defcustom gnus-visible-headers
149   "^From:\\|^Newsgroups:\\|^Subject:\\|^Date:\\|^Followup-To:\\|^Reply-To:\\|^Organization:\\|^Summary:\\|^Keywords:\\|^To:\\|^[BGF]?Cc:\\|^Posted-To:\\|^Mail-Copies-To:\\|^Apparently-To:\\|^Gnus-Warning:\\|^Resent-From:\\|^X-Sent:"
150   "*All headers that do not match this regexp will be hidden.
151 This variable can also be a list of regexp of headers to remain visible.
152 If this variable is non-nil, `gnus-ignored-headers' will be ignored."
153   :type '(repeat :value-to-internal (lambda (widget value)
154                                       (custom-split-regexp-maybe value))
155                  :match (lambda (widget value)
156                           (or (stringp value)
157                               (widget-editable-list-match widget value)))
158                  regexp)
159   :group 'gnus-article-hiding)
160
161 (defcustom gnus-sorted-header-list
162   '("^From:" "^Subject:" "^Summary:" "^Keywords:" "^Newsgroups:"
163     "^Followup-To:" "^To:" "^Cc:" "^Date:" "^Organization:")
164   "*This variable is a list of regular expressions.
165 If it is non-nil, headers that match the regular expressions will
166 be placed first in the article buffer in the sequence specified by
167 this list."
168   :type '(repeat regexp)
169   :group 'gnus-article-hiding)
170
171 (defcustom gnus-boring-article-headers '(empty followup-to reply-to)
172   "Headers that are only to be displayed if they have interesting data.
173 Possible values in this list are `empty', `newsgroups', `followup-to',
174 `reply-to', `date', `long-to', and `many-to'."
175   :type '(set (const :tag "Headers with no content." empty)
176               (const :tag "Newsgroups with only one group." newsgroups)
177               (const :tag "Followup-to identical to newsgroups." followup-to)
178               (const :tag "Reply-to identical to from." reply-to)
179               (const :tag "Date less than four days old." date)
180               (const :tag "Very long To and/or Cc header." long-to)
181               (const :tag "Multiple To and/or Cc headers." many-to))
182   :group 'gnus-article-hiding)
183
184 (defcustom gnus-signature-separator '("^-- $" "^-- *$")
185   "Regexp matching signature separator.
186 This can also be a list of regexps.  In that case, it will be checked
187 from head to tail looking for a separator.  Searches will be done from
188 the end of the buffer."
189   :type '(repeat string)
190   :group 'gnus-article-signature)
191
192 (defcustom gnus-signature-limit nil
193   "Provide a limit to what is considered a signature.
194 If it is a number, no signature may not be longer (in characters) than
195 that number.  If it is a floating point number, no signature may be
196 longer (in lines) than that number.  If it is a function, the function
197 will be called without any parameters, and if it returns nil, there is
198 no signature in the buffer.  If it is a string, it will be used as a
199 regexp.  If it matches, the text in question is not a signature."
200   :type '(choice (integer :value 200)
201                  (number :value 4.0)
202                  (function :value fun)
203                  (regexp :value ".*"))
204   :group 'gnus-article-signature)
205
206 (defcustom gnus-hidden-properties '(invisible t intangible t)
207   "Property list to use for hiding text."
208   :type 'sexp
209   :group 'gnus-article-hiding)
210
211 (defcustom gnus-article-x-face-command
212   (cond
213    ;; Fixme: This isn't the right thing for mixed graphical and and
214    ;; non-graphical frames in a session.
215    ;; gnus-xmas.el overrides this for XEmacs.
216    ((and (fboundp 'image-type-available-p)
217          (image-type-available-p 'xbm))
218     'gnus-article-display-xface)
219    ((and (not (featurep 'xemacs))
220          window-system
221          (module-installed-p 'x-face-mule))
222     'x-face-mule-gnus-article-display-x-face)
223    (t
224     "{ echo '/* Width=48, Height=48 */'; uncompface; } | icontopbm | display -"))
225   "*String or function to be executed to display an X-Face header.
226 If it is a string, the command will be executed in a sub-shell
227 asynchronously.  The compressed face will be piped to this command."
228   :type '(choice string
229                  (function-item gnus-article-display-xface)
230                  (function-item x-face-mule-gnus-article-display-x-face)
231                  function)
232   :group 'gnus-article-washing)
233
234 (defcustom gnus-article-x-face-too-ugly nil
235   "Regexp matching posters whose face shouldn't be shown automatically."
236   :type '(choice regexp (const nil))
237   :group 'gnus-article-washing)
238
239 (defcustom gnus-article-banner-alist nil
240   "Banner alist for stripping.
241 For example, 
242      ((egroups . \"^[ \\t\\n]*-------------------+\\\\( eGroups Sponsor -+\\\\)?....\\n\\\\(.+\\n\\\\)+\"))"
243   :type '(repeat (cons symbol regexp))
244   :group 'gnus-article-washing)
245
246 (defcustom gnus-emphasis-alist
247   (let ((format
248          "\\(\\s-\\|^\\|[-\"]\\|\\s(\\)\\(%s\\(\\w+\\(\\s-+\\w+\\)*[.,]?\\)%s\\)\\(\\s-\\|[-,;:\"]\\s-\\|[?!.]+\\s-\\|\\s)\\)")
249         (types
250          '(("_" "_" underline)
251            ("/" "/" italic)
252            ("\\*" "\\*" bold)
253            ("_/" "/_" underline-italic)
254            ("_\\*" "\\*_" underline-bold)
255            ("\\*/" "/\\*" bold-italic)
256            ("_\\*/" "/\\*_" underline-bold-italic))))
257     `(("\\(\\s-\\|^\\)\\(_\\(\\(\\w\\|_[^_]\\)+\\)_\\)\\(\\s-\\|[?!.,;]\\)"
258        2 3 gnus-emphasis-underline)
259       ,@(mapcar
260          (lambda (spec)
261            (list
262             (format format (car spec) (car (cdr spec)))
263             2 3 (intern (format "gnus-emphasis-%s" (nth 2 spec)))))
264          types)))
265   "*Alist that says how to fontify certain phrases.
266 Each item looks like this:
267
268   (\"_\\\\(\\\\w+\\\\)_\" 0 1 'underline)
269
270 The first element is a regular expression to be matched.  The second
271 is a number that says what regular expression grouping used to find
272 the entire emphasized word.  The third is a number that says what
273 regexp grouping should be displayed and highlighted.  The fourth
274 is the face used for highlighting."
275   :type '(repeat (list :value ("" 0 0 default)
276                        regexp
277                        (integer :tag "Match group")
278                        (integer :tag "Emphasize group")
279                        face))
280   :group 'gnus-article-emphasis)
281
282 (defcustom gnus-emphasize-whitespace-regexp "^[ \t]+\\|[ \t]*\n"
283   "A regexp to describe whitespace which should not be emphasized.
284 Typical values are \"^[ \\t]+\\\\|[ \\t]*\\n\" and \"[ \\t]+\\\\|[ \\t]*\\n\".
285 The former avoids underlining of leading and trailing whitespace,
286 and the latter avoids underlining any whitespace at all."
287   :group 'gnus-article-emphasis
288   :type 'regexp)
289
290 (defface gnus-emphasis-bold '((t (:bold t)))
291   "Face used for displaying strong emphasized text (*word*)."
292   :group 'gnus-article-emphasis)
293
294 (defface gnus-emphasis-italic '((t (:italic t)))
295   "Face used for displaying italic emphasized text (/word/)."
296   :group 'gnus-article-emphasis)
297
298 (defface gnus-emphasis-underline '((t (:underline t)))
299   "Face used for displaying underlined emphasized text (_word_)."
300   :group 'gnus-article-emphasis)
301
302 (defface gnus-emphasis-underline-bold '((t (:bold t :underline t)))
303   "Face used for displaying underlined bold emphasized text (_*word*_)."
304   :group 'gnus-article-emphasis)
305
306 (defface gnus-emphasis-underline-italic '((t (:italic t :underline t)))
307   "Face used for displaying underlined italic emphasized text (_/word/_)."
308   :group 'gnus-article-emphasis)
309
310 (defface gnus-emphasis-bold-italic '((t (:bold t :italic t)))
311   "Face used for displaying bold italic emphasized text (/*word*/)."
312   :group 'gnus-article-emphasis)
313
314 (defface gnus-emphasis-underline-bold-italic
315   '((t (:bold t :italic t :underline t)))
316   "Face used for displaying underlined bold italic emphasized text.
317 Esample: (_/*word*/_)."
318   :group 'gnus-article-emphasis)
319
320 (defface gnus-emphasis-highlight-words
321   '((t (:background "black" :foreground "yellow")))
322   "Face used for displaying highlighted words."
323   :group 'gnus-article-emphasis)
324
325 (defcustom gnus-article-time-format "%a, %b %d %Y %T %Z"
326   "Format for display of Date headers in article bodies.
327 See `format-time-string' for the possible values.
328
329 The variable can also be function, which should return a complete Date
330 header.  The function is called with one argument, the time, which can
331 be fed to `format-time-string'."
332   :type '(choice string symbol)
333   :link '(custom-manual "(gnus)Article Date")
334   :group 'gnus-article-washing)
335
336 (eval-and-compile
337   (autoload 'mail-extract-address-components "mail-extr"))
338
339 (defcustom gnus-save-all-headers t
340   "*If non-nil, don't remove any headers before saving."
341   :group 'gnus-article-saving
342   :type 'boolean)
343
344 (defcustom gnus-prompt-before-saving 'always
345   "*This variable says how much prompting is to be done when saving articles.
346 If it is nil, no prompting will be done, and the articles will be
347 saved to the default files.  If this variable is `always', each and
348 every article that is saved will be preceded by a prompt, even when
349 saving large batches of articles.  If this variable is neither nil not
350 `always', there the user will be prompted once for a file name for
351 each invocation of the saving commands."
352   :group 'gnus-article-saving
353   :type '(choice (item always)
354                  (item :tag "never" nil)
355                  (sexp :tag "once" :format "%t\n" :value t)))
356
357 (defcustom gnus-saved-headers gnus-visible-headers
358   "Headers to keep if `gnus-save-all-headers' is nil.
359 If `gnus-save-all-headers' is non-nil, this variable will be ignored.
360 If that variable is nil, however, all headers that match this regexp
361 will be kept while the rest will be deleted before saving."
362   :group 'gnus-article-saving
363   :type 'regexp)
364
365 (defcustom gnus-default-article-saver 'gnus-summary-save-in-rmail
366   "A function to save articles in your favourite format.
367 The function must be interactively callable (in other words, it must
368 be an Emacs command).
369
370 Gnus provides the following functions:
371
372 * gnus-summary-save-in-rmail (Rmail format)
373 * gnus-summary-save-in-mail (Unix mail format)
374 * gnus-summary-save-in-folder (MH folder)
375 * gnus-summary-save-in-file (article format)
376 * gnus-summary-save-in-vm (use VM's folder format)
377 * gnus-summary-write-to-file (article format -- overwrite)."
378   :group 'gnus-article-saving
379   :type '(radio (function-item gnus-summary-save-in-rmail)
380                 (function-item gnus-summary-save-in-mail)
381                 (function-item gnus-summary-save-in-folder)
382                 (function-item gnus-summary-save-in-file)
383                 (function-item gnus-summary-save-in-vm)
384                 (function-item gnus-summary-write-to-file)))
385
386 (defcustom gnus-rmail-save-name 'gnus-plain-save-name
387   "A function generating a file name to save articles in Rmail format.
388 The function is called with NEWSGROUP, HEADERS, and optional LAST-FILE."
389   :group 'gnus-article-saving
390   :type 'function)
391
392 (defcustom gnus-mail-save-name 'gnus-plain-save-name
393   "A function generating a file name to save articles in Unix mail format.
394 The function is called with NEWSGROUP, HEADERS, and optional LAST-FILE."
395   :group 'gnus-article-saving
396   :type 'function)
397
398 (defcustom gnus-folder-save-name 'gnus-folder-save-name
399   "A function generating a file name to save articles in MH folder.
400 The function is called with NEWSGROUP, HEADERS, and optional LAST-FOLDER."
401   :group 'gnus-article-saving
402   :type 'function)
403
404 (defcustom gnus-file-save-name 'gnus-numeric-save-name
405   "A function generating a file name to save articles in article format.
406 The function is called with NEWSGROUP, HEADERS, and optional
407 LAST-FILE."
408   :group 'gnus-article-saving
409   :type 'function)
410
411 (defcustom gnus-split-methods
412   '((gnus-article-archive-name)
413     (gnus-article-nndoc-name))
414   "*Variable used to suggest where articles are to be saved.
415 For instance, if you would like to save articles related to Gnus in
416 the file \"gnus-stuff\", and articles related to VM in \"vm-stuff\",
417 you could set this variable to something like:
418
419  '((\"^Subject:.*gnus\\|^Newsgroups:.*gnus\" \"gnus-stuff\")
420    (\"^Subject:.*vm\\|^Xref:.*vm\" \"vm-stuff\"))
421
422 This variable is an alist where the where the key is the match and the
423 value is a list of possible files to save in if the match is non-nil.
424
425 If the match is a string, it is used as a regexp match on the
426 article.  If the match is a symbol, that symbol will be funcalled
427 from the buffer of the article to be saved with the newsgroup as the
428 parameter.  If it is a list, it will be evaled in the same buffer.
429
430 If this form or function returns a string, this string will be used as
431 a possible file name; and if it returns a non-nil list, that list will
432 be used as possible file names."
433   :group 'gnus-article-saving
434   :type '(repeat (choice (list :value (fun) function)
435                          (cons :value ("" "") regexp (repeat string))
436                          (sexp :value nil))))
437
438 (defcustom gnus-article-display-method-for-mime
439   'gnus-article-display-mime-message
440   "Function to display a MIME message.
441 The function is called from the article buffer."
442   :group 'gnus-article-mime
443   :type 'function)
444
445 (defcustom gnus-article-display-method-for-traditional
446   'gnus-article-display-traditional-message
447   "*Function to display a traditional message.
448 The function is called from the article buffer."
449   :group 'gnus-article-mime
450   :type 'function)
451
452 (defcustom gnus-page-delimiter "^\^L"
453   "*Regexp describing what to use as article page delimiters.
454 The default value is \"^\^L\", which is a form linefeed at the
455 beginning of a line."
456   :type 'regexp
457   :group 'gnus-article-various)
458
459 (defcustom gnus-article-mode-line-format "Gnus: %g [%w] %S%m"
460   "*The format specification for the article mode line.
461 See `gnus-summary-mode-line-format' for a closer description.
462
463 The following additional specs are available:
464
465 %w  The article washing status.
466 %m  The number of MIME parts in the article."
467   :type 'string
468   :group 'gnus-article-various)
469
470 (defcustom gnus-article-mode-hook nil
471   "*A hook for Gnus article mode."
472   :type 'hook
473   :group 'gnus-article-various)
474
475 (defcustom gnus-article-menu-hook nil
476   "*Hook run after the creation of the article mode menu."
477   :type 'hook
478   :group 'gnus-article-various)
479
480 (defcustom gnus-article-prepare-hook nil
481   "*A hook called after an article has been prepared in the article buffer."
482   :type 'hook
483   :group 'gnus-article-various)
484
485 (defcustom gnus-article-hide-pgp-hook nil
486   "*A hook called after successfully hiding a PGP signature."
487   :type 'hook
488   :group 'gnus-article-various)
489
490 (defcustom gnus-article-button-face 'bold
491   "Face used for highlighting buttons in the article buffer.
492
493 An article button is a piece of text that you can activate by pressing
494 `RET' or `mouse-2' above it."
495   :type 'face
496   :group 'gnus-article-buttons)
497
498 (defcustom gnus-article-mouse-face 'highlight
499   "Face used for mouse highlighting in the article buffer.
500
501 Article buttons will be displayed in this face when the cursor is
502 above them."
503   :type 'face
504   :group 'gnus-article-buttons)
505
506 (defcustom gnus-signature-face 'gnus-signature-face
507   "Face used for highlighting a signature in the article buffer.
508 Obsolete; use the face `gnus-signature-face' for customizations instead."
509   :type 'face
510   :group 'gnus-article-highlight
511   :group 'gnus-article-signature)
512
513 (defface gnus-signature-face
514   '((t (:italic t)))
515   "Face used for highlighting a signature in the article buffer."
516   :group 'gnus-article-highlight
517   :group 'gnus-article-signature)
518
519 (defface gnus-header-from-face
520   '((((class color)
521       (background dark))
522      (:foreground "spring green"))
523     (((class color)
524       (background light))
525      (:foreground "red3"))
526     (t
527      (:italic t)))
528   "Face used for displaying from headers."
529   :group 'gnus-article-headers
530   :group 'gnus-article-highlight)
531
532 (defface gnus-header-subject-face
533   '((((class color)
534       (background dark))
535      (:foreground "SeaGreen3"))
536     (((class color)
537       (background light))
538      (:foreground "red4"))
539     (t
540      (:bold t :italic t)))
541   "Face used for displaying subject headers."
542   :group 'gnus-article-headers
543   :group 'gnus-article-highlight)
544
545 (defface gnus-header-newsgroups-face
546   '((((class color)
547       (background dark))
548      (:foreground "yellow" :italic t))
549     (((class color)
550       (background light))
551      (:foreground "MidnightBlue" :italic t))
552     (t
553      (:italic t)))
554   "Face used for displaying newsgroups headers."
555   :group 'gnus-article-headers
556   :group 'gnus-article-highlight)
557
558 (defface gnus-header-name-face
559   '((((class color)
560       (background dark))
561      (:foreground "SeaGreen"))
562     (((class color)
563       (background light))
564      (:foreground "maroon"))
565     (t
566      (:bold t)))
567   "Face used for displaying header names."
568   :group 'gnus-article-headers
569   :group 'gnus-article-highlight)
570
571 (defface gnus-header-content-face
572   '((((class color)
573       (background dark))
574      (:foreground "forest green" :italic t))
575     (((class color)
576       (background light))
577      (:foreground "indianred4" :italic t))
578     (t
579      (:italic t)))  "Face used for displaying header content."
580   :group 'gnus-article-headers
581   :group 'gnus-article-highlight)
582
583 (defcustom gnus-header-face-alist
584   '(("From" nil gnus-header-from-face)
585     ("Subject" nil gnus-header-subject-face)
586     ("Newsgroups:.*," nil gnus-header-newsgroups-face)
587     ("" gnus-header-name-face gnus-header-content-face))
588   "*Controls highlighting of article header.
589
590 An alist of the form (HEADER NAME CONTENT).
591
592 HEADER is a regular expression which should match the name of an
593 header header and NAME and CONTENT are either face names or nil.
594
595 The name of each header field will be displayed using the face
596 specified by the first element in the list where HEADER match the
597 header name and NAME is non-nil.  Similarly, the content will be
598 displayed by the first non-nil matching CONTENT face."
599   :group 'gnus-article-headers
600   :group 'gnus-article-highlight
601   :type '(repeat (list (regexp :tag "Header")
602                        (choice :tag "Name"
603                                (item :tag "skip" nil)
604                                (face :value default))
605                        (choice :tag "Content"
606                                (item :tag "skip" nil)
607                                (face :value default)))))
608
609 (defcustom gnus-article-decode-hook nil
610   "*Hook run to decode charsets in articles."
611   :group 'gnus-article-headers
612   :type 'hook)
613
614 (defcustom gnus-display-mime-function 'gnus-display-mime
615   "Function to display MIME articles."
616   :group 'gnus-article-mime
617   :type 'function)
618
619 (defvar gnus-decode-header-function 'mail-decode-encoded-word-region
620   "Function used to decode headers.")
621
622 (defvar gnus-article-dumbquotes-map
623   '(("\202" ",")
624     ("\203" "f")
625     ("\204" ",,")
626     ("\205" "...")
627     ("\213" "<")
628     ("\214" "OE")
629     ("\221" "`")
630     ("\222" "'")
631     ("\223" "``")
632     ("\224" "\"")
633     ("\225" "*")
634     ("\226" "-")
635     ("\227" "--")
636     ("\231" "(TM)")
637     ("\233" ">")
638     ("\234" "oe")
639     ("\264" "'"))
640   "Table for MS-to-Latin1 translation.")
641
642 (defcustom gnus-ignored-mime-types nil
643   "List of MIME types that should be ignored by Gnus."
644   :group 'gnus-article-mime
645   :type '(repeat regexp))
646
647 (defcustom gnus-unbuttonized-mime-types '(".*/.*")
648   "List of MIME types that should not be given buttons when rendered inline."
649   :group 'gnus-article-mime
650   :type '(repeat regexp))
651
652 (defcustom gnus-article-mime-part-function nil
653   "Function called with a MIME handle as the argument.
654 This is meant for people who want to do something automatic based
655 on parts -- for instance, adding Vcard info to a database."
656   :group 'gnus-article-mime
657   :type 'function)
658
659 (defcustom gnus-mime-multipart-functions nil
660   "An alist of MIME types to functions to display them.")
661
662 (defcustom gnus-article-date-lapsed-new-header nil
663   "Whether the X-Sent and Date headers can coexist.
664 When using `gnus-treat-date-lapsed', the \"X-Sent:\" header will
665 either replace the old \"Date:\" header (if this variable is nil), or
666 be added below it (otherwise)."
667   :group 'gnus-article-headers
668   :type 'boolean)
669
670 (defcustom gnus-article-mime-match-handle-function 'undisplayed-alternative
671   "Function called with a MIME handle as the argument.
672 This is meant for people who want to view first matched part.
673 For `undisplayed-alternative' (default), the first undisplayed 
674 part or alternative part is used.  For `undisplayed', the first 
675 undisplayed part is used.  For a function, the first part which 
676 the function return `t' is used.  For `nil', the first part is
677 used."
678   :group 'gnus-article-mime
679   :type '(choice 
680           (item :tag "first" :value nil)
681           (item :tag "undisplayed" :value undisplayed)
682           (item :tag "undisplayed or alternative" 
683                 :value undisplayed-alternative)
684           (function)))
685
686 (defcustom gnus-mime-action-alist
687   '(("save to file" . gnus-mime-save-part)
688     ("display as text" . gnus-mime-inline-part)
689     ("view the part" . gnus-mime-view-part)
690     ("pipe to command" . gnus-mime-pipe-part)
691     ("toggle display" . gnus-article-press-button)
692     ("view as type" . gnus-mime-view-part-as-type)
693     ("internalize type" . gnus-mime-internalize-part)
694     ("externalize type" . gnus-mime-externalize-part))
695   "An alist of actions that run on the MIME attachment."
696   :group 'gnus-article-mime
697   :type '(repeat (cons (string :tag "name")
698                        (function))))
699
700 ;;;
701 ;;; The treatment variables
702 ;;;
703
704 (defvar gnus-part-display-hook nil
705   "Hook called on parts that are to receive treatment.")
706
707 (defvar gnus-article-treat-custom
708   '(choice (const :tag "Off" nil)
709            (const :tag "On" t)
710            (const :tag "Header" head)
711            (const :tag "Last" last)
712            (const :tag "Mime" mime)
713            (integer :tag "Less")
714            (repeat :tag "Groups" regexp)
715            (sexp :tag "Predicate")))
716
717 (defvar gnus-article-treat-head-custom
718   '(choice (const :tag "Off" nil)
719            (const :tag "Header" head)))
720
721 (defvar gnus-article-treat-types '("text/plain")
722   "Parts to treat.")
723
724 (defvar gnus-inhibit-treatment nil
725   "Whether to inhibit treatment.")
726
727 (defcustom gnus-treat-highlight-signature '(or last (typep "text/x-vcard"))
728   "Highlight the signature.
729 Valid values are nil, t, `head', `last', an integer or a predicate.
730 See the manual for details."
731   :group 'gnus-article-treat
732   :type gnus-article-treat-custom)
733 (put 'gnus-treat-highlight-signature 'highlight t)
734
735 (defcustom gnus-treat-buttonize 100000
736   "Add buttons.
737 Valid values are nil, t, `head', `last', an integer or a predicate.
738 See the manual for details."
739   :group 'gnus-article-treat
740   :type gnus-article-treat-custom)
741 (put 'gnus-treat-buttonize 'highlight t)
742
743 (defcustom gnus-treat-buttonize-head 'head
744   "Add buttons to the head.
745 Valid values are nil, t, `head', `last', an integer or a predicate.
746 See the manual for details."
747   :group 'gnus-article-treat
748   :type gnus-article-treat-head-custom)
749 (put 'gnus-treat-buttonize-head 'highlight t)
750
751 (defcustom gnus-treat-emphasize nil
752   "Emphasize text.
753 Valid values are nil, t, `head', `last', an integer or a predicate.
754 See the manual for details."
755   :group 'gnus-article-treat
756   :type gnus-article-treat-custom)
757 (put 'gnus-treat-emphasize 'highlight t)
758
759 (defcustom gnus-treat-strip-cr nil
760   "Remove carriage returns.
761 Valid values are nil, t, `head', `last', an integer or a predicate.
762 See the manual for details."
763   :group 'gnus-article-treat
764   :type gnus-article-treat-custom)
765
766 (defcustom gnus-treat-hide-headers 'head
767   "Hide headers.
768 Valid values are nil, t, `head', `last', an integer or a predicate.
769 See the manual for details."
770   :group 'gnus-article-treat
771   :type gnus-article-treat-head-custom)
772
773 (defcustom gnus-treat-hide-boring-headers nil
774   "Hide boring headers.
775 Valid values are nil, t, `head', `last', an integer or a predicate.
776 See the manual for details."
777   :group 'gnus-article-treat
778   :type gnus-article-treat-head-custom)
779
780 (defcustom gnus-treat-hide-signature nil
781   "Hide the signature.
782 Valid values are nil, t, `head', `last', an integer or a predicate.
783 See the manual for details."
784   :group 'gnus-article-treat
785   :type gnus-article-treat-custom)
786
787 (defcustom gnus-treat-fill-article nil
788   "Fill the article.
789 Valid values are nil, t, `head', `last', an integer or a predicate.
790 See the manual for details."
791   :group 'gnus-article-treat
792   :type gnus-article-treat-custom)
793
794 (defcustom gnus-treat-hide-citation nil
795   "Hide cited text.
796 Valid values are nil, t, `head', `last', an integer or a predicate.
797 See the manual for details."
798   :group 'gnus-article-treat
799   :type gnus-article-treat-custom)
800
801 (defcustom gnus-treat-hide-citation-maybe nil
802   "Hide cited text.
803 Valid values are nil, t, `head', `last', an integer or a predicate.
804 See the manual for details."
805   :group 'gnus-article-treat
806   :type gnus-article-treat-custom)
807
808 (defcustom gnus-treat-strip-list-identifiers 'head
809   "Strip list identifiers from `gnus-list-identifiers`.
810 Valid values are nil, t, `head', `last', an integer or a predicate.
811 See the manual for details."
812   :group 'gnus-article-treat
813   :type gnus-article-treat-custom)
814
815 (defcustom gnus-treat-strip-pgp t
816   "Strip PGP signatures.
817 Valid values are nil, t, `head', `last', an integer or a predicate.
818 See the manual for details."
819   :group 'gnus-article-treat
820   :type gnus-article-treat-custom)
821
822 (defcustom gnus-treat-strip-pem nil
823   "Strip PEM signatures.
824 Valid values are nil, t, `head', `last', an integer or a predicate.
825 See the manual for details."
826   :group 'gnus-article-treat
827   :type gnus-article-treat-custom)
828
829 (defcustom gnus-treat-strip-banner t
830   "Strip banners from articles.
831 The banner to be stripped is specified in the `banner' group parameter.
832 Valid values are nil, t, `head', `last', an integer or a predicate.
833 See the manual for details."
834   :group 'gnus-article-treat
835   :type gnus-article-treat-custom)
836
837 (defcustom gnus-treat-highlight-headers 'head
838   "Highlight the headers.
839 Valid values are nil, t, `head', `last', an integer or a predicate.
840 See the manual for details."
841   :group 'gnus-article-treat
842   :type gnus-article-treat-head-custom)
843 (put 'gnus-treat-highlight-headers 'highlight t)
844
845 (defcustom gnus-treat-highlight-citation t
846   "Highlight cited text.
847 Valid values are nil, t, `head', `last', an integer or a predicate.
848 See the manual for details."
849   :group 'gnus-article-treat
850   :type gnus-article-treat-custom)
851 (put 'gnus-treat-highlight-citation 'highlight t)
852
853 (defcustom gnus-treat-date-ut nil
854   "Display the Date in UT (GMT).
855 Valid values are nil, t, `head', `last', an integer or a predicate.
856 See the manual for details."
857   :group 'gnus-article-treat
858   :type gnus-article-treat-head-custom)
859
860 (defcustom gnus-treat-date-local nil
861   "Display the Date in the local timezone.
862 Valid values are nil, t, `head', `last', an integer or a predicate.
863 See the manual for details."
864   :group 'gnus-article-treat
865   :type gnus-article-treat-head-custom)
866
867 (defcustom gnus-treat-date-lapsed nil
868   "Display the Date header in a way that says how much time has elapsed.
869 Valid values are nil, t, `head', `last', an integer or a predicate.
870 See the manual for details."
871   :group 'gnus-article-treat
872   :type gnus-article-treat-head-custom)
873
874 (defcustom gnus-treat-date-original nil
875   "Display the date in the original timezone.
876 Valid values are nil, t, `head', `last', an integer or a predicate.
877 See the manual for details."
878   :group 'gnus-article-treat
879   :type gnus-article-treat-head-custom)
880
881 (defcustom gnus-treat-date-iso8601 nil
882   "Display the date in the ISO8601 format.
883 Valid values are nil, t, `head', `last', an integer or a predicate.
884 See the manual for details."
885   :group 'gnus-article-treat
886   :type gnus-article-treat-head-custom)
887
888 (defcustom gnus-treat-date-user-defined nil
889   "Display the date in a user-defined format.
890 The format is defined by the `gnus-article-time-format' variable.
891 Valid values are nil, t, `head', `last', an integer or a predicate.
892 See the manual for details."
893   :group 'gnus-article-treat
894   :type gnus-article-treat-head-custom)
895
896 (defcustom gnus-treat-strip-headers-in-body t
897   "Strip the X-No-Archive header line from the beginning of the body.
898 Valid values are nil, t, `head', `last', an integer or a predicate.
899 See the manual for details."
900   :group 'gnus-article-treat
901   :type gnus-article-treat-custom)
902
903 (defcustom gnus-treat-strip-trailing-blank-lines nil
904   "Strip trailing blank lines.
905 Valid values are nil, t, `head', `last', an integer or a predicate.
906 See the manual for details."
907   :group 'gnus-article-treat
908   :type gnus-article-treat-custom)
909
910 (defcustom gnus-treat-strip-leading-blank-lines nil
911   "Strip leading blank lines.
912 Valid values are nil, t, `head', `last', an integer or a predicate.
913 See the manual for details."
914   :group 'gnus-article-treat
915   :type gnus-article-treat-custom)
916
917 (defcustom gnus-treat-strip-multiple-blank-lines nil
918   "Strip multiple blank lines.
919 Valid values are nil, t, `head', `last', an integer or a predicate.
920 See the manual for details."
921   :group 'gnus-article-treat
922   :type gnus-article-treat-custom)
923
924 (defcustom gnus-treat-overstrike t
925   "Treat overstrike highlighting.
926 Valid values are nil, t, `head', `last', an integer or a predicate.
927 See the manual for details."
928   :group 'gnus-article-treat
929   :type gnus-article-treat-custom)
930 (put 'gnus-treat-overstrike 'highlight t)
931
932 (defcustom gnus-treat-display-xface
933   (and (or (and (fboundp 'image-type-available-p)
934                 (image-type-available-p 'xbm))
935            (and (featurep 'xemacs) (featurep 'xface))
936            (eq 'x-face-mule-gnus-article-display-x-face
937                gnus-article-x-face-command))
938        'head)
939   "Display X-Face headers.
940 Valid values are nil, t, `head', `last', an integer or a predicate.
941 See the manual for details."
942   :group 'gnus-article-treat
943   :type gnus-article-treat-head-custom)
944 (put 'gnus-treat-display-xface 'highlight t)
945
946 (defcustom gnus-treat-display-smileys
947   (if (or (and (featurep 'xemacs)
948                (featurep 'xpm))
949           (and (fboundp 'image-type-available-p)
950                (image-type-available-p 'pbm))
951           (and (not (featurep 'xemacs))
952                window-system
953                (module-installed-p 'gnus-bitmap)))
954       t
955     nil)
956   "Display smileys.
957 Valid values are nil, t, `head', `last', an integer or a predicate.
958 See the manual for details."
959   :group 'gnus-article-treat
960   :type gnus-article-treat-custom)
961 (put 'gnus-treat-display-smileys 'highlight t)
962
963 (defcustom gnus-treat-display-picons (if (featurep 'xemacs) 'head nil)
964   "Display picons.
965 Valid values are nil, t, `head', `last', an integer or a predicate.
966 See the manual for details."
967   :group 'gnus-article-treat
968   :type gnus-article-treat-head-custom)
969 (put 'gnus-treat-display-picons 'highlight t)
970
971 (defcustom gnus-treat-capitalize-sentences nil
972   "Capitalize sentence-starting words.
973 Valid values are nil, t, `head', `last', an integer or a predicate.
974 See the manual for details."
975   :group 'gnus-article-treat
976   :type gnus-article-treat-custom)
977
978 (defcustom gnus-treat-fill-long-lines nil
979   "Fill long lines.
980 Valid values are nil, t, `head', `last', an integer or a predicate.
981 See the manual for details."
982   :group 'gnus-article-treat
983   :type gnus-article-treat-custom)
984
985 (defcustom gnus-treat-play-sounds nil
986   "Play sounds.
987 Valid values are nil, t, `head', `last', an integer or a predicate.
988 See the manual for details."
989   :group 'gnus-article-treat
990   :type gnus-article-treat-custom)
991
992 (defcustom gnus-treat-decode-article-as-default-mime-charset nil
993   "Decode an article as `default-mime-charset'.  For instance, if you want to
994 attempt to decode an article even if the value of `gnus-show-mime' is nil,
995 you could set this variable to something like: nil for don't decode, t for
996 decode the body, '(or header t) for the whole article, etc."
997   :group 'gnus-article-treat
998   :type '(radio (const :tag "Off" nil)
999                 (const :tag "Decode body" t)
1000                 (const :tag "Decode all" (or head t))))
1001
1002 (defcustom gnus-treat-translate nil
1003   "Translate articles from one language to another.
1004 Valid values are nil, t, `head', `last', an integer or a predicate.
1005 See the manual for details."
1006   :group 'gnus-article-treat
1007   :type gnus-article-treat-custom)
1008
1009 ;;; Internal variables
1010
1011 (defvar article-goto-body-goes-to-point-min-p nil)
1012 (defvar gnus-article-wash-types nil)
1013 (defvar gnus-article-emphasis-alist nil)
1014
1015 (defvar gnus-article-mime-handle-alist-1 nil)
1016 (defvar gnus-treatment-function-alist
1017   '((gnus-treat-decode-article-as-default-mime-charset
1018      gnus-article-decode-article-as-default-mime-charset)
1019     (gnus-treat-strip-banner gnus-article-strip-banner)
1020     (gnus-treat-strip-headers-in-body gnus-article-strip-headers-in-body)
1021     (gnus-treat-buttonize gnus-article-add-buttons)
1022     (gnus-treat-fill-article gnus-article-fill-cited-article)
1023     (gnus-treat-fill-long-lines gnus-article-fill-long-lines)
1024     (gnus-treat-strip-cr gnus-article-remove-cr)
1025     (gnus-treat-display-xface gnus-article-display-x-face)
1026     (gnus-treat-hide-headers gnus-article-maybe-hide-headers)
1027     (gnus-treat-hide-boring-headers gnus-article-hide-boring-headers)
1028     (gnus-treat-hide-signature gnus-article-hide-signature)
1029     (gnus-treat-hide-citation gnus-article-hide-citation)
1030     (gnus-treat-hide-citation-maybe gnus-article-hide-citation-maybe)
1031     (gnus-treat-strip-list-identifiers gnus-article-hide-list-identifiers)
1032     (gnus-treat-strip-pgp gnus-article-hide-pgp)
1033     (gnus-treat-strip-pem gnus-article-hide-pem)
1034     (gnus-treat-highlight-headers gnus-article-highlight-headers)
1035     (gnus-treat-emphasize gnus-article-emphasize)
1036     (gnus-treat-highlight-citation gnus-article-highlight-citation)
1037     (gnus-treat-highlight-signature gnus-article-highlight-signature)
1038     (gnus-treat-date-ut gnus-article-date-ut)
1039     (gnus-treat-date-local gnus-article-date-local)
1040     (gnus-treat-date-lapsed gnus-article-date-lapsed)
1041     (gnus-treat-date-original gnus-article-date-original)
1042     (gnus-treat-date-user-defined gnus-article-date-user)
1043     (gnus-treat-date-iso8601 gnus-article-date-iso8601)
1044     (gnus-treat-strip-trailing-blank-lines
1045      gnus-article-remove-trailing-blank-lines)
1046     (gnus-treat-strip-leading-blank-lines
1047      gnus-article-strip-leading-blank-lines)
1048     (gnus-treat-strip-multiple-blank-lines
1049      gnus-article-strip-multiple-blank-lines)
1050     (gnus-treat-overstrike gnus-article-treat-overstrike)
1051     (gnus-treat-buttonize-head gnus-article-add-buttons-to-head)
1052     (gnus-treat-display-smileys gnus-article-smiley-display)
1053     (gnus-treat-capitalize-sentences gnus-article-capitalize-sentences)
1054     (gnus-treat-display-picons gnus-article-display-picons)
1055     (gnus-treat-play-sounds gnus-earcon-display)))
1056
1057 (defvar gnus-article-mime-handle-alist nil)
1058 (defvar article-lapsed-timer nil)
1059 (defvar gnus-article-current-summary nil)
1060
1061 (defvar gnus-article-mode-syntax-table
1062   (let ((table (copy-syntax-table text-mode-syntax-table)))
1063     (modify-syntax-entry ?- "w" table)
1064     (modify-syntax-entry ?> ")" table)
1065     (modify-syntax-entry ?< "(" table)
1066     table)
1067   "Syntax table used in article mode buffers.
1068 Initialized from `text-mode-syntax-table.")
1069
1070 (defvar gnus-save-article-buffer nil)
1071
1072 (defvar gnus-article-mode-line-format-alist
1073   (nconc '((?w (gnus-article-wash-status) ?s)
1074            (?m (gnus-article-mime-part-status) ?s))
1075          gnus-summary-mode-line-format-alist))
1076
1077 (defvar gnus-number-of-articles-to-be-saved nil)
1078
1079 (defvar gnus-inhibit-hiding nil)
1080
1081 (defsubst gnus-article-hide-text (b e props)
1082   "Set text PROPS on the B to E region, extending `intangible' 1 past B."
1083   (add-text-properties b e props)
1084   (when (memq 'intangible props)
1085     (put-text-property
1086      (max (1- b) (point-min))
1087      b 'intangible (cddr (memq 'intangible props)))))
1088 (defsubst gnus-article-unhide-text (b e)
1089   "Remove hidden text properties from region between B and E."
1090   (remove-text-properties b e gnus-hidden-properties)
1091   (when (memq 'intangible gnus-hidden-properties)
1092     (put-text-property (max (1- b) (point-min))
1093                        b 'intangible nil)))
1094
1095 (defun gnus-article-hide-text-type (b e type)
1096   "Hide text of TYPE between B and E."
1097   (push type gnus-article-wash-types)
1098   (gnus-article-hide-text
1099    b e (cons 'article-type (cons type gnus-hidden-properties))))
1100
1101 (defun gnus-article-unhide-text-type (b e type)
1102   "Unhide text of TYPE between B and E."
1103   (setq gnus-article-wash-types
1104         (delq type gnus-article-wash-types))
1105   (remove-text-properties
1106    b e (cons 'article-type (cons type gnus-hidden-properties)))
1107   (when (memq 'intangible gnus-hidden-properties)
1108     (put-text-property (max (1- b) (point-min))
1109                        b 'intangible nil)))
1110
1111 (defun gnus-article-hide-text-of-type (type)
1112   "Hide text of TYPE in the current buffer."
1113   (save-excursion
1114     (let ((b (point-min))
1115           (e (point-max)))
1116       (while (setq b (text-property-any b e 'article-type type))
1117         (add-text-properties b (incf b) gnus-hidden-properties)))))
1118
1119 (defun gnus-article-delete-text-of-type (type)
1120   "Delete text of TYPE in the current buffer."
1121   (save-excursion
1122     (let ((b (point-min)))
1123       (while (setq b (text-property-any b (point-max) 'article-type type))
1124         (delete-region
1125          b (or (text-property-not-all b (point-max) 'article-type type)
1126                (point-max)))))))
1127
1128 (defun gnus-article-delete-invisible-text ()
1129   "Delete all invisible text in the current buffer."
1130   (save-excursion
1131     (let ((b (point-min)))
1132       (while (setq b (text-property-any b (point-max) 'invisible t))
1133         (delete-region
1134          b (or (text-property-not-all b (point-max) 'invisible t)
1135                (point-max)))))))
1136
1137 (defun gnus-article-text-type-exists-p (type)
1138   "Say whether any text of type TYPE exists in the buffer."
1139   (text-property-any (point-min) (point-max) 'article-type type))
1140
1141 (defsubst gnus-article-header-rank ()
1142   "Give the rank of the string HEADER as given by `gnus-sorted-header-list'."
1143   (let ((list gnus-sorted-header-list)
1144         (i 0))
1145     (while list
1146       (when (looking-at (car list))
1147         (setq list nil))
1148       (setq list (cdr list))
1149       (incf i))
1150     i))
1151
1152 (defun article-hide-headers (&optional arg delete)
1153   "Hide unwanted headers and possibly sort them as well."
1154   (interactive (gnus-article-hidden-arg))
1155   ;; Lars said that this function might be inhibited.
1156   (if (gnus-article-check-hidden-text 'headers arg)
1157       (progn
1158         ;; Show boring headers as well.
1159         (gnus-article-show-hidden-text 'boring-headers)
1160         (when (eq 1 (point-min))
1161           (set-window-start (get-buffer-window (current-buffer)) 1)))
1162   (unless gnus-inhibit-hiding
1163     (save-excursion
1164       (save-restriction
1165         (let ((buffer-read-only nil)
1166               (inhibit-read-only t)
1167               (case-fold-search t)
1168               (max (1+ (length gnus-sorted-header-list)))
1169               (ignored (when (not gnus-visible-headers)
1170                          (cond ((stringp gnus-ignored-headers)
1171                                 gnus-ignored-headers)
1172                                ((listp gnus-ignored-headers)
1173                                 (mapconcat 'identity gnus-ignored-headers
1174                                            "\\|")))))
1175               (visible
1176                (cond ((stringp gnus-visible-headers)
1177                       gnus-visible-headers)
1178                      ((and gnus-visible-headers
1179                            (listp gnus-visible-headers))
1180                       (mapconcat 'identity gnus-visible-headers "\\|"))))
1181               (inhibit-point-motion-hooks t)
1182               beg)
1183           ;; First we narrow to just the headers.
1184           (article-narrow-to-head)
1185           ;; Hide any "From " lines at the beginning of (mail) articles.
1186           (while (looking-at "From ")
1187             (forward-line 1))
1188           (unless (bobp)
1189             (if delete
1190                 (delete-region (point-min) (point))
1191               (gnus-article-hide-text (point-min) (point)
1192                                       (nconc (list 'article-type 'headers)
1193                                              gnus-hidden-properties))))
1194           ;; Then treat the rest of the header lines.
1195           ;; Then we use the two regular expressions
1196           ;; `gnus-ignored-headers' and `gnus-visible-headers' to
1197           ;; select which header lines is to remain visible in the
1198           ;; article buffer.
1199           (while (re-search-forward "^[^ \t]*:" nil t)
1200             (beginning-of-line)
1201             ;; Mark the rank of the header.
1202             (put-text-property
1203              (point) (1+ (point)) 'message-rank
1204              (if (or (and visible (looking-at visible))
1205                      (and ignored
1206                           (not (looking-at ignored))))
1207                  (gnus-article-header-rank)
1208                (+ 2 max)))
1209             (forward-line 1))
1210           (message-sort-headers-1)
1211           (when (setq beg (text-property-any
1212                            (point-min) (point-max) 'message-rank (+ 2 max)))
1213             ;; We delete or make invisible the unwanted headers.
1214             (push 'headers gnus-article-wash-types)
1215             (if delete
1216                 (progn
1217                   (add-text-properties
1218                    (point-min) (+ 5 (point-min))
1219                    '(article-type headers dummy-invisible t))
1220                   (delete-region beg (point-max)))
1221               (gnus-article-hide-text-type beg (point-max) 'headers))))))))
1222   )
1223
1224 (defun article-hide-boring-headers (&optional arg)
1225   "Toggle hiding of headers that aren't very interesting.
1226 If given a negative prefix, always show; if given a positive prefix,
1227 always hide."
1228   (interactive (gnus-article-hidden-arg))
1229   (when (and (not (gnus-article-check-hidden-text 'boring-headers arg))
1230              (not gnus-show-all-headers))
1231     (save-excursion
1232       (save-restriction
1233         (let ((buffer-read-only nil)
1234               (list gnus-boring-article-headers)
1235               (inhibit-point-motion-hooks t)
1236               elem)
1237           (article-narrow-to-head)
1238           (while list
1239             (setq elem (pop list))
1240             (goto-char (point-min))
1241             (cond
1242              ;; Hide empty headers.
1243              ((eq elem 'empty)
1244               (while (re-search-forward "^[^: \t]+:[ \t]*\n[^ \t]" nil t)
1245                 (forward-line -1)
1246                 (gnus-article-hide-text-type
1247                  (progn (beginning-of-line) (point))
1248                  (progn
1249                    (end-of-line)
1250                    (if (re-search-forward "^[^ \t]" nil t)
1251                        (match-beginning 0)
1252                      (point-max)))
1253                  'boring-headers)))
1254              ;; Hide boring Newsgroups header.
1255              ((eq elem 'newsgroups)
1256               (when (equal (gnus-fetch-field "newsgroups")
1257                            (gnus-group-real-name
1258                             (if (boundp 'gnus-newsgroup-name)
1259                                 gnus-newsgroup-name
1260                               "")))
1261                 (gnus-article-hide-header "newsgroups")))
1262              ((eq elem 'followup-to)
1263               (when (equal (message-fetch-field "followup-to")
1264                            (message-fetch-field "newsgroups"))
1265                 (gnus-article-hide-header "followup-to")))
1266              ((eq elem 'reply-to)
1267               (let ((from (message-fetch-field "from"))
1268                     (reply-to (message-fetch-field "reply-to")))
1269                 (when (and
1270                        from reply-to
1271                        (ignore-errors
1272                          (equal
1273                           (nth 1 (funcall gnus-extract-address-components from))
1274                           (nth 1 (funcall gnus-extract-address-components reply-to)))))
1275                   (gnus-article-hide-header "reply-to"))))
1276              ((eq elem 'date)
1277               (let ((date (message-fetch-field "date")))
1278                 (when (and date
1279                            (< (days-between (current-time-string) date)
1280                               4))
1281                   (gnus-article-hide-header "date"))))
1282              ((eq elem 'long-to)
1283               (let ((to (message-fetch-field "to"))
1284                     (cc (message-fetch-field "cc")))
1285                 (when (> (length to) 1024)
1286                   (gnus-article-hide-header "to"))
1287                 (when (> (length cc) 1024)
1288                   (gnus-article-hide-header "cc"))))
1289              ((eq elem 'many-to)
1290               (let ((to-count 0)
1291                     (cc-count 0))
1292                 (goto-char (point-min))
1293                 (while (re-search-forward "^to:" nil t)
1294                   (setq to-count (1+ to-count)))
1295                 (when (> to-count 1)
1296                   (while (> to-count 0)
1297                     (goto-char (point-min))
1298                     (save-restriction
1299                       (re-search-forward "^to:" nil nil to-count)
1300                       (forward-line -1)
1301                       (narrow-to-region (point) (point-max))
1302                       (gnus-article-hide-header "to"))
1303                     (setq to-count (1- to-count))))
1304                 (goto-char (point-min))
1305                 (while (re-search-forward "^cc:" nil t)
1306                   (setq cc-count (1+ cc-count)))
1307                 (when (> cc-count 1)
1308                   (while (> cc-count 0)
1309                     (goto-char (point-min))
1310                     (save-restriction
1311                       (re-search-forward "^cc:" nil nil cc-count)
1312                       (forward-line -1)
1313                       (narrow-to-region (point) (point-max))
1314                       (gnus-article-hide-header "cc"))
1315                     (setq cc-count (1- cc-count)))))))))))))
1316
1317 (defun gnus-article-hide-header (header)
1318   (save-excursion
1319     (goto-char (point-min))
1320     (when (re-search-forward (concat "^" header ":") nil t)
1321       (gnus-article-hide-text-type
1322        (progn (beginning-of-line) (point))
1323        (progn
1324          (end-of-line)
1325          (if (re-search-forward "^[^ \t]" nil t)
1326              (match-beginning 0)
1327            (point-max)))
1328        'boring-headers))))
1329
1330 (defun article-toggle-headers (&optional arg)
1331   "Toggle hiding of headers.  If given a negative prefix, always show;
1332 if given a positive prefix, always hide."
1333   (interactive (gnus-article-hidden-arg))
1334   (let ((force (when (numberp arg)
1335                  (cond ((> arg 0) 'always-hide)
1336                        ((< arg 0) 'always-show))))
1337         (window (get-buffer-window gnus-article-buffer))
1338         (header-end (point-min))
1339         header-start field-end field-start
1340         (inhibit-point-motion-hooks t)
1341         (inhibit-read-only t)
1342         buffer-read-only)
1343     (save-restriction
1344       (widen)
1345       (while (and (setq header-start
1346                         (text-property-any header-end (point-max)
1347                                            'article-treated-header t))
1348                   (setq header-end
1349                         (text-property-not-all header-start (point-max)
1350                                                'article-treated-header t)))
1351         (setq field-end header-start)
1352         (cond
1353          (;; Hide exposed invisible fields.
1354           (and (not (eq 'always-show force))
1355                (setq field-start
1356                      (text-property-any field-end header-end
1357                                         'exposed-invisible-field t)))
1358           (while (and field-start
1359                       (setq field-end (text-property-not-all
1360                                        field-start header-end
1361                                        'exposed-invisible-field t)))
1362             (add-text-properties field-start field-end gnus-hidden-properties)
1363             (setq field-start (text-property-any field-end header-end
1364                                                  'exposed-invisible-field t)))
1365           (put-text-property header-start header-end
1366                              'exposed-invisible-field nil))
1367          (;; Expose invisible fields.
1368           (and (not (eq 'always-hide force))
1369                (setq field-start
1370                      (text-property-any field-end header-end 'invisible t)))
1371           (while (and field-start
1372                       (setq field-end (text-property-not-all
1373                                        field-start header-end
1374                                        'invisible t)))
1375             ;; If the invisible text is not terminated with newline, we
1376             ;; won't expose it.  Because it may be created by x-face-mule.
1377             ;; BTW, XEmacs sometimes fail in putting a invisible text
1378             ;; property with `gnus-article-hide-text' (really?).  In that
1379             ;; case, the invisible text might be started from the middle of
1380             ;; a line so we will expose the sort of thing.
1381             (when (or (not (or (eq header-start field-start)
1382                                (eq ?\n (char-before field-start))))
1383                       (eq ?\n (char-before field-end)))
1384               (remove-text-properties field-start field-end
1385                                       gnus-hidden-properties)
1386               (put-text-property field-start field-end
1387                                  'exposed-invisible-field t))
1388             (setq field-start (text-property-any field-end header-end
1389                                                  'invisible t))))
1390          (;; Hide fields.
1391           (not (eq 'always-show force))
1392           (narrow-to-region header-start header-end)
1393           (article-hide-headers)
1394           ;; Re-display X-Face image under XEmacs.
1395           (when (and (featurep 'xemacs)
1396                      (gnus-functionp gnus-article-x-face-command))
1397             (let ((func (cadr (assq 'gnus-treat-display-xface
1398                                     gnus-treatment-function-alist)))
1399                   (condition 'head))
1400               (when (and (not gnus-inhibit-treatment)
1401                          func
1402                          (gnus-treat-predicate gnus-treat-display-xface))
1403                 (funcall func)
1404                 (put-text-property header-start header-end 'read-only nil))))
1405           (widen))
1406          ))
1407       (goto-char (point-min))
1408       (when window
1409         (set-window-start window (point-min))))))
1410
1411 (defvar gnus-article-normalized-header-length 40
1412   "Length of normalized headers.")
1413
1414 (defun article-normalize-headers ()
1415   "Make all header lines 40 characters long."
1416   (interactive)
1417   (let ((buffer-read-only nil)
1418         column)
1419     (save-excursion
1420       (save-restriction
1421         (article-narrow-to-head)
1422         (while (not (eobp))
1423           (cond
1424            ((< (setq column (- (gnus-point-at-eol) (point)))
1425                gnus-article-normalized-header-length)
1426             (end-of-line)
1427             (insert (make-string
1428                      (- gnus-article-normalized-header-length column)
1429                      ? )))
1430            ((> column gnus-article-normalized-header-length)
1431             (gnus-put-text-property
1432              (progn
1433                (forward-char gnus-article-normalized-header-length)
1434                (point))
1435              (gnus-point-at-eol)
1436              'invisible t))
1437            (t
1438             ;; Do nothing.
1439             ))
1440           (forward-line 1))))))
1441
1442 (defun article-treat-dumbquotes ()
1443   "Translate M******** sm*rtq**t*s into proper text.
1444 Note that this function guesses whether a character is a sm*rtq**t* or
1445 not, so it should only be used interactively."
1446   (interactive)
1447   (article-translate-strings gnus-article-dumbquotes-map))
1448
1449 (defun article-translate-characters (from to)
1450   "Translate all characters in the body of the article according to FROM and TO.
1451 FROM is a string of characters to translate from; to is a string of
1452 characters to translate to."
1453   (save-excursion
1454     (when (article-goto-body)
1455       (let ((buffer-read-only nil)
1456             (x (make-string 225 ?x))
1457             (i -1))
1458         (while (< (incf i) (length x))
1459           (aset x i i))
1460         (setq i 0)
1461         (while (< i (length from))
1462           (aset x (aref from i) (aref to i))
1463           (incf i))
1464         (translate-region (point) (point-max) x)))))
1465
1466 (defun article-translate-strings (map)
1467   "Translate all string in the body of the article according to MAP.
1468 MAP is an alist where the elements are on the form (\"from\" \"to\")."
1469   (save-excursion
1470     (when (article-goto-body)
1471       (let ((buffer-read-only nil)
1472             elem)
1473         (while (setq elem (pop map))
1474           (save-excursion
1475             (while (search-forward (car elem) nil t)
1476               (replace-match (cadr elem)))))))))
1477
1478 (defun article-treat-overstrike ()
1479   "Translate overstrikes into bold text."
1480   (interactive)
1481   (save-excursion
1482     (when (article-goto-body)
1483       (let ((buffer-read-only nil))
1484         (while (search-forward "\b" nil t)
1485           (let ((next (char-after))
1486                 start end previous)
1487             (backward-char 2)
1488             (setq start (point)
1489                   previous (char-after))
1490             (forward-char 3)
1491             (setq end (point))
1492             (backward-char)
1493             ;; We do the boldification/underlining by hiding the
1494             ;; overstrikes and putting the proper text property
1495             ;; on the letters.
1496             (cond
1497              ((eq next previous)
1498               (gnus-article-hide-text-type start (point) 'overstrike)
1499               (put-text-property (point) end 'face 'bold))
1500              ((eq next ?_)
1501               (gnus-article-hide-text-type
1502                (1- (point)) (1+ (point)) 'overstrike)
1503               (put-text-property
1504                start (1- (point)) 'face 'underline))
1505              ((eq previous ?_)
1506               (gnus-article-hide-text-type start (point) 'overstrike)
1507               (put-text-property
1508                (point) end 'face 'underline)))))))))
1509
1510 (defun article-fill-long-lines ()
1511   "Fill lines that are wider than the window width."
1512   (interactive)
1513   (save-excursion
1514     (let ((buffer-read-only nil)
1515           (width (window-width (get-buffer-window (current-buffer)))))
1516       (save-restriction
1517         (article-goto-body)
1518         (let ((adaptive-fill-mode nil))
1519           (while (not (eobp))
1520             (end-of-line)
1521             (when (>= (current-column) (min fill-column width))
1522               (narrow-to-region (point) (gnus-point-at-bol))
1523               (fill-paragraph nil)
1524               (goto-char (point-max))
1525               (widen))
1526             (forward-line 1)))))))
1527
1528 (defun article-capitalize-sentences ()
1529   "Capitalize the first word in each sentence."
1530   (interactive)
1531   (save-excursion
1532     (let ((buffer-read-only nil)
1533           (paragraph-start "^[\n\^L]"))
1534       (article-goto-body)
1535       (while (not (eobp))
1536         (capitalize-word 1)
1537         (forward-sentence)))))
1538
1539 (defun article-remove-cr ()
1540   "Remove trailing CRs and then translate remaining CRs into LFs."
1541   (interactive)
1542   (save-excursion
1543     (let ((buffer-read-only nil))
1544       (goto-char (point-min))
1545       (while (re-search-forward "\r+$" nil t)
1546         (replace-match "" t t))
1547       (goto-char (point-min))
1548       (while (search-forward "\r" nil t)
1549         (replace-match "\n" t t)))))
1550
1551 (defun article-remove-trailing-blank-lines ()
1552   "Remove all trailing blank lines from the article."
1553   (interactive)
1554   (save-excursion
1555     (let ((buffer-read-only nil))
1556       (goto-char (point-max))
1557       (delete-region
1558        (point)
1559        (progn
1560          (while (and (not (bobp))
1561                      (looking-at "^[ \t]*$")
1562                      (not (gnus-annotation-in-region-p
1563                            (point) (gnus-point-at-eol))))
1564            (forward-line -1))
1565          (forward-line 1)
1566          (point))))))
1567
1568 (defun article-display-x-face (&optional force)
1569   "Look for an X-Face header and display it if present."
1570   (interactive (list 'force))
1571   (save-excursion
1572     ;; Delete the old process, if any.
1573     (when (process-status "article-x-face")
1574       (delete-process "article-x-face"))
1575     (let ((inhibit-point-motion-hooks t)
1576           (case-fold-search t)
1577           from last)
1578       (save-restriction
1579         (article-narrow-to-head)
1580         (goto-char (point-min))
1581         (setq from (message-fetch-field "from"))
1582         (goto-char (point-min))
1583         (while (and gnus-article-x-face-command
1584                     (not last)
1585                     (or force
1586                         ;; Check whether this face is censored.
1587                         (not gnus-article-x-face-too-ugly)
1588                         (and gnus-article-x-face-too-ugly from
1589                              (not (string-match gnus-article-x-face-too-ugly
1590                                                 from))))
1591                     ;; Has to be present.
1592                     (re-search-forward "^X-Face:[ \t]*" nil t))
1593           ;; This used to try to do multiple faces (`while' instead of
1594           ;; `when' above), but (a) sending multiple EOFs to xv doesn't
1595           ;; work (b) it can crash some versions of Emacs (c) are
1596           ;; multiple faces really something to encourage?
1597           (when (stringp gnus-article-x-face-command)
1598             (setq last t))
1599           ;; We now have the area of the buffer where the X-Face is stored.
1600           (save-excursion
1601             (let ((beg (point))
1602                   (end (1- (re-search-forward "^\\($\\|[^ \t]\\)" nil t))))
1603               ;; We display the face.
1604               (if (symbolp gnus-article-x-face-command)
1605                   ;; The command is a lisp function, so we call it.
1606                   (if (gnus-functionp gnus-article-x-face-command)
1607                       (funcall gnus-article-x-face-command beg end)
1608                     (error "%s is not a function" gnus-article-x-face-command))
1609                 ;; The command is a string, so we interpret the command
1610                 ;; as a, well, command, and fork it off.
1611                 (let ((process-connection-type nil))
1612                   (process-kill-without-query
1613                    (start-process
1614                     "article-x-face" nil shell-file-name shell-command-switch
1615                     gnus-article-x-face-command))
1616                   (process-send-region "article-x-face" beg end)
1617                   (process-send-eof "article-x-face"))))))))))
1618
1619 (defun article-decode-mime-words ()
1620   "Decode all MIME-encoded words in the article."
1621   (interactive)
1622   (save-excursion
1623     (set-buffer gnus-article-buffer)
1624     (let ((inhibit-point-motion-hooks t)
1625           buffer-read-only
1626           (mail-parse-charset gnus-newsgroup-charset)
1627           (mail-parse-ignored-charsets 
1628            (save-excursion (set-buffer gnus-summary-buffer)
1629                            gnus-newsgroup-ignored-charsets)))
1630       (mail-decode-encoded-word-region (point-min) (point-max)))))
1631
1632 (defun article-decode-charset (&optional prompt)
1633   "Decode charset-encoded text in the article.
1634 If PROMPT (the prefix), prompt for a coding system to use."
1635   (interactive "P")
1636   (let ((inhibit-point-motion-hooks t) (case-fold-search t)
1637         buffer-read-only
1638         (mail-parse-charset gnus-newsgroup-charset)
1639         (mail-parse-ignored-charsets 
1640          (save-excursion (condition-case nil
1641                              (set-buffer gnus-summary-buffer)
1642                            (error))
1643                          gnus-newsgroup-ignored-charsets))
1644         ct cte ctl charset format)
1645   (save-excursion
1646     (save-restriction
1647       (article-narrow-to-head)
1648       (setq ct (message-fetch-field "Content-Type" t)
1649             cte (message-fetch-field "Content-Transfer-Encoding" t)
1650             ctl (and ct (ignore-errors
1651                           (mail-header-parse-content-type ct)))
1652             charset (cond
1653                      (prompt
1654                       (mm-read-coding-system "Charset to decode: "))
1655                      (ctl
1656                       (mail-content-type-get ctl 'charset)))
1657             format (and ctl (mail-content-type-get ctl 'format)))
1658       (when cte
1659         (setq cte (mail-header-strip cte)))
1660       (if (and ctl (not (string-match "/" (car ctl)))) 
1661           (setq ctl nil))
1662       (goto-char (point-max)))
1663     (forward-line 1)
1664     (save-restriction
1665       (narrow-to-region (point) (point-max))
1666       (when (and (eq mail-parse-charset 'gnus-decoded)
1667                  (eq (mm-body-7-or-8) '8bit))
1668         ;; The text code could have been decoded.
1669         (setq charset mail-parse-charset))
1670       (when (and (or (not ctl)
1671                      (equal (car ctl) "text/plain"))
1672                  (not format)) ;; article with format will decode later.
1673         (mm-decode-body
1674          charset (and cte (intern (downcase
1675                                    (gnus-strip-whitespace cte))))
1676          (car ctl)))))))
1677
1678 (defun article-decode-encoded-words ()
1679   "Remove encoded-word encoding from headers."
1680   (let (buffer-read-only)
1681     (let ((charset (save-excursion
1682                      (set-buffer gnus-summary-buffer)
1683                      default-mime-charset)))
1684       (mime-decode-header-in-buffer charset)
1685       )))
1686
1687 (defun article-de-quoted-unreadable (&optional force)
1688   "Translate a quoted-printable-encoded article.
1689 If FORCE, decode the article whether it is marked as quoted-printable
1690 or not."
1691   (interactive (list 'force))
1692   (save-excursion
1693     (let ((buffer-read-only nil) type charset)
1694       (if (gnus-buffer-live-p gnus-original-article-buffer)
1695           (with-current-buffer gnus-original-article-buffer
1696             (setq type
1697                   (gnus-fetch-field "content-transfer-encoding"))
1698             (let* ((ct (gnus-fetch-field "content-type"))
1699                    (ctl (and ct 
1700                              (ignore-errors
1701                                (mail-header-parse-content-type ct)))))
1702               (setq charset (and ctl
1703                                  (mail-content-type-get ctl 'charset)))
1704               (if (stringp charset)
1705                   (setq charset (intern (downcase charset)))))))
1706       (unless charset 
1707         (setq charset gnus-newsgroup-charset))
1708       (when (or force
1709                 (and type (let ((case-fold-search t))
1710                             (string-match "quoted-printable" type))))
1711         (article-goto-body)
1712         (quoted-printable-decode-region
1713          (point) (point-max) (mm-charset-to-coding-system charset))))))
1714
1715 (defun article-de-base64-unreadable (&optional force)
1716   "Translate a base64 article.
1717 If FORCE, decode the article whether it is marked as base64 not."
1718   (interactive (list 'force))
1719   (save-excursion
1720     (let ((buffer-read-only nil) type charset)
1721       (if (gnus-buffer-live-p gnus-original-article-buffer)
1722           (with-current-buffer gnus-original-article-buffer
1723             (setq type
1724                   (gnus-fetch-field "content-transfer-encoding"))
1725             (let* ((ct (gnus-fetch-field "content-type"))
1726                    (ctl (and ct 
1727                              (ignore-errors
1728                                (mail-header-parse-content-type ct)))))
1729               (setq charset (and ctl
1730                                  (mail-content-type-get ctl 'charset)))
1731               (if (stringp charset)
1732                   (setq charset (intern (downcase charset)))))))
1733       (unless charset 
1734         (setq charset gnus-newsgroup-charset))
1735       (when (or force
1736                 (and type (let ((case-fold-search t))
1737                             (string-match "base64" type))))
1738         (article-goto-body)
1739         (save-restriction
1740           (narrow-to-region (point) (point-max))
1741           (base64-decode-region (point-min) (point-max))
1742           (mm-decode-coding-region
1743            (point-min) (point-max) (mm-charset-to-coding-system charset)))))))
1744
1745 (eval-when-compile
1746   (require 'rfc1843))
1747
1748 (defun article-decode-HZ ()
1749   "Translate a HZ-encoded article."
1750   (interactive)
1751   (require 'rfc1843)
1752   (save-excursion
1753     (let ((buffer-read-only nil))
1754       (rfc1843-decode-region (point-min) (point-max)))))
1755
1756 (defun article-wash-html ()
1757   "Format an html article."
1758   (interactive)
1759   (save-excursion
1760     (let ((buffer-read-only nil)
1761           charset)
1762       (if (gnus-buffer-live-p gnus-original-article-buffer)
1763           (with-current-buffer gnus-original-article-buffer
1764             (let* ((ct (gnus-fetch-field "content-type"))
1765                    (ctl (and ct 
1766                              (ignore-errors
1767                                (mail-header-parse-content-type ct)))))
1768               (setq charset (and ctl
1769                                  (mail-content-type-get ctl 'charset)))
1770               (if (stringp charset)
1771                   (setq charset (intern (downcase charset)))))))
1772       (unless charset 
1773         (setq charset gnus-newsgroup-charset))
1774       (article-goto-body)
1775       (save-window-excursion
1776         (save-restriction
1777           (narrow-to-region (point) (point-max))
1778           (mm-setup-w3)
1779           (let ((w3-strict-width (window-width))
1780                 (url-standalone-mode t))
1781             (condition-case var
1782                 (w3-region (point-min) (point-max))
1783               (error))))))))
1784
1785 (defun article-hide-list-identifiers ()
1786   "Remove list identifies from the Subject header.
1787 The `gnus-list-identifiers' variable specifies what to do."
1788   (interactive)
1789   (save-excursion
1790     (save-restriction
1791       (let ((inhibit-point-motion-hooks t)
1792             buffer-read-only)
1793         (article-narrow-to-head)
1794         (let ((regexp (if (stringp gnus-list-identifiers) gnus-list-identifiers
1795                         (mapconcat 'identity gnus-list-identifiers " *\\|"))))
1796           (when regexp
1797             (goto-char (point-min))
1798             (when (re-search-forward
1799                    (concat "^Subject: +\\(\\(\\(Re: +\\)?\\(" regexp 
1800                            " *\\)\\)+\\(Re: +\\)?\\)")
1801                    nil t)
1802               (let ((s (or (match-string 3) (match-string 5))))
1803                 (delete-region (match-beginning 1) (match-end 1))
1804                 (when s
1805                   (goto-char (match-beginning 1))
1806                   (insert s))))))))))
1807
1808 (defun article-hide-pgp ()
1809   "Remove any PGP headers and signatures in the current article."
1810   (interactive)
1811   (save-excursion
1812     (save-restriction
1813       (let ((inhibit-point-motion-hooks t)
1814             buffer-read-only beg end)
1815         (article-goto-body)
1816         ;; Hide the "header".
1817         (when (re-search-forward "^-----BEGIN PGP SIGNED MESSAGE-----\n" nil t)
1818           (push 'pgp gnus-article-wash-types)
1819           (delete-region (match-beginning 0) (match-end 0))
1820           ;; Remove armor headers (rfc2440 6.2)
1821           (delete-region (point) (or (re-search-forward "^[ \t]*\n" nil t)
1822                                      (point)))
1823           (setq beg (point))
1824           ;; Hide the actual signature.
1825           (and (search-forward "\n-----BEGIN PGP SIGNATURE-----\n" nil t)
1826                (setq end (1+ (match-beginning 0)))
1827                (delete-region
1828                 end
1829                 (if (search-forward "\n-----END PGP SIGNATURE-----\n" nil t)
1830                     (match-end 0)
1831                   ;; Perhaps we shouldn't hide to the end of the buffer
1832                   ;; if there is no end to the signature?
1833                   (point-max))))
1834           ;; Hide "- " PGP quotation markers.
1835           (when (and beg end)
1836             (narrow-to-region beg end)
1837             (goto-char (point-min))
1838             (while (re-search-forward "^- " nil t)
1839               (delete-region
1840                (match-beginning 0) (match-end 0)))
1841             (widen))
1842           (gnus-run-hooks 'gnus-article-hide-pgp-hook))))))
1843
1844 (defun article-hide-pem (&optional arg)
1845   "Toggle hiding of any PEM headers and signatures in the current article.
1846 If given a negative prefix, always show; if given a positive prefix,
1847 always hide."
1848   (interactive (gnus-article-hidden-arg))
1849   (unless (gnus-article-check-hidden-text 'pem arg)
1850     (save-excursion
1851       (let (buffer-read-only end)
1852         (goto-char (point-min))
1853         ;; Hide the horrendously ugly "header".
1854         (when (and (search-forward
1855                     "\n-----BEGIN PRIVACY-ENHANCED MESSAGE-----\n"
1856                     nil t)
1857                    (setq end (1+ (match-beginning 0))))
1858           (push 'pem gnus-article-wash-types)
1859           (gnus-article-hide-text-type
1860            end
1861            (if (search-forward "\n\n" nil t)
1862                (match-end 0)
1863              (point-max))
1864            'pem)
1865           ;; Hide the trailer as well
1866           (when (search-forward "\n-----END PRIVACY-ENHANCED MESSAGE-----\n"
1867                                 nil t)
1868             (gnus-article-hide-text-type
1869              (match-beginning 0) (match-end 0) 'pem)))))))
1870
1871 (defun article-strip-banner ()
1872   "Strip the banner specified by the `banner' group parameter."
1873   (interactive)
1874   (save-excursion
1875     (save-restriction
1876       (let ((inhibit-point-motion-hooks t)
1877             (banner (gnus-group-find-parameter gnus-newsgroup-name 'banner))
1878             (gnus-signature-limit nil)
1879             buffer-read-only beg end)
1880         (when banner
1881           (article-goto-body)
1882           (cond
1883            ((eq banner 'signature)
1884             (when (gnus-article-narrow-to-signature)
1885               (widen)
1886               (forward-line -1)
1887               (delete-region (point) (point-max))))
1888            ((symbolp banner)
1889             (if (setq banner (cdr (assq banner gnus-article-banner-alist)))
1890                 (while (re-search-forward banner nil t)
1891                   (delete-region (match-beginning 0) (match-end 0)))))
1892            ((stringp banner)
1893             (while (re-search-forward banner nil t)
1894               (delete-region (match-beginning 0) (match-end 0))))))))))
1895
1896 (defun article-babel ()
1897   "Translate article using an online translation service."
1898   (interactive)
1899   (require 'babel)
1900   (save-excursion
1901     (set-buffer gnus-article-buffer)
1902     (when (article-goto-body)
1903       (let* ((buffer-read-only nil)
1904              (start (point))
1905              (end (point-max))
1906              (orig (buffer-substring start end))
1907              (trans (babel-as-string orig)))
1908         (save-restriction
1909           (narrow-to-region start end)
1910           (delete-region start end)
1911           (insert trans))))))
1912
1913 (defun article-hide-signature (&optional arg)
1914   "Hide the signature in the current article.
1915 If given a negative prefix, always show; if given a positive prefix,
1916 always hide."
1917   (interactive (gnus-article-hidden-arg))
1918   (save-excursion
1919     (save-restriction
1920       (if (interactive-p)
1921           (progn
1922             (widen)
1923             (article-goto-body))
1924         (goto-char (point-min)))
1925       (unless (gnus-article-check-hidden-text 'signature arg)
1926         (let ((buffer-read-only nil)
1927               (button (point)))
1928           (while (setq button (text-property-any button (point-max)
1929                                                  'gnus-callback
1930                                                  'gnus-signature-toggle))
1931             (setq button (text-property-not-all button (point-max)
1932                                                 'gnus-callback
1933                                                 'gnus-signature-toggle))
1934             (when (and button (not (eobp)))
1935               (gnus-article-hide-text-type
1936                (1+ button)
1937                (next-single-property-change (1+ button) 'mime-view-entity
1938                                             nil (point-max))
1939                'signature))))))))
1940
1941 (defun article-strip-headers-in-body ()
1942   "Strip offensive headers from bodies."
1943   (interactive)
1944   (save-excursion
1945     (article-goto-body)
1946     (let ((case-fold-search t))
1947       (when (looking-at "x-no-archive:")
1948         (gnus-delete-line)))))
1949
1950 (defun article-strip-leading-blank-lines ()
1951   "Remove all blank lines from the beginning of the article."
1952   (interactive)
1953   (save-excursion
1954     (let ((inhibit-point-motion-hooks t)
1955           buffer-read-only)
1956       (when (article-goto-body)
1957         (while (and (not (eobp))
1958                     (looking-at "[ \t]*$"))
1959           (gnus-delete-line))))))
1960
1961 (defun article-narrow-to-head ()
1962   "Narrow the buffer to the head of the message.
1963 Point is left at the beginning of the narrowed-to region."
1964   (narrow-to-region
1965    (goto-char (point-min))
1966    (if (search-forward "\n\n" nil 1)
1967        (1- (point))
1968      (point-max)))
1969   (goto-char (point-min)))
1970
1971 (defun article-goto-body ()
1972   "Place point at the start of the body."
1973   (goto-char (point-min))
1974   (cond
1975    ;; This variable is only bound when dealing with separate
1976    ;; MIME body parts.
1977    (article-goto-body-goes-to-point-min-p
1978     t)
1979    ((search-forward "\n\n" nil t)
1980     t)
1981    (t
1982     (goto-char (point-max))
1983     nil)))
1984
1985 (defun article-strip-multiple-blank-lines ()
1986   "Replace consecutive blank lines with one empty line."
1987   (interactive)
1988   (save-excursion
1989     (let ((inhibit-point-motion-hooks t)
1990           buffer-read-only)
1991       ;; First make all blank lines empty.
1992       (article-goto-body)
1993       (while (re-search-forward "^[ \t]+$" nil t)
1994         (unless (gnus-annotation-in-region-p
1995                  (match-beginning 0) (match-end 0))
1996           (replace-match "" nil t)))
1997       ;; Then replace multiple empty lines with a single empty line.
1998       (article-goto-body)
1999       (while (re-search-forward "\n\n\n+" nil t)
2000         (unless (gnus-annotation-in-region-p
2001                  (match-beginning 0) (match-end 0))
2002           (replace-match "\n\n" t t))))))
2003
2004 (defun article-strip-leading-space ()
2005   "Remove all white space from the beginning of the lines in the article."
2006   (interactive)
2007   (save-excursion
2008     (let ((inhibit-point-motion-hooks t)
2009           buffer-read-only)
2010       (article-goto-body)
2011       (while (re-search-forward "^[ \t]+" nil t)
2012         (replace-match "" t t)))))
2013
2014 (defun article-strip-trailing-space ()
2015   "Remove all white space from the end of the lines in the article."
2016   (interactive)
2017   (save-excursion
2018     (let ((inhibit-point-motion-hooks t)
2019           buffer-read-only)
2020       (article-goto-body)
2021       (while (re-search-forward "[ \t]+$" nil t)
2022         (replace-match "" t t)))))
2023
2024 (defun article-strip-blank-lines ()
2025   "Strip leading, trailing and multiple blank lines."
2026   (interactive)
2027   (article-strip-leading-blank-lines)
2028   (article-remove-trailing-blank-lines)
2029   (article-strip-multiple-blank-lines))
2030
2031 (defun article-strip-all-blank-lines ()
2032   "Strip all blank lines."
2033   (interactive)
2034   (save-excursion
2035     (let ((inhibit-point-motion-hooks t)
2036           buffer-read-only)
2037       (article-goto-body)
2038       (while (re-search-forward "^[ \t]*\n" nil t)
2039         (replace-match "" t t)))))
2040
2041 (defun gnus-article-narrow-to-signature ()
2042   "Narrow to the signature; return t if a signature is found, else nil."
2043   (let ((inhibit-point-motion-hooks t))
2044     (when (gnus-article-search-signature)
2045       (forward-line 1)
2046       ;; Check whether we have some limits to what we consider
2047       ;; to be a signature.
2048       (let ((limits (if (listp gnus-signature-limit) gnus-signature-limit
2049                       (list gnus-signature-limit)))
2050             limit limited)
2051         (while (setq limit (pop limits))
2052           (if (or (and (integerp limit)
2053                        (< (- (point-max) (point)) limit))
2054                   (and (floatp limit)
2055                        (< (count-lines (point) (point-max)) limit))
2056                   (and (gnus-functionp limit)
2057                        (funcall limit))
2058                   (and (stringp limit)
2059                        (not (re-search-forward limit nil t))))
2060               ()                        ; This limit did not succeed.
2061             (setq limited t
2062                   limits nil)))
2063         (unless limited
2064           (narrow-to-region (point) (point-max))
2065           t)))))
2066
2067 (defun gnus-article-search-signature ()
2068   "Search the current buffer for the signature separator.
2069 Put point at the beginning of the signature separator."
2070   (let ((cur (point)))
2071     (goto-char (point-max))
2072     (if (if (stringp gnus-signature-separator)
2073             (re-search-backward gnus-signature-separator nil t)
2074           (let ((seps gnus-signature-separator))
2075             (while (and seps
2076                         (not (re-search-backward (car seps) nil t)))
2077               (pop seps))
2078             seps))
2079         t
2080       (goto-char cur)
2081       nil)))
2082
2083 (defun gnus-article-hidden-arg ()
2084   "Return the current prefix arg as a number, or 0 if no prefix."
2085   (list (if current-prefix-arg
2086             (prefix-numeric-value current-prefix-arg)
2087           0)))
2088
2089 (defun gnus-article-check-hidden-text (type arg)
2090   "Return nil if hiding is necessary.
2091 Arg can be nil or a number.  Nil and positive means hide, negative
2092 means show, 0 means toggle."
2093   (save-excursion
2094     (save-restriction
2095       (let ((hide (gnus-article-hidden-text-p type)))
2096         (cond
2097          ((or (null arg)
2098               (> arg 0))
2099           nil)
2100          ((< arg 0)
2101           (gnus-article-show-hidden-text type))
2102          (t
2103           (if (eq hide 'hidden)
2104               (gnus-article-show-hidden-text type)
2105             nil)))))))
2106
2107 (defun gnus-article-hidden-text-p (type)
2108   "Say whether the current buffer contains hidden text of type TYPE."
2109   (let ((pos (text-property-any (point-min) (point-max) 'article-type type)))
2110     (while (and pos
2111                 (not (get-text-property pos 'invisible))
2112                 (not (get-text-property pos 'dummy-invisible)))
2113       (setq pos
2114             (text-property-any (1+ pos) (point-max) 'article-type type)))
2115     (if pos
2116         'hidden
2117       nil)))
2118
2119 (defun gnus-article-show-hidden-text (type &optional hide)
2120   "Show all hidden text of type TYPE.
2121 If HIDE, hide the text instead."
2122   (save-excursion
2123     (let ((buffer-read-only nil)
2124           (inhibit-point-motion-hooks t)
2125           (end (point-min))
2126           beg)
2127       (while (setq beg (text-property-any end (point-max) 'article-type type))
2128         (goto-char beg)
2129         (setq end (or
2130                    (text-property-not-all beg (point-max) 'article-type type)
2131                    (point-max)))
2132         (if hide
2133             (gnus-article-hide-text beg end gnus-hidden-properties)
2134           (gnus-article-unhide-text beg end))
2135         (goto-char end))
2136       t)))
2137
2138 (defconst article-time-units
2139   `((year . ,(* 365.25 24 60 60))
2140     (week . ,(* 7 24 60 60))
2141     (day . ,(* 24 60 60))
2142     (hour . ,(* 60 60))
2143     (minute . 60)
2144     (second . 1))
2145   "Mapping from time units to seconds.")
2146
2147 (defun article-date-ut (&optional type highlight header)
2148   "Convert DATE date to universal time in the current article.
2149 If TYPE is `local', convert to local time; if it is `lapsed', output
2150 how much time has lapsed since DATE.  For `lapsed', the value of
2151 `gnus-article-date-lapsed-new-header' says whether the \"X-Sent:\" header
2152 should replace the \"Date:\" one, or should be added below it."
2153   (interactive (list 'ut t))
2154   (let* ((header (or header
2155                      (and (eq 1 (point-min))
2156                           (mail-header-date (save-excursion
2157                                               (set-buffer gnus-summary-buffer)
2158                                               gnus-current-headers)))
2159                      (message-fetch-field "date")
2160                      ""))
2161          (date (if (vectorp header) (mail-header-date header)
2162                  header))
2163          (inhibit-point-motion-hooks t)
2164          bface eface date-pos)
2165     (when (and date (not (string= date "")))
2166       (save-excursion
2167         (save-restriction
2168           (article-narrow-to-head)
2169           (when (or (and (eq type 'lapsed)
2170                          gnus-article-date-lapsed-new-header
2171                          ;; Attempt to get the face of X-Sent first.
2172                          (re-search-forward "^X-Sent:[ \t]" nil t))
2173                     (re-search-forward "^Date:[ \t]" nil t)
2174                     ;; If Date is missing, try again for X-Sent.
2175                     (re-search-forward "^X-Sent:[ \t]" nil t))
2176             (setq bface (get-text-property (gnus-point-at-bol) 'face)
2177                   date (or (get-text-property (gnus-point-at-bol)
2178                                                 'original-date)
2179                              date)
2180                   eface (get-text-property (1- (gnus-point-at-eol))
2181                                            'face)))
2182           (let ((buffer-read-only nil))
2183             ;; Delete any old X-Sent headers.
2184             (when (setq date-pos
2185                         (text-property-any (point-min) (point-max)
2186                                            'article-date-lapsed t))
2187               (goto-char (setq date-pos (set-marker (make-marker) date-pos)))
2188               (delete-region (match-beginning 0)
2189                              (progn (forward-line 1) (point))))
2190             (goto-char (point-min))
2191             ;; Delete any old Date headers.
2192             (while (re-search-forward "^Date:[ \t]" nil t)
2193               (unless date-pos
2194                 (setq date-pos (match-beginning 0)))
2195               (unless (and (eq type 'lapsed)
2196                            gnus-article-date-lapsed-new-header)
2197                 (delete-region (match-beginning 0)
2198                                (progn (message-next-header) (point)))))
2199             (if date-pos
2200                 (progn
2201                   (goto-char date-pos)
2202                   (unless (bolp)
2203                     ;; Possibly, Date has been deleted.
2204                     (insert "\n"))
2205                   (when (and (eq type 'lapsed)
2206                              gnus-article-date-lapsed-new-header
2207                              (looking-at "Date:"))
2208                     (forward-line 1)))
2209               (goto-char (point-min)))
2210             (insert (article-make-date-line date type))
2211             (when (eq type 'lapsed)
2212               (put-text-property (gnus-point-at-bol) (point)
2213                                  'article-date-lapsed t))
2214             (insert "\n")
2215             (forward-line -1)
2216             ;; Do highlighting.
2217             (when (looking-at "\\([^:]+\\): *\\(.*\\)$")
2218               (put-text-property (match-beginning 1) (1+ (match-end 1))
2219                                  'original-date date)
2220               (put-text-property (match-beginning 1) (1+ (match-end 1))
2221                                  'face bface)
2222               (put-text-property (match-beginning 2) (match-end 2)
2223                                  'face eface))))))))
2224
2225 (defun article-make-date-line (date type)
2226   "Return a DATE line of TYPE."
2227   (let ((time (condition-case ()
2228                   (date-to-time date)
2229                 (error '(0 0)))))
2230     (cond
2231      ;; Convert to the local timezone.  We have to slap a
2232      ;; `condition-case' round the calls to the timezone
2233      ;; functions since they aren't particularly resistant to
2234      ;; buggy dates.
2235      ((eq type 'local)
2236       (let ((tz (car (current-time-zone time))))
2237         (format "Date: %s %s%02d%02d" (current-time-string time)
2238                 (if (> tz 0) "+" "-") (/ (abs tz) 3600) 
2239                 (/ (% (abs tz) 3600) 60))))
2240      ;; Convert to Universal Time.
2241      ((eq type 'ut)
2242       (concat "Date: "
2243               (current-time-string
2244                (let* ((e (parse-time-string date))
2245                       (tm (apply 'encode-time e))
2246                       (ms (car tm))
2247                       (ls (- (cadr tm) (car (current-time-zone time)))))
2248                  (cond ((< ls 0) (list (1- ms) (+ ls 65536)))
2249                        ((> ls 65535) (list (1+ ms) (- ls 65536)))
2250                        (t (list ms ls)))))
2251               " UT"))
2252      ;; Get the original date from the article.
2253      ((eq type 'original)
2254       (concat "Date: " (if (string-match "\n+$" date)
2255                            (substring date 0 (match-beginning 0))
2256                          date)))
2257      ;; Let the user define the format.
2258      ((eq type 'user)
2259       (if (gnus-functionp gnus-article-time-format)
2260           (funcall gnus-article-time-format time)
2261         (concat
2262          "Date: "
2263          (format-time-string gnus-article-time-format time))))
2264      ;; ISO 8601.
2265      ((eq type 'iso8601)
2266       (let ((tz (car (current-time-zone time))))
2267         (concat
2268          "Date: "
2269          (format-time-string "%Y%m%dT%H%M%S" time)
2270          (format "%s%02d%02d"
2271                  (if (> tz 0) "+" "-") (/ (abs tz) 3600) 
2272                  (/ (% (abs tz) 3600) 60)))))
2273      ;; Do an X-Sent lapsed format.
2274      ((eq type 'lapsed)
2275       ;; If the date is seriously mangled, the timezone functions are
2276       ;; liable to bug out, so we ignore all errors.
2277       (let* ((now (current-time))
2278              (real-time (subtract-time now time))
2279              (real-sec (and real-time
2280                             (+ (* (float (car real-time)) 65536)
2281                                (cadr real-time))))
2282              (sec (and real-time (abs real-sec)))
2283              num prev)
2284         (cond
2285          ((null real-time)
2286           "X-Sent: Unknown")
2287          ((zerop sec)
2288           "X-Sent: Now")
2289          (t
2290           (concat
2291            "X-Sent: "
2292            ;; This is a bit convoluted, but basically we go
2293            ;; through the time units for years, weeks, etc,
2294            ;; and divide things to see whether that results
2295            ;; in positive answers.
2296            (mapconcat
2297             (lambda (unit)
2298               (if (zerop (setq num (ffloor (/ sec (cdr unit)))))
2299                   ;; The (remaining) seconds are too few to
2300                   ;; be divided into this time unit.
2301                   ""
2302                 ;; It's big enough, so we output it.
2303                 (setq sec (- sec (* num (cdr unit))))
2304                 (prog1
2305                     (concat (if prev ", " "") (int-to-string
2306                                                (floor num))
2307                             " " (symbol-name (car unit))
2308                             (if (> num 1) "s" ""))
2309                   (setq prev t))))
2310             article-time-units "")
2311            ;; If dates are odd, then it might appear like the
2312            ;; article was sent in the future.
2313            (if (> real-sec 0)
2314                " ago"
2315              " in the future"))))))
2316      (t
2317       (error "Unknown conversion type: %s" type)))))
2318
2319 (defun article-date-local (&optional highlight)
2320   "Convert the current article date to the local timezone."
2321   (interactive (list t))
2322   (article-date-ut 'local highlight))
2323
2324 (defun article-date-original (&optional highlight)
2325   "Convert the current article date to what it was originally.
2326 This is only useful if you have used some other date conversion
2327 function and want to see what the date was before converting."
2328   (interactive (list t))
2329   (article-date-ut 'original highlight))
2330
2331 (defun article-date-lapsed (&optional highlight)
2332   "Convert the current article date to time lapsed since it was sent."
2333   (interactive (list t))
2334   (article-date-ut 'lapsed highlight))
2335
2336 (defun article-update-date-lapsed ()
2337   "Function to be run from a timer to update the lapsed time line."
2338   (let (deactivate-mark)
2339     (save-excursion
2340       (ignore-errors
2341         (walk-windows
2342          (lambda (w)
2343            (set-buffer (window-buffer w))
2344            (when (eq major-mode 'gnus-article-mode)
2345              (goto-char (point-min))
2346              (when (re-search-forward "^X-Sent:" nil t)
2347                (article-date-lapsed t))))
2348          nil 'visible)))))
2349
2350 (defun gnus-start-date-timer (&optional n)
2351   "Start a timer to update the X-Sent header in the article buffers.
2352 The numerical prefix says how frequently (in seconds) the function
2353 is to run."
2354   (interactive "p")
2355   (unless n
2356     (setq n 1))
2357   (gnus-stop-date-timer)
2358   (setq article-lapsed-timer
2359         (nnheader-run-at-time 1 n 'article-update-date-lapsed)))
2360
2361 (defun gnus-stop-date-timer ()
2362   "Stop the X-Sent timer."
2363   (interactive)
2364   (when article-lapsed-timer
2365     (nnheader-cancel-timer article-lapsed-timer)
2366     (setq article-lapsed-timer nil)))
2367
2368 (defun article-date-user (&optional highlight)
2369   "Convert the current article date to the user-defined format.
2370 This format is defined by the `gnus-article-time-format' variable."
2371   (interactive (list t))
2372   (article-date-ut 'user highlight))
2373
2374 (defun article-date-iso8601 (&optional highlight)
2375   "Convert the current article date to ISO8601."
2376   (interactive (list t))
2377   (article-date-ut 'iso8601 highlight))
2378
2379 (defun article-show-all ()
2380   "Show all hidden text in the article buffer."
2381   (interactive)
2382   (save-excursion
2383     (widen)
2384     (let ((buffer-read-only nil))
2385       (gnus-article-unhide-text (point-min) (point-max))
2386       (gnus-remove-text-with-property 'gnus-prev)
2387       (gnus-remove-text-with-property 'gnus-next))))
2388
2389 (defun article-show-all-headers ()
2390   "Show all hidden headers in the article buffer."
2391   (interactive)
2392   (save-excursion
2393     (save-restriction
2394       (widen)
2395       (article-narrow-to-head)
2396       (let ((buffer-read-only nil))
2397         (gnus-article-unhide-text (point-min) (point-max))))))
2398
2399 (defun article-emphasize (&optional arg)
2400   "Emphasize text according to `gnus-emphasis-alist'."
2401   (interactive (gnus-article-hidden-arg))
2402   (unless (gnus-article-check-hidden-text 'emphasis arg)
2403     (save-excursion
2404       (let ((alist (or 
2405                     (condition-case nil
2406                         (with-current-buffer gnus-summary-buffer 
2407                           gnus-article-emphasis-alist) 
2408                       (error))
2409                     gnus-emphasis-alist))
2410             (buffer-read-only nil)
2411             (props (append '(article-type emphasis)
2412                            gnus-hidden-properties))
2413             regexp elem beg invisible visible face)
2414         (article-goto-body)
2415         (setq beg (point))
2416         (while (setq elem (pop alist))
2417           (goto-char beg)
2418           (setq regexp (car elem)
2419                 invisible (nth 1 elem)
2420                 visible (nth 2 elem)
2421                 face (nth 3 elem))
2422           (while (re-search-forward regexp nil t)
2423             (when (and (match-beginning visible) (match-beginning invisible))
2424               (push 'emphasis gnus-article-wash-types)
2425               (gnus-article-hide-text
2426                (match-beginning invisible) (match-end invisible) props)
2427               (gnus-article-unhide-text-type
2428                (match-beginning visible) (match-end visible) 'emphasis)
2429               (gnus-put-text-property-excluding-newlines
2430                (match-beginning visible) (match-end visible) 'face face)
2431               (goto-char (match-end invisible)))))))))
2432
2433 (defun gnus-article-setup-highlight-words (&optional highlight-words)
2434   "Setup newsgroup emphasis alist."
2435   (unless gnus-article-emphasis-alist
2436     (let ((name (and gnus-newsgroup-name
2437                      (gnus-group-real-name gnus-newsgroup-name))))
2438       (make-local-variable 'gnus-article-emphasis-alist)
2439       (setq gnus-article-emphasis-alist
2440             (nconc
2441              (let ((alist gnus-group-highlight-words-alist) elem highlight)
2442                (while (setq elem (pop alist))
2443                  (when (and name (string-match (car elem) name))
2444                    (setq alist nil
2445                          highlight (copy-sequence (cdr elem)))))
2446                highlight)
2447              (copy-sequence highlight-words)
2448              (if gnus-newsgroup-name
2449                  (copy-sequence (gnus-group-find-parameter
2450                                  gnus-newsgroup-name 'highlight-words t)))
2451              gnus-emphasis-alist)))))
2452
2453 (defvar gnus-summary-article-menu)
2454 (defvar gnus-summary-post-menu)
2455
2456 ;;; Saving functions.
2457
2458 (defun gnus-article-save (save-buffer file &optional num)
2459   "Save the currently selected article."
2460   (unless gnus-save-all-headers
2461     ;; Remove headers according to `gnus-saved-headers'.
2462     (let ((gnus-visible-headers
2463            (or gnus-saved-headers gnus-visible-headers))
2464           (gnus-article-buffer save-buffer))
2465       (save-excursion
2466         (set-buffer save-buffer)
2467         (article-hide-headers 1 t))))
2468   (save-window-excursion
2469     (if (not gnus-default-article-saver)
2470         (error "No default saver is defined")
2471       ;; !!! Magic!  The saving functions all save
2472       ;; `gnus-save-article-buffer' (or so they think), but we
2473       ;; bind that variable to our save-buffer.
2474       (set-buffer gnus-article-buffer)
2475       (let* ((gnus-save-article-buffer save-buffer)
2476              (filename
2477               (cond
2478                ((not gnus-prompt-before-saving) 'default)
2479                ((eq gnus-prompt-before-saving 'always) nil)
2480                (t file)))
2481              (gnus-number-of-articles-to-be-saved
2482               (when (eq gnus-prompt-before-saving t)
2483                 num)))                  ; Magic
2484         (set-buffer gnus-article-current-summary)
2485         (funcall gnus-default-article-saver filename)))))
2486
2487 (defun gnus-read-save-file-name (prompt &optional filename
2488                                         function group headers variable)
2489   (let ((default-name
2490           (funcall function group headers (symbol-value variable)))
2491         result)
2492     (setq result
2493         (expand-file-name
2494      (cond
2495       ((eq filename 'default)
2496        default-name)
2497       ((eq filename t)
2498        default-name)
2499       (filename filename)
2500       (t
2501        (let* ((split-name (gnus-get-split-value gnus-split-methods))
2502               (prompt
2503                (format prompt
2504                        (if (and gnus-number-of-articles-to-be-saved
2505                                 (> gnus-number-of-articles-to-be-saved 1))
2506                            (format "these %d articles"
2507                                    gnus-number-of-articles-to-be-saved)
2508                          "this article")))
2509               (file
2510                ;; Let the split methods have their say.
2511                (cond
2512                 ;; No split name was found.
2513                 ((null split-name)
2514                  (read-file-name
2515                   (concat prompt " (default "
2516                           (file-name-nondirectory default-name) ") ")
2517                   (file-name-directory default-name)
2518                   default-name))
2519                 ;; A single group name is returned.
2520                 ((stringp split-name)
2521                  (setq default-name
2522                        (funcall function split-name headers
2523                                 (symbol-value variable)))
2524                  (read-file-name
2525                   (concat prompt " (default "
2526                           (file-name-nondirectory default-name) ") ")
2527                   (file-name-directory default-name)
2528                   default-name))
2529                 ;; A single split name was found
2530                 ((= 1 (length split-name))
2531                  (let* ((name (expand-file-name
2532                                (car split-name) gnus-article-save-directory))
2533                         (dir (cond ((file-directory-p name)
2534                                     (file-name-as-directory name))
2535                                    ((file-exists-p name) name)
2536                                    (t gnus-article-save-directory))))
2537                    (read-file-name
2538                     (concat prompt " (default " name ") ")
2539                     dir name)))
2540                 ;; A list of splits was found.
2541                 (t
2542                  (setq split-name (nreverse split-name))
2543                  (let (result)
2544                    (let ((file-name-history
2545                           (nconc split-name file-name-history)))
2546                      (setq result
2547                            (expand-file-name
2548                             (read-file-name
2549                              (concat prompt " (`M-p' for defaults) ")
2550                              gnus-article-save-directory
2551                              (car split-name))
2552                             gnus-article-save-directory)))
2553                    (car (push result file-name-history)))))))
2554          ;; Create the directory.
2555          (gnus-make-directory (file-name-directory file))
2556          ;; If we have read a directory, we append the default file name.
2557          (when (file-directory-p file)
2558            (setq file (expand-file-name (file-name-nondirectory default-name)
2559                                         (file-name-as-directory file))))
2560          ;; Possibly translate some characters.
2561          (nnheader-translate-file-chars file))))))
2562     (gnus-make-directory (file-name-directory result))
2563     (set variable result)))
2564
2565 (defun gnus-article-archive-name (group)
2566   "Return the first instance of an \"Archive-name\" in the current buffer."
2567   (let ((case-fold-search t))
2568     (when (re-search-forward "archive-name: *\\([^ \n\t]+\\)[ \t]*$" nil t)
2569       (nnheader-concat gnus-article-save-directory
2570                        (match-string 1)))))
2571
2572 (defun gnus-article-nndoc-name (group)
2573   "If GROUP is an nndoc group, return the name of the parent group."
2574   (when (eq (car (gnus-find-method-for-group group)) 'nndoc)
2575     (gnus-group-get-parameter group 'save-article-group)))
2576
2577 (defun gnus-summary-save-in-rmail (&optional filename)
2578   "Append this article to Rmail file.
2579 Optional argument FILENAME specifies file name.
2580 Directory to save to is default to `gnus-article-save-directory'."
2581   (setq filename (gnus-read-save-file-name
2582                   "Save %s in rmail file:" filename
2583                   gnus-rmail-save-name gnus-newsgroup-name
2584                   gnus-current-headers 'gnus-newsgroup-last-rmail))
2585   (gnus-eval-in-buffer-window gnus-save-article-buffer
2586     (save-excursion
2587       (save-restriction
2588         (widen)
2589         (gnus-output-to-rmail filename))))
2590   filename)
2591
2592 (defun gnus-summary-save-in-mail (&optional filename)
2593   "Append this article to Unix mail file.
2594 Optional argument FILENAME specifies file name.
2595 Directory to save to is default to `gnus-article-save-directory'."
2596   (setq filename (gnus-read-save-file-name
2597                   "Save %s in Unix mail file:" filename
2598                   gnus-mail-save-name gnus-newsgroup-name
2599                   gnus-current-headers 'gnus-newsgroup-last-mail))
2600   (gnus-eval-in-buffer-window gnus-save-article-buffer
2601     (save-excursion
2602       (save-restriction
2603         (widen)
2604         (if (and (file-readable-p filename)
2605                  (mail-file-babyl-p filename))
2606             (rmail-output-to-rmail-file filename t)
2607           (gnus-output-to-mail filename)))))
2608   filename)
2609
2610 (defun gnus-summary-save-in-file (&optional filename overwrite)
2611   "Append this article to file.
2612 Optional argument FILENAME specifies file name.
2613 Directory to save to is default to `gnus-article-save-directory'."
2614   (setq filename (gnus-read-save-file-name
2615                   "Save %s in file:" filename
2616                   gnus-file-save-name gnus-newsgroup-name
2617                   gnus-current-headers 'gnus-newsgroup-last-file))
2618   (gnus-eval-in-buffer-window gnus-save-article-buffer
2619     (save-excursion
2620       (save-restriction
2621         (widen)
2622         (when (and overwrite
2623                    (file-exists-p filename))
2624           (delete-file filename))
2625         (gnus-output-to-file filename))))
2626   filename)
2627
2628 (defun gnus-summary-write-to-file (&optional filename)
2629   "Write this article to a file.
2630 Optional argument FILENAME specifies file name.
2631 The directory to save in defaults to `gnus-article-save-directory'."
2632   (gnus-summary-save-in-file nil t))
2633
2634 (defun gnus-summary-save-body-in-file (&optional filename)
2635   "Append this article body to a file.
2636 Optional argument FILENAME specifies file name.
2637 The directory to save in defaults to `gnus-article-save-directory'."
2638   (setq filename (gnus-read-save-file-name
2639                   "Save %s body in file:" filename
2640                   gnus-file-save-name gnus-newsgroup-name
2641                   gnus-current-headers 'gnus-newsgroup-last-file))
2642   (gnus-eval-in-buffer-window gnus-save-article-buffer
2643     (save-excursion
2644       (save-restriction
2645         (widen)
2646         (when (article-goto-body)
2647           (narrow-to-region (point) (point-max)))
2648         (gnus-output-to-file filename))))
2649   filename)
2650
2651 (defun gnus-summary-save-in-pipe (&optional command)
2652   "Pipe this article to subprocess."
2653   (setq command
2654         (cond ((and (eq command 'default)
2655                     gnus-last-shell-command)
2656                gnus-last-shell-command)
2657               (command command)
2658               (t (read-string
2659                   (format
2660                    "Shell command on %s: "
2661                    (if (and gnus-number-of-articles-to-be-saved
2662                             (> gnus-number-of-articles-to-be-saved 1))
2663                        (format "these %d articles"
2664                                gnus-number-of-articles-to-be-saved)
2665                      "this article"))
2666                   gnus-last-shell-command))))
2667   (when (string-equal command "")
2668     (setq command gnus-last-shell-command))
2669   (gnus-eval-in-buffer-window gnus-article-buffer
2670     (save-restriction
2671       (widen)
2672       (shell-command-on-region (point-min) (point-max) command nil)))
2673   (setq gnus-last-shell-command command))
2674
2675 ;;; Article file names when saving.
2676
2677 (defun gnus-capitalize-newsgroup (newsgroup)
2678   "Capitalize NEWSGROUP name."
2679   (when (not (zerop (length newsgroup)))
2680     (concat (char-to-string (upcase (aref newsgroup 0)))
2681             (substring newsgroup 1))))
2682
2683 (defun gnus-Numeric-save-name (newsgroup headers &optional last-file)
2684   "Generate file name from NEWSGROUP, HEADERS, and optional LAST-FILE.
2685 If variable `gnus-use-long-file-name' is non-nil, it is ~/News/News.group/num.
2686 Otherwise, it is like ~/News/news/group/num."
2687   (let ((default
2688           (expand-file-name
2689            (concat (if (gnus-use-long-file-name 'not-save)
2690                        (gnus-capitalize-newsgroup newsgroup)
2691                      (gnus-newsgroup-directory-form newsgroup))
2692                    "/" (int-to-string (mail-header-number headers)))
2693            gnus-article-save-directory)))
2694     (if (and last-file
2695              (string-equal (file-name-directory default)
2696                            (file-name-directory last-file))
2697              (string-match "^[0-9]+$" (file-name-nondirectory last-file)))
2698         default
2699       (or last-file default))))
2700
2701 (defun gnus-numeric-save-name (newsgroup headers &optional last-file)
2702   "Generate file name from NEWSGROUP, HEADERS, and optional LAST-FILE.
2703 If variable `gnus-use-long-file-name' is non-nil, it is
2704 ~/News/news.group/num.  Otherwise, it is like ~/News/news/group/num."
2705   (let ((default
2706           (expand-file-name
2707            (concat (if (gnus-use-long-file-name 'not-save)
2708                        newsgroup
2709                      (gnus-newsgroup-directory-form newsgroup))
2710                    "/" (int-to-string (mail-header-number headers)))
2711            gnus-article-save-directory)))
2712     (if (and last-file
2713              (string-equal (file-name-directory default)
2714                            (file-name-directory last-file))
2715              (string-match "^[0-9]+$" (file-name-nondirectory last-file)))
2716         default
2717       (or last-file default))))
2718
2719 (defun gnus-plain-save-name (newsgroup headers &optional last-file)
2720   "Generate file name from NEWSGROUP, HEADERS, and optional LAST-FILE.
2721 If variable `gnus-use-long-file-name' is non-nil, it is
2722 ~/News/news.group.  Otherwise, it is like ~/News/news/group/news."
2723   (or last-file
2724       (expand-file-name
2725        (if (gnus-use-long-file-name 'not-save)
2726            newsgroup
2727          (expand-file-name "news" (gnus-newsgroup-directory-form newsgroup)))
2728        gnus-article-save-directory)))
2729
2730 (eval-and-compile
2731   (mapcar
2732    (lambda (func)
2733      (let (afunc gfunc)
2734        (if (consp func)
2735            (setq afunc (car func)
2736                  gfunc (cdr func))
2737          (setq afunc func
2738                gfunc (intern (format "gnus-%s" func))))
2739        (defalias gfunc
2740          (if (fboundp afunc)
2741            `(lambda (&optional interactive &rest args)
2742               ,(documentation afunc t)
2743               (interactive (list t))
2744               (save-excursion
2745                 (set-buffer gnus-article-buffer)
2746                 (if interactive
2747                     (call-interactively ',afunc)
2748                   (apply ',afunc args))))))))
2749    '(article-hide-headers
2750      article-hide-boring-headers
2751      article-toggle-headers
2752      article-treat-overstrike
2753      article-fill-long-lines
2754      article-capitalize-sentences
2755      article-remove-cr
2756      article-display-x-face
2757      article-de-quoted-unreadable
2758      article-de-base64-unreadable
2759      article-decode-HZ
2760      article-wash-html
2761      article-hide-list-identifiers
2762      article-hide-pgp
2763      article-strip-banner
2764      article-babel
2765      article-hide-pem
2766      article-hide-signature
2767      article-strip-headers-in-body
2768      article-remove-trailing-blank-lines
2769      article-strip-leading-blank-lines
2770      article-strip-multiple-blank-lines
2771      article-strip-leading-space
2772      article-strip-trailing-space
2773      article-strip-blank-lines
2774      article-strip-all-blank-lines
2775      article-date-local
2776      article-date-iso8601
2777      article-date-original
2778      article-date-ut
2779      article-decode-mime-words
2780      article-decode-charset
2781      article-decode-encoded-words
2782      article-date-user
2783      article-date-lapsed
2784      article-emphasize
2785      article-treat-dumbquotes
2786      article-normalize-headers
2787      (article-show-all-headers . gnus-article-show-all-headers)
2788      (article-show-all . gnus-article-show-all))))
2789 \f
2790 ;;;
2791 ;;; Gnus article mode
2792 ;;;
2793
2794 (put 'gnus-article-mode 'mode-class 'special)
2795
2796 (gnus-define-keys gnus-article-mode-map
2797   " " gnus-article-goto-next-page
2798   "\177" gnus-article-goto-prev-page
2799   [delete] gnus-article-goto-prev-page
2800   [backspace] gnus-article-goto-prev-page
2801   "\C-c^" gnus-article-refer-article
2802   "h" gnus-article-show-summary
2803   "s" gnus-article-show-summary
2804   "\C-c\C-m" gnus-article-mail
2805   "?" gnus-article-describe-briefly
2806   "e" gnus-summary-edit-article
2807   "<" beginning-of-buffer
2808   ">" end-of-buffer
2809   "\C-c\C-i" gnus-info-find-node
2810   "\C-c\C-b" gnus-bug
2811
2812   "\C-d" gnus-article-read-summary-keys
2813   "\M-*" gnus-article-read-summary-keys
2814   "\M-#" gnus-article-read-summary-keys
2815   "\M-^" gnus-article-read-summary-keys
2816   "\M-g" gnus-article-read-summary-keys)
2817
2818 ;; Define almost undefined keys to `gnus-article-read-summary-keys'.
2819 (mapcar
2820  (lambda (key)
2821    (unless (lookup-key gnus-article-mode-map key)
2822      (define-key gnus-article-mode-map key
2823        'gnus-article-read-summary-keys)))
2824  (delq nil
2825        (append
2826         (mapcar
2827          (lambda (elt)
2828            (let ((key (car elt)))
2829              (and (> (length key) 0)
2830                   (not (eq 'menu-bar (aref key 0)))
2831                   (symbolp (lookup-key gnus-summary-mode-map key))
2832                   key)))
2833          (accessible-keymaps gnus-summary-mode-map))
2834         (let ((c 127)
2835               keys)
2836           (while (>= c 32)
2837             (push (char-to-string c) keys)
2838             (decf c))
2839           keys))))
2840
2841 (defun gnus-article-make-menu-bar ()
2842   (gnus-turn-off-edit-menu 'article)
2843   (unless (boundp 'gnus-article-article-menu)
2844     (easy-menu-define
2845      gnus-article-article-menu gnus-article-mode-map ""
2846      '("Article"
2847        ["Scroll forwards" gnus-article-goto-next-page t]
2848        ["Scroll backwards" gnus-article-goto-prev-page t]
2849        ["Show summary" gnus-article-show-summary t]
2850        ["Fetch Message-ID at point" gnus-article-refer-article t]
2851        ["Mail to address at point" gnus-article-mail t]
2852        ["Send a bug report" gnus-bug t]))
2853
2854     (easy-menu-define
2855      gnus-article-treatment-menu gnus-article-mode-map ""
2856      '("Treatment"
2857        ["Hide headers" gnus-article-toggle-headers t]
2858        ["Hide signature" gnus-article-hide-signature t]
2859        ["Hide citation" gnus-article-hide-citation t]
2860        ["Treat overstrike" gnus-article-treat-overstrike t]
2861        ["Remove carriage return" gnus-article-remove-cr t]
2862        ["Decode HZ" gnus-article-decode-HZ t]))
2863
2864     ;; Note "Commands" menu is defined in gnus-sum.el for consistency
2865
2866     (when (boundp 'gnus-summary-post-menu)
2867       (define-key gnus-article-mode-map [menu-bar post]
2868         (cons "Post" gnus-summary-post-menu)))
2869
2870     (gnus-run-hooks 'gnus-article-menu-hook)))
2871
2872 (defun gnus-article-mode ()
2873   "Major mode for displaying an article.
2874
2875 All normal editing commands are switched off.
2876
2877 The following commands are available in addition to all summary mode
2878 commands:
2879 \\<gnus-article-mode-map>
2880 \\[gnus-article-next-page]\t Scroll the article one page forwards
2881 \\[gnus-article-prev-page]\t Scroll the article one page backwards
2882 \\[gnus-article-refer-article]\t Go to the article referred to by an article id near point
2883 \\[gnus-article-show-summary]\t Display the summary buffer
2884 \\[gnus-article-mail]\t Send a reply to the address near point
2885 \\[gnus-article-describe-briefly]\t Describe the current mode briefly
2886 \\[gnus-info-find-node]\t Go to the Gnus info node"
2887   (interactive)
2888   (when (gnus-visual-p 'article-menu 'menu)
2889     (gnus-article-make-menu-bar))
2890   (gnus-simplify-mode-line)
2891   (setq mode-name "Article")
2892   (setq major-mode 'gnus-article-mode)
2893   (make-local-variable 'minor-mode-alist)
2894   (unless (assq 'gnus-show-mime minor-mode-alist)
2895     (push (list 'gnus-show-mime " MIME") minor-mode-alist))
2896   (use-local-map gnus-article-mode-map)
2897   (gnus-update-format-specifications nil 'article-mode)
2898   (set (make-local-variable 'page-delimiter) gnus-page-delimiter)
2899   (make-local-variable 'gnus-page-broken)
2900   (make-local-variable 'gnus-button-marker-list)
2901   (make-local-variable 'gnus-article-current-summary)
2902   (make-local-variable 'gnus-article-mime-handles)
2903   (make-local-variable 'gnus-article-decoded-p)
2904   (make-local-variable 'gnus-article-mime-handle-alist)
2905   (make-local-variable 'gnus-article-wash-types)
2906   (gnus-set-default-directory)
2907   (buffer-disable-undo)
2908   (setq buffer-read-only t)
2909   (set-syntax-table gnus-article-mode-syntax-table)
2910   (gnus-run-hooks 'gnus-article-mode-hook))
2911
2912 (defun gnus-article-setup-buffer ()
2913   "Initialize the article buffer."
2914   (let* ((name (if gnus-single-article-buffer "*Article*"
2915                  (concat "*Article " gnus-newsgroup-name "*")))
2916          (original
2917           (progn (string-match "\\*Article" name)
2918                  (concat " *Original Article"
2919                          (substring name (match-end 0))))))
2920     (setq gnus-article-buffer name)
2921     (setq gnus-original-article-buffer original)
2922     (setq gnus-article-mime-handle-alist nil)
2923     ;; This might be a variable local to the summary buffer.
2924     (unless gnus-single-article-buffer
2925       (save-excursion
2926         (set-buffer gnus-summary-buffer)
2927         (setq gnus-article-buffer name)
2928         (setq gnus-original-article-buffer original)
2929         (gnus-set-global-variables)))
2930     (gnus-article-setup-highlight-words)
2931     ;; Init original article buffer.
2932     (save-excursion
2933       (set-buffer (gnus-get-buffer-create gnus-original-article-buffer))
2934       (set-buffer-multibyte nil)
2935       (setq major-mode 'gnus-original-article-mode)
2936       (make-local-variable 'gnus-original-article))
2937     (if (get-buffer name)
2938         (save-excursion
2939           (set-buffer name)
2940           (buffer-disable-undo)
2941           (setq buffer-read-only t)
2942           (unless (eq major-mode 'gnus-article-mode)
2943             (gnus-article-mode))
2944           (current-buffer))
2945       (save-excursion
2946         (set-buffer (gnus-get-buffer-create name))
2947         (gnus-article-mode)
2948         (make-local-variable 'gnus-summary-buffer)
2949         (gnus-summary-set-local-parameters gnus-newsgroup-name)
2950         (current-buffer)))))
2951
2952 ;; Set article window start at LINE, where LINE is the number of lines
2953 ;; from the head of the article.
2954 (defun gnus-article-set-window-start (&optional line)
2955   (set-window-start
2956    (get-buffer-window gnus-article-buffer t)
2957    (save-excursion
2958      (set-buffer gnus-article-buffer)
2959      (goto-char (point-min))
2960      (if (not line)
2961          (point-min)
2962        (gnus-message 6 "Moved to bookmark")
2963        (search-forward "\n\n" nil t)
2964        (forward-line line)
2965        (point)))))
2966
2967 ;;; @@ article filters
2968 ;;;
2969
2970 (defun gnus-article-display-mime-message ()
2971   "Article display method for MIME message."
2972   ;; called from `gnus-original-article-buffer'.
2973   (let (charset all-headers)
2974     (with-current-buffer gnus-summary-buffer
2975       (setq charset default-mime-charset
2976             all-headers gnus-have-all-headers))
2977     (make-local-variable 'default-mime-charset)
2978     (setq default-mime-charset charset)
2979     (with-current-buffer (get-buffer-create gnus-article-buffer)
2980       (make-local-variable 'default-mime-charset)
2981       (setq default-mime-charset charset))
2982     (mime-display-message mime-message-structure
2983                           gnus-article-buffer nil gnus-article-mode-map)
2984     (when all-headers
2985       (gnus-article-hide-headers nil -1)))
2986   (run-hooks 'gnus-mime-article-prepare-hook))
2987
2988 (defun gnus-article-display-traditional-message ()
2989   "Article display method for traditional message."
2990   (set-buffer gnus-article-buffer)
2991   (let (buffer-read-only)
2992     (erase-buffer)
2993     (insert-buffer-substring gnus-original-article-buffer)))
2994
2995 (defun gnus-article-make-full-mail-header (&optional number charset)
2996   "Create a new mail header structure in a raw article buffer."
2997   (unless (and number charset)
2998     (save-current-buffer
2999       (set-buffer gnus-summary-buffer)
3000       (unless number
3001         (setq number (or (cdr gnus-article-current) 0)))
3002       (unless charset
3003         (setq charset (or default-mime-charset 'x-ctext)))))
3004   (goto-char (point-min))
3005   (let ((header-end (if (search-forward "\n\n" nil t)
3006                         (1- (point))
3007                       (goto-char (point-max))))
3008         (chars (- (point-max) (point)))
3009         (lines (count-lines (point) (point-max)))
3010         (default-mime-charset charset)
3011         xref)
3012     (narrow-to-region (point-min) header-end)
3013     (setq xref (std11-fetch-field "xref"))
3014     (prog1
3015         (make-full-mail-header
3016          number
3017          (std11-fetch-field "subject")
3018          (std11-fetch-field "from")
3019          (std11-fetch-field "date")
3020          (std11-fetch-field "message-id")
3021          (std11-fetch-field "references")
3022          chars
3023          lines
3024          (when xref (concat "Xref: " xref)))
3025       (widen))))
3026
3027 (defun gnus-article-prepare (article &optional all-headers header)
3028   "Prepare ARTICLE in article mode buffer.
3029 ARTICLE should either be an article number or a Message-ID.
3030 If ARTICLE is an id, HEADER should be the article headers.
3031 If ALL-HEADERS is non-nil, no headers are hidden."
3032   (save-excursion
3033     ;; Make sure we start in a summary buffer.
3034     (unless (eq major-mode 'gnus-summary-mode)
3035       (set-buffer gnus-summary-buffer))
3036     (setq gnus-summary-buffer (current-buffer))
3037     (let* ((gnus-article (if header (mail-header-number header) article))
3038            (summary-buffer (current-buffer))
3039            (gnus-tmp-internal-hook gnus-article-internal-prepare-hook)
3040            (group gnus-newsgroup-name)
3041            result)
3042       (save-excursion
3043         (gnus-article-setup-buffer)
3044         (set-buffer gnus-original-article-buffer)
3045         ;; Deactivate active regions.
3046         (when (and (boundp 'transient-mark-mode)
3047                    transient-mark-mode)
3048           (setq mark-active nil))
3049         (if (not (setq result (let ((buffer-read-only nil))
3050                                 (gnus-request-article-this-buffer
3051                                  article group))))
3052             ;; There is no such article.
3053             (save-excursion
3054               (when (and (numberp article)
3055                          (not (memq article gnus-newsgroup-sparse)))
3056                 (setq gnus-article-current
3057                       (cons gnus-newsgroup-name article))
3058                 (set-buffer gnus-summary-buffer)
3059                 (setq gnus-current-article article)
3060                 (if (eq (gnus-article-mark article) gnus-undownloaded-mark)
3061                     (progn
3062                       (gnus-summary-set-agent-mark article)
3063                       (message "Message marked for downloading"))
3064                   (gnus-summary-mark-article article gnus-canceled-mark)
3065                   (unless (memq article gnus-newsgroup-sparse)
3066                     (gnus-error 1 "No such article (may have expired or been canceled)")))))
3067           (if (or (eq result 'pseudo)
3068                   (eq result 'nneething))
3069               (progn
3070                 (save-excursion
3071                   (set-buffer summary-buffer)
3072                   (push article gnus-newsgroup-history)
3073                   (setq gnus-last-article gnus-current-article
3074                         gnus-current-article 0
3075                         gnus-current-headers nil
3076                         gnus-article-current nil)
3077                   (if (eq result 'nneething)
3078                       (gnus-configure-windows 'summary)
3079                     (gnus-configure-windows 'article))
3080                   (gnus-set-global-variables))
3081                 (let ((gnus-article-mime-handle-alist-1
3082                        gnus-article-mime-handle-alist))
3083                   (gnus-set-mode-line 'article)))
3084             ;; The result from the `request' was an actual article -
3085             ;; or at least some text that is now displayed in the
3086             ;; article buffer.
3087             (when (and (numberp article)
3088                        (not (eq article gnus-current-article)))
3089               ;; Seems like a new article has been selected.
3090               ;; `gnus-current-article' must be an article number.
3091               (save-excursion
3092                 (set-buffer summary-buffer)
3093                 (push article gnus-newsgroup-history)
3094                 (setq gnus-last-article gnus-current-article
3095                       gnus-current-article article
3096                       gnus-current-headers
3097                       (gnus-summary-article-header gnus-current-article)
3098                       gnus-article-current
3099                       (cons gnus-newsgroup-name gnus-current-article))
3100                 (unless (vectorp gnus-current-headers)
3101                   (setq gnus-current-headers nil))
3102                 (gnus-summary-goto-subject gnus-current-article)
3103                 (when (gnus-summary-show-thread)
3104                   ;; If the summary buffer really was folded, the
3105                   ;; previous goto may not actually have gone to
3106                   ;; the right article, but the thread root instead.
3107                   ;; So we go again.
3108                   (gnus-summary-goto-subject gnus-current-article))
3109                 (gnus-run-hooks 'gnus-mark-article-hook)
3110                 (gnus-set-mode-line 'summary)
3111                 (when (gnus-visual-p 'article-highlight 'highlight)
3112                   (gnus-run-hooks 'gnus-visual-mark-article-hook))
3113                 ;; Set the global newsgroup variables here.
3114                 (gnus-set-global-variables)
3115                 (setq gnus-have-all-headers
3116                       (or all-headers gnus-show-all-headers))))
3117             (save-excursion
3118               (gnus-configure-windows 'article))
3119             (when (or (numberp article)
3120                       (stringp article))
3121               (gnus-article-prepare-display)
3122               ;; Do page break.
3123               (goto-char (point-min))
3124               (setq gnus-page-broken
3125                     (when gnus-break-pages
3126                       (gnus-narrow-to-page)
3127                       t)))
3128             (let ((gnus-article-mime-handle-alist-1
3129                    gnus-article-mime-handle-alist))
3130               (gnus-set-mode-line 'article))
3131             (article-goto-body)
3132             (set-window-point (get-buffer-window (current-buffer)) (point))
3133             (gnus-configure-windows 'article)
3134             t))))))
3135
3136 (defun gnus-article-prepare-mime-display (&optional number)
3137   (goto-char (point-min))
3138   (when (re-search-forward "^[^\t ]+:" nil t)
3139     (goto-char (match-beginning 0)))
3140   (let ((entity (if (eq 1 (point-min))
3141                     (get-text-property 1 'mime-view-entity)
3142                   (get-text-property (point) 'mime-view-entity)))
3143         last-entity child-entity next type)
3144     (setq child-entity (mime-entity-children entity))
3145     (if child-entity
3146         (setq last-entity (nth (1- (length child-entity))
3147                                child-entity))
3148       (setq last-entity entity))
3149     (save-restriction
3150       (narrow-to-region (point)
3151                         (if (search-forward "\n\n" nil t)
3152                             (point)
3153                           (point-max)))
3154       (gnus-treat-article 'head)
3155       (put-text-property (point-min) (point-max) 'article-treated-header t)
3156       (goto-char (point-max)))
3157     (while (and (not (eobp)) entity)
3158       (setq next (set-marker
3159                   (make-marker)
3160                   (next-single-property-change (point) 'mime-view-entity
3161                                                nil (point-max))))
3162       (let ((types (mime-entity-content-type entity)))
3163         (while (eq 'multipart (mime-content-type-primary-type types))
3164           (setq entity (car (mime-entity-children entity))
3165                 types (mime-entity-content-type entity)))
3166         (when types
3167           (setq type (format "%s/%s"
3168                              (mime-content-type-primary-type types)
3169                              (mime-content-type-subtype types)))))
3170       (if (string-equal type "message/rfc822")
3171           (progn
3172             (setq next (point))
3173             (let ((children (mime-entity-children entity))
3174                   last-children)
3175               (when children
3176                 (setq last-children (nth (1- (length children)) children))
3177                 (while
3178                     (and
3179                      (not (eq last-children
3180                               (get-text-property next 'mime-view-entity)))
3181                      (setq next
3182                            (next-single-property-change next
3183                                                         'mime-view-entity
3184                                                         nil (point-max)))))))
3185             (setq next (next-single-property-change next 'mime-view-entity
3186                                                     nil (point-max)))
3187             (save-restriction
3188               (narrow-to-region (point) next)
3189               (gnus-article-prepare-mime-display)
3190               (goto-char (point-max)))
3191             (setq entity (get-text-property (point) 'mime-view-entity)))
3192         (save-restriction
3193           (narrow-to-region (point) next)
3194           ;; Kludge. We have to count true number, but for now,
3195           ;; part number is here only to achieve `last'.
3196           (gnus-treat-article nil 1
3197                               (if (eq entity last-entity)
3198                                   1 2)
3199                               type)
3200           (goto-char (point-max)))
3201         (setq entity (get-text-property next 'mime-view-entity))))))
3202
3203 ;;;###autoload
3204 (defun gnus-article-prepare-display ()
3205   "Make the current buffer look like a nice article."
3206   (setq gnus-article-wash-types nil)
3207   (gnus-run-hooks 'gnus-tmp-internal-hook)
3208   (gnus-run-hooks 'gnus-article-prepare-hook)
3209   ;; Display message.
3210   (let (mime-display-header-hook mime-display-text/plain-hook)
3211     (funcall (if gnus-show-mime
3212                  (progn
3213                    (setq mime-message-structure gnus-current-headers)
3214                    (mime-buffer-entity-set-buffer-internal
3215                     mime-message-structure
3216                     gnus-original-article-buffer)
3217                    (mime-entity-set-representation-type-internal
3218                     mime-message-structure 'mime-buffer-entity)
3219                    (luna-send mime-message-structure
3220                               'initialize-instance
3221                               mime-message-structure)
3222                    gnus-article-display-method-for-mime)
3223                gnus-article-display-method-for-traditional)))
3224   ;; Associate this article with the current summary buffer.
3225   (setq gnus-article-current-summary gnus-summary-buffer)
3226   ;; Call the treatment functions.
3227   (let ((inhibit-read-only t)
3228         buffer-read-only)
3229     (save-restriction
3230       (widen)
3231       (if gnus-show-mime
3232           (gnus-article-prepare-mime-display)
3233         (narrow-to-region (goto-char (point-min))
3234                           (if (search-forward "\n\n" nil t)
3235                               (point)
3236                             (point-max)))
3237         (gnus-treat-article 'head)
3238         (put-text-property (point-min) (point-max) 'article-treated-header t)
3239         (goto-char (point-max))
3240         (widen)
3241         (narrow-to-region (point) (point-max))
3242         (gnus-treat-article nil))
3243       (put-text-property (point-min) (point-max) 'read-only nil)))
3244   ;; Perform the article display hooks.  Incidentally, this hook is
3245   ;; an obsolete variable by now.
3246   (gnus-run-hooks 'gnus-article-display-hook))
3247
3248 (defun gnus-article-decode-article-as-default-mime-charset ()
3249   "Decode an article as `default-mime-charset'.  It won't work if the
3250 value of the variable `gnus-show-mime' is non-nil."
3251   (unless gnus-show-mime
3252     (decode-mime-charset-region (point-min) (point-max)
3253                                 (with-current-buffer gnus-summary-buffer
3254                                   default-mime-charset))))
3255
3256 ;;;
3257 ;;; Gnus MIME viewing functions
3258 ;;;
3259
3260 (defvar gnus-mime-button-line-format "%{%([%p. %d%T]%)%}%e\n"
3261   "The following specs can be used:
3262 %t  The MIME type
3263 %T  MIME type, along with additional info
3264 %n  The `name' parameter
3265 %d  The description, if any
3266 %l  The length of the encoded part
3267 %p  The part identifier number
3268 %e  Dots if the part isn't displayed")
3269
3270 (defvar gnus-mime-button-line-format-alist
3271   '((?t gnus-tmp-type ?s)
3272     (?T gnus-tmp-type-long ?s)
3273     (?n gnus-tmp-name ?s)
3274     (?d gnus-tmp-description ?s)
3275     (?p gnus-tmp-id ?s)
3276     (?l gnus-tmp-length ?d)
3277     (?e gnus-tmp-dots ?s)))
3278
3279 (defvar gnus-mime-button-commands
3280   '((gnus-article-press-button "\r" "Toggle Display")
3281     (gnus-mime-view-part "v" "View Interactively...")
3282     (gnus-mime-view-part-as-type "t" "View As Type...")
3283     (gnus-mime-save-part "o" "Save...")
3284     (gnus-mime-copy-part "c" "View As Text, In Other Buffer")
3285     (gnus-mime-inline-part "i" "View As Text, In This Buffer")
3286     (gnus-mime-internalize-part "E" "View Internally")
3287     (gnus-mime-externalize-part "e" "View Externally")
3288     (gnus-mime-pipe-part "|" "Pipe To Command...")
3289     (gnus-mime-action-on-part "." "Take action on the part")))
3290
3291 (defun gnus-article-mime-part-status ()
3292   (with-current-buffer gnus-article-buffer
3293     (let ((entity (get-text-property (point-min) 'mime-view-entity)))
3294       (if (and entity (mime-entity-children entity))
3295           (format " (%d parts)" (length (mime-entity-children entity)))
3296         ""))))
3297
3298 (defvar gnus-mime-button-map
3299   (let ((map (make-sparse-keymap)))
3300     (set-keymap-parent map gnus-article-mode-map)
3301     (define-key map gnus-mouse-2 'gnus-article-push-button)
3302     (define-key map gnus-down-mouse-3 'gnus-mime-button-menu)
3303     (dolist (c gnus-mime-button-commands)
3304       (define-key map (cadr c) (car c)))
3305     map))
3306
3307 (defun gnus-mime-button-menu (event)
3308   "Construct a context-sensitive menu of MIME commands."
3309   (interactive "e")
3310   (save-excursion
3311     (let ((pos (event-start event)))
3312       (set-buffer (window-buffer (posn-window pos)))
3313       (goto-char (posn-point pos))
3314       (gnus-article-check-buffer)
3315       (let ((response (x-popup-menu
3316                        t `("MIME Part"
3317                            ("" ,@(mapcar (lambda (c)
3318                                            (cons (caddr c) (car c)))
3319                                          gnus-mime-button-commands))))))
3320         (if response
3321             (call-interactively response))))))
3322
3323 (defun gnus-mime-view-all-parts (&optional handles)
3324   "View all the MIME parts."
3325   (interactive)
3326   (save-current-buffer
3327     (set-buffer gnus-article-buffer)
3328     (let ((handles (or handles gnus-article-mime-handles))
3329           (mail-parse-charset gnus-newsgroup-charset)
3330           (mail-parse-ignored-charsets 
3331            (save-excursion (set-buffer gnus-summary-buffer)
3332                            gnus-newsgroup-ignored-charsets)))
3333       (if (stringp (car handles))
3334           (gnus-mime-view-all-parts (cdr handles))
3335         (mapcar 'mm-display-part handles)))))
3336
3337 (defun gnus-mime-save-part ()
3338   "Save the MIME part under point."
3339   (interactive)
3340   (gnus-article-check-buffer)
3341   (let ((data (get-text-property (point) 'gnus-data)))
3342     (mm-save-part data)))
3343
3344 (defun gnus-mime-pipe-part ()
3345   "Pipe the MIME part under point to a process."
3346   (interactive)
3347   (gnus-article-check-buffer)
3348   (let ((data (get-text-property (point) 'gnus-data)))
3349     (mm-pipe-part data)))
3350
3351 (defun gnus-mime-view-part ()
3352   "Interactively choose a viewing method for the MIME part under point."
3353   (interactive)
3354   (gnus-article-check-buffer)
3355   (let ((data (get-text-property (point) 'gnus-data)))
3356     (mm-interactively-view-part data)))
3357
3358 (defun gnus-mime-view-part-as-type-internal ()
3359   (gnus-article-check-buffer)
3360   (let* ((name (mail-content-type-get
3361                 (mm-handle-type (get-text-property (point) 'gnus-data))
3362                 'name))
3363          (def-type (and name (mm-default-file-encoding name))))
3364     (and def-type (cons def-type 0))))
3365
3366 (defun gnus-mime-view-part-as-type (mime-type)
3367   "Choose a MIME media type, and view the part as such."
3368   (interactive
3369    (list (completing-read
3370           "View as MIME type: "
3371           (mapcar #'list (mailcap-mime-types))
3372           nil nil
3373           (gnus-mime-view-part-as-type-internal))))
3374   (gnus-article-check-buffer)
3375   (let ((handle (get-text-property (point) 'gnus-data)))
3376     (gnus-mm-display-part
3377      (mm-make-handle (mm-handle-buffer handle)
3378                      (cons mime-type (cdr (mm-handle-type handle)))
3379                      (mm-handle-encoding handle)
3380                      (mm-handle-undisplayer handle)
3381                      (mm-handle-disposition handle)
3382                      (mm-handle-description handle)
3383                      (mm-handle-cache handle)
3384                      (mm-handle-id handle)))))
3385
3386 (defun gnus-mime-copy-part (&optional handle)
3387   "Put the the MIME part under point into a new buffer."
3388   (interactive)
3389   (gnus-article-check-buffer)
3390   (let* ((handle (or handle (get-text-property (point) 'gnus-data)))
3391          (contents (mm-get-part handle))|
3392          (base (file-name-nondirectory
3393                 (or
3394                  (mail-content-type-get (mm-handle-type handle) 'name)
3395                  (mail-content-type-get (mm-handle-type handle)
3396                                         'filename)
3397                  "*decoded*")))
3398          (buffer (generate-new-buffer base)))
3399     (switch-to-buffer buffer)
3400     (insert contents)
3401     ;; We do it this way to make `normal-mode' set the appropriate mode.
3402     (unwind-protect
3403         (progn
3404           (setq buffer-file-name (expand-file-name base))
3405           (normal-mode))
3406       (setq buffer-file-name nil))
3407     (goto-char (point-min))))
3408
3409 (defun gnus-mime-inline-part (&optional handle arg)
3410   "Insert the MIME part under point into the current buffer."
3411   (interactive (list nil current-prefix-arg))
3412   (gnus-article-check-buffer)
3413   (let* ((handle (or handle (get-text-property (point) 'gnus-data)))
3414          contents charset
3415          (b (point))
3416          buffer-read-only)
3417     (if (mm-handle-undisplayer handle)
3418         (mm-remove-part handle)
3419       (setq contents (mm-get-part handle))
3420       (cond
3421        ((not arg)
3422         (setq charset (or (mail-content-type-get
3423                            (mm-handle-type handle) 'charset)
3424                           gnus-newsgroup-charset)))
3425        ((numberp arg)
3426         (setq charset
3427               (or (cdr (assq arg 
3428                              gnus-summary-show-article-charset-alist))
3429                   (read-coding-system "Charset: ")))))
3430       (forward-line 2)
3431       (mm-insert-inline handle
3432                         (if (and charset 
3433                                  (setq charset (mm-charset-to-coding-system 
3434                                                 charset))
3435                                  (not (eq charset 'ascii)))
3436                             (mm-decode-coding-string contents charset)
3437                           contents))
3438       (goto-char b))))
3439
3440 (defun gnus-mime-externalize-part (&optional handle)
3441   "View the MIME part under point with an external viewer."
3442   (interactive)
3443   (gnus-article-check-buffer)
3444   (let* ((handle (or handle (get-text-property (point) 'gnus-data)))
3445          (mm-user-display-methods nil)
3446          (mm-inlined-types nil)
3447          (mail-parse-charset gnus-newsgroup-charset)
3448          (mail-parse-ignored-charsets 
3449           (save-excursion (set-buffer gnus-summary-buffer)
3450                           gnus-newsgroup-ignored-charsets)))
3451     (if (mm-handle-undisplayer handle)
3452         (mm-remove-part handle)
3453       (mm-display-part handle))))
3454
3455 (defun gnus-mime-internalize-part (&optional handle)
3456   "View the MIME part under point with an internal viewer.
3457 In no internal viewer is available, use an external viewer."
3458   (interactive)
3459   (gnus-article-check-buffer)
3460   (let* ((handle (or handle (get-text-property (point) 'gnus-data)))
3461          (mm-inlined-types '(".*"))
3462          (mm-inline-large-images t)
3463          (mail-parse-charset gnus-newsgroup-charset)
3464          (mail-parse-ignored-charsets 
3465           (save-excursion (set-buffer gnus-summary-buffer)
3466                           gnus-newsgroup-ignored-charsets)))
3467     (if (mm-handle-undisplayer handle)
3468         (mm-remove-part handle)
3469       (mm-display-part handle))))
3470
3471 (defun gnus-mime-action-on-part (&optional action)
3472   "Do something with the MIME attachment at \(point\)."
3473   (interactive
3474    (list (completing-read "Action: " gnus-mime-action-alist)))
3475   (gnus-article-check-buffer)
3476   (let ((action-pair (assoc action gnus-mime-action-alist)))
3477     (if action-pair
3478         (funcall (cdr action-pair)))))
3479
3480
3481 (defun gnus-article-part-wrapper (n function)
3482   (save-current-buffer
3483     (set-buffer gnus-article-buffer)
3484     (when (> n (length gnus-article-mime-handle-alist))
3485       (error "No such part"))
3486     (gnus-article-goto-part n)
3487     (let ((handle (cdr (assq n gnus-article-mime-handle-alist))))
3488       (funcall function handle))))
3489
3490 (defun gnus-article-pipe-part (n)
3491   "Pipe MIME part N, which is the numerical prefix."
3492   (interactive "p")
3493   (gnus-article-part-wrapper n 'mm-pipe-part))
3494
3495 (defun gnus-article-save-part (n)
3496   "Save MIME part N, which is the numerical prefix."
3497   (interactive "p")
3498   (gnus-article-part-wrapper n 'mm-save-part))
3499
3500 (defun gnus-article-interactively-view-part (n)
3501   "View MIME part N interactively, which is the numerical prefix."
3502   (interactive "p")
3503   (gnus-article-part-wrapper n 'mm-interactively-view-part))
3504
3505 (defun gnus-article-copy-part (n)
3506   "Copy MIME part N, which is the numerical prefix."
3507   (interactive "p")
3508   (gnus-article-part-wrapper n 'gnus-mime-copy-part))
3509
3510 (defun gnus-article-externalize-part (n)
3511   "View MIME part N externally, which is the numerical prefix."
3512   (interactive "p")
3513   (gnus-article-part-wrapper n 'gnus-mime-externalize-part))
3514
3515 (defun gnus-article-inline-part (n)
3516   "Inline MIME part N, which is the numerical prefix."
3517   (interactive "p")
3518   (gnus-article-part-wrapper n 'gnus-mime-inline-part))
3519
3520 (defun gnus-article-mime-match-handle-first (condition)
3521   (if condition
3522       (let ((alist gnus-article-mime-handle-alist) ihandle n)
3523         (while (setq ihandle (pop alist))
3524           (if (and (cond 
3525                     ((functionp condition)
3526                      (funcall condition (cdr ihandle)))
3527                     ((eq condition 'undisplayed) 
3528                      (not (or (mm-handle-undisplayer (cdr ihandle))
3529                               (equal (mm-handle-media-type (cdr ihandle))
3530                                      "multipart/alternative"))))
3531                     ((eq condition 'undisplayed-alternative)
3532                      (not (mm-handle-undisplayer (cdr ihandle))))
3533                     (t t))
3534                    (gnus-article-goto-part (car ihandle))
3535                    (or (not n) (< (car ihandle) n)))
3536               (setq n (car ihandle))))
3537         (or n 1))
3538     1))
3539
3540 (defun gnus-article-view-part (&optional n)
3541   "View MIME part N, which is the numerical prefix."
3542   (interactive "P")
3543   (save-current-buffer
3544     (set-buffer gnus-article-buffer)
3545     (or (numberp n) (setq n (gnus-article-mime-match-handle-first 
3546                              gnus-article-mime-match-handle-function)))
3547     (when (> n (length gnus-article-mime-handle-alist))
3548       (error "No such part"))
3549     (let ((handle (cdr (assq n gnus-article-mime-handle-alist))))
3550       (when (gnus-article-goto-part n)
3551         (if (equal (car handle) "multipart/alternative")
3552             (gnus-article-press-button)
3553           (when (eq (gnus-mm-display-part handle) 'internal)
3554             (gnus-set-window-start)))))))
3555
3556 (defsubst gnus-article-mime-total-parts ()
3557   (if (bufferp (car gnus-article-mime-handles))
3558       1 ;; single part
3559     (1- (length gnus-article-mime-handles))))
3560
3561 (defun gnus-mm-display-part (handle)
3562   "Display HANDLE and fix MIME button."
3563   (let ((id (get-text-property (point) 'gnus-part))
3564         (point (point))
3565         buffer-read-only)
3566     (forward-line 1)
3567     (prog1
3568         (let ((window (selected-window))
3569               (mail-parse-charset gnus-newsgroup-charset)
3570               (mail-parse-ignored-charsets 
3571                (save-excursion (set-buffer gnus-summary-buffer)
3572                                gnus-newsgroup-ignored-charsets)))
3573           (save-excursion
3574             (unwind-protect
3575                 (let ((win (get-buffer-window (current-buffer) t))
3576                       (beg (point)))
3577                   (when win
3578                     (select-window win))
3579                   (goto-char point)
3580                   (forward-line)
3581                   (if (mm-handle-displayed-p handle)
3582                       ;; This will remove the part.
3583                       (mm-display-part handle)
3584                     (save-restriction
3585                       (narrow-to-region (point) (1+ (point)))
3586                       (mm-display-part handle)
3587                       ;; We narrow to the part itself and
3588                       ;; then call the treatment functions.
3589                       (goto-char (point-min))
3590                       (forward-line 1)
3591                       (narrow-to-region (point) (point-max))
3592                       (gnus-treat-article
3593                        nil id
3594                        (gnus-article-mime-total-parts)
3595                        (mm-handle-media-type handle)))))
3596               (select-window window))))
3597       (goto-char point)
3598       (delete-region (gnus-point-at-bol) (progn (forward-line 1) (point)))
3599       (gnus-insert-mime-button
3600        handle id (list (mm-handle-displayed-p handle)))
3601       (goto-char point))))
3602
3603 (defun gnus-article-goto-part (n)
3604   "Go to MIME part N."
3605   (let ((point (text-property-any (point-min) (point-max) 'gnus-part n)))
3606     (when point
3607       (goto-char point))))
3608
3609 (defun gnus-insert-mime-button (handle gnus-tmp-id &optional displayed)
3610   (let ((gnus-tmp-name
3611          (or (mail-content-type-get (mm-handle-type handle)
3612                                     'name)
3613              (mail-content-type-get (mm-handle-disposition handle)
3614                                     'filename)
3615              ""))
3616         (gnus-tmp-type (mm-handle-media-type handle))
3617         (gnus-tmp-description
3618          (mail-decode-encoded-word-string (or (mm-handle-description handle)
3619                                               "")))
3620         (gnus-tmp-dots
3621          (if (if displayed (car displayed)
3622                (mm-handle-displayed-p handle))
3623              "" "..."))
3624         (gnus-tmp-length (with-current-buffer (mm-handle-buffer handle)
3625                            (buffer-size)))
3626         gnus-tmp-type-long b e)
3627     (when (string-match ".*/" gnus-tmp-name)
3628       (setq gnus-tmp-name (replace-match "" t t gnus-tmp-name)))
3629     (setq gnus-tmp-type-long (concat gnus-tmp-type
3630                                      (and (not (equal gnus-tmp-name ""))
3631                                           (concat "; " gnus-tmp-name))))
3632     (or (equal gnus-tmp-description "")
3633         (setq gnus-tmp-type-long (concat " --- " gnus-tmp-type-long)))
3634     (unless (bolp)
3635       (insert "\n"))
3636     (setq b (point))
3637     (gnus-eval-format
3638      gnus-mime-button-line-format gnus-mime-button-line-format-alist
3639      `(local-map ,gnus-mime-button-map
3640                  keymap ,gnus-mime-button-map
3641                  gnus-callback gnus-mm-display-part
3642                  gnus-part ,gnus-tmp-id
3643                  article-type annotation
3644                  gnus-data ,handle))
3645     (setq e (point))
3646     (widget-convert-button
3647      'link b e
3648      :mime-handle handle
3649      :action 'gnus-widget-press-button
3650      :button-keymap gnus-mime-button-map
3651      :help-echo
3652      (lambda (widget/window &optional overlay pos)
3653        ;; Needed to properly clear the message due to a bug in
3654        ;; wid-edit (XEmacs only).
3655        (if (boundp 'help-echo-owns-message)
3656            (setq help-echo-owns-message t))
3657        (format
3658         "%S: %s the MIME part; %S: more options"
3659         (aref gnus-mouse-2 0)
3660         ;; XEmacs will get a single widget arg; Emacs 21 will get
3661         ;; window, overlay, position.
3662         (if (mm-handle-displayed-p
3663              (if overlay
3664                  (with-current-buffer (gnus-overlay-buffer overlay)
3665                    (widget-get (widget-at (gnus-overlay-start overlay))
3666                                :mime-handle))
3667                (widget-get widget/window :mime-handle)))
3668             "hide" "show")
3669         (aref gnus-down-mouse-3 0))))))
3670
3671 (defun gnus-widget-press-button (elems el)
3672   (goto-char (widget-get elems :from))
3673   (gnus-article-press-button))
3674
3675 (defvar gnus-displaying-mime nil)
3676
3677 (defun gnus-display-mime (&optional ihandles)
3678   "Display the MIME parts."
3679   (save-excursion
3680     (save-selected-window
3681       (let ((window (get-buffer-window gnus-article-buffer))
3682             (point (point)))
3683         (when window
3684           (select-window window)
3685           ;; We have to do this since selecting the window
3686           ;; may change the point.  So we set the window point.
3687           (set-window-point window point)))
3688       (let* ((handles (or ihandles (mm-dissect-buffer) (mm-uu-dissect)))
3689              buffer-read-only handle name type b e display)
3690         (when (and (not ihandles)
3691                    (not gnus-displaying-mime))
3692           ;; Top-level call; we clean up.
3693           (when gnus-article-mime-handles
3694             (mm-destroy-parts gnus-article-mime-handles)
3695             (setq gnus-article-mime-handle-alist nil));; A trick.
3696           (setq gnus-article-mime-handles handles)
3697           ;; We allow users to glean info from the handles.
3698           (when gnus-article-mime-part-function
3699             (gnus-mime-part-function handles)))
3700         (if (and handles
3701                  (or (not (stringp (car handles)))
3702                      (cdr handles)))
3703             (progn
3704               (when (and (not ihandles)
3705                          (not gnus-displaying-mime))
3706                 ;; Clean up for mime parts.
3707                 (article-goto-body)
3708                 (delete-region (point) (point-max)))
3709               (let ((gnus-displaying-mime t))
3710                 (gnus-mime-display-part handles)))
3711           (save-restriction
3712             (article-goto-body)
3713             (narrow-to-region (point) (point-max))
3714             (gnus-treat-article nil 1 1)
3715             (widen)))
3716         (unless ihandles
3717           ;; Highlight the headers.
3718           (save-excursion
3719             (save-restriction
3720               (article-goto-body)
3721               (narrow-to-region (point-min) (point))
3722               (gnus-treat-article 'head))))))))
3723
3724 (defvar gnus-mime-display-multipart-as-mixed nil)
3725
3726 (defun gnus-mime-display-part (handle)
3727   (cond
3728    ;; Single part.
3729    ((not (stringp (car handle)))
3730     (gnus-mime-display-single handle))
3731    ;; User-defined multipart
3732    ((cdr (assoc (car handle) gnus-mime-multipart-functions))
3733     (funcall (cdr (assoc (car handle) gnus-mime-multipart-functions))
3734              handle))
3735    ;; multipart/alternative
3736    ((and (equal (car handle) "multipart/alternative")
3737          (not gnus-mime-display-multipart-as-mixed))
3738     (let ((id (1+ (length gnus-article-mime-handle-alist))))
3739       (push (cons id handle) gnus-article-mime-handle-alist)
3740       (gnus-mime-display-alternative (cdr handle) nil nil id)))
3741    ;; multipart/related
3742    ((and (equal (car handle) "multipart/related")
3743          (not gnus-mime-display-multipart-as-mixed))
3744     ;;;!!!We should find the start part, but we just default
3745     ;;;!!!to the first part.
3746     (gnus-mime-display-part (cadr handle)))
3747    ;; Other multiparts are handled like multipart/mixed.
3748    (t
3749     (gnus-mime-display-mixed (cdr handle)))))
3750
3751 (defun gnus-mime-part-function (handles)
3752   (if (stringp (car handles))
3753       (mapcar 'gnus-mime-part-function (cdr handles))
3754     (funcall gnus-article-mime-part-function handles)))
3755
3756 (defun gnus-mime-display-mixed (handles)
3757   (mapcar 'gnus-mime-display-part handles))
3758
3759 (defun gnus-mime-display-single (handle)
3760   (let ((type (mm-handle-media-type handle))
3761         (ignored gnus-ignored-mime-types)
3762         (not-attachment t)
3763         (move nil)
3764         display text)
3765     (catch 'ignored
3766       (progn
3767         (while ignored
3768           (when (string-match (pop ignored) type)
3769             (throw 'ignored nil)))
3770         (if (and (setq not-attachment
3771                        (and (not (mm-inline-override-p handle))
3772                             (or (not (mm-handle-disposition handle))
3773                                 (equal (car (mm-handle-disposition handle))
3774                                        "inline")
3775                                 (mm-attachment-override-p handle))))
3776                  (mm-automatic-display-p handle)
3777                  (or (mm-inlined-p handle)
3778                      (mm-automatic-external-display-p type)))
3779             (setq display t)
3780           (when (equal (mm-handle-media-supertype handle) "text")
3781             (setq text t)))
3782         (let ((id (1+ (length gnus-article-mime-handle-alist)))
3783               beg)
3784           (push (cons id handle) gnus-article-mime-handle-alist)
3785           (when (or (not display)
3786                     (not (gnus-unbuttonized-mime-type-p type)))
3787             ;(gnus-article-insert-newline)
3788             (gnus-insert-mime-button
3789              handle id (list (or display (and not-attachment text))))
3790             (gnus-article-insert-newline)
3791             ;(gnus-article-insert-newline)
3792             (setq move t))
3793           (setq beg (point))
3794           (cond
3795            (display
3796             (when move
3797               (forward-line -2)
3798               (setq beg (point)))
3799             (let ((mail-parse-charset gnus-newsgroup-charset)
3800                   (mail-parse-ignored-charsets 
3801                    (save-excursion (condition-case ()
3802                                        (set-buffer gnus-summary-buffer)
3803                                      (error))
3804                                    gnus-newsgroup-ignored-charsets)))
3805               (mm-display-part handle t))
3806             (goto-char (point-max)))
3807            ((and text not-attachment)
3808             (when move
3809               (forward-line -2)
3810               (setq beg (point)))
3811             (gnus-article-insert-newline)
3812             (mm-insert-inline handle (mm-get-part handle))
3813             (goto-char (point-max))))
3814           ;; Do highlighting.
3815           (save-excursion
3816             (save-restriction
3817               (narrow-to-region beg (point))
3818               (gnus-treat-article
3819                nil id 
3820                (gnus-article-mime-total-parts)
3821                (mm-handle-media-type handle)))))))))
3822
3823 (defun gnus-unbuttonized-mime-type-p (type)
3824   "Say whether TYPE is to be unbuttonized."
3825   (unless gnus-inhibit-mime-unbuttonizing
3826     (catch 'found
3827       (let ((types gnus-unbuttonized-mime-types))
3828         (while types
3829           (when (string-match (pop types) type)
3830             (throw 'found t)))))))
3831
3832 (defun gnus-article-insert-newline ()
3833   "Insert a newline, but mark it as undeletable."
3834   (gnus-put-text-property
3835    (point) (progn (insert "\n") (point)) 'gnus-undeletable t))
3836
3837 (defun gnus-mime-display-alternative (handles &optional preferred ibegend id)
3838   (let* ((preferred (or preferred (mm-preferred-alternative handles)))
3839          (ihandles handles)
3840          (point (point))
3841          handle buffer-read-only from props begend not-pref)
3842     (save-window-excursion
3843       (save-restriction
3844         (when ibegend
3845           (narrow-to-region (car ibegend)
3846                             (or (cdr ibegend)
3847                                 (progn
3848                                   (goto-char (car ibegend))
3849                                   (forward-line 2)
3850                                   (point))))
3851           (delete-region (point-min) (point-max))
3852           (mm-remove-parts handles))
3853         (setq begend (list (point-marker)))
3854         ;; Do the toggle.
3855         (unless (setq not-pref (cadr (member preferred ihandles)))
3856           (setq not-pref (car ihandles)))
3857         (when (or ibegend
3858                   (not (gnus-unbuttonized-mime-type-p
3859                         "multipart/alternative")))
3860           (gnus-add-text-properties
3861            (setq from (point))
3862            (progn
3863              (insert (format "%d.  " id))
3864              (point))
3865            `(gnus-callback
3866              (lambda (handles)
3867                (unless ,(not ibegend)
3868                  (setq gnus-article-mime-handle-alist
3869                        ',gnus-article-mime-handle-alist))
3870                (gnus-mime-display-alternative
3871                 ',ihandles ',not-pref ',begend ,id))
3872              local-map ,gnus-mime-button-map
3873              ,gnus-mouse-face-prop ,gnus-article-mouse-face
3874              face ,gnus-article-button-face
3875              keymap ,gnus-mime-button-map
3876              gnus-part ,id
3877              gnus-data ,handle))
3878           (widget-convert-button 'link from (point)
3879                                  :action 'gnus-widget-press-button
3880                                  :button-keymap gnus-widget-button-keymap)
3881           ;; Do the handles
3882           (while (setq handle (pop handles))
3883             (gnus-add-text-properties
3884              (setq from (point))
3885              (progn
3886                (insert (format "(%c) %-18s"
3887                                (if (equal handle preferred) ?* ? )
3888                                (mm-handle-media-type handle)))
3889                (point))
3890              `(gnus-callback
3891                (lambda (handles)
3892                  (unless ,(not ibegend)
3893                    (setq gnus-article-mime-handle-alist
3894                          ',gnus-article-mime-handle-alist))
3895                  (gnus-mime-display-alternative
3896                   ',ihandles ',handle ',begend ,id))
3897                local-map ,gnus-mime-button-map
3898                ,gnus-mouse-face-prop ,gnus-article-mouse-face
3899                face ,gnus-article-button-face
3900                keymap ,gnus-mime-button-map
3901                gnus-part ,id
3902                gnus-data ,handle))
3903             (widget-convert-button 'link from (point)
3904                                    :action 'gnus-widget-press-button
3905                                    :button-keymap gnus-widget-button-keymap)
3906             (insert "  "))
3907           (insert "\n\n"))
3908         (when preferred
3909           (if (stringp (car preferred))
3910               (gnus-display-mime preferred)
3911             (let ((mail-parse-charset gnus-newsgroup-charset)
3912                   (mail-parse-ignored-charsets 
3913                    (save-excursion (set-buffer gnus-summary-buffer)
3914                                    gnus-newsgroup-ignored-charsets)))
3915               (mm-display-part preferred)
3916               ;; Do highlighting.
3917               (save-excursion
3918                 (save-restriction
3919                   (narrow-to-region (car begend) (point-max))
3920                   (gnus-treat-article
3921                    nil (length gnus-article-mime-handle-alist)
3922                    (gnus-article-mime-total-parts)
3923                    (mm-handle-media-type handle))))))
3924           (goto-char (point-max))
3925           (setcdr begend (point-marker)))))
3926     (when ibegend
3927       (goto-char point))))
3928
3929 (defun gnus-article-wash-status ()
3930   "Return a string which display status of article washing."
3931   (save-excursion
3932     (set-buffer gnus-article-buffer)
3933     (let ((cite (memq 'cite gnus-article-wash-types))
3934           (headers (memq 'headers gnus-article-wash-types))
3935           (boring (memq 'boring-headers gnus-article-wash-types))
3936           (pgp (memq 'pgp gnus-article-wash-types))
3937           (pem (memq 'pem gnus-article-wash-types))
3938           (signature (memq 'signature gnus-article-wash-types))
3939           (overstrike (memq 'overstrike gnus-article-wash-types))
3940           (emphasis (memq 'emphasis gnus-article-wash-types)))
3941       (format "%c%c%c%c%c%c%c"
3942               (if cite ?c ? )
3943               (if (or headers boring) ?h ? )
3944               (if (or pgp pem) ?p ? )
3945               (if signature ?s ? )
3946               (if overstrike ?o ? )
3947               (if gnus-show-mime ?m ? )
3948               (if emphasis ?e ? )))))
3949
3950 (defalias 'gnus-article-hide-headers-if-wanted 'gnus-article-maybe-hide-headers)
3951
3952 (defun gnus-article-maybe-hide-headers ()
3953   "Hide unwanted headers if `gnus-have-all-headers' is nil.
3954 Provided for backwards compatibility."
3955   (when (and (or (not (gnus-buffer-live-p gnus-summary-buffer))
3956                  (not (save-excursion (set-buffer gnus-summary-buffer)
3957                                       gnus-have-all-headers)))
3958              (not gnus-inhibit-hiding))
3959     (gnus-article-hide-headers)))
3960
3961 ;;; Article savers.
3962
3963 (defun gnus-output-to-file (file-name)
3964   "Append the current article to a file named FILE-NAME."
3965   (let ((artbuf (current-buffer)))
3966     (with-temp-buffer
3967       (insert-buffer-substring artbuf)
3968       ;; Append newline at end of the buffer as separator, and then
3969       ;; save it to file.
3970       (goto-char (point-max))
3971       (insert "\n")
3972       (write-region-as-binary (point-min) (point-max) file-name 'append)
3973       t)))
3974
3975 (defun gnus-narrow-to-page (&optional arg)
3976   "Narrow the article buffer to a page.
3977 If given a numerical ARG, move forward ARG pages."
3978   (interactive "P")
3979   (setq arg (if arg (prefix-numeric-value arg) 0))
3980   (save-excursion
3981     (set-buffer gnus-article-buffer)
3982     (goto-char (point-min))
3983     (widen)
3984     ;; Remove any old next/prev buttons.
3985     (when (gnus-visual-p 'page-marker)
3986       (let ((buffer-read-only nil))
3987         (gnus-remove-text-with-property 'gnus-prev)
3988         (gnus-remove-text-with-property 'gnus-next)))
3989     (when
3990         (cond ((< arg 0)
3991                (re-search-backward page-delimiter nil 'move (1+ (abs arg))))
3992               ((> arg 0)
3993                (re-search-forward page-delimiter nil 'move arg)))
3994       (goto-char (match-end 0)))
3995     (narrow-to-region
3996      (point)
3997      (if (re-search-forward page-delimiter nil 'move)
3998          (match-beginning 0)
3999        (point)))
4000     (when (and (gnus-visual-p 'page-marker)
4001                (not (= (point-min) 1)))
4002       (save-excursion
4003         (goto-char (point-min))
4004         (gnus-insert-prev-page-button)))
4005     (when (and (gnus-visual-p 'page-marker)
4006                (< (+ (point-max) 2) (buffer-size)))
4007       (save-excursion
4008         (goto-char (point-max))
4009         (gnus-insert-next-page-button)))))
4010
4011 ;; Article mode commands
4012
4013 (defun gnus-article-goto-next-page ()
4014   "Show the next page of the article."
4015   (interactive)
4016   (when (gnus-article-next-page)
4017     (goto-char (point-min))
4018     (gnus-article-read-summary-keys nil (gnus-character-to-event ?n))))
4019
4020 (defun gnus-article-goto-prev-page ()
4021   "Show the next page of the article."
4022   (interactive)
4023   (if (bobp) (gnus-article-read-summary-keys nil (gnus-character-to-event ?p))
4024     (gnus-article-prev-page nil)))
4025
4026 (defun gnus-article-next-page (&optional lines)
4027   "Show the next page of the current article.
4028 If end of article, return non-nil.  Otherwise return nil.
4029 Argument LINES specifies lines to be scrolled up."
4030   (interactive "p")
4031   (let ((start (window-start))
4032         end-of-buffer end-of-page)
4033     (save-excursion
4034       (move-to-window-line -1)
4035       (if (<= (point) start)
4036           (progn
4037             (forward-line 2)
4038             (setq start (point)))
4039         (forward-line 1)
4040         (setq start nil))
4041       (unless (or (cond ((eq (1+ (buffer-size)) (point))
4042                          (and (pos-visible-in-window-p)
4043                               (setq end-of-buffer t)))
4044                         ((eobp)
4045                          (setq end-of-page t)))
4046                   (not lines))
4047         (move-to-window-line lines)
4048         (unless (search-backward "\n\n" nil t)
4049           (setq start (point)))))
4050     (cond (end-of-buffer t)
4051           (end-of-page
4052            (gnus-narrow-to-page 1)
4053            nil)
4054           (t
4055            (if start
4056                (set-window-start (selected-window) start)
4057              (let (window-pixel-scroll-increment)
4058                (scroll-up lines)))
4059            nil))))
4060
4061 (defun gnus-article-prev-page (&optional lines)
4062   "Show previous page of current article.
4063 Argument LINES specifies lines to be scrolled down."
4064   (interactive "p")
4065   (let (beginning-of-buffer beginning-of-page)
4066     (save-excursion
4067       (move-to-window-line 0)
4068       (cond ((eq 1 (point))
4069              (setq beginning-of-buffer t))
4070             ((bobp)
4071              (setq beginning-of-page t))))
4072     (cond (beginning-of-buffer)
4073           (beginning-of-page
4074            (gnus-narrow-to-page -1))
4075           (t
4076            (condition-case nil
4077                (let (window-pixel-scroll-increment)
4078                  (scroll-down lines))
4079              (beginning-of-buffer
4080               (goto-char (point-min))))))))
4081
4082 (defun gnus-article-refer-article ()
4083   "Read article specified by message-id around point."
4084   (interactive)
4085   (let ((point (point)))
4086     (search-forward ">" nil t)          ;Move point to end of "<....>".
4087     (if (re-search-backward "\\(<[^<> \t\n]+>\\)" nil t)
4088         (let ((message-id (match-string 1)))
4089           (goto-char point)
4090           (set-buffer gnus-summary-buffer)
4091           (gnus-summary-refer-article message-id))
4092       (goto-char (point))
4093       (error "No references around point"))))
4094
4095 (defun gnus-article-show-summary ()
4096   "Reconfigure windows to show summary buffer."
4097   (interactive)
4098   (if (not (gnus-buffer-live-p gnus-summary-buffer))
4099       (error "There is no summary buffer for this article buffer")
4100     (gnus-article-set-globals)
4101     (gnus-configure-windows 'article)
4102     (gnus-summary-goto-subject gnus-current-article)
4103     (gnus-summary-position-point)))
4104
4105 (defun gnus-article-describe-briefly ()
4106   "Describe article mode commands briefly."
4107   (interactive)
4108   (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")))
4109
4110 (defun gnus-article-summary-command ()
4111   "Execute the last keystroke in the summary buffer."
4112   (interactive)
4113   (let ((obuf (current-buffer))
4114         (owin (current-window-configuration))
4115         func)
4116     (switch-to-buffer gnus-article-current-summary 'norecord)
4117     (setq func (lookup-key (current-local-map) (this-command-keys)))
4118     (call-interactively func)
4119     (set-buffer obuf)
4120     (set-window-configuration owin)
4121     (set-window-point (get-buffer-window (current-buffer)) (point))))
4122
4123 (defun gnus-article-summary-command-nosave ()
4124   "Execute the last keystroke in the summary buffer."
4125   (interactive)
4126   (let (func)
4127     (pop-to-buffer gnus-article-current-summary 'norecord)
4128     (setq func (lookup-key (current-local-map) (this-command-keys)))
4129     (call-interactively func)))
4130
4131 (defun gnus-article-check-buffer ()
4132   "Beep if not in an article buffer."
4133   (unless (eq (get-buffer gnus-article-buffer) (current-buffer))
4134     (error "Command invoked outside of a Gnus article buffer")))
4135
4136 (defun gnus-article-read-summary-keys (&optional arg key not-restore-window)
4137   "Read a summary buffer key sequence and execute it from the article buffer."
4138   (interactive "P")
4139   (gnus-article-check-buffer)
4140   (let ((nosaves
4141          '("q" "Q"  "c" "r" "R" "\C-c\C-f" "m"  "a" "f" "F"
4142            "Zc" "ZC" "ZE" "ZJ" "ZQ" "ZZ" "Zn" "ZR" "ZG" "ZN" "ZP"
4143            "=" "^" "\M-^" "|"))
4144         (nosave-but-article
4145          '("A\r"))
4146         (nosave-in-article
4147          '("\C-d"))
4148         (up-to-top
4149          '("n" "Gn" "p" "Gp"))
4150         keys new-sum-point)
4151     (save-excursion
4152       (set-buffer gnus-article-current-summary)
4153       (let (gnus-pick-mode)
4154         (push (or key last-command-event) unread-command-events)
4155         (setq keys (static-if (featurep 'xemacs)
4156                        (events-to-keys (read-key-sequence nil))
4157                      (read-key-sequence nil)))))
4158     (message "")
4159
4160     (if (or (member keys nosaves)
4161             (member keys nosave-but-article)
4162             (member keys nosave-in-article))
4163         (let (func)
4164           (save-window-excursion
4165             (pop-to-buffer gnus-article-current-summary 'norecord)
4166             ;; We disable the pick minor mode commands.
4167             (let (gnus-pick-mode)
4168               (setq func (lookup-key (current-local-map) keys))))
4169           (if (or (not func)
4170                   (numberp func))
4171               (ding)
4172             (unless (member keys nosave-in-article)
4173               (set-buffer gnus-article-current-summary))
4174             (call-interactively func)
4175             (setq new-sum-point (point)))
4176           (when (member keys nosave-but-article)
4177             (pop-to-buffer gnus-article-buffer 'norecord)))
4178       ;; These commands should restore window configuration.
4179       (let ((obuf (current-buffer))
4180             (owin (current-window-configuration))
4181             (opoint (point))
4182             (summary gnus-article-current-summary)
4183             func in-buffer selected)
4184         (if not-restore-window
4185             (pop-to-buffer summary 'norecord)
4186           (switch-to-buffer summary 'norecord))
4187         (setq in-buffer (current-buffer))
4188         ;; We disable the pick minor mode commands.
4189         (if (setq func (let (gnus-pick-mode)
4190                          (lookup-key (current-local-map) keys)))
4191             (progn
4192               (call-interactively func)
4193               (setq new-sum-point (point)))
4194           (ding))
4195         (when (eq in-buffer (current-buffer))
4196           (setq selected (gnus-summary-select-article))
4197           (set-buffer obuf)
4198           (unless not-restore-window
4199             (set-window-configuration owin))
4200           (when (eq selected 'old)
4201             (article-goto-body)
4202             (set-window-start (get-buffer-window (current-buffer))
4203                               1)
4204             (set-window-point (get-buffer-window (current-buffer))
4205                               (point)))
4206           (let ((win (get-buffer-window gnus-article-current-summary)))
4207             (when win
4208               (set-window-point win new-sum-point))))))))
4209
4210 (defun gnus-article-hide (&optional arg force)
4211   "Hide all the gruft in the current article.
4212 This means that PGP stuff, signatures, cited text and (some)
4213 headers will be hidden.
4214 If given a prefix, show the hidden text instead."
4215   (interactive (append (gnus-article-hidden-arg) (list 'force)))
4216   (gnus-article-hide-headers arg)
4217   (gnus-article-hide-list-identifiers arg)
4218   (gnus-article-hide-pgp arg)
4219   (gnus-article-hide-citation-maybe arg force)
4220   (gnus-article-hide-signature arg))
4221
4222 (defun gnus-article-maybe-highlight ()
4223   "Do some article highlighting if article highlighting is requested."
4224   (when (gnus-visual-p 'article-highlight 'highlight)
4225     (gnus-article-highlight-some)))
4226
4227 (defun gnus-check-group-server ()
4228   ;; Make sure the connection to the server is alive.
4229   (unless (gnus-server-opened
4230            (gnus-find-method-for-group gnus-newsgroup-name))
4231     (gnus-check-server (gnus-find-method-for-group gnus-newsgroup-name))
4232     (gnus-request-group gnus-newsgroup-name t)))
4233
4234 (defun gnus-request-article-this-buffer (article group)
4235   "Get an article and insert it into this buffer."
4236   (let (do-update-line sparse-header)
4237     (prog1
4238         (save-excursion
4239           (erase-buffer)
4240           (gnus-kill-all-overlays)
4241           (setq group (or group gnus-newsgroup-name))
4242
4243           ;; Using `gnus-request-article' directly will insert the article into
4244           ;; `nntp-server-buffer' - so we'll save some time by not having to
4245           ;; copy it from the server buffer into the article buffer.
4246
4247           ;; We only request an article by message-id when we do not have the
4248           ;; headers for it, so we'll have to get those.
4249           (when (stringp article)
4250             (gnus-read-header article))
4251
4252           ;; If the article number is negative, that means that this article
4253           ;; doesn't belong in this newsgroup (possibly), so we find its
4254           ;; message-id and request it by id instead of number.
4255           (when (and (numberp article)
4256                      gnus-summary-buffer
4257                      (get-buffer gnus-summary-buffer)
4258                      (gnus-buffer-exists-p gnus-summary-buffer))
4259             (save-excursion
4260               (set-buffer gnus-summary-buffer)
4261               (let ((header (gnus-summary-article-header article)))
4262                 (when (< article 0)
4263                   (cond
4264                    ((memq article gnus-newsgroup-sparse)
4265                     ;; This is a sparse gap article.
4266                     (setq do-update-line article)
4267                     (setq article (mail-header-id header))
4268                     (setq sparse-header (gnus-read-header article))
4269                     (setq gnus-newsgroup-sparse
4270                           (delq article gnus-newsgroup-sparse)))
4271                    ((vectorp header)
4272                     ;; It's a real article.
4273                     (setq article (mail-header-id header)))
4274                    (t
4275                     ;; It is an extracted pseudo-article.
4276                     (setq article 'pseudo)
4277                     (gnus-request-pseudo-article header))))
4278
4279                 (let ((method (gnus-find-method-for-group
4280                                gnus-newsgroup-name)))
4281                   (when (and (eq (car method) 'nneething)
4282                              (vectorp header))
4283                     (let ((dir (expand-file-name
4284                                 (mail-header-subject header)
4285                                 (file-name-as-directory
4286                                  (or (cadr (assq 'nneething-address method))
4287                                      (nth 1 method))))))
4288                       (when (file-directory-p dir)
4289                         (setq article 'nneething)
4290                         (gnus-group-enter-directory dir))))))))
4291
4292           (cond
4293            ;; Refuse to select canceled articles.
4294            ((and (numberp article)
4295                  gnus-summary-buffer
4296                  (get-buffer gnus-summary-buffer)
4297                  (gnus-buffer-exists-p gnus-summary-buffer)
4298                  (eq (cdr (save-excursion
4299                             (set-buffer gnus-summary-buffer)
4300                             (assq article gnus-newsgroup-reads)))
4301                      gnus-canceled-mark))
4302             nil)
4303            ;; Check the backlog.
4304            ((and gnus-keep-backlog
4305                  (gnus-backlog-request-article group article (current-buffer)))
4306             'article)
4307            ;; Check asynchronous pre-fetch.
4308            ((gnus-async-request-fetched-article group article (current-buffer))
4309             (gnus-async-prefetch-next group article gnus-summary-buffer)
4310             (when (and (numberp article) gnus-keep-backlog)
4311               (gnus-backlog-enter-article group article (current-buffer)))
4312             'article)
4313            ;; Check the cache.
4314            ((and gnus-use-cache
4315                  (numberp article)
4316                  (gnus-cache-request-article article group))
4317             'article)
4318            ;; Get the article and put into the article buffer.
4319            ((or (stringp article)
4320                 (numberp article))
4321             (let ((gnus-override-method gnus-override-method)
4322                   (methods (and (stringp article) 
4323                                 gnus-refer-article-method))
4324                   result
4325                   (buffer-read-only nil))
4326               (setq methods
4327                     (if (listp methods)
4328                         methods
4329                       (list methods)))
4330               (when (and (null gnus-override-method)
4331                          methods)
4332                 (setq gnus-override-method (pop methods)))
4333               (while (not result)
4334                 (when (eq gnus-override-method 'current)
4335                   (setq gnus-override-method gnus-current-select-method))
4336                 (erase-buffer)
4337                 (gnus-kill-all-overlays)
4338                 (let ((gnus-newsgroup-name group))
4339                   (gnus-check-group-server))
4340                 (when (gnus-request-article article group (current-buffer))
4341                   (when (numberp article)
4342                     (gnus-async-prefetch-next group article 
4343                                               gnus-summary-buffer)
4344                     (when gnus-keep-backlog
4345                       (gnus-backlog-enter-article
4346                        group article (current-buffer))))
4347                   (setq result 'article))
4348                 (if (not result)
4349                     (if methods
4350                         (setq gnus-override-method (pop methods))
4351                       (setq result 'done))))
4352               (and (eq result 'article) 'article)))
4353            ;; It was a pseudo.
4354            (t article)))
4355
4356       ;; Associate this article with the current summary buffer.
4357       (setq gnus-article-current-summary gnus-summary-buffer)
4358
4359       ;; Take the article from the original article buffer
4360       ;; and place it in the buffer it's supposed to be in.
4361       (when (and (get-buffer gnus-article-buffer)
4362                  (equal (buffer-name (current-buffer))
4363                         (buffer-name (get-buffer gnus-article-buffer))))
4364         (save-excursion
4365           (if (get-buffer gnus-original-article-buffer)
4366               (set-buffer gnus-original-article-buffer)
4367             (set-buffer (gnus-get-buffer-create gnus-original-article-buffer))
4368             (set-buffer-multibyte nil)
4369             (buffer-disable-undo)
4370             (setq major-mode 'gnus-original-article-mode)
4371             (setq buffer-read-only t))
4372           (let (buffer-read-only)
4373             (erase-buffer)
4374             (insert-buffer-substring gnus-article-buffer))
4375           (setq gnus-original-article (cons group article)))
4376
4377         ;; Decode charsets.
4378         (run-hooks 'gnus-article-decode-hook)
4379         ;; Mark article as decoded or not.
4380         (setq gnus-article-decoded-p gnus-article-decode-hook))
4381
4382       ;; Update sparse articles.
4383       (when (and do-update-line
4384                  (or (numberp article)
4385                      (stringp article)))
4386         (let ((buf (current-buffer)))
4387           (set-buffer gnus-summary-buffer)
4388           (gnus-summary-update-article do-update-line sparse-header)
4389           (gnus-summary-goto-subject do-update-line nil t)
4390           (set-window-point (get-buffer-window (current-buffer) t)
4391                             (point))
4392           (set-buffer buf))))))
4393
4394 ;;;
4395 ;;; Article editing
4396 ;;;
4397
4398 (defcustom gnus-article-edit-mode-hook nil
4399   "Hook run in article edit mode buffers."
4400   :group 'gnus-article-various
4401   :type 'hook)
4402
4403 (defcustom gnus-article-edit-article-setup-function
4404   'gnus-article-mime-edit-article-setup
4405   "Function called to setup an editing article buffer."
4406   :group 'gnus-article-various
4407   :type 'function)
4408
4409 (defvar gnus-article-edit-done-function nil)
4410
4411 (defvar gnus-article-edit-mode-map nil)
4412
4413 ;; Should we be using derived.el for this?
4414 (unless gnus-article-edit-mode-map
4415   (setq gnus-article-edit-mode-map (make-sparse-keymap))
4416   (set-keymap-parent gnus-article-edit-mode-map text-mode-map)
4417
4418   (gnus-define-keys gnus-article-edit-mode-map
4419     "\C-c\C-c" gnus-article-edit-done
4420     "\C-c\C-k" gnus-article-edit-exit)
4421
4422   (gnus-define-keys (gnus-article-edit-wash-map
4423                      "\C-c\C-w" gnus-article-edit-mode-map)
4424     "f" gnus-article-edit-full-stops))
4425
4426 (defun gnus-article-edit-mode ()
4427   "Major mode for editing articles.
4428 This is an extended text-mode.
4429
4430 \\{gnus-article-edit-mode-map}"
4431   (interactive)
4432   (setq major-mode 'gnus-article-edit-mode)
4433   (setq mode-name "Article Edit")
4434   (use-local-map gnus-article-edit-mode-map)
4435   (make-local-variable 'gnus-article-edit-done-function)
4436   (make-local-variable 'gnus-prev-winconf)
4437   (setq buffer-read-only nil)
4438   (buffer-enable-undo)
4439   (widen)
4440   (gnus-run-hooks 'text-mode-hook 'gnus-article-edit-mode-hook))
4441
4442 (defun gnus-article-edit (&optional force)
4443   "Edit the current article.
4444 This will have permanent effect only in mail groups.
4445 If FORCE is non-nil, allow editing of articles even in read-only
4446 groups."
4447   (interactive "P")
4448   (when (and (not force)
4449              (gnus-group-read-only-p))
4450     (error "The current newsgroup does not support article editing"))
4451   (gnus-article-date-original)
4452   (gnus-article-edit-article
4453    'ignore
4454    `(lambda (no-highlight)
4455       'ignore
4456       (gnus-summary-edit-article-done
4457        ,(or (mail-header-references gnus-current-headers) "")
4458        ,(gnus-group-read-only-p) ,gnus-summary-buffer no-highlight))))
4459
4460 (defun gnus-article-edit-article (start-func exit-func)
4461   "Start editing the contents of the current article buffer."
4462   (let ((winconf (current-window-configuration)))
4463     (set-buffer gnus-article-buffer)
4464     (gnus-article-edit-mode)
4465     (funcall start-func)
4466     (gnus-configure-windows 'edit-article)
4467     (setq gnus-article-edit-done-function exit-func)
4468     (setq gnus-prev-winconf winconf)
4469     (when gnus-article-edit-article-setup-function
4470       (funcall gnus-article-edit-article-setup-function))
4471     (gnus-message 6 "C-c C-c to end edits")))
4472
4473 (defun gnus-article-edit-done (&optional arg)
4474   "Update the article edits and exit."
4475   (interactive "P")
4476   (save-excursion
4477     (save-restriction
4478       (widen)
4479       (when (article-goto-body)
4480         (let ((lines (count-lines (point) (point-max)))
4481               (length (- (point-max) (point)))
4482               (case-fold-search t)
4483               (body (copy-marker (point))))
4484           (goto-char (point-min))
4485           (when (re-search-forward "^content-length:[ \t]\\([0-9]+\\)" body t)
4486             (delete-region (match-beginning 1) (match-end 1))
4487             (insert (number-to-string length)))
4488           (goto-char (point-min))
4489           (when (re-search-forward
4490                  "^x-content-length:[ \t]\\([0-9]+\\)" body t)
4491             (delete-region (match-beginning 1) (match-end 1))
4492             (insert (number-to-string length)))
4493           (goto-char (point-min))
4494           (when (re-search-forward "^lines:[ \t]\\([0-9]+\\)" body t)
4495             (delete-region (match-beginning 1) (match-end 1))
4496             (insert (number-to-string lines)))))))
4497   (let ((func gnus-article-edit-done-function)
4498         (buf (current-buffer))
4499         (start (window-start)))
4500     (remove-hook 'gnus-article-mode-hook
4501                  'gnus-article-mime-edit-article-unwind)
4502     (gnus-article-edit-exit)
4503     (save-excursion
4504       (set-buffer buf)
4505       (let ((buffer-read-only nil))
4506         (funcall func arg))
4507       ;; The cache and backlog have to be flushed somewhat.
4508       (when gnus-keep-backlog
4509         (gnus-backlog-remove-article
4510          (car gnus-article-current) (cdr gnus-article-current)))
4511       ;; Flush original article as well.
4512       (save-excursion
4513         (when (get-buffer gnus-original-article-buffer)
4514           (set-buffer gnus-original-article-buffer)
4515           (setq gnus-original-article nil)))
4516       (when gnus-use-cache
4517         (gnus-cache-update-article
4518          (car gnus-article-current) (cdr gnus-article-current))))
4519     (set-buffer buf)
4520     (set-window-start (get-buffer-window buf) start)
4521     (set-window-point (get-buffer-window buf) (point))))
4522
4523 (defun gnus-article-edit-exit ()
4524   "Exit the article editing without updating."
4525   (interactive)
4526   ;; We remove all text props from the article buffer.
4527   (let ((buf (buffer-substring-no-properties (point-min) (point-max)))
4528         (curbuf (current-buffer))
4529         (p (point))
4530         (window-start (window-start)))
4531     (erase-buffer)
4532     (insert buf)
4533     (let ((winconf gnus-prev-winconf))
4534       (gnus-article-mode)
4535       (set-window-configuration winconf)
4536       ;; Tippy-toe some to make sure that point remains where it was.
4537       (save-current-buffer
4538         (set-buffer curbuf)
4539         (set-window-start (get-buffer-window (current-buffer)) window-start)
4540         (goto-char p)))))
4541
4542 (defun gnus-article-edit-full-stops ()
4543   "Interactively repair spacing at end of sentences."
4544   (interactive)
4545   (save-excursion
4546     (goto-char (point-min))
4547     (search-forward-regexp "^$" nil t)
4548     (let ((case-fold-search nil))
4549       (query-replace-regexp "\\([.!?][])}]* \\)\\([[({A-Z]\\)" "\\1 \\2"))))
4550
4551 ;;;
4552 ;;; Article editing with MIME-Edit
4553 ;;;
4554
4555 (defcustom gnus-article-mime-edit-article-setup-hook nil
4556   "Hook run after setting up a MIME editing article buffer."
4557   :group 'gnus-article-various
4558   :type 'hook)
4559
4560 (defun gnus-article-mime-edit-article-unwind ()
4561   "Unwind `gnus-article-buffer' if article editing was given up."
4562   (remove-hook 'gnus-article-mode-hook 'gnus-article-mime-edit-article-unwind)
4563   (when mime-edit-mode-flag
4564     (mime-edit-exit 'nomime 'no-error)
4565     (message ""))
4566   (when (featurep 'font-lock)
4567     (setq font-lock-defaults nil)
4568     (font-lock-mode 0)))
4569
4570 (defun gnus-article-mime-edit-article-setup ()
4571   "Convert current buffer to MIME-Edit buffer and turn on MIME-Edit mode
4572 after replacing with the original article."
4573   (setq gnus-show-mime t)
4574   (setq gnus-article-edit-done-function
4575         `(lambda (&rest args)
4576            (when mime-edit-mode-flag
4577              (mime-edit-exit)
4578              (message ""))
4579            (goto-char (point-min))
4580            (let (case-fold-search)
4581              (when (re-search-forward
4582                     (format "^%s$" (regexp-quote mail-header-separator))
4583                     nil t)
4584                (replace-match "")))
4585            (when (featurep 'font-lock)
4586              (setq font-lock-defaults nil)
4587              (font-lock-mode 0))
4588            (apply ,gnus-article-edit-done-function args)
4589            (set-buffer gnus-original-article-buffer)
4590            (erase-buffer)
4591            (insert-buffer gnus-article-buffer)
4592            (setq gnus-current-headers (gnus-article-make-full-mail-header))
4593            (gnus-article-prepare-display)))
4594   (substitute-key-definition
4595    'gnus-article-edit-exit 'gnus-article-mime-edit-exit
4596    gnus-article-edit-mode-map)
4597   (erase-buffer)
4598   (insert-buffer gnus-original-article-buffer)
4599   (mime-edit-again)
4600   (when (featurep 'font-lock)
4601     (set (make-local-variable 'font-lock-defaults)
4602          '(message-font-lock-keywords t))
4603     (font-lock-set-defaults)
4604     (turn-on-font-lock))
4605   (add-hook 'gnus-article-mode-hook 'gnus-article-mime-edit-article-unwind)
4606   (gnus-run-hooks 'gnus-article-mime-edit-article-setup-hook))
4607
4608 (defun gnus-article-mime-edit-exit ()
4609   "Exit the article MIME editing without updating."
4610   (interactive)
4611   (let ((winconf gnus-prev-winconf)
4612         buf)
4613     (when mime-edit-mode-flag
4614       (mime-edit-exit)
4615       (message ""))
4616     (goto-char (point-min))
4617     (let (case-fold-search)
4618       (when (re-search-forward
4619              (format "^%s$" (regexp-quote mail-header-separator)) nil t)
4620         (replace-match "")))
4621     (when (featurep 'font-lock)
4622       (setq font-lock-defaults nil)
4623       (font-lock-mode 0))
4624     ;; We remove all text props from the article buffer.
4625     (setq buf (format "%s" (buffer-string)))
4626     (set-buffer (get-buffer-create gnus-original-article-buffer))
4627     (erase-buffer)
4628     (insert buf)
4629     (setq gnus-current-headers (gnus-article-make-full-mail-header))
4630     (gnus-article-prepare-display)
4631     (set-window-configuration winconf)))
4632
4633 ;;;
4634 ;;; Article highlights
4635 ;;;
4636
4637 ;; Written by Per Abrahamsen <abraham@iesd.auc.dk>.
4638
4639 ;;; Internal Variables:
4640
4641 (defcustom gnus-button-url-regexp "\\b\\(\\(www\\.\\|\\(s?https?\\|ftp\\|file\\|gopher\\|news\\|telnet\\|wais\\|mailto\\):\\)\\(//[-a-zA-Z0-9_.]+:[0-9]*\\)?\\([-a-zA-Z0-9_=!?#$@~`%&*+|\\/:;.,]\\|\\w\\)+\\([-a-zA-Z0-9_=#$@~`%&*+|\\/]\\|\\w\\)\\)"
4642   "Regular expression that matches URLs."
4643   :group 'gnus-article-buttons
4644   :type 'regexp)
4645
4646 (defcustom gnus-button-alist
4647   `(("<\\(url:[>\n\t ]*?\\)?news:[>\n\t ]*\\([^>\n\t ]*@[^>\n\t ]*\\)>"
4648      0 t gnus-button-message-id 2)
4649     ("\\bnews:\\([^>\n\t ]*@[^>)!;:,\n\t ]*\\)" 0 t gnus-button-message-id 1)
4650     ("\\(\\b<\\(url:[>\n\t ]*\\)?news:[>\n\t ]*\\(//\\)?\\([^>\n\t ]*\\)>\\)"
4651      1 t
4652      gnus-button-fetch-group 4)
4653     ("\\bnews:\\(//\\)?\\([^'\">\n\t ]+\\)" 0 t gnus-button-fetch-group 2)
4654     ("\\bin\\( +article\\| +message\\)? +\\(<\\([^\n @<>]+@[^\n @<>]+\\)>\\)" 2
4655      t gnus-button-message-id 3)
4656     ("\\(<URL: *\\)mailto: *\\([^> \n\t]+\\)>" 0 t gnus-url-mailto 2)
4657     ("mailto:\\([-a-zA-Z.@_+0-9%]+\\)" 0 t gnus-url-mailto 1)
4658     ("\\bmailto:\\([^ \n\t]+\\)" 0 t gnus-url-mailto 1)
4659     ;; This is how URLs _should_ be embedded in text...
4660     ("<URL: *\\([^<>]*\\)>" 0 t gnus-button-embedded-url 1)
4661     ;; Raw URLs.
4662     (,gnus-button-url-regexp 0 t browse-url 0))
4663   "*Alist of regexps matching buttons in article bodies.
4664
4665 Each entry has the form (REGEXP BUTTON FORM CALLBACK PAR...), where
4666 REGEXP: is the string matching text around the button,
4667 BUTTON: is the number of the regexp grouping actually matching the button,
4668 FORM: is a lisp expression which must eval to true for the button to
4669 be added,
4670 CALLBACK: is the function to call when the user push this button, and each
4671 PAR: is a number of a regexp grouping whose text will be passed to CALLBACK.
4672
4673 CALLBACK can also be a variable, in that case the value of that
4674 variable it the real callback function."
4675   :group 'gnus-article-buttons
4676   :type '(repeat (list regexp
4677                        (integer :tag "Button")
4678                        (sexp :tag "Form")
4679                        (function :tag "Callback")
4680                        (repeat :tag "Par"
4681                                :inline t
4682                                (integer :tag "Regexp group")))))
4683
4684 (defcustom gnus-header-button-alist
4685   `(("^\\(References\\|Message-I[Dd]\\):" "<[^>]+>"
4686      0 t gnus-button-message-id 0)
4687     ("^\\(From\\|Reply-To\\):" ": *\\(.+\\)$" 1 t gnus-button-reply 1)
4688     ("^\\(Cc\\|To\\):" "[^ \t\n<>,()\"]+@[^ \t\n<>,()\"]+"
4689      0 t gnus-button-mailto 0)
4690     ("^X-[Uu][Rr][Ll]:" ,gnus-button-url-regexp 0 t browse-url 0)
4691     ("^Subject:" ,gnus-button-url-regexp 0 t browse-url 0)
4692     ("^[^:]+:" ,gnus-button-url-regexp 0 t browse-url 0)
4693     ("^[^:]+:" "\\(<\\(url: \\)?news:\\([^>\n ]*\\)>\\)" 1 t
4694      gnus-button-message-id 3))
4695   "*Alist of headers and regexps to match buttons in article heads.
4696
4697 This alist is very similar to `gnus-button-alist', except that each
4698 alist has an additional HEADER element first in each entry:
4699
4700 \(HEADER REGEXP BUTTON FORM CALLBACK PAR)
4701
4702 HEADER is a regexp to match a header.  For a fuller explanation, see
4703 `gnus-button-alist'."
4704   :group 'gnus-article-buttons
4705   :group 'gnus-article-headers
4706   :type '(repeat (list (regexp :tag "Header")
4707                        regexp
4708                        (integer :tag "Button")
4709                        (sexp :tag "Form")
4710                        (function :tag "Callback")
4711                        (repeat :tag "Par"
4712                                :inline t
4713                                (integer :tag "Regexp group")))))
4714
4715 (defvar gnus-button-regexp nil)
4716 (defvar gnus-button-marker-list nil)
4717 ;; Regexp matching any of the regexps from `gnus-button-alist'.
4718
4719 (defvar gnus-button-last nil)
4720 ;; The value of `gnus-button-alist' when `gnus-button-regexp' was build.
4721
4722 ;;; Commands:
4723
4724 (defun gnus-article-push-button (event)
4725   "Check text under the mouse pointer for a callback function.
4726 If the text under the mouse pointer has a `gnus-callback' property,
4727 call it with the value of the `gnus-data' text property."
4728   (interactive "e")
4729   (set-buffer (window-buffer (posn-window (event-start event))))
4730   (let* ((pos (posn-point (event-start event)))
4731          (data (get-text-property pos 'gnus-data))
4732          (fun (get-text-property pos 'gnus-callback)))
4733     (goto-char pos)
4734     (when fun
4735       (funcall fun data))))
4736
4737 (defun gnus-article-press-button ()
4738   "Check text at point for a callback function.
4739 If the text at point has a `gnus-callback' property,
4740 call it with the value of the `gnus-data' text property."
4741   (interactive)
4742   (let* ((data (get-text-property (point) 'gnus-data))
4743          (fun (get-text-property (point) 'gnus-callback)))
4744     (when fun
4745       (funcall fun data))))
4746
4747 (defun gnus-article-prev-button (n)
4748   "Move point to N buttons backward.
4749 If N is negative, move forward instead."
4750   (interactive "p")
4751   (gnus-article-next-button (- n)))
4752
4753 (defun gnus-article-next-button (n)
4754   "Move point to N buttons forward.
4755 If N is negative, move backward instead."
4756   (interactive "p")
4757   (let ((function (if (< n 0) 'previous-single-property-change
4758                     'next-single-property-change))
4759         (inhibit-point-motion-hooks t)
4760         (backward (< n 0))
4761         (limit (if (< n 0) (point-min) (point-max))))
4762     (setq n (abs n))
4763     (while (and (not (= limit (point)))
4764                 (> n 0))
4765       ;; Skip past the current button.
4766       (when (get-text-property (point) 'gnus-callback)
4767         (goto-char (funcall function (point) 'gnus-callback nil limit)))
4768       ;; Go to the next (or previous) button.
4769       (gnus-goto-char (funcall function (point) 'gnus-callback nil limit))
4770       ;; Put point at the start of the button.
4771       (when (and backward (not (get-text-property (point) 'gnus-callback)))
4772         (goto-char (funcall function (point) 'gnus-callback nil limit)))
4773       ;; Skip past intangible buttons.
4774       (when (get-text-property (point) 'intangible)
4775         (incf n))
4776       (decf n))
4777     (unless (zerop n)
4778       (gnus-message 5 "No more buttons"))
4779     n))
4780
4781 (defun gnus-article-highlight (&optional force)
4782   "Highlight current article.
4783 This function calls `gnus-article-highlight-headers',
4784 `gnus-article-highlight-citation',
4785 `gnus-article-highlight-signature', and `gnus-article-add-buttons' to
4786 do the highlighting.  See the documentation for those functions."
4787   (interactive (list 'force))
4788   (gnus-article-highlight-headers)
4789   (gnus-article-highlight-citation force)
4790   (gnus-article-highlight-signature)
4791   (gnus-article-add-buttons force)
4792   (gnus-article-add-buttons-to-head))
4793
4794 (defun gnus-article-highlight-some (&optional force)
4795   "Highlight current article.
4796 This function calls `gnus-article-highlight-headers',
4797 `gnus-article-highlight-signature', and `gnus-article-add-buttons' to
4798 do the highlighting.  See the documentation for those functions."
4799   (interactive (list 'force))
4800   (gnus-article-highlight-headers)
4801   (gnus-article-highlight-signature)
4802   (gnus-article-add-buttons))
4803
4804 (defun gnus-article-highlight-headers ()
4805   "Highlight article headers as specified by `gnus-header-face-alist'."
4806   (interactive)
4807   (save-excursion
4808     (set-buffer gnus-article-buffer)
4809     (save-restriction
4810       (let ((alist gnus-header-face-alist)
4811             (buffer-read-only nil)
4812             (case-fold-search t)
4813             (inhibit-point-motion-hooks t)
4814             entry regexp header-face field-face from hpoints fpoints)
4815         (article-narrow-to-head)
4816         (while (setq entry (pop alist))
4817           (goto-char (point-min))
4818           (setq regexp (concat "^\\("
4819                                (if (string-equal "" (nth 0 entry))
4820                                    "[^\t ]"
4821                                  (nth 0 entry))
4822                                "\\)")
4823                 header-face (nth 1 entry)
4824                 field-face (nth 2 entry))
4825           (while (and (re-search-forward regexp nil t)
4826                       (not (eobp)))
4827             (beginning-of-line)
4828             (setq from (point))
4829             (unless (search-forward ":" nil t)
4830               (forward-char 1))
4831             (when (and header-face
4832                        (not (memq (point) hpoints)))
4833               (push (point) hpoints)
4834               (gnus-put-text-property from (point) 'face header-face))
4835             (when (and field-face
4836                        (not (memq (setq from (point)) fpoints)))
4837               (push from fpoints)
4838               (if (re-search-forward "^[^ \t]" nil t)
4839                   (forward-char -2)
4840                 (goto-char (point-max)))
4841               (gnus-put-text-property from (point) 'face field-face))))))))
4842
4843 (defun gnus-article-highlight-signature ()
4844   "Highlight the signature in an article.
4845 It does this by highlighting everything after
4846 `gnus-signature-separator' using `gnus-signature-face'."
4847   (interactive)
4848   (when gnus-signature-face
4849     (save-excursion
4850       (set-buffer gnus-article-buffer)
4851       (let ((buffer-read-only nil)
4852             (inhibit-point-motion-hooks t))
4853         (save-restriction
4854           (when (gnus-article-narrow-to-signature)
4855             (gnus-overlay-put (gnus-make-overlay (point-min) (point-max))
4856                               'face gnus-signature-face)))))))
4857
4858 (defun gnus-article-buttonize-signature ()
4859   "Add button to the signature."
4860   (interactive)
4861   (save-excursion
4862     (set-buffer gnus-article-buffer)
4863     (let ((buffer-read-only nil)
4864           (inhibit-point-motion-hooks t))
4865       (when (gnus-article-search-signature)
4866         (gnus-article-add-button (match-beginning 0) (match-end 0)
4867                                  'gnus-signature-toggle
4868                                  (set-marker (make-marker)
4869                                              (1+ (match-end 0))))))))
4870
4871 (defun gnus-button-in-region-p (b e prop)
4872   "Say whether PROP exists in the region."
4873   (text-property-not-all b e prop nil))
4874
4875 (defun gnus-article-add-buttons (&optional force)
4876   "Find external references in the article and make buttons of them.
4877 \"External references\" are things like Message-IDs and URLs, as
4878 specified by `gnus-button-alist'."
4879   (interactive (list 'force))
4880   (save-excursion
4881     (set-buffer gnus-article-buffer)
4882     (let ((buffer-read-only nil)
4883           (inhibit-point-motion-hooks t)
4884           (case-fold-search t)
4885           (alist gnus-button-alist)
4886           beg entry regexp)
4887       ;; Remove all old markers.
4888       (let (marker entry new-list)
4889         (while (setq marker (pop gnus-button-marker-list))
4890           (if (or (< marker (point-min)) (>= marker (point-max)))
4891               (push marker new-list)
4892             (goto-char marker)
4893             (when (setq entry (gnus-button-entry))
4894               (put-text-property (match-beginning (nth 1 entry))
4895                                  (match-end (nth 1 entry))
4896                                  'gnus-callback nil))
4897             (set-marker marker nil)))
4898         (setq gnus-button-marker-list new-list))
4899       ;; We skip the headers.
4900       (article-goto-body)
4901       (setq beg (point))
4902       (while (setq entry (pop alist))
4903         (setq regexp (car entry))
4904         (goto-char beg)
4905         (while (re-search-forward regexp nil t)
4906           (let* ((start (and entry (match-beginning (nth 1 entry))))
4907                  (end (and entry (match-end (nth 1 entry))))
4908                  (from (match-beginning 0)))
4909             (when (and (or (eq t (nth 2 entry))
4910                            (eval (nth 2 entry)))
4911                        (not (gnus-button-in-region-p
4912                              start end 'gnus-callback)))
4913               ;; That optional form returned non-nil, so we add the
4914               ;; button.
4915               (gnus-article-add-button
4916                start end 'gnus-button-push
4917                (car (push (set-marker (make-marker) from)
4918                           gnus-button-marker-list))))))))))
4919
4920 ;; Add buttons to the head of an article.
4921 (defun gnus-article-add-buttons-to-head ()
4922   "Add buttons to the head of the article."
4923   (interactive)
4924   (save-excursion
4925     (set-buffer gnus-article-buffer)
4926     (save-restriction
4927       (let ((buffer-read-only nil)
4928             (inhibit-point-motion-hooks t)
4929             (case-fold-search t)
4930             (alist gnus-header-button-alist)
4931             entry beg end)
4932         (article-narrow-to-head)
4933         (while alist
4934           ;; Each alist entry.
4935           (setq entry (car alist)
4936                 alist (cdr alist))
4937           (goto-char (point-min))
4938           (while (re-search-forward (car entry) nil t)
4939             ;; Each header matching the entry.
4940             (setq beg (match-beginning 0))
4941             (setq end (or (and (re-search-forward "^[^ \t]" nil t)
4942                                (match-beginning 0))
4943                           (point-max)))
4944             (goto-char beg)
4945             (while (re-search-forward (nth 1 entry) end t)
4946               ;; Each match within a header.
4947               (let* ((entry (cdr entry))
4948                      (start (match-beginning (nth 1 entry)))
4949                      (end (match-end (nth 1 entry)))
4950                      (form (nth 2 entry)))
4951                 (goto-char (match-end 0))
4952                 (when (eval form)
4953                   (gnus-article-add-button
4954                    start end (nth 3 entry)
4955                    (buffer-substring (match-beginning (nth 4 entry))
4956                                      (match-end (nth 4 entry)))))))
4957             (goto-char end)))))))
4958
4959 ;;; External functions:
4960
4961 (defun gnus-article-add-button (from to fun &optional data)
4962   "Create a button between FROM and TO with callback FUN and data DATA."
4963   (when gnus-article-button-face
4964     (gnus-overlay-put (gnus-make-overlay from to)
4965                       'face gnus-article-button-face))
4966   (gnus-add-text-properties
4967    from to
4968    (nconc (and gnus-article-mouse-face
4969                (list gnus-mouse-face-prop gnus-article-mouse-face))
4970           (list 'gnus-callback fun)
4971           (and data (list 'gnus-data data))))
4972   (widget-convert-button 'link from to :action 'gnus-widget-press-button
4973                          ;; Quote `:button-keymap' for Mule 2.3
4974                          ;; but it won't work.
4975                          ':button-keymap gnus-widget-button-keymap))
4976
4977 ;;; Internal functions:
4978
4979 (defun gnus-article-set-globals ()
4980   (save-excursion
4981     (set-buffer gnus-summary-buffer)
4982     (gnus-set-global-variables)))
4983
4984 (defun gnus-signature-toggle (end)
4985   (save-excursion
4986     (set-buffer gnus-article-buffer)
4987     (let ((buffer-read-only nil)
4988           (inhibit-point-motion-hooks t)
4989           (limit (next-single-property-change end 'mime-view-entity
4990                                               nil (point-max))))
4991       (if (get-text-property end 'invisible)
4992           (gnus-article-unhide-text end limit)
4993         (gnus-article-hide-text end limit gnus-hidden-properties)))))
4994
4995 (defun gnus-button-entry ()
4996   ;; Return the first entry in `gnus-button-alist' matching this place.
4997   (let ((alist gnus-button-alist)
4998         (entry nil))
4999     (while alist
5000       (setq entry (pop alist))
5001       (if (looking-at (car entry))
5002           (setq alist nil)
5003         (setq entry nil)))
5004     entry))
5005
5006 (defun gnus-button-push (marker)
5007   ;; Push button starting at MARKER.
5008   (save-excursion
5009     (goto-char marker)
5010     (let* ((entry (gnus-button-entry))
5011            (inhibit-point-motion-hooks t)
5012            (fun (nth 3 entry))
5013            (args (mapcar (lambda (group)
5014                            (let ((string (match-string group)))
5015                              (gnus-set-text-properties
5016                               0 (length string) nil string)
5017                              string))
5018                          (nthcdr 4 entry))))
5019       (cond
5020        ((fboundp fun)
5021         (apply fun args))
5022        ((and (boundp fun)
5023              (fboundp (symbol-value fun)))
5024         (apply (symbol-value fun) args))
5025        (t
5026         (gnus-message 1 "You must define `%S' to use this button"
5027                       (cons fun args)))))))
5028
5029 (defun gnus-button-message-id (message-id)
5030   "Fetch MESSAGE-ID."
5031   (save-excursion
5032     (set-buffer gnus-summary-buffer)
5033     (gnus-summary-refer-article message-id)))
5034
5035 (defun gnus-button-fetch-group (address)
5036   "Fetch GROUP specified by ADDRESS."
5037   (if (not (string-match "[:/]" address))
5038       ;; This is just a simple group url.
5039       (gnus-group-read-ephemeral-group address gnus-select-method)
5040     (if (not (string-match "^\\([^:/]+\\)\\(:\\([^/]+\\)/\\)?\\(.*\\)$"
5041                            address))
5042         (error "Can't parse %s" address)
5043       (gnus-group-read-ephemeral-group
5044        (match-string 4 address)
5045        `(nntp ,(match-string 1 address)
5046               (nntp-address ,(match-string 1 address))
5047               (nntp-port-number ,(if (match-end 3)
5048                                      (match-string 3 address)
5049                                    "nntp")))))))
5050
5051 (defun gnus-url-parse-query-string (query &optional downcase)
5052   (let (retval pairs cur key val)
5053     (setq pairs (split-string query "&"))
5054     (while pairs
5055       (setq cur (car pairs)
5056             pairs (cdr pairs))
5057       (if (not (string-match "=" cur))
5058           nil                           ; Grace
5059         (setq key (gnus-url-unhex-string (substring cur 0 (match-beginning 0)))
5060               val (gnus-url-unhex-string (substring cur (match-end 0) nil)))
5061         (if downcase
5062             (setq key (downcase key)))
5063         (setq cur (assoc key retval))
5064         (if cur
5065             (setcdr cur (cons val (cdr cur)))
5066           (setq retval (cons (list key val) retval)))))
5067     retval))
5068
5069 (defun gnus-url-unhex (x)
5070   (if (> x ?9)
5071       (if (>= x ?a)
5072           (+ 10 (- x ?a))
5073         (+ 10 (- x ?A)))
5074     (- x ?0)))
5075
5076 (defun gnus-url-unhex-string (str &optional allow-newlines)
5077   "Remove %XXX embedded spaces, etc in a url.
5078 If optional second argument ALLOW-NEWLINES is non-nil, then allow the
5079 decoding of carriage returns and line feeds in the string, which is normally
5080 forbidden in URL encoding."
5081   (setq str (or str ""))
5082   (let ((tmp "")
5083         (case-fold-search t))
5084     (while (string-match "%[0-9a-f][0-9a-f]" str)
5085       (let* ((start (match-beginning 0))
5086              (ch1 (gnus-url-unhex (elt str (+ start 1))))
5087              (code (+ (* 16 ch1)
5088                       (gnus-url-unhex (elt str (+ start 2))))))
5089         (setq tmp (concat
5090                    tmp (substring str 0 start)
5091                    (cond
5092                     (allow-newlines
5093                      (char-to-string code))
5094                     ((or (= code ?\n) (= code ?\r))
5095                      " ")
5096                     (t (char-to-string code))))
5097               str (substring str (match-end 0)))))
5098     (setq tmp (concat tmp str))
5099     tmp))
5100
5101 (defun gnus-url-mailto (url)
5102   ;; Send mail to someone
5103   (when (string-match "mailto:/*\\(.*\\)" url)
5104     (setq url (substring url (match-beginning 1) nil)))
5105   (let (to args subject func)
5106     (if (string-match (regexp-quote "?") url)
5107         (setq to (gnus-url-unhex-string (substring url 0 (match-beginning 0)))
5108               args (gnus-url-parse-query-string
5109                     (substring url (match-end 0) nil) t))
5110       (setq to (gnus-url-unhex-string url)))
5111     (setq args (cons (list "to" to) args)
5112           subject (cdr-safe (assoc "subject" args)))
5113     (gnus-setup-message 'reply
5114       (message-mail)
5115       (while args
5116         (setq func (intern-soft (concat "message-goto-" (downcase (caar args)))))
5117         (if (fboundp func)
5118             (funcall func)
5119           (message-position-on-field (caar args)))
5120         (insert (mapconcat 'identity (cdar args) ", "))
5121         (setq args (cdr args)))
5122       (if subject
5123           (message-goto-body)
5124         (message-goto-subject)))))
5125
5126 (defun gnus-button-mailto (address)
5127   "Mail to ADDRESS."
5128   (set-buffer (gnus-copy-article-buffer))
5129   (message-reply address))
5130
5131 (defalias 'gnus-button-reply 'message-reply)
5132
5133 (defun gnus-button-embedded-url (address)
5134   "Activate ADDRESS with `browse-url'."
5135   (browse-url (gnus-strip-whitespace address)))
5136
5137 (defun gnus-article-smiley-display ()
5138   "Display \"smileys\" as small graphical icons."
5139   (smiley-toggle-buffer 1 (current-buffer) (point-min) (point-max)))
5140
5141 ;;; Next/prev buttons in the article buffer.
5142
5143 (defvar gnus-next-page-line-format "%{%(Next page...%)%}\n")
5144 (defvar gnus-prev-page-line-format "%{%(Previous page...%)%}\n")
5145
5146 (defvar gnus-prev-page-map nil)
5147 (unless gnus-prev-page-map
5148   (setq gnus-prev-page-map (make-sparse-keymap))
5149   (define-key gnus-prev-page-map gnus-mouse-2 'gnus-button-prev-page)
5150   (define-key gnus-prev-page-map "\r" 'gnus-button-prev-page))
5151
5152 (static-if (featurep 'xemacs)
5153     (defun gnus-insert-prev-page-button ()
5154       (let ((buffer-read-only nil))
5155         (gnus-eval-format
5156          gnus-prev-page-line-format nil
5157          `(gnus-prev t local-map ,gnus-prev-page-map
5158                      gnus-callback gnus-article-button-prev-page
5159                      article-type annotation))))
5160   (defun gnus-insert-prev-page-button ()
5161     (let ((buffer-read-only nil)
5162           (situation (get-text-property (point-min) 'mime-view-situation)))
5163       (set-keymap-parent gnus-prev-page-map (current-local-map))
5164       (gnus-eval-format
5165        gnus-prev-page-line-format nil
5166        `(gnus-prev t local-map ,gnus-prev-page-map
5167                    gnus-callback gnus-article-button-prev-page
5168                    article-type annotation
5169                    mime-view-situation ,situation))))
5170   )
5171
5172 (defvar gnus-next-page-map nil)
5173 (unless gnus-next-page-map
5174   (setq gnus-next-page-map (make-sparse-keymap))
5175   (define-key gnus-next-page-map gnus-mouse-2 'gnus-button-next-page)
5176   (define-key gnus-next-page-map "\r" 'gnus-button-next-page))
5177
5178 (defun gnus-button-next-page ()
5179   "Go to the next page."
5180   (interactive)
5181   (let ((win (selected-window)))
5182     (select-window (get-buffer-window gnus-article-buffer t))
5183     (gnus-article-next-page)
5184     (select-window win)))
5185
5186 (defun gnus-button-prev-page ()
5187   "Go to the prev page."
5188   (interactive)
5189   (let ((win (selected-window)))
5190     (select-window (get-buffer-window gnus-article-buffer t))
5191     (gnus-article-prev-page)
5192     (select-window win)))
5193
5194 (static-if (featurep 'xemacs)
5195     (defun gnus-insert-next-page-button ()
5196       (let ((buffer-read-only nil))
5197         (gnus-eval-format gnus-next-page-line-format nil
5198                           `(gnus-next
5199                             t local-map ,gnus-next-page-map
5200                             gnus-callback gnus-article-button-next-page
5201                             article-type annotation))))
5202   (defun gnus-insert-next-page-button ()
5203     (let ((buffer-read-only nil)
5204           (situation (get-text-property (point-min) 'mime-view-situation)))
5205       (set-keymap-parent gnus-next-page-map (current-local-map))
5206       (gnus-eval-format gnus-next-page-line-format nil
5207                         `(gnus-next
5208                           t local-map ,gnus-next-page-map
5209                           gnus-callback gnus-article-button-next-page
5210                           article-type annotation
5211                           mime-view-situation ,situation))))
5212   )
5213
5214 (defun gnus-article-button-next-page (arg)
5215   "Go to the next page."
5216   (interactive "P")
5217   (let ((win (selected-window)))
5218     (select-window (get-buffer-window gnus-article-buffer t))
5219     (gnus-article-next-page)
5220     (select-window win)))
5221
5222 (defun gnus-article-button-prev-page (arg)
5223   "Go to the prev page."
5224   (interactive "P")
5225   (let ((win (selected-window)))
5226     (select-window (get-buffer-window gnus-article-buffer t))
5227     (gnus-article-prev-page)
5228     (select-window win)))
5229
5230 (defvar gnus-decode-header-methods
5231   '(mail-decode-encoded-word-region)
5232   "List of methods used to decode headers.
5233
5234 This variable is a list of FUNCTION or (REGEXP . FUNCTION).  If item
5235 is FUNCTION, FUNCTION will be apply to all newsgroups.  If item is a
5236 (REGEXP . FUNCTION), FUNCTION will be only apply to thes newsgroups
5237 whose names match REGEXP.
5238
5239 For example:
5240 ((\"chinese\" . gnus-decode-encoded-word-region-by-guess)
5241  mail-decode-encoded-word-region
5242  (\"chinese\" . rfc1843-decode-region))
5243 ")
5244
5245 (defvar gnus-decode-header-methods-cache nil)
5246
5247 (defun gnus-multi-decode-header (start end)
5248   "Apply the functions from `gnus-encoded-word-methods' that match."
5249   (unless (and gnus-decode-header-methods-cache
5250                (eq gnus-newsgroup-name
5251                    (car gnus-decode-header-methods-cache)))
5252     (setq gnus-decode-header-methods-cache (list gnus-newsgroup-name))
5253     (mapcar (lambda (x)
5254               (if (symbolp x)
5255                   (nconc gnus-decode-header-methods-cache (list x))
5256                 (if (and gnus-newsgroup-name
5257                          (string-match (car x) gnus-newsgroup-name))
5258                     (nconc gnus-decode-header-methods-cache
5259                            (list (cdr x))))))
5260           gnus-decode-header-methods))
5261   (let ((xlist gnus-decode-header-methods-cache))
5262     (pop xlist)
5263     (save-restriction
5264       (narrow-to-region start end)
5265       (while xlist
5266         (funcall (pop xlist) (point-min) (point-max))))))
5267
5268 ;;;
5269 ;;; Treatment top-level handling.
5270 ;;;
5271
5272 (defun gnus-treat-article (condition &optional part-number total-parts type)
5273   (let ((length (- (point-max) (point-min)))
5274         (alist gnus-treatment-function-alist)
5275         (article-goto-body-goes-to-point-min-p t)
5276         (treated-type
5277          (or (not type)
5278              (catch 'found
5279                (let ((list gnus-article-treat-types))
5280                  (while list
5281                    (when (string-match (pop list) type)
5282                      (throw 'found t)))))))
5283         (highlightp (gnus-visual-p 'article-highlight 'highlight))
5284         (entity (static-unless (featurep 'xemacs)
5285                   (when (eq 'head condition)
5286                     (get-text-property (point-min) 'mime-view-entity))))
5287         val elem buttonized)
5288     (gnus-run-hooks 'gnus-part-display-hook)
5289     (unless gnus-inhibit-treatment
5290       (while (setq elem (pop alist))
5291         (setq val
5292               (save-excursion
5293                 (if (gnus-buffer-live-p gnus-summary-buffer)
5294                     (set-buffer gnus-summary-buffer))
5295                 (symbol-value (car elem))))
5296         (when (and (or (consp val)
5297                        treated-type)
5298                    (gnus-treat-predicate val)
5299                    (or (not (get (car elem) 'highlight))
5300                        highlightp))
5301           (when (and (not buttonized)
5302                      (memq (car elem)
5303                            '(gnus-treat-hide-signature
5304                              gnus-treat-highlight-signature)))
5305             (gnus-article-buttonize-signature)
5306             (setq buttonized t))
5307           (save-restriction
5308             (funcall (cadr elem)))))
5309       ;; FSF Emacsen does not inherit the existing text properties
5310       ;; in the new text, so we should do it for `mime-view-entity'.
5311       (static-unless (featurep 'xemacs)
5312         (when entity
5313           (put-text-property (point-min) (point-max)
5314                              'mime-view-entity entity))))))
5315
5316 ;; Dynamic variables.
5317 (eval-when-compile
5318   (defvar part-number)
5319   (defvar total-parts)
5320   (defvar type)
5321   (defvar condition)
5322   (defvar length))
5323
5324 (defun gnus-treat-predicate (val)
5325   (cond
5326    ((null val)
5327     nil)
5328    ((and (listp val)
5329          (stringp (car val)))
5330     (apply 'gnus-or (mapcar `(lambda (s)
5331                                (string-match s ,(or gnus-newsgroup-name "")))
5332                             val)))
5333    ((listp val)
5334     (let ((pred (pop val)))
5335       (cond
5336        ((eq pred 'or)
5337         (apply 'gnus-or (mapcar 'gnus-treat-predicate val)))
5338        ((eq pred 'and)
5339         (apply 'gnus-and (mapcar 'gnus-treat-predicate val)))
5340        ((eq pred 'not)
5341         (not (gnus-treat-predicate (car val))))
5342        ((eq pred 'typep)
5343         (equal (car val) type))
5344        (t
5345         (error "%S is not a valid predicate" pred)))))
5346    ((eq val 'mime)
5347     gnus-show-mime)
5348    (condition
5349     (eq condition val))
5350    ((eq val t)
5351     t)
5352    ((eq val 'head)
5353     nil)
5354    ((eq val 'last)
5355     (eq part-number total-parts))
5356    ((numberp val)
5357     (< length val))
5358    (t
5359     (error "%S is not a valid value" val))))
5360
5361
5362 ;;; @ for mime-view
5363 ;;;
5364
5365 (defun gnus-article-header-presentation-method (entity situation)
5366   (mime-insert-header entity)
5367   )
5368
5369 (set-alist 'mime-header-presentation-method-alist
5370            'gnus-original-article-mode
5371            #'gnus-article-header-presentation-method)
5372
5373 (defun gnus-mime-preview-quitting-method ()
5374   (mime-preview-kill-buffer)
5375   (delete-other-windows)
5376   (gnus-article-show-summary)
5377   (gnus-summary-select-article gnus-show-all-headers t))
5378
5379 (set-alist 'mime-preview-quitting-method-alist
5380            'gnus-original-article-mode #'gnus-mime-preview-quitting-method)
5381
5382 (set-alist 'mime-preview-following-method-alist
5383            'gnus-original-article-mode #'gnus-following-method)
5384
5385 (set-alist 'mime-preview-over-to-previous-method-alist
5386            'gnus-original-article-mode
5387            (lambda ()
5388              (if (> (point-min) 1)
5389                  (gnus-article-prev-page)
5390                (gnus-article-read-summary-keys
5391                 nil (gnus-character-to-event ?P)))))
5392
5393 (set-alist 'mime-preview-over-to-next-method-alist
5394            'gnus-original-article-mode'
5395            (lambda ()
5396              (if (< (point-max) (buffer-size))
5397                  (gnus-article-next-page)
5398                (gnus-article-read-summary-keys
5399                 nil (gnus-character-to-event ?N)))))
5400
5401
5402 ;;; @ end
5403 ;;;
5404
5405 (gnus-ems-redefine)
5406
5407 (provide 'gnus-art)
5408
5409 (run-hooks 'gnus-art-load-hook)
5410
5411 ;;; gnus-art.el ends here