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