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