*** empty log message ***
[gnus] / lisp / gnus-art.el
1 ;;; gnus-art.el --- article mode commands for Gnus
2 ;; Copyright (C) 1996 Free Software Foundation, Inc.
3
4 ;; Author: Lars Magne Ingebrigtsen <larsi@ifi.uio.no>
5 ;; Keywords: news
6
7 ;; This file is part of GNU Emacs.
8
9 ;; GNU Emacs is free software; you can redistribute it and/or modify
10 ;; it under the terms of the GNU General Public License as published by
11 ;; the Free Software Foundation; either version 2, or (at your option)
12 ;; any later version.
13
14 ;; GNU Emacs is distributed in the hope that it will be useful,
15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 ;; GNU General Public License for more details.
18
19 ;; You should have received a copy of the GNU General Public License
20 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
21 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 ;; Boston, MA 02111-1307, USA.
23
24 ;;; Commentary:
25
26 ;;; Code:
27
28 (require 'gnus-load)
29 (require 'gnus-sum)
30 (require 'article)
31 (require 'gnus-spec)
32 (require 'gnus-int)
33
34 (defvar gnus-article-save-directory gnus-directory
35   "*Name of the directory articles will be saved in (default \"~/News\").")
36
37 (defvar gnus-save-all-headers t
38   "*If non-nil, don't remove any headers before saving.")
39
40 (defvar gnus-prompt-before-saving 'always
41   "*This variable says how much prompting is to be done when saving articles.
42 If it is nil, no prompting will be done, and the articles will be
43 saved to the default files.  If this variable is `always', each and
44 every article that is saved will be preceded by a prompt, even when
45 saving large batches of articles.  If this variable is neither nil not
46 `always', there the user will be prompted once for a file name for
47 each invocation of the saving commands.")
48
49 (defvar gnus-saved-headers gnus-visible-headers
50   "*Headers to keep if `gnus-save-all-headers' is nil.
51 If `gnus-save-all-headers' is non-nil, this variable will be ignored.
52 If that variable is nil, however, all headers that match this regexp
53 will be kept while the rest will be deleted before saving.")
54
55 (defvar gnus-default-article-saver 'gnus-summary-save-in-rmail
56   "*A function to save articles in your favorite format.
57 The function must be interactively callable (in other words, it must
58 be an Emacs command).
59
60 Gnus provides the following functions:
61
62 * gnus-summary-save-in-rmail (Rmail format)
63 * gnus-summary-save-in-mail (Unix mail format)
64 * gnus-summary-save-in-folder (MH folder)
65 * gnus-summary-save-in-file (article format).
66 * gnus-summary-save-in-vm (use VM's folder format).")
67
68 (defvar gnus-rmail-save-name (function gnus-plain-save-name)
69   "*A function generating a file name to save articles in Rmail format.
70 The function is called with NEWSGROUP, HEADERS, and optional LAST-FILE.")
71
72 (defvar gnus-mail-save-name (function gnus-plain-save-name)
73   "*A function generating a file name to save articles in Unix mail format.
74 The function is called with NEWSGROUP, HEADERS, and optional LAST-FILE.")
75
76 (defvar gnus-folder-save-name (function gnus-folder-save-name)
77   "*A function generating a file name to save articles in MH folder.
78 The function is called with NEWSGROUP, HEADERS, and optional LAST-FOLDER.")
79
80 (defvar gnus-file-save-name (function gnus-numeric-save-name)
81   "*A function generating a file name to save articles in article format.
82 The function is called with NEWSGROUP, HEADERS, and optional
83 LAST-FILE.")
84
85 (defvar gnus-split-methods
86   '((gnus-article-archive-name))
87   "*Variable used to suggest where articles are to be saved.
88 For instance, if you would like to save articles related to Gnus in
89 the file \"gnus-stuff\", and articles related to VM in \"vm-stuff\",
90 you could set this variable to something like:
91
92  '((\"^Subject:.*gnus\\|^Newsgroups:.*gnus\" \"gnus-stuff\")
93    (\"^Subject:.*vm\\|^Xref:.*vm\" \"vm-stuff\"))
94
95 This variable is an alist where the where the key is the match and the
96 value is a list of possible files to save in if the match is non-nil.
97
98 If the match is a string, it is used as a regexp match on the
99 article.  If the match is a symbol, that symbol will be funcalled
100 from the buffer of the article to be saved with the newsgroup as the
101 parameter.  If it is a list, it will be evaled in the same buffer.
102
103 If this form or function returns a string, this string will be used as
104 a possible file name; and if it returns a non-nil list, that list will
105 be used as possible file names.")
106
107 (defvar gnus-strict-mime t
108   "*If nil, MIME-decode even if there is no Mime-Version header in the article.")
109
110 (defvar gnus-show-mime-method 'metamail-buffer
111   "*Function to process a MIME message.
112 The function is called from the article buffer.")
113
114 (defvar gnus-decode-encoded-word-method (lambda ())
115   "*Function to decode a MIME encoded-words.
116 The function is called from the article buffer.")
117
118 (defvar gnus-page-delimiter "^\^L"
119   "*Regexp describing what to use as article page delimiters.
120 The default value is \"^\^L\", which is a form linefeed at the
121 beginning of a line.")
122
123 (defvar gnus-article-mode-line-format "Gnus: %%b %S"
124   "*The format specification for the article mode line.
125 See `gnus-summary-mode-line-format' for a closer description.")
126
127 (defvar gnus-article-mode-hook nil
128   "*A hook for Gnus article mode.")
129
130 (defvar gnus-article-prepare-hook nil
131   "*A hook called after an article has been prepared in the article buffer.
132 If you want to run a special decoding program like nkf, use this hook.")
133
134 ;(defvar gnus-article-display-hook nil
135 ;  "*A hook called after the article is displayed in the article buffer.
136 ;The hook is designed to change the contents of the article
137 ;buffer.  Typical functions that this hook may contain are
138 ;`gnus-article-hide-headers' (hide selected headers),
139 ;`gnus-article-maybe-highlight' (perform fancy article highlighting),
140 ;`gnus-article-hide-signature' (hide signature) and
141 ;`gnus-article-treat-overstrike' (turn \"^H_\" into bold characters).")
142 ;(add-hook 'gnus-article-display-hook 'gnus-article-hide-headers-if-wanted)
143 ;(add-hook 'gnus-article-display-hook 'gnus-article-treat-overstrike)
144 ;(add-hook 'gnus-article-display-hook 'gnus-article-maybe-highlight)
145
146 ;;; Internal variables
147
148 (defvar gnus-article-mode-line-format-alist
149     (nconc '((?w (gnus-article-wash-status) ?s))
150            gnus-summary-mode-line-format-alist))
151
152 ;;; Provide a mapping from `gnus-*' commands to Article commands.
153
154 (eval-and-compile
155   (mapcar
156    (lambda (func)
157      (let (afunc gfunc)
158        (if (consp func)
159            (setq afunc (car func)
160                  gfunc (cdr func))
161          (setq afunc func
162                gfunc (intern (format "gnus-%s" func))))
163        (fset gfunc 
164              `(lambda (&optional interactive &rest args)
165                 ,(documentation afunc t)
166                 (interactive (list t))
167                 (save-excursion
168                   (set-buffer gnus-article-buffer)
169                   (if interactive
170                       (call-interactively ',afunc)
171                     (apply ',afunc args)))))))
172    '(article-hide-headers
173      article-hide-boring-headers
174      article-treat-overstrike
175      (article-fill . gnus-article-word-wrap)
176      article-remove-cr
177      article-remove-trailing-blank-lines
178      article-display-x-face
179      article-de-quoted-unreadable
180      article-mime-decode-quoted-printable
181      article-hide-pgp
182      article-hide-pem
183      article-hide-signature
184      article-strip-leading-blank-lines
185      article-date-local
186      article-date-original
187      article-date-lapsed
188      article-emphasize
189      (article-show-all . gnus-article-show-all-headers))))
190
191 (defalias 'gnus-decode-rfc1522 'article-decode-rfc1522)
192
193 ;;; Saving functions.
194
195 (defun gnus-article-save (save-buffer file)
196   "Save the currently selected article."
197   (unless gnus-save-all-headers
198     ;; Remove headers accoring to `gnus-saved-headers'.
199     (let ((gnus-visible-headers
200            (or gnus-saved-headers gnus-visible-headers))
201           (gnus-article-buffer save-buffer))
202       (gnus-article-hide-headers 1 t)))
203   (save-window-excursion
204     (if (not gnus-default-article-saver)
205         (error "No default saver is defined.")
206       ;; !!! Magic!  The saving functions all save
207       ;; `gnus-original-article-buffer' (or so they think),
208       ;; but we bind that variable to our save-buffer.
209       (set-buffer gnus-article-buffer)
210       (let ((gnus-original-article-buffer save-buffer))
211         (set-buffer gnus-summary-buffer)
212         (funcall
213          gnus-default-article-saver
214          (cond
215           ((not gnus-prompt-before-saving)
216            'default)
217           ((eq gnus-prompt-before-saving 'always)
218            nil)
219           (t file)))))))
220
221 (defun gnus-read-save-file-name (prompt default-name)
222   (let* ((split-name (gnus-get-split-value gnus-split-methods))
223          (file
224           ;; Let the split methods have their say.
225           (cond
226            ;; No split name was found.
227            ((null split-name)
228             (read-file-name
229              (concat prompt " (default "
230                      (file-name-nondirectory default-name) ") ")
231              (file-name-directory default-name)
232              default-name))
233            ;; A single split name was found
234            ((= 1 (length split-name))
235             (let* ((name (car split-name))
236                    (dir (cond ((file-directory-p name)
237                                (file-name-as-directory name))
238                               ((file-exists-p name) name)
239                               (t gnus-article-save-directory))))
240               (read-file-name
241                (concat prompt " (default " name ") ")
242                dir name)))
243            ;; A list of splits was found.
244            (t
245             (setq split-name (nreverse split-name))
246             (let (result)
247               (let ((file-name-history (nconc split-name file-name-history)))
248                 (setq result
249                       (read-file-name
250                        (concat prompt " (`M-p' for defaults) ")
251                        gnus-article-save-directory
252                        (car split-name))))
253               (car (push result file-name-history)))))))
254     ;; Create the directory.
255     (unless (equal (directory-file-name file) file)
256       (make-directory (file-name-directory file) t))
257     ;; If we have read a directory, we append the default file name.
258     (when (file-directory-p file)
259       (setq file (concat (file-name-as-directory file)
260                          (file-name-nondirectory default-name))))
261     ;; Possibly translate some characters.
262     (nnheader-translate-file-chars file)))
263
264 (defun gnus-article-archive-name (group)
265   "Return the first instance of an \"Archive-name\" in the current buffer."
266   (let ((case-fold-search t))
267     (when (re-search-forward "archive-name: *\\([^ \n\t]+\\)[ \t]*$" nil t)
268       (nnheader-concat gnus-article-save-directory
269                        (match-string 1)))))
270
271 (defun gnus-summary-save-in-rmail (&optional filename)
272   "Append this article to Rmail file.
273 Optional argument FILENAME specifies file name.
274 Directory to save to is default to `gnus-article-save-directory'."
275   (interactive)
276   (gnus-set-global-variables)
277   (let ((default-name
278           (funcall gnus-rmail-save-name gnus-newsgroup-name
279                    gnus-current-headers gnus-newsgroup-last-rmail)))
280     (setq filename
281           (cond ((eq filename 'default)
282                  default-name)
283                 (filename filename)
284                 (t (gnus-read-save-file-name
285                     "Save in rmail file:" default-name))))
286     (make-directory (file-name-directory filename) t)
287     (gnus-eval-in-buffer-window gnus-original-article-buffer
288       (save-excursion
289         (save-restriction
290           (widen)
291           (gnus-output-to-rmail filename))))
292     ;; Remember the directory name to save articles
293     (setq gnus-newsgroup-last-rmail filename)))
294
295 (defun gnus-summary-save-in-mail (&optional filename)
296   "Append this article to Unix mail file.
297 Optional argument FILENAME specifies file name.
298 Directory to save to is default to `gnus-article-save-directory'."
299   (interactive)
300   (gnus-set-global-variables)
301   (let ((default-name
302           (funcall gnus-mail-save-name gnus-newsgroup-name
303                    gnus-current-headers gnus-newsgroup-last-mail)))
304     (setq filename
305           (cond ((eq filename 'default)
306                  default-name)
307                 (filename filename)
308                 (t (gnus-read-save-file-name
309                     "Save in Unix mail file:" default-name))))
310     (setq filename
311           (expand-file-name filename
312                             (and default-name
313                                  (file-name-directory default-name))))
314     (make-directory (file-name-directory filename) t)
315     (gnus-eval-in-buffer-window gnus-original-article-buffer
316       (save-excursion
317         (save-restriction
318           (widen)
319           (if (and (file-readable-p filename) (mail-file-babyl-p filename))
320               (gnus-output-to-rmail filename)
321             (let ((mail-use-rfc822 t))
322               (rmail-output filename 1 t t))))))
323     ;; Remember the directory name to save articles.
324     (setq gnus-newsgroup-last-mail filename)))
325
326 (defun gnus-summary-save-in-file (&optional filename)
327   "Append this article to file.
328 Optional argument FILENAME specifies file name.
329 Directory to save to is default to `gnus-article-save-directory'."
330   (interactive)
331   (gnus-set-global-variables)
332   (let ((default-name
333           (funcall gnus-file-save-name gnus-newsgroup-name
334                    gnus-current-headers gnus-newsgroup-last-file)))
335     (setq filename
336           (cond ((eq filename 'default)
337                  default-name)
338                 (filename filename)
339                 (t (gnus-read-save-file-name
340                     "Save in file:" default-name))))
341     (make-directory (file-name-directory filename) t)
342     (gnus-eval-in-buffer-window gnus-original-article-buffer
343       (save-excursion
344         (save-restriction
345           (widen)
346           (gnus-output-to-file filename))))
347     ;; Remember the directory name to save articles.
348     (setq gnus-newsgroup-last-file filename)))
349
350 (defun gnus-summary-save-body-in-file (&optional filename)
351   "Append this article body to a file.
352 Optional argument FILENAME specifies file name.
353 The directory to save in defaults to `gnus-article-save-directory'."
354   (interactive)
355   (gnus-set-global-variables)
356   (let ((default-name
357           (funcall gnus-file-save-name gnus-newsgroup-name
358                    gnus-current-headers gnus-newsgroup-last-file)))
359     (setq filename
360           (cond ((eq filename 'default)
361                  default-name)
362                 (filename filename)
363                 (t (gnus-read-save-file-name
364                     "Save body in file:" default-name))))
365     (make-directory (file-name-directory filename) t)
366     (gnus-eval-in-buffer-window gnus-original-article-buffer
367       (save-excursion
368         (save-restriction
369           (widen)
370           (goto-char (point-min))
371           (and (search-forward "\n\n" nil t)
372                (narrow-to-region (point) (point-max)))
373           (gnus-output-to-file filename))))
374     ;; Remember the directory name to save articles.
375     (setq gnus-newsgroup-last-file filename)))
376
377 (defun gnus-summary-save-in-pipe (&optional command)
378   "Pipe this article to subprocess."
379   (interactive)
380   (gnus-set-global-variables)
381   (setq command
382         (cond ((eq command 'default)
383                gnus-last-shell-command)
384               (command command)
385               (t (read-string "Shell command on article: "
386                               gnus-last-shell-command))))
387   (if (string-equal command "")
388       (setq command gnus-last-shell-command))
389   (gnus-eval-in-buffer-window gnus-article-buffer
390     (save-restriction
391       (widen)
392       (shell-command-on-region (point-min) (point-max) command nil)))
393   (setq gnus-last-shell-command command))
394
395 ;;; Article file names when saving.
396
397 (defun gnus-capitalize-newsgroup (newsgroup)
398   "Capitalize NEWSGROUP name."
399   (and (not (zerop (length newsgroup)))
400        (concat (char-to-string (upcase (aref newsgroup 0)))
401                (substring newsgroup 1))))
402
403 (defun gnus-Numeric-save-name (newsgroup headers &optional last-file)
404   "Generate file name from NEWSGROUP, HEADERS, and optional LAST-FILE.
405 If variable `gnus-use-long-file-name' is nil, it is ~/News/News.group/num.
406 Otherwise, it is like ~/News/news/group/num."
407   (let ((default
408           (expand-file-name
409            (concat (if (gnus-use-long-file-name 'not-save)
410                        (gnus-capitalize-newsgroup newsgroup)
411                      (gnus-newsgroup-directory-form newsgroup))
412                    "/" (int-to-string (mail-header-number headers)))
413            gnus-article-save-directory)))
414     (if (and last-file
415              (string-equal (file-name-directory default)
416                            (file-name-directory last-file))
417              (string-match "^[0-9]+$" (file-name-nondirectory last-file)))
418         default
419       (or last-file default))))
420
421 (defun gnus-numeric-save-name (newsgroup headers &optional last-file)
422   "Generate file name from NEWSGROUP, HEADERS, and optional LAST-FILE.
423 If variable `gnus-use-long-file-name' is non-nil, it is
424 ~/News/news.group/num.  Otherwise, it is like ~/News/news/group/num."
425   (let ((default
426           (expand-file-name
427            (concat (if (gnus-use-long-file-name 'not-save)
428                        newsgroup
429                      (gnus-newsgroup-directory-form newsgroup))
430                    "/" (int-to-string (mail-header-number headers)))
431            gnus-article-save-directory)))
432     (if (and last-file
433              (string-equal (file-name-directory default)
434                            (file-name-directory last-file))
435              (string-match "^[0-9]+$" (file-name-nondirectory last-file)))
436         default
437       (or last-file default))))
438
439 (defun gnus-Plain-save-name (newsgroup headers &optional last-file)
440   "Generate file name from NEWSGROUP, HEADERS, and optional LAST-FILE.
441 If variable `gnus-use-long-file-name' is non-nil, it is
442 ~/News/News.group.  Otherwise, it is like ~/News/news/group/news."
443   (or last-file
444       (expand-file-name
445        (if (gnus-use-long-file-name 'not-save)
446            (gnus-capitalize-newsgroup newsgroup)
447          (concat (gnus-newsgroup-directory-form newsgroup) "/news"))
448        gnus-article-save-directory)))
449
450 (defun gnus-plain-save-name (newsgroup headers &optional last-file)
451   "Generate file name from NEWSGROUP, HEADERS, and optional LAST-FILE.
452 If variable `gnus-use-long-file-name' is non-nil, it is
453 ~/News/news.group.  Otherwise, it is like ~/News/news/group/news."
454   (or last-file
455       (expand-file-name
456        (if (gnus-use-long-file-name 'not-save)
457            newsgroup
458          (concat (gnus-newsgroup-directory-form newsgroup) "/news"))
459        gnus-article-save-directory)))
460
461 \f
462 ;;;
463 ;;; Gnus article mode
464 ;;;
465
466 (put 'gnus-article-mode 'mode-class 'special)
467
468 (when t
469   (gnus-define-keys gnus-article-mode-map
470     " " gnus-article-goto-next-page
471     "\177" gnus-article-goto-prev-page
472     [delete] gnus-article-goto-prev-page
473     "\C-c^" gnus-article-refer-article
474     "h" gnus-article-show-summary
475     "s" gnus-article-show-summary
476     "\C-c\C-m" gnus-article-mail
477     "?" gnus-article-describe-briefly
478     gnus-mouse-2 gnus-article-push-button
479     "\r" gnus-article-press-button
480     "\t" gnus-article-next-button
481     "\M-\t" gnus-article-prev-button
482     "<" beginning-of-buffer
483     ">" end-of-buffer
484     "\C-c\C-i" gnus-info-find-node
485     "\C-c\C-b" gnus-bug)
486
487   (substitute-key-definition
488    'undefined 'gnus-article-read-summary-keys gnus-article-mode-map))
489
490 (defun gnus-article-mode ()
491   "Major mode for displaying an article.
492
493 All normal editing commands are switched off.
494
495 The following commands are available:
496
497 \\<gnus-article-mode-map>
498 \\[gnus-article-next-page]\t Scroll the article one page forwards
499 \\[gnus-article-prev-page]\t Scroll the article one page backwards
500 \\[gnus-article-refer-article]\t Go to the article referred to by an article id near point
501 \\[gnus-article-show-summary]\t Display the summary buffer
502 \\[gnus-article-mail]\t Send a reply to the address near point
503 \\[gnus-article-describe-briefly]\t Describe the current mode briefly
504 \\[gnus-info-find-node]\t Go to the Gnus info node"
505   (interactive)
506   (when (and menu-bar-mode
507              (gnus-visual-p 'article-menu 'menu))
508     (gnus-article-make-menu-bar))
509   (kill-all-local-variables)
510   (gnus-simplify-mode-line)
511   (setq mode-name "Article")
512   (setq major-mode 'gnus-article-mode)
513   (make-local-variable 'minor-mode-alist)
514   (or (assq 'gnus-show-mime minor-mode-alist)
515       (setq minor-mode-alist
516             (cons (list 'gnus-show-mime " MIME") minor-mode-alist)))
517   (use-local-map gnus-article-mode-map)
518   (gnus-update-format-specifications nil 'article-mode)
519   (make-local-variable 'page-delimiter)
520   (setq page-delimiter gnus-page-delimiter)
521   (buffer-disable-undo (current-buffer))
522   (setq buffer-read-only t)             ;Disable modification
523   (run-hooks 'gnus-article-mode-hook))
524
525 (defun gnus-article-setup-buffer ()
526   "Initialize the article buffer."
527   (let* ((name (if gnus-single-article-buffer "*Article*"
528                  (concat "*Article " gnus-newsgroup-name "*")))
529          (original
530           (progn (string-match "\\*Article" name)
531                  (concat " *Original Article"
532                          (substring name (match-end 0))))))
533     (setq gnus-article-buffer name)
534     (setq gnus-original-article-buffer original)
535     ;; This might be a variable local to the summary buffer.
536     (unless gnus-single-article-buffer
537       (save-excursion
538         (set-buffer gnus-summary-buffer)
539         (setq gnus-article-buffer name)
540         (setq gnus-original-article-buffer original)
541         (gnus-set-global-variables))
542       (make-local-variable 'gnus-summary-buffer))
543     ;; Init original article buffer.
544     (save-excursion
545       (set-buffer (get-buffer-create gnus-original-article-buffer))
546       (buffer-disable-undo (current-buffer))
547       (setq major-mode 'gnus-original-article-mode)
548       (gnus-add-current-to-buffer-list)
549       (make-local-variable 'gnus-original-article))
550     (if (get-buffer name)
551         (save-excursion
552           (set-buffer name)
553           (buffer-disable-undo (current-buffer))
554           (setq buffer-read-only t)
555           (gnus-add-current-to-buffer-list)
556           (or (eq major-mode 'gnus-article-mode)
557               (gnus-article-mode))
558           (current-buffer))
559       (save-excursion
560         (set-buffer (get-buffer-create name))
561         (gnus-add-current-to-buffer-list)
562         (gnus-article-mode)
563         (current-buffer)))))
564
565 ;; Set article window start at LINE, where LINE is the number of lines
566 ;; from the head of the article.
567 (defun gnus-article-set-window-start (&optional line)
568   (set-window-start
569    (get-buffer-window gnus-article-buffer t)
570    (save-excursion
571      (set-buffer gnus-article-buffer)
572      (goto-char (point-min))
573      (if (not line)
574          (point-min)
575        (gnus-message 6 "Moved to bookmark")
576        (search-forward "\n\n" nil t)
577        (forward-line line)
578        (point)))))
579
580 (defun gnus-article-prepare (article &optional all-headers header)
581   "Prepare ARTICLE in article mode buffer.
582 ARTICLE should either be an article number or a Message-ID.
583 If ARTICLE is an id, HEADER should be the article headers.
584 If ALL-HEADERS is non-nil, no headers are hidden."
585   (save-excursion
586     ;; Make sure we start in a summary buffer.
587     (unless (eq major-mode 'gnus-summary-mode)
588       (set-buffer gnus-summary-buffer))
589     (setq gnus-summary-buffer (current-buffer))
590     ;; Make sure the connection to the server is alive.
591     (unless (gnus-server-opened
592              (gnus-find-method-for-group gnus-newsgroup-name))
593       (gnus-check-server (gnus-find-method-for-group gnus-newsgroup-name))
594       (gnus-request-group gnus-newsgroup-name t))
595     (let* ((article (if header (mail-header-number header) article))
596            (summary-buffer (current-buffer))
597            (internal-hook gnus-article-internal-prepare-hook)
598            (group gnus-newsgroup-name)
599            result)
600       (save-excursion
601         (gnus-article-setup-buffer)
602         (set-buffer gnus-article-buffer)
603         ;; Deactivate active regions.
604         (when (and (boundp 'transient-mark-mode)
605                    transient-mark-mode)
606           (setq mark-active nil))
607         (if (not (setq result (let ((buffer-read-only nil))
608                                 (gnus-request-article-this-buffer
609                                  article group))))
610             ;; There is no such article.
611             (save-excursion
612               (when (and (numberp article)
613                          (not (memq article gnus-newsgroup-sparse)))
614                 (setq gnus-article-current
615                       (cons gnus-newsgroup-name article))
616                 (set-buffer gnus-summary-buffer)
617                 (setq gnus-current-article article)
618                 (gnus-summary-mark-article article gnus-canceled-mark))
619               (unless (memq article gnus-newsgroup-sparse)
620                 (gnus-error
621                  1 "No such article (may have expired or been canceled)")))
622           (if (or (eq result 'pseudo) (eq result 'nneething))
623               (progn
624                 (save-excursion
625                   (set-buffer summary-buffer)
626                   (setq gnus-last-article gnus-current-article
627                         gnus-newsgroup-history (cons gnus-current-article
628                                                      gnus-newsgroup-history)
629                         gnus-current-article 0
630                         gnus-current-headers nil
631                         gnus-article-current nil)
632                   (if (eq result 'nneething)
633                       (gnus-configure-windows 'summary)
634                     (gnus-configure-windows 'article))
635                   (gnus-set-global-variables))
636                 (gnus-set-mode-line 'article))
637             ;; The result from the `request' was an actual article -
638             ;; or at least some text that is now displayed in the
639             ;; article buffer.
640             (if (and (numberp article)
641                      (not (eq article gnus-current-article)))
642                 ;; Seems like a new article has been selected.
643                 ;; `gnus-current-article' must be an article number.
644                 (save-excursion
645                   (set-buffer summary-buffer)
646                   (setq gnus-last-article gnus-current-article
647                         gnus-newsgroup-history (cons gnus-current-article
648                                                      gnus-newsgroup-history)
649                         gnus-current-article article
650                         gnus-current-headers
651                         (gnus-summary-article-header gnus-current-article)
652                         gnus-article-current
653                         (cons gnus-newsgroup-name gnus-current-article))
654                   (unless (vectorp gnus-current-headers)
655                     (setq gnus-current-headers nil))
656                   (gnus-summary-show-thread)
657                   (run-hooks 'gnus-mark-article-hook)
658                   (gnus-set-mode-line 'summary)
659                   (and (gnus-visual-p 'article-highlight 'highlight)
660                        (run-hooks 'gnus-visual-mark-article-hook))
661                   ;; Set the global newsgroup variables here.
662                   ;; Suggested by Jim Sisolak
663                   ;; <sisolak@trans4.neep.wisc.edu>.
664                   (gnus-set-global-variables)
665                   (setq gnus-have-all-headers
666                         (or all-headers gnus-show-all-headers))
667                   (and gnus-use-cache
668                        (vectorp (gnus-summary-article-header article))
669                        (gnus-cache-possibly-enter-article
670                         group article
671                         (gnus-summary-article-header article)
672                         (memq article gnus-newsgroup-marked)
673                         (memq article gnus-newsgroup-dormant)
674                         (memq article gnus-newsgroup-unreads)))))
675             (when (or (numberp article)
676                       (stringp article))
677               ;; Hooks for getting information from the article.
678               ;; This hook must be called before being narrowed.
679               (let (buffer-read-only)
680                 (run-hooks 'internal-hook)
681                 (run-hooks 'gnus-article-prepare-hook)
682                 ;; Decode MIME message.
683                 (if gnus-show-mime
684                     (if (or (not gnus-strict-mime)
685                             (gnus-fetch-field "Mime-Version"))
686                         (funcall gnus-show-mime-method)
687                       (funcall gnus-decode-encoded-word-method)))
688                 ;; Perform the article display hooks.
689                 (run-hooks 'gnus-article-display-hook))
690               ;; Do page break.
691               (goto-char (point-min))
692               (and gnus-break-pages (gnus-narrow-to-page)))
693             (gnus-set-mode-line 'article)
694             (gnus-configure-windows 'article)
695             (goto-char (point-min))
696             t))))))
697
698 (defun gnus-article-wash-status ()
699   "Return a string which display status of article washing."
700   (save-excursion
701     (set-buffer gnus-article-buffer)
702     (let ((cite (article-hidden-text-p 'cite))
703           (headers (article-hidden-text-p 'headers))
704           (boring (article-hidden-text-p 'boring-headers))
705           (pgp (article-hidden-text-p 'pgp))
706           (pem (article-hidden-text-p 'pem))
707           (signature (article-hidden-text-p 'signature))
708           (overstrike (article-hidden-text-p 'overstrike))
709           (emphasis (article-hidden-text-p 'emphasis))
710           (mime gnus-show-mime))
711       (format "%c%c%c%c%c%c%c"
712               (if cite ?c ? )
713               (if (or headers boring) ?h ? )
714               (if (or pgp pem) ?p ? )
715               (if signature ?s ? )
716               (if overstrike ?o ? )
717               (if mime ?m ? )
718               (if emphasis ?e ? )))))
719
720 (defun gnus-article-hide-headers-if-wanted ()
721   "Hide unwanted headers if `gnus-have-all-headers' is nil.
722 Provided for backwards compatibility."
723   (or (save-excursion (set-buffer gnus-summary-buffer) gnus-have-all-headers)
724       gnus-inhibit-hiding
725       (gnus-article-hide-headers)))
726
727 ;;; Article savers.
728
729 (defun gnus-output-to-rmail (file-name)
730   "Append the current article to an Rmail file named FILE-NAME."
731   (require 'rmail)
732   ;; Most of these codes are borrowed from rmailout.el.
733   (setq file-name (expand-file-name file-name))
734   (setq rmail-default-rmail-file file-name)
735   (let ((artbuf (current-buffer))
736         (tmpbuf (get-buffer-create " *Gnus-output*")))
737     (save-excursion
738       (or (get-file-buffer file-name)
739           (file-exists-p file-name)
740           (if (gnus-yes-or-no-p
741                (concat "\"" file-name "\" does not exist, create it? "))
742               (let ((file-buffer (create-file-buffer file-name)))
743                 (save-excursion
744                   (set-buffer file-buffer)
745                   (rmail-insert-rmail-file-header)
746                   (let ((require-final-newline nil))
747                     (write-region (point-min) (point-max) file-name t 1)))
748                 (kill-buffer file-buffer))
749             (error "Output file does not exist")))
750       (set-buffer tmpbuf)
751       (buffer-disable-undo (current-buffer))
752       (erase-buffer)
753       (insert-buffer-substring artbuf)
754       (gnus-convert-article-to-rmail)
755       ;; Decide whether to append to a file or to an Emacs buffer.
756       (let ((outbuf (get-file-buffer file-name)))
757         (if (not outbuf)
758             (append-to-file (point-min) (point-max) file-name)
759           ;; File has been visited, in buffer OUTBUF.
760           (set-buffer outbuf)
761           (let ((buffer-read-only nil)
762                 (msg (and (boundp 'rmail-current-message)
763                           (symbol-value 'rmail-current-message))))
764             ;; If MSG is non-nil, buffer is in RMAIL mode.
765             (if msg
766                 (progn (widen)
767                        (narrow-to-region (point-max) (point-max))))
768             (insert-buffer-substring tmpbuf)
769             (if msg
770                 (progn
771                   (goto-char (point-min))
772                   (widen)
773                   (search-backward "\^_")
774                   (narrow-to-region (point) (point-max))
775                   (goto-char (1+ (point-min)))
776                   (rmail-count-new-messages t)
777                   (rmail-show-message msg)))))))
778     (kill-buffer tmpbuf)))
779
780 (defun gnus-output-to-file (file-name)
781   "Append the current article to a file named FILE-NAME."
782   (let ((artbuf (current-buffer)))
783     (nnheader-temp-write nil
784       (insert-buffer-substring artbuf)
785       ;; Append newline at end of the buffer as separator, and then
786       ;; save it to file.
787       (goto-char (point-max))
788       (insert "\n")
789       (append-to-file (point-min) (point-max) file-name))))
790
791 (defun gnus-convert-article-to-rmail ()
792   "Convert article in current buffer to Rmail message format."
793   (let ((buffer-read-only nil))
794     ;; Convert article directly into Babyl format.
795     ;; Suggested by Rob Austein <sra@lcs.mit.edu>
796     (goto-char (point-min))
797     (insert "\^L\n0, unseen,,\n*** EOOH ***\n")
798     (while (search-forward "\n\^_" nil t) ;single char
799       (replace-match "\n^_" t t))       ;2 chars: "^" and "_"
800     (goto-char (point-max))
801     (insert "\^_")))
802
803 (defun gnus-narrow-to-page (&optional arg)
804   "Narrow the article buffer to a page.
805 If given a numerical ARG, move forward ARG pages."
806   (interactive "P")
807   (setq arg (if arg (prefix-numeric-value arg) 0))
808   (save-excursion
809     (set-buffer gnus-article-buffer)
810     (goto-char (point-min))
811     (widen)
812     (when (gnus-visual-p 'page-marker)
813       (let ((buffer-read-only nil))
814         (gnus-remove-text-with-property 'gnus-prev)
815         (gnus-remove-text-with-property 'gnus-next)))
816     (when
817         (cond ((< arg 0)
818                (re-search-backward page-delimiter nil 'move (1+ (abs arg))))
819               ((> arg 0)
820                (re-search-forward page-delimiter nil 'move arg)))
821       (goto-char (match-end 0)))
822     (narrow-to-region
823      (point)
824      (if (re-search-forward page-delimiter nil 'move)
825          (match-beginning 0)
826        (point)))
827     (when (and (gnus-visual-p 'page-marker)
828                (not (= (point-min) 1)))
829       (save-excursion
830         (goto-char (point-min))
831         (gnus-insert-prev-page-button)))
832     (when (and (gnus-visual-p 'page-marker)
833                (not (= (1- (point-max)) (buffer-size))))
834       (save-excursion
835         (goto-char (point-max))
836         (gnus-insert-next-page-button)))))
837
838 ;; Article mode commands
839
840 (defun gnus-article-goto-next-page ()
841   "Show the next page of the article."
842   (interactive)
843   (when (gnus-article-next-page)
844     (gnus-article-read-summary-keys nil (gnus-character-to-event ?n))))
845
846 (defun gnus-article-goto-prev-page ()
847   "Show the next page of the article."
848   (interactive)
849   (if (bobp) (gnus-article-read-summary-keys nil (gnus-character-to-event ?n))
850     (gnus-article-prev-page nil)))
851
852 (defun gnus-article-next-page (&optional lines)
853   "Show the next page of the current article.
854 If end of article, return non-nil.  Otherwise return nil.
855 Argument LINES specifies lines to be scrolled up."
856   (interactive "p")
857   (move-to-window-line -1)
858   ;; Fixed by enami@ptgd.sony.co.jp (enami tsugutomo)
859   (if (save-excursion
860         (end-of-line)
861         (and (pos-visible-in-window-p)  ;Not continuation line.
862              (eobp)))
863       ;; Nothing in this page.
864       (if (or (not gnus-break-pages)
865               (save-excursion
866                 (save-restriction
867                   (widen) (forward-line 1) (eobp)))) ;Real end-of-buffer?
868           t                             ;Nothing more.
869         (gnus-narrow-to-page 1)         ;Go to next page.
870         nil)
871     ;; More in this page.
872     (condition-case ()
873         (scroll-up lines)
874       (end-of-buffer
875        ;; Long lines may cause an end-of-buffer error.
876        (goto-char (point-max))))
877     (move-to-window-line 0)
878     nil))
879
880 (defun gnus-article-prev-page (&optional lines)
881   "Show previous page of current article.
882 Argument LINES specifies lines to be scrolled down."
883   (interactive "p")
884   (move-to-window-line 0)
885   (if (and gnus-break-pages
886            (bobp)
887            (not (save-restriction (widen) (bobp)))) ;Real beginning-of-buffer?
888       (progn
889         (gnus-narrow-to-page -1)        ;Go to previous page.
890         (goto-char (point-max))
891         (recenter -1))
892     (prog1
893         (condition-case ()
894             (scroll-down lines)
895           (error nil))
896       (move-to-window-line 0))))
897
898 (defun gnus-article-refer-article ()
899   "Read article specified by message-id around point."
900   (interactive)
901   (let ((point (point)))
902     (search-forward ">" nil t)          ;Move point to end of "<....>".
903     (if (re-search-backward "\\(<[^<> \t\n]+>\\)" nil t)
904         (let ((message-id (match-string 1)))
905           (goto-char point)
906           (set-buffer gnus-summary-buffer)
907           (gnus-summary-refer-article message-id))
908       (goto-char (point))
909       (error "No references around point"))))
910
911 (defun gnus-article-show-summary ()
912   "Reconfigure windows to show summary buffer."
913   (interactive)
914   (gnus-configure-windows 'article)
915   (gnus-summary-goto-subject gnus-current-article))
916
917 (defun gnus-article-describe-briefly ()
918   "Describe article mode commands briefly."
919   (interactive)
920   (gnus-message 6
921                 (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")))
922
923 (defun gnus-article-summary-command ()
924   "Execute the last keystroke in the summary buffer."
925   (interactive)
926   (let ((obuf (current-buffer))
927         (owin (current-window-configuration))
928         func)
929     (switch-to-buffer gnus-summary-buffer 'norecord)
930     (setq func (lookup-key (current-local-map) (this-command-keys)))
931     (call-interactively func)
932     (set-buffer obuf)
933     (set-window-configuration owin)
934     (set-window-point (get-buffer-window (current-buffer)) (point))))
935
936 (defun gnus-article-summary-command-nosave ()
937   "Execute the last keystroke in the summary buffer."
938   (interactive)
939   (let (func)
940     (pop-to-buffer gnus-summary-buffer 'norecord)
941     (setq func (lookup-key (current-local-map) (this-command-keys)))
942     (call-interactively func)))
943
944 (defun gnus-article-read-summary-keys (&optional arg key not-restore-window)
945   "Read a summary buffer key sequence and execute it from the article buffer."
946   (interactive "P")
947   (let ((nosaves
948          '("q" "Q"  "c" "r" "R" "\C-c\C-f" "m"  "a" "f" "F"
949            "Zc" "ZC" "ZE" "ZQ" "ZZ" "Zn" "ZR" "ZG" "ZN" "ZP"
950            "=" "^" "\M-^" "|"))
951         (nosave-but-article
952          '("A\r"))
953         keys)
954     (save-excursion
955       (set-buffer gnus-summary-buffer)
956       (push (or key last-command-event) unread-command-events)
957       (setq keys (read-key-sequence nil)))
958     (message "")
959
960     (if (or (member keys nosaves)
961             (member keys nosave-but-article))
962         (let (func)
963           (save-window-excursion
964             (pop-to-buffer gnus-summary-buffer 'norecord)
965             (setq func (lookup-key (current-local-map) keys)))
966           (if (not func)
967               (ding)
968             (set-buffer gnus-summary-buffer)
969             (call-interactively func))
970           (when (member keys nosave-but-article)
971             (pop-to-buffer gnus-article-buffer 'norecord)))
972       (let ((obuf (current-buffer))
973             (owin (current-window-configuration))
974             (opoint (point))
975             func in-buffer)
976         (if not-restore-window
977             (pop-to-buffer gnus-summary-buffer 'norecord)
978           (switch-to-buffer gnus-summary-buffer 'norecord))
979         (setq in-buffer (current-buffer))
980         (if (setq func (lookup-key (current-local-map) keys))
981             (call-interactively func)
982           (ding))
983         (when (eq in-buffer (current-buffer))
984           (set-buffer obuf)
985           (unless not-restore-window
986             (set-window-configuration owin))
987           (set-window-point (get-buffer-window (current-buffer)) opoint))))))
988
989 (defun gnus-article-hide (&optional arg force)
990   "Hide all the gruft in the current article.
991 This means that PGP stuff, signatures, cited text and (some)
992 headers will be hidden.
993 If given a prefix, show the hidden text instead."
994   (interactive (list current-prefix-arg 'force))
995   (gnus-article-hide-headers arg)
996   (gnus-article-hide-pgp arg)
997   (gnus-article-hide-citation-maybe arg force)
998   (gnus-article-hide-signature arg))
999
1000 (defun gnus-article-maybe-highlight ()
1001   "Do some article highlighting if `article-visual' is non-nil."
1002   (if (gnus-visual-p 'article-highlight 'highlight)
1003       (gnus-article-highlight-some)))
1004
1005 (defun gnus-request-article-this-buffer (article group)
1006   "Get an article and insert it into this buffer."
1007   (let (do-update-line)
1008     (prog1
1009         (save-excursion
1010           (erase-buffer)
1011           (gnus-kill-all-overlays)
1012           (setq group (or group gnus-newsgroup-name))
1013
1014           ;; Open server if it has closed.
1015           (gnus-check-server (gnus-find-method-for-group group))
1016
1017           ;; Using `gnus-request-article' directly will insert the article into
1018           ;; `nntp-server-buffer' - so we'll save some time by not having to
1019           ;; copy it from the server buffer into the article buffer.
1020
1021           ;; We only request an article by message-id when we do not have the
1022           ;; headers for it, so we'll have to get those.
1023           (when (stringp article)
1024             (let ((gnus-override-method gnus-refer-article-method))
1025               (gnus-read-header article)))
1026
1027           ;; If the article number is negative, that means that this article
1028           ;; doesn't belong in this newsgroup (possibly), so we find its
1029           ;; message-id and request it by id instead of number.
1030           (when (and (numberp article)
1031                      gnus-summary-buffer
1032                      (get-buffer gnus-summary-buffer)
1033                      (buffer-name (get-buffer gnus-summary-buffer)))
1034             (save-excursion
1035               (set-buffer gnus-summary-buffer)
1036               (let ((header (gnus-summary-article-header article)))
1037                 (if (< article 0)
1038                     (cond 
1039                      ((memq article gnus-newsgroup-sparse)
1040                       ;; This is a sparse gap article.
1041                       (setq do-update-line article)
1042                       (setq article (mail-header-id header))
1043                       (let ((gnus-override-method gnus-refer-article-method))
1044                         (gnus-read-header article))
1045                       (setq gnus-newsgroup-sparse
1046                             (delq article gnus-newsgroup-sparse)))
1047                      ((vectorp header)
1048                       ;; It's a real article.
1049                       (setq article (mail-header-id header)))
1050                      (t
1051                       ;; It is an extracted pseudo-article.
1052                       (setq article 'pseudo)
1053                       (gnus-request-pseudo-article header))))
1054                 
1055                 (let ((method (gnus-find-method-for-group 
1056                                gnus-newsgroup-name)))
1057                   (if (not (eq (car method) 'nneething))
1058                       ()
1059                     (let ((dir (concat (file-name-as-directory (nth 1 method))
1060                                        (mail-header-subject header))))
1061                       (if (file-directory-p dir)
1062                           (progn
1063                             (setq article 'nneething)
1064                             (gnus-group-enter-directory dir)))))))))
1065
1066           (cond
1067            ;; Refuse to select canceled articles.
1068            ((and (numberp article)
1069                  gnus-summary-buffer
1070                  (get-buffer gnus-summary-buffer)
1071                  (buffer-name (get-buffer gnus-summary-buffer))
1072                  (eq (cdr (save-excursion
1073                             (set-buffer gnus-summary-buffer)
1074                             (assq article gnus-newsgroup-reads)))
1075                      gnus-canceled-mark))
1076             nil)
1077            ;; We first check `gnus-original-article-buffer'.
1078            ((and (get-buffer gnus-original-article-buffer)
1079                  (numberp article)
1080                  (save-excursion
1081                    (set-buffer gnus-original-article-buffer)
1082                    (and (equal (car gnus-original-article) group)
1083                         (eq (cdr gnus-original-article) article))))
1084             (insert-buffer-substring gnus-original-article-buffer)
1085             'article)
1086            ;; Check the backlog.
1087            ((and gnus-keep-backlog
1088                  (gnus-backlog-request-article group article (current-buffer)))
1089             'article)
1090            ;; Check asynchronous pre-fetch.
1091            ((gnus-async-request-fetched-article group article (current-buffer))
1092             (gnus-async-prefetch-next group article gnus-summary-buffer)
1093             'article)
1094            ;; Check the cache.
1095            ((and gnus-use-cache
1096                  (numberp article)
1097                  (gnus-cache-request-article article group))
1098             'article)
1099            ;; Get the article and put into the article buffer.
1100            ((or (stringp article) (numberp article))
1101             (let ((gnus-override-method
1102                    (and (stringp article) gnus-refer-article-method))
1103                   (buffer-read-only nil))
1104               (erase-buffer)
1105               (gnus-kill-all-overlays)
1106               (when (gnus-request-article article group (current-buffer))
1107                 (when (numberp article)
1108                   (gnus-async-prefetch-next group article gnus-summary-buffer)
1109                   (when gnus-keep-backlog
1110                     (gnus-backlog-enter-article 
1111                      group article (current-buffer))))
1112                 'article)))
1113            ;; It was a pseudo.
1114            (t article)))
1115
1116       ;; Take the article from the original article buffer
1117       ;; and place it in the buffer it's supposed to be in.
1118       (when (and (get-buffer gnus-article-buffer)
1119                  ;;(numberp article)
1120                  (equal (buffer-name (current-buffer))
1121                         (buffer-name (get-buffer gnus-article-buffer))))
1122         (save-excursion
1123           (if (get-buffer gnus-original-article-buffer)
1124               (set-buffer (get-buffer gnus-original-article-buffer))
1125             (set-buffer (get-buffer-create gnus-original-article-buffer))
1126             (buffer-disable-undo (current-buffer))
1127             (setq major-mode 'gnus-original-article-mode)
1128             (setq buffer-read-only t)
1129             (gnus-add-current-to-buffer-list))
1130           (let (buffer-read-only)
1131             (erase-buffer)
1132             (insert-buffer-substring gnus-article-buffer))
1133           (setq gnus-original-article (cons group article))))
1134     
1135       ;; Update sparse articles.
1136       (when (and do-update-line
1137                  (or (numberp article)
1138                      (stringp article)))
1139         (let ((buf (current-buffer)))
1140           (set-buffer gnus-summary-buffer)
1141           (gnus-summary-update-article do-update-line)
1142           (gnus-summary-goto-subject do-update-line nil t)
1143           (set-window-point (get-buffer-window (current-buffer) t)
1144                             (point))
1145           (set-buffer buf))))))
1146
1147 (defun gnus-article-date-ut (&optional type highlight)
1148   "Convert DATE date to universal time in the current article.
1149 If TYPE is `local', convert to local time; if it is `lapsed', output
1150 how much time has lapsed since DATE."
1151   (interactive (list 'ut t))
1152   (let ((headers (or gnus-current-headers (gnus-summary-article-header))))
1153     (save-excursion
1154       (set-buffer gnus-article-buffer)
1155       (article-date-ut type highlight headers))))
1156
1157 ;;;
1158 ;;; Article editing
1159 ;;;
1160
1161 (defvar gnus-article-edit-mode-hook nil
1162   "*Hook run in article edit mode buffers.")
1163
1164 (defvar gnus-article-edit-mode-map nil)
1165
1166 (unless gnus-article-edit-mode-map 
1167   (setq gnus-article-edit-mode-map (copy-keymap text-mode-map))
1168
1169   (gnus-define-keys gnus-article-edit-mode-map
1170     "\C-c\C-c" 'gnus-summary-edit-article-done)
1171
1172   (gnus-define-keys (gnus-article-edit-wash-map
1173                      "\C-c\C-w" gnus-article-edit-mode-map)
1174     "f" gnus-article-edit-full-stops))
1175
1176 (defun gnus-article-edit-mode ()
1177   "Major mode for editing articles.
1178 This is an extended text-mode.
1179
1180 \\{gnus-article-edit-mode-map}"
1181   (interactive)
1182   (kill-all-local-variables)
1183   (setq major-mode 'gnus-article-edit-mode)
1184   (setq mode-name "Article Edit")
1185   (make-local-variable 'minor-mode-alist)
1186   (use-local-map gnus-article-edit-mode-map)
1187   (run-hooks 'text-mode 'gnus-article-edit-mode-hook))
1188
1189 (defun gnus-article-edit-full-stops ()
1190   "Interactively repair spacing at end of sentences."
1191   (interactive)
1192   (save-excursion
1193     (goto-char (point-min))
1194     (search-forward-regexp "^$" nil t)
1195     (let ((case-fold-search nil))
1196       (query-replace-regexp "\\([.!?][])}]* \\)\\([[({A-Z]\\)" "\\1 \\2"))))
1197
1198 (provide 'gnus-art)
1199
1200 ;;; gnus-art.el ends here