2c00b116f0338269be646a95f3014a8f39c77661
[gnus] / lisp / gnus-sum.el
1 ;;; gnus-sum.el --- summary mode commands for Gnus
2 ;; Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002
3 ;;        Free Software Foundation, Inc.
4
5 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
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
29 (eval-when-compile (require 'cl))
30
31 (require 'gnus)
32 (require 'gnus-group)
33 (require 'gnus-spec)
34 (require 'gnus-range)
35 (require 'gnus-int)
36 (require 'gnus-undo)
37 (require 'gnus-util)
38 (require 'mm-decode)
39 (require 'nnoo)
40
41 (autoload 'gnus-summary-limit-include-cached "gnus-cache" nil t)
42 (autoload 'gnus-cache-write-active "gnus-cache")
43 (autoload 'gnus-mailing-list-insinuate "gnus-ml" nil t)
44 (autoload 'turn-on-gnus-mailing-list-mode "gnus-ml" nil t)
45 (autoload 'mm-uu-dissect "mm-uu")
46 (autoload 'gnus-article-outlook-deuglify-article "deuglify"
47   "Deuglify broken Outlook (Express) articles and redisplay."
48   t)
49
50 (defcustom gnus-kill-summary-on-exit t
51   "*If non-nil, kill the summary buffer when you exit from it.
52 If nil, the summary will become a \"*Dead Summary*\" buffer, and
53 it will be killed sometime later."
54   :group 'gnus-summary-exit
55   :type 'boolean)
56
57 (defcustom gnus-fetch-old-headers nil
58   "*Non-nil means that Gnus will try to build threads by grabbing old headers.
59 If an unread article in the group refers to an older, already read (or
60 just marked as read) article, the old article will not normally be
61 displayed in the Summary buffer.  If this variable is non-nil, Gnus
62 will attempt to grab the headers to the old articles, and thereby
63 build complete threads.  If it has the value `some', only enough
64 headers to connect otherwise loose threads will be displayed.  This
65 variable can also be a number.  In that case, no more than that number
66 of old headers will be fetched.  If it has the value `invisible', all
67 old headers will be fetched, but none will be displayed.
68
69 The server has to support NOV for any of this to work."
70   :group 'gnus-thread
71   :type '(choice (const :tag "off" nil)
72                  (const some)
73                  number
74                  (sexp :menu-tag "other" t)))
75
76 (defcustom gnus-refer-thread-limit 200
77   "*The number of old headers to fetch when doing \\<gnus-summary-mode-map>\\[gnus-summary-refer-thread].
78 If t, fetch all the available old headers."
79   :group 'gnus-thread
80   :type '(choice number
81                  (sexp :menu-tag "other" t)))
82
83 (defcustom gnus-summary-make-false-root 'adopt
84   "*nil means that Gnus won't gather loose threads.
85 If the root of a thread has expired or been read in a previous
86 session, the information necessary to build a complete thread has been
87 lost.  Instead of having many small sub-threads from this original thread
88 scattered all over the summary buffer, Gnus can gather them.
89
90 If non-nil, Gnus will try to gather all loose sub-threads from an
91 original thread into one large thread.
92
93 If this variable is non-nil, it should be one of `none', `adopt',
94 `dummy' or `empty'.
95
96 If this variable is `none', Gnus will not make a false root, but just
97 present the sub-threads after another.
98 If this variable is `dummy', Gnus will create a dummy root that will
99 have all the sub-threads as children.
100 If this variable is `adopt', Gnus will make one of the \"children\"
101 the parent and mark all the step-children as such.
102 If this variable is `empty', the \"children\" are printed with empty
103 subject fields.  (Or rather, they will be printed with a string
104 given by the `gnus-summary-same-subject' variable.)"
105   :group 'gnus-thread
106   :type '(choice (const :tag "off" nil)
107                  (const none)
108                  (const dummy)
109                  (const adopt)
110                  (const empty)))
111
112 (defcustom gnus-summary-gather-exclude-subject "^ *$\\|^(none)$"
113   "*A regexp to match subjects to be excluded from loose thread gathering.
114 As loose thread gathering is done on subjects only, that means that
115 there can be many false gatherings performed.  By rooting out certain
116 common subjects, gathering might become saner."
117   :group 'gnus-thread
118   :type 'regexp)
119
120 (defcustom gnus-summary-gather-subject-limit nil
121   "*Maximum length of subject comparisons when gathering loose threads.
122 Use nil to compare full subjects.  Setting this variable to a low
123 number will help gather threads that have been corrupted by
124 newsreaders chopping off subject lines, but it might also mean that
125 unrelated articles that have subject that happen to begin with the
126 same few characters will be incorrectly gathered.
127
128 If this variable is `fuzzy', Gnus will use a fuzzy algorithm when
129 comparing subjects."
130   :group 'gnus-thread
131   :type '(choice (const :tag "off" nil)
132                  (const fuzzy)
133                  (sexp :menu-tag "on" t)))
134
135 (defcustom gnus-simplify-subject-functions nil
136   "List of functions taking a string argument that simplify subjects.
137 The functions are applied recursively.
138
139 Useful functions to put in this list include:
140 `gnus-simplify-subject-re', `gnus-simplify-subject-fuzzy',
141 `gnus-simplify-whitespace', and `gnus-simplify-all-whitespace'."
142   :group 'gnus-thread
143   :type '(repeat function))
144
145 (defcustom gnus-simplify-ignored-prefixes nil
146   "*Remove matches for this regexp from subject lines when simplifying fuzzily."
147   :group 'gnus-thread
148   :type '(choice (const :tag "off" nil)
149                  regexp))
150
151 (defcustom gnus-build-sparse-threads nil
152   "*If non-nil, fill in the gaps in threads.
153 If `some', only fill in the gaps that are needed to tie loose threads
154 together.  If `more', fill in all leaf nodes that Gnus can find.  If
155 non-nil and non-`some', fill in all gaps that Gnus manages to guess."
156   :group 'gnus-thread
157   :type '(choice (const :tag "off" nil)
158                  (const some)
159                  (const more)
160                  (sexp :menu-tag "all" t)))
161
162 (defcustom gnus-summary-thread-gathering-function
163   'gnus-gather-threads-by-subject
164   "*Function used for gathering loose threads.
165 There are two pre-defined functions: `gnus-gather-threads-by-subject',
166 which only takes Subjects into consideration; and
167 `gnus-gather-threads-by-references', which compared the References
168 headers of the articles to find matches."
169   :group 'gnus-thread
170   :type '(radio (function-item gnus-gather-threads-by-subject)
171                 (function-item gnus-gather-threads-by-references)
172                 (function :tag "other")))
173
174 (defcustom gnus-summary-same-subject ""
175   "*String indicating that the current article has the same subject as the previous.
176 This variable will only be used if the value of
177 `gnus-summary-make-false-root' is `empty'."
178   :group 'gnus-summary-format
179   :type 'string)
180
181 (defcustom gnus-summary-goto-unread t
182   "*If t, many commands will go to the next unread article.
183 This applies to marking commands as well as other commands that
184 \"naturally\" select the next article, like, for instance, `SPC' at
185 the end of an article.
186
187 If nil, the marking commands do NOT go to the next unread article
188 \(they go to the next article instead).  If `never', commands that
189 usually go to the next unread article, will go to the next article,
190 whether it is read or not."
191   :group 'gnus-summary-marks
192   :link '(custom-manual "(gnus)Setting Marks")
193   :type '(choice (const :tag "off" nil)
194                  (const never)
195                  (sexp :menu-tag "on" t)))
196
197 (defcustom gnus-summary-default-score 0
198   "*Default article score level.
199 All scores generated by the score files will be added to this score.
200 If this variable is nil, scoring will be disabled."
201   :group 'gnus-score-default
202   :type '(choice (const :tag "disable")
203                  integer))
204
205 (defcustom gnus-summary-default-high-score 0
206   "*Default threshold for a high scored article.
207 An article will be highlighted as high scored if its score is greater
208 than this score."
209   :group 'gnus-score-default
210   :type 'integer)
211
212 (defcustom gnus-summary-default-low-score 0
213   "*Default threshold for a low scored article.
214 An article will be highlighted as low scored if its score is smaller
215 than this score."
216   :group 'gnus-score-default
217   :type 'integer)
218
219 (defcustom gnus-summary-zcore-fuzz 0
220   "*Fuzziness factor for the zcore in the summary buffer.
221 Articles with scores closer than this to `gnus-summary-default-score'
222 will not be marked."
223   :group 'gnus-summary-format
224   :type 'integer)
225
226 (defcustom gnus-simplify-subject-fuzzy-regexp nil
227   "*Strings to be removed when doing fuzzy matches.
228 This can either be a regular expression or list of regular expressions
229 that will be removed from subject strings if fuzzy subject
230 simplification is selected."
231   :group 'gnus-thread
232   :type '(repeat regexp))
233
234 (defcustom gnus-show-threads t
235   "*If non-nil, display threads in summary mode."
236   :group 'gnus-thread
237   :type 'boolean)
238
239 (defcustom gnus-thread-hide-subtree nil
240   "*If non-nil, hide all threads initially.
241 This can be a predicate specifier which says which threads to hide.
242 If threads are hidden, you have to run the command
243 `gnus-summary-show-thread' by hand or use `gnus-select-article-hook'
244 to expose hidden threads."
245   :group 'gnus-thread
246   :type 'boolean)
247
248 (defcustom gnus-thread-hide-killed t
249   "*If non-nil, hide killed threads automatically."
250   :group 'gnus-thread
251   :type 'boolean)
252
253 (defcustom gnus-thread-ignore-subject t
254   "*If non-nil, which is the default, ignore subjects and do all threading based on the Reference header.
255 If nil, articles that have different subjects from their parents will
256 start separate threads."
257   :group 'gnus-thread
258   :type 'boolean)
259
260 (defcustom gnus-thread-operation-ignore-subject t
261   "*If non-nil, subjects will be ignored when doing thread commands.
262 This affects commands like `gnus-summary-kill-thread' and
263 `gnus-summary-lower-thread'.
264
265 If this variable is nil, articles in the same thread with different
266 subjects will not be included in the operation in question.  If this
267 variable is `fuzzy', only articles that have subjects that are fuzzily
268 equal will be included."
269   :group 'gnus-thread
270   :type '(choice (const :tag "off" nil)
271                  (const fuzzy)
272                  (sexp :tag "on" t)))
273
274 (defcustom gnus-thread-indent-level 4
275   "*Number that says how much each sub-thread should be indented."
276   :group 'gnus-thread
277   :type 'integer)
278
279 (defcustom gnus-auto-extend-newsgroup t
280   "*If non-nil, extend newsgroup forward and backward when requested."
281   :group 'gnus-summary-choose
282   :type 'boolean)
283
284 (defcustom gnus-auto-select-first t
285   "*If non-nil, select the article under point.
286 Which article this is is controlled by the `gnus-auto-select-subject'
287 variable.
288
289 If you want to prevent automatic selection of articles in some
290 newsgroups, set the variable to nil in `gnus-select-group-hook'."
291   :group 'gnus-group-select
292   :type '(choice (const :tag "none" nil)
293                  (sexp :menu-tag "first" t)))
294
295 (defcustom gnus-auto-select-subject 'unread
296   "*Says what subject to place under point when entering a group.
297
298 This variable can either be the symbols `first' (place point on the
299 first subject), `unread' (place point on the subject line of the first
300 unread article), `best' (place point on the subject line of the
301 higest-scored article), `unseen' (place point on the subject line of
302 the first unseen article), 'unseen-or-unread' (place point on the subject
303 line of the first unseen article or, if all article have been seen, on the
304 subject line of the first unread article), or a function to be called to
305 place point on some subject line."
306   :group 'gnus-group-select
307   :type '(choice (const best)
308                  (const unread)
309                  (const first)
310                  (const unseen)
311                  (const unseen-or-unread)))
312
313 (defcustom gnus-auto-select-next t
314   "*If non-nil, offer to go to the next group from the end of the previous.
315 If the value is t and the next newsgroup is empty, Gnus will exit
316 summary mode and go back to group mode.  If the value is neither nil
317 nor t, Gnus will select the following unread newsgroup.  In
318 particular, if the value is the symbol `quietly', the next unread
319 newsgroup will be selected without any confirmation, and if it is
320 `almost-quietly', the next group will be selected without any
321 confirmation if you are located on the last article in the group.
322 Finally, if this variable is `slightly-quietly', the `Z n' command
323 will go to the next group without confirmation."
324   :group 'gnus-summary-maneuvering
325   :type '(choice (const :tag "off" nil)
326                  (const quietly)
327                  (const almost-quietly)
328                  (const slightly-quietly)
329                  (sexp :menu-tag "on" t)))
330
331 (defcustom gnus-auto-select-same nil
332   "*If non-nil, select the next article with the same subject.
333 If there are no more articles with the same subject, go to
334 the first unread article."
335   :group 'gnus-summary-maneuvering
336   :type 'boolean)
337
338 (defcustom gnus-summary-check-current nil
339   "*If non-nil, consider the current article when moving.
340 The \"unread\" movement commands will stay on the same line if the
341 current article is unread."
342   :group 'gnus-summary-maneuvering
343   :type 'boolean)
344
345 (defcustom gnus-auto-center-summary t
346   "*If non-nil, always center the current summary buffer.
347 In particular, if `vertical' do only vertical recentering.  If non-nil
348 and non-`vertical', do both horizontal and vertical recentering."
349   :group 'gnus-summary-maneuvering
350   :type '(choice (const :tag "none" nil)
351                  (const vertical)
352                  (integer :tag "height")
353                  (sexp :menu-tag "both" t)))
354
355 (defcustom gnus-show-all-headers nil
356   "*If non-nil, don't hide any headers."
357   :group 'gnus-article-hiding
358   :group 'gnus-article-headers
359   :type 'boolean)
360
361 (defcustom gnus-summary-ignore-duplicates nil
362   "*If non-nil, ignore articles with identical Message-ID headers."
363   :group 'gnus-summary
364   :type 'boolean)
365
366 (defcustom gnus-single-article-buffer t
367   "*If non-nil, display all articles in the same buffer.
368 If nil, each group will get its own article buffer."
369   :group 'gnus-article-various
370   :type 'boolean)
371
372 (defcustom gnus-break-pages t
373   "*If non-nil, do page breaking on articles.
374 The page delimiter is specified by the `gnus-page-delimiter'
375 variable."
376   :group 'gnus-article-various
377   :type 'boolean)
378
379 (defcustom gnus-move-split-methods nil
380   "*Variable used to suggest where articles are to be moved to.
381 It uses the same syntax as the `gnus-split-methods' variable.
382 However, whereas `gnus-split-methods' specifies file names as targets,
383 this variable specifies group names."
384   :group 'gnus-summary-mail
385   :type '(repeat (choice (list :value (fun) function)
386                          (cons :value ("" "") regexp (repeat string))
387                          (sexp :value nil))))
388
389 (defcustom gnus-unread-mark ?           ;Whitespace
390   "*Mark used for unread articles."
391   :group 'gnus-summary-marks
392   :type 'character)
393
394 (defcustom gnus-ticked-mark ?!
395   "*Mark used for ticked articles."
396   :group 'gnus-summary-marks
397   :type 'character)
398
399 (defcustom gnus-dormant-mark ??
400   "*Mark used for dormant articles."
401   :group 'gnus-summary-marks
402   :type 'character)
403
404 (defcustom gnus-del-mark ?r
405   "*Mark used for del'd articles."
406   :group 'gnus-summary-marks
407   :type 'character)
408
409 (defcustom gnus-read-mark ?R
410   "*Mark used for read articles."
411   :group 'gnus-summary-marks
412   :type 'character)
413
414 (defcustom gnus-expirable-mark ?E
415   "*Mark used for expirable articles."
416   :group 'gnus-summary-marks
417   :type 'character)
418
419 (defcustom gnus-killed-mark ?K
420   "*Mark used for killed articles."
421   :group 'gnus-summary-marks
422   :type 'character)
423
424 (defcustom gnus-spam-mark ?H
425   "*Mark used for spam articles."
426   :group 'gnus-summary-marks
427   :type 'character)
428
429 (defcustom gnus-souped-mark ?F
430   "*Mark used for souped articles."
431   :group 'gnus-summary-marks
432   :type 'character)
433
434 (defcustom gnus-kill-file-mark ?X
435   "*Mark used for articles killed by kill files."
436   :group 'gnus-summary-marks
437   :type 'character)
438
439 (defcustom gnus-low-score-mark ?Y
440   "*Mark used for articles with a low score."
441   :group 'gnus-summary-marks
442   :type 'character)
443
444 (defcustom gnus-catchup-mark ?C
445   "*Mark used for articles that are caught up."
446   :group 'gnus-summary-marks
447   :type 'character)
448
449 (defcustom gnus-replied-mark ?A
450   "*Mark used for articles that have been replied to."
451   :group 'gnus-summary-marks
452   :type 'character)
453
454 (defcustom gnus-forwarded-mark ?F
455   "*Mark used for articles that have been forwarded."
456   :group 'gnus-summary-marks
457   :type 'character)
458
459 (defcustom gnus-recent-mark ?N
460   "*Mark used for articles that are recent."
461   :group 'gnus-summary-marks
462   :type 'character)
463
464 (defcustom gnus-cached-mark ?*
465   "*Mark used for articles that are in the cache."
466   :group 'gnus-summary-marks
467   :type 'character)
468
469 (defcustom gnus-saved-mark ?S
470   "*Mark used for articles that have been saved."
471   :group 'gnus-summary-marks
472   :type 'character)
473
474 (defcustom gnus-unseen-mark ?.
475   "*Mark used for articles that haven't been seen."
476   :group 'gnus-summary-marks
477   :type 'character)
478
479 (defcustom gnus-no-mark ?               ;Whitespace
480   "*Mark used for articles that have no other secondary mark."
481   :group 'gnus-summary-marks
482   :type 'character)
483
484 (defcustom gnus-ancient-mark ?O
485   "*Mark used for ancient articles."
486   :group 'gnus-summary-marks
487   :type 'character)
488
489 (defcustom gnus-sparse-mark ?Q
490   "*Mark used for sparsely reffed articles."
491   :group 'gnus-summary-marks
492   :type 'character)
493
494 (defcustom gnus-canceled-mark ?G
495   "*Mark used for canceled articles."
496   :group 'gnus-summary-marks
497   :type 'character)
498
499 (defcustom gnus-duplicate-mark ?M
500   "*Mark used for duplicate articles."
501   :group 'gnus-summary-marks
502   :type 'character)
503
504 (defcustom gnus-undownloaded-mark ?@
505   "*Mark used for articles that weren't downloaded."
506   :group 'gnus-summary-marks
507   :type 'character)
508
509 (defcustom gnus-downloadable-mark ?%
510   "*Mark used for articles that are to be downloaded."
511   :group 'gnus-summary-marks
512   :type 'character)
513
514 (defcustom gnus-unsendable-mark ?=
515   "*Mark used for articles that won't be sent."
516   :group 'gnus-summary-marks
517   :type 'character)
518
519 (defcustom gnus-score-over-mark ?+
520   "*Score mark used for articles with high scores."
521   :group 'gnus-summary-marks
522   :type 'character)
523
524 (defcustom gnus-score-below-mark ?-
525   "*Score mark used for articles with low scores."
526   :group 'gnus-summary-marks
527   :type 'character)
528
529 (defcustom gnus-empty-thread-mark ?     ;Whitespace
530   "*There is no thread under the article."
531   :group 'gnus-summary-marks
532   :type 'character)
533
534 (defcustom gnus-not-empty-thread-mark ?=
535   "*There is a thread under the article."
536   :group 'gnus-summary-marks
537   :type 'character)
538
539 (defcustom gnus-view-pseudo-asynchronously nil
540   "*If non-nil, Gnus will view pseudo-articles asynchronously."
541   :group 'gnus-extract-view
542   :type 'boolean)
543
544 (defcustom gnus-auto-expirable-marks
545   (list gnus-killed-mark gnus-del-mark gnus-catchup-mark
546         gnus-low-score-mark gnus-ancient-mark gnus-read-mark
547         gnus-souped-mark gnus-duplicate-mark)
548   "*The list of marks converted into expiration if a group is auto-expirable."
549   :version "21.1"
550   :group 'gnus-summary
551   :type '(repeat character))
552
553 (defcustom gnus-inhibit-user-auto-expire t
554   "*If non-nil, user marking commands will not mark an article as expirable, even if the group has auto-expire turned on."
555   :version "21.1"
556   :group 'gnus-summary
557   :type 'boolean)
558
559 (defcustom gnus-view-pseudos nil
560   "*If `automatic', pseudo-articles will be viewed automatically.
561 If `not-confirm', pseudos will be viewed automatically, and the user
562 will not be asked to confirm the command."
563   :group 'gnus-extract-view
564   :type '(choice (const :tag "off" nil)
565                  (const automatic)
566                  (const not-confirm)))
567
568 (defcustom gnus-view-pseudos-separately t
569   "*If non-nil, one pseudo-article will be created for each file to be viewed.
570 If nil, all files that use the same viewing command will be given as a
571 list of parameters to that command."
572   :group 'gnus-extract-view
573   :type 'boolean)
574
575 (defcustom gnus-insert-pseudo-articles t
576   "*If non-nil, insert pseudo-articles when decoding articles."
577   :group 'gnus-extract-view
578   :type 'boolean)
579
580 (defcustom gnus-summary-dummy-line-format
581   "  %(:                          :%) %S\n"
582   "*The format specification for the dummy roots in the summary buffer.
583 It works along the same lines as a normal formatting string,
584 with some simple extensions.
585
586 %S  The subject
587
588 General format specifiers can also be used.
589 See `(gnus)Formatting Variables'."
590   :link '(custom-manual "(gnus)Formatting Variables")
591   :group 'gnus-threading
592   :type 'string)
593
594 (defcustom gnus-summary-mode-line-format "Gnus: %g [%A] %Z"
595   "*The format specification for the summary mode line.
596 It works along the same lines as a normal formatting string,
597 with some simple extensions:
598
599 %G  Group name
600 %p  Unprefixed group name
601 %A  Current article number
602 %z  Current article score
603 %V  Gnus version
604 %U  Number of unread articles in the group
605 %e  Number of unselected articles in the group
606 %Z  A string with unread/unselected article counts
607 %g  Shortish group name
608 %S  Subject of the current article
609 %u  User-defined spec
610 %s  Current score file name
611 %d  Number of dormant articles
612 %r  Number of articles that have been marked as read in this session
613 %E  Number of articles expunged by the score files"
614   :group 'gnus-summary-format
615   :type 'string)
616
617 (defcustom gnus-list-identifiers nil
618   "Regexp that matches list identifiers to be removed from subject.
619 This can also be a list of regexps."
620   :version "21.1"
621   :group 'gnus-summary-format
622   :group 'gnus-article-hiding
623   :type '(choice (const :tag "none" nil)
624                  (regexp :value ".*")
625                  (repeat :value (".*") regexp)))
626
627 (defcustom gnus-summary-mark-below 0
628   "*Mark all articles with a score below this variable as read.
629 This variable is local to each summary buffer and usually set by the
630 score file."
631   :group 'gnus-score-default
632   :type 'integer)
633
634 (defcustom gnus-article-sort-functions '(gnus-article-sort-by-number)
635   "*List of functions used for sorting articles in the summary buffer.
636
637 Each function takes two articles and returns non-nil if the first
638 article should be sorted before the other.  If you use more than one
639 function, the primary sort function should be the last.  You should
640 probably always include `gnus-article-sort-by-number' in the list of
641 sorting functions -- preferably first.  Also note that sorting by date
642 is often much slower than sorting by number, and the sorting order is
643 very similar.  (Sorting by date means sorting by the time the message
644 was sent, sorting by number means sorting by arrival time.)
645
646 Ready-made functions include `gnus-article-sort-by-number',
647 `gnus-article-sort-by-author', `gnus-article-sort-by-subject',
648 `gnus-article-sort-by-date', `gnus-article-sort-by-random'
649 and `gnus-article-sort-by-score'.
650
651 When threading is turned on, the variable `gnus-thread-sort-functions'
652 controls how articles are sorted."
653   :group 'gnus-summary-sort
654   :type '(repeat (choice (function-item gnus-article-sort-by-number)
655                          (function-item gnus-article-sort-by-author)
656                          (function-item gnus-article-sort-by-subject)
657                          (function-item gnus-article-sort-by-date)
658                          (function-item gnus-article-sort-by-score)
659                          (function-item gnus-article-sort-by-random)
660                          (function :tag "other"))))
661
662 (defcustom gnus-thread-sort-functions '(gnus-thread-sort-by-number)
663   "*List of functions used for sorting threads in the summary buffer.
664 By default, threads are sorted by article number.
665
666 Each function takes two threads and returns non-nil if the first
667 thread should be sorted before the other.  If you use more than one
668 function, the primary sort function should be the last.  You should
669 probably always include `gnus-thread-sort-by-number' in the list of
670 sorting functions -- preferably first.  Also note that sorting by date
671 is often much slower than sorting by number, and the sorting order is
672 very similar.  (Sorting by date means sorting by the time the message
673 was sent, sorting by number means sorting by arrival time.)
674
675 Ready-made functions include `gnus-thread-sort-by-number',
676 `gnus-thread-sort-by-author', `gnus-thread-sort-by-subject',
677 `gnus-thread-sort-by-date', `gnus-thread-sort-by-score',
678 `gnus-thread-sort-by-most-recent-number',
679 `gnus-thread-sort-by-most-recent-date',
680 `gnus-thread-sort-by-random', and
681 `gnus-thread-sort-by-total-score' (see `gnus-thread-score-function').
682
683 When threading is turned off, the variable
684 `gnus-article-sort-functions' controls how articles are sorted."
685   :group 'gnus-summary-sort
686   :type '(repeat (choice (function-item gnus-thread-sort-by-number)
687                          (function-item gnus-thread-sort-by-author)
688                          (function-item gnus-thread-sort-by-subject)
689                          (function-item gnus-thread-sort-by-date)
690                          (function-item gnus-thread-sort-by-score)
691                          (function-item gnus-thread-sort-by-total-score)
692                          (function-item gnus-thread-sort-by-random)
693                          (function :tag "other"))))
694
695 (defcustom gnus-thread-score-function '+
696   "*Function used for calculating the total score of a thread.
697
698 The function is called with the scores of the article and each
699 subthread and should then return the score of the thread.
700
701 Some functions you can use are `+', `max', or `min'."
702   :group 'gnus-summary-sort
703   :type 'function)
704
705 (defcustom gnus-summary-expunge-below nil
706   "All articles that have a score less than this variable will be expunged.
707 This variable is local to the summary buffers."
708   :group 'gnus-score-default
709   :type '(choice (const :tag "off" nil)
710                  integer))
711
712 (defcustom gnus-thread-expunge-below nil
713   "All threads that have a total score less than this variable will be expunged.
714 See `gnus-thread-score-function' for en explanation of what a
715 \"thread score\" is.
716
717 This variable is local to the summary buffers."
718   :group 'gnus-threading
719   :group 'gnus-score-default
720   :type '(choice (const :tag "off" nil)
721                  integer))
722
723 (defcustom gnus-summary-mode-hook nil
724   "*A hook for Gnus summary mode.
725 This hook is run before any variables are set in the summary buffer."
726   :options '(turn-on-gnus-mailing-list-mode gnus-pick-mode)
727   :group 'gnus-summary-various
728   :type 'hook)
729
730 ;; Extracted from gnus-xmas-redefine in order to preserve user settings
731 (when (featurep 'xemacs)
732   (add-hook 'gnus-summary-mode-hook 'gnus-xmas-summary-menu-add)
733   (add-hook 'gnus-summary-mode-hook 'gnus-xmas-setup-summary-toolbar)
734   (add-hook 'gnus-summary-mode-hook
735             'gnus-xmas-switch-horizontal-scrollbar-off))
736
737 (defcustom gnus-summary-menu-hook nil
738   "*Hook run after the creation of the summary mode menu."
739   :group 'gnus-summary-visual
740   :type 'hook)
741
742 (defcustom gnus-summary-exit-hook nil
743   "*A hook called on exit from the summary buffer.
744 It will be called with point in the group buffer."
745   :group 'gnus-summary-exit
746   :type 'hook)
747
748 (defcustom gnus-summary-prepare-hook nil
749   "*A hook called after the summary buffer has been generated.
750 If you want to modify the summary buffer, you can use this hook."
751   :group 'gnus-summary-various
752   :type 'hook)
753
754 (defcustom gnus-summary-prepared-hook nil
755   "*A hook called as the last thing after the summary buffer has been generated."
756   :group 'gnus-summary-various
757   :type 'hook)
758
759 (defcustom gnus-summary-generate-hook nil
760   "*A hook run just before generating the summary buffer.
761 This hook is commonly used to customize threading variables and the
762 like."
763   :group 'gnus-summary-various
764   :type 'hook)
765
766 (defcustom gnus-select-group-hook nil
767   "*A hook called when a newsgroup is selected.
768
769 If you'd like to simplify subjects like the
770 `gnus-summary-next-same-subject' command does, you can use the
771 following hook:
772
773  (add-hook gnus-select-group-hook
774            (lambda ()
775              (mapcar (lambda (header)
776                        (mail-header-set-subject
777                         header
778                         (gnus-simplify-subject
779                          (mail-header-subject header) 're-only)))
780                      gnus-newsgroup-headers)))"
781   :group 'gnus-group-select
782   :type 'hook)
783
784 (defcustom gnus-select-article-hook nil
785   "*A hook called when an article is selected."
786   :group 'gnus-summary-choose
787   :type 'hook)
788
789 (defcustom gnus-visual-mark-article-hook
790   (list 'gnus-highlight-selected-summary)
791   "*Hook run after selecting an article in the summary buffer.
792 It is meant to be used for highlighting the article in some way.  It
793 is not run if `gnus-visual' is nil."
794   :group 'gnus-summary-visual
795   :type 'hook)
796
797 (defcustom gnus-parse-headers-hook nil
798   "*A hook called before parsing the headers."
799   :group 'gnus-various
800   :type 'hook)
801
802 (defcustom gnus-exit-group-hook nil
803   "*A hook called when exiting summary mode.
804 This hook is not called from the non-updating exit commands like `Q'."
805   :group 'gnus-various
806   :type 'hook)
807
808 (defcustom gnus-summary-update-hook
809   (list 'gnus-summary-highlight-line)
810   "*A hook called when a summary line is changed.
811 The hook will not be called if `gnus-visual' is nil.
812
813 The default function `gnus-summary-highlight-line' will
814 highlight the line according to the `gnus-summary-highlight'
815 variable."
816   :group 'gnus-summary-visual
817   :type 'hook)
818
819 (defcustom gnus-mark-article-hook '(gnus-summary-mark-read-and-unread-as-read)
820   "*A hook called when an article is selected for the first time.
821 The hook is intended to mark an article as read (or unread)
822 automatically when it is selected."
823   :group 'gnus-summary-choose
824   :type 'hook)
825
826 (defcustom gnus-group-no-more-groups-hook nil
827   "*A hook run when returning to group mode having no more (unread) groups."
828   :group 'gnus-group-select
829   :type 'hook)
830
831 (defcustom gnus-ps-print-hook nil
832   "*A hook run before ps-printing something from Gnus."
833   :group 'gnus-summary
834   :type 'hook)
835
836 (defcustom gnus-summary-display-arrow
837   (and (fboundp 'display-graphic-p)
838        (display-graphic-p))
839   "*If non-nil, display an arrow highlighting the current article."
840   :version "21.1"
841   :group 'gnus-summary
842   :type 'boolean)
843
844 (defcustom gnus-summary-selected-face 'gnus-summary-selected-face
845   "Face used for highlighting the current article in the summary buffer."
846   :group 'gnus-summary-visual
847   :type 'face)
848
849 (defcustom gnus-summary-highlight
850   '(((eq mark gnus-canceled-mark)
851      . gnus-summary-cancelled-face)
852     ((and (> score default-high)
853           (or (eq mark gnus-dormant-mark)
854               (eq mark gnus-ticked-mark)))
855      . gnus-summary-high-ticked-face)
856     ((and (< score default-low)
857           (or (eq mark gnus-dormant-mark)
858               (eq mark gnus-ticked-mark)))
859      . gnus-summary-low-ticked-face)
860     ((or (eq mark gnus-dormant-mark)
861          (eq mark gnus-ticked-mark))
862      . gnus-summary-normal-ticked-face)
863     ((and (> score default-high) (eq mark gnus-ancient-mark))
864      . gnus-summary-high-ancient-face)
865     ((and (< score default-low) (eq mark gnus-ancient-mark))
866      . gnus-summary-low-ancient-face)
867     ((eq mark gnus-ancient-mark)
868      . gnus-summary-normal-ancient-face)
869     (downloaded
870      . gnus-agent-downloaded-article-face)
871     ((and (> score default-high) (eq mark gnus-unread-mark))
872      . gnus-summary-high-unread-face)
873     ((and (< score default-low) (eq mark gnus-unread-mark))
874      . gnus-summary-low-unread-face)
875     ((eq mark gnus-unread-mark)
876      . gnus-summary-normal-unread-face)
877     ((and (> score default-high) (memq mark (list gnus-downloadable-mark
878                                                   gnus-undownloaded-mark)))
879      . gnus-summary-high-unread-face)
880     ((and (< score default-low) (memq mark (list gnus-downloadable-mark
881                                                  gnus-undownloaded-mark)))
882      . gnus-summary-low-unread-face)
883     ((and (memq mark (list gnus-downloadable-mark gnus-undownloaded-mark))
884           (memq article gnus-newsgroup-unreads))
885      . gnus-summary-normal-unread-face)
886     ((memq mark (list gnus-downloadable-mark gnus-undownloaded-mark))
887      . gnus-summary-normal-read-face)
888     ((> score default-high)
889      . gnus-summary-high-read-face)
890     ((< score default-low)
891      . gnus-summary-low-read-face)
892     (t
893      . gnus-summary-normal-read-face))
894   "*Controls the highlighting of summary buffer lines.
895
896 A list of (FORM . FACE) pairs.  When deciding how a a particular
897 summary line should be displayed, each form is evaluated.  The content
898 of the face field after the first true form is used.  You can change
899 how those summary lines are displayed, by editing the face field.
900
901 You can use the following variables in the FORM field.
902
903 score:        The article's score
904 default:      The default article score.
905 default-high: The default score for high scored articles.
906 default-low:  The default score for low scored articles.
907 below:        The score below which articles are automatically marked as read.
908 mark:         The articles mark."
909   :group 'gnus-summary-visual
910   :type '(repeat (cons (sexp :tag "Form" nil)
911                        face)))
912
913 (defcustom gnus-alter-header-function nil
914   "Function called to allow alteration of article header structures.
915 The function is called with one parameter, the article header vector,
916 which it may alter in any way.")
917
918 (defvar gnus-decode-encoded-word-function 'mail-decode-encoded-word-string
919   "Variable that says which function should be used to decode a string with encoded words.")
920
921 (defcustom gnus-extra-headers '(To Newsgroups)
922   "*Extra headers to parse."
923   :version "21.1"
924   :group 'gnus-summary
925   :type '(repeat symbol))
926
927 (defcustom gnus-ignored-from-addresses
928   (and user-mail-address (regexp-quote user-mail-address))
929   "*Regexp of From headers that may be suppressed in favor of To headers."
930   :version "21.1"
931   :group 'gnus-summary
932   :type 'regexp)
933
934 (defcustom gnus-newsgroup-ignored-charsets '(unknown-8bit x-unknown)
935   "List of charsets that should be ignored.
936 When these charsets are used in the \"charset\" parameter, the
937 default charset will be used instead."
938   :version "21.1"
939   :type '(repeat symbol)
940   :group 'gnus-charset)
941
942 (gnus-define-group-parameter
943  ignored-charsets
944  :type list
945  :function-document
946  "Return the ignored charsets of GROUP."
947  :variable gnus-group-ignored-charsets-alist
948  :variable-default
949  '(("alt\\.chinese\\.text" iso-8859-1))
950  :variable-document
951  "Alist of regexps (to match group names) and charsets that should be ignored.
952 When these charsets are used in the \"charset\" parameter, the
953 default charset will be used instead."
954  :variable-group gnus-charset
955  :variable-type '(repeat (cons (regexp :tag "Group")
956                                (repeat symbol)))
957  :parameter-type '(choice :tag "Ignored charsets"
958                           :value nil
959                           (repeat (symbol)))
960  :parameter-document       "\
961 List of charsets that should be ignored.
962
963 When these charsets are used in the \"charset\" parameter, the
964 default charset will be used instead.")
965
966 (defcustom gnus-group-highlight-words-alist nil
967   "Alist of group regexps and highlight regexps.
968 This variable uses the same syntax as `gnus-emphasis-alist'."
969   :version "21.1"
970   :type '(repeat (cons (regexp :tag "Group")
971                        (repeat (list (regexp :tag "Highlight regexp")
972                                      (number :tag "Group for entire word" 0)
973                                      (number :tag "Group for displayed part" 0)
974                                      (symbol :tag "Face"
975                                              gnus-emphasis-highlight-words)))))
976   :group 'gnus-summary-visual)
977
978 (defcustom gnus-summary-show-article-charset-alist
979   nil
980   "Alist of number and charset.
981 The article will be shown with the charset corresponding to the
982 numbered argument.
983 For example: ((1 . cn-gb-2312) (2 . big5))."
984   :version "21.1"
985   :type '(repeat (cons (number :tag "Argument" 1)
986                        (symbol :tag "Charset")))
987   :group 'gnus-charset)
988
989 (defcustom gnus-preserve-marks t
990   "Whether marks are preserved when moving, copying and respooling messages."
991   :version "21.1"
992   :type 'boolean
993   :group 'gnus-summary-marks)
994
995 (defcustom gnus-alter-articles-to-read-function nil
996   "Function to be called to alter the list of articles to be selected."
997   :type '(choice (const nil) function)
998   :group 'gnus-summary)
999
1000 (defcustom gnus-orphan-score nil
1001   "*All orphans get this score added.  Set in the score file."
1002   :group 'gnus-score-default
1003   :type '(choice (const nil)
1004                  integer))
1005
1006 (defcustom gnus-summary-save-parts-default-mime "image/.*"
1007   "*A regexp to match MIME parts when saving multiple parts of a message
1008 with gnus-summary-save-parts (X m). This regexp will be used by default
1009 when prompting the user for which type of files to save."
1010   :group 'gnus-summary
1011   :type 'regexp)
1012
1013 (defcustom gnus-read-all-available-headers nil
1014   "Whether Gnus should parse all headers made available to it.
1015 This is mostly relevant for slow backends where the user may
1016 wish to widen the summary buffer to include all headers
1017 that were fetched.  Say, for nnultimate groups."
1018   :group 'gnus-summary
1019   :type '(choice boolean regexp))
1020
1021 (defcustom gnus-summary-muttprint-program "muttprint"
1022   "Command (and optional arguments) used to run Muttprint."
1023   :version "21.3"
1024   :group 'gnus-summary
1025   :type 'string)
1026
1027 (defcustom gnus-article-loose-mime nil
1028   "If non-nil, don't require MIME-Version header.
1029 Some brain-damaged MUA/MTA, e.g. Lotus Domino 5.0.6 clients, does not
1030 supply the MIME-Version header or deliberately strip it From the mail.
1031 Set it to non-nil, Gnus will treat some articles as MIME even if
1032 the MIME-Version header is missed."
1033   :version "21.3"
1034   :type 'boolean
1035   :group 'gnus-article)
1036
1037 ;;; Internal variables
1038
1039 (defvar gnus-summary-display-cache nil)
1040 (defvar gnus-article-mime-handles nil)
1041 (defvar gnus-article-decoded-p nil)
1042 (defvar gnus-article-charset nil)
1043 (defvar gnus-article-ignored-charsets nil)
1044 (defvar gnus-scores-exclude-files nil)
1045 (defvar gnus-page-broken nil)
1046
1047 (defvar gnus-original-article nil)
1048 (defvar gnus-article-internal-prepare-hook nil)
1049 (defvar gnus-newsgroup-process-stack nil)
1050
1051 (defvar gnus-thread-indent-array nil)
1052 (defvar gnus-thread-indent-array-level gnus-thread-indent-level)
1053 (defvar gnus-sort-gathered-threads-function 'gnus-thread-sort-by-number
1054   "Function called to sort the articles within a thread after it has been gathered together.")
1055
1056 (defvar gnus-summary-save-parts-type-history nil)
1057 (defvar gnus-summary-save-parts-last-directory nil)
1058
1059 ;; Avoid highlighting in kill files.
1060 (defvar gnus-summary-inhibit-highlight nil)
1061 (defvar gnus-newsgroup-selected-overlay nil)
1062 (defvar gnus-inhibit-limiting nil)
1063 (defvar gnus-newsgroup-adaptive-score-file nil)
1064 (defvar gnus-current-score-file nil)
1065 (defvar gnus-current-move-group nil)
1066 (defvar gnus-current-copy-group nil)
1067 (defvar gnus-current-crosspost-group nil)
1068 (defvar gnus-newsgroup-display nil)
1069
1070 (defvar gnus-newsgroup-dependencies nil)
1071 (defvar gnus-newsgroup-adaptive nil)
1072 (defvar gnus-summary-display-article-function nil)
1073 (defvar gnus-summary-highlight-line-function nil
1074   "Function called after highlighting a summary line.")
1075
1076 (defvar gnus-summary-line-format-alist
1077   `((?N ,(macroexpand '(mail-header-number gnus-tmp-header)) ?d)
1078     (?S ,(macroexpand '(mail-header-subject gnus-tmp-header)) ?s)
1079     (?s gnus-tmp-subject-or-nil ?s)
1080     (?n gnus-tmp-name ?s)
1081     (?A (car (cdr (funcall gnus-extract-address-components gnus-tmp-from)))
1082         ?s)
1083     (?a (or (car (funcall gnus-extract-address-components gnus-tmp-from))
1084             gnus-tmp-from) ?s)
1085     (?F gnus-tmp-from ?s)
1086     (?x ,(macroexpand '(mail-header-xref gnus-tmp-header)) ?s)
1087     (?D ,(macroexpand '(mail-header-date gnus-tmp-header)) ?s)
1088     (?d (gnus-dd-mmm (mail-header-date gnus-tmp-header)) ?s)
1089     (?o (gnus-date-iso8601 (mail-header-date gnus-tmp-header)) ?s)
1090     (?M ,(macroexpand '(mail-header-id gnus-tmp-header)) ?s)
1091     (?r ,(macroexpand '(mail-header-references gnus-tmp-header)) ?s)
1092     (?c (or (mail-header-chars gnus-tmp-header) 0) ?d)
1093     (?k (gnus-summary-line-message-size gnus-tmp-header) ?s)
1094     (?L gnus-tmp-lines ?s)
1095     (?I gnus-tmp-indentation ?s)
1096     (?T (if (= gnus-tmp-level 0) "" (make-string (frame-width) ? )) ?s)
1097     (?R gnus-tmp-replied ?c)
1098     (?\[ gnus-tmp-opening-bracket ?c)
1099     (?\] gnus-tmp-closing-bracket ?c)
1100     (?\> (make-string gnus-tmp-level ? ) ?s)
1101     (?\< (make-string (max 0 (- 20 gnus-tmp-level)) ? ) ?s)
1102     (?i gnus-tmp-score ?d)
1103     (?z gnus-tmp-score-char ?c)
1104     (?l (bbb-grouplens-score gnus-tmp-header) ?s)
1105     (?V (gnus-thread-total-score (and (boundp 'thread) (car thread))) ?d)
1106     (?U gnus-tmp-unread ?c)
1107     (?f (gnus-summary-from-or-to-or-newsgroups gnus-tmp-header gnus-tmp-from)
1108         ?s)
1109     (?t (gnus-summary-number-of-articles-in-thread
1110          (and (boundp 'thread) (car thread)) gnus-tmp-level)
1111         ?d)
1112     (?e (gnus-summary-number-of-articles-in-thread
1113          (and (boundp 'thread) (car thread)) gnus-tmp-level t)
1114         ?c)
1115     (?u gnus-tmp-user-defined ?s)
1116     (?P (gnus-pick-line-number) ?d)
1117     (?B gnus-tmp-thread-tree-header-string ?s)
1118     (user-date (gnus-user-date
1119                 ,(macroexpand '(mail-header-date gnus-tmp-header))) ?s))
1120   "An alist of format specifications that can appear in summary lines.
1121 These are paired with what variables they correspond with, along with
1122 the type of the variable (string, integer, character, etc).")
1123
1124 (defvar gnus-summary-dummy-line-format-alist
1125   `((?S gnus-tmp-subject ?s)
1126     (?N gnus-tmp-number ?d)
1127     (?u gnus-tmp-user-defined ?s)))
1128
1129 (defvar gnus-summary-mode-line-format-alist
1130   `((?G gnus-tmp-group-name ?s)
1131     (?g (gnus-short-group-name gnus-tmp-group-name) ?s)
1132     (?p (gnus-group-real-name gnus-tmp-group-name) ?s)
1133     (?A gnus-tmp-article-number ?d)
1134     (?Z gnus-tmp-unread-and-unselected ?s)
1135     (?V gnus-version ?s)
1136     (?U gnus-tmp-unread-and-unticked ?d)
1137     (?S gnus-tmp-subject ?s)
1138     (?e gnus-tmp-unselected ?d)
1139     (?u gnus-tmp-user-defined ?s)
1140     (?d (length gnus-newsgroup-dormant) ?d)
1141     (?t (length gnus-newsgroup-marked) ?d)
1142     (?h (length gnus-newsgroup-spam-marked) ?d)
1143     (?r (length gnus-newsgroup-reads) ?d)
1144     (?z (gnus-summary-article-score gnus-tmp-article-number) ?d)
1145     (?E gnus-newsgroup-expunged-tally ?d)
1146     (?s (gnus-current-score-file-nondirectory) ?s)))
1147
1148 (defvar gnus-last-search-regexp nil
1149   "Default regexp for article search command.")
1150
1151 (defvar gnus-last-shell-command nil
1152   "Default shell command on article.")
1153
1154 (defvar gnus-newsgroup-begin nil)
1155 (defvar gnus-newsgroup-end nil)
1156 (defvar gnus-newsgroup-last-rmail nil)
1157 (defvar gnus-newsgroup-last-mail nil)
1158 (defvar gnus-newsgroup-last-folder nil)
1159 (defvar gnus-newsgroup-last-file nil)
1160 (defvar gnus-newsgroup-auto-expire nil)
1161 (defvar gnus-newsgroup-active nil)
1162
1163 (defvar gnus-newsgroup-data nil)
1164 (defvar gnus-newsgroup-data-reverse nil)
1165 (defvar gnus-newsgroup-limit nil)
1166 (defvar gnus-newsgroup-limits nil)
1167
1168 (defvar gnus-newsgroup-unreads nil
1169   "Sorted list of unread articles in the current newsgroup.")
1170
1171 (defvar gnus-newsgroup-unselected nil
1172   "Sorted list of unselected unread articles in the current newsgroup.")
1173
1174 (defvar gnus-newsgroup-reads nil
1175   "Alist of read articles and article marks in the current newsgroup.")
1176
1177 (defvar gnus-newsgroup-expunged-tally nil)
1178
1179 (defvar gnus-newsgroup-marked nil
1180   "Sorted list of ticked articles in the current newsgroup (a subset of unread art).")
1181
1182 (defvar gnus-newsgroup-spam-marked nil
1183   "List of ranges of articles that have been marked as spam.")
1184
1185 (defvar gnus-newsgroup-killed nil
1186   "List of ranges of articles that have been through the scoring process.")
1187
1188 (defvar gnus-newsgroup-cached nil
1189   "Sorted list of articles that come from the article cache.")
1190
1191 (defvar gnus-newsgroup-saved nil
1192   "List of articles that have been saved.")
1193
1194 (defvar gnus-newsgroup-kill-headers nil)
1195
1196 (defvar gnus-newsgroup-replied nil
1197   "List of articles that have been replied to in the current newsgroup.")
1198
1199 (defvar gnus-newsgroup-forwarded nil
1200   "List of articles that have been forwarded in the current newsgroup.")
1201
1202 (defvar gnus-newsgroup-recent nil
1203   "List of articles that have are recent in the current newsgroup.")
1204
1205 (defvar gnus-newsgroup-expirable nil
1206   "Sorted list of articles in the current newsgroup that can be expired.")
1207
1208 (defvar gnus-newsgroup-processable nil
1209   "List of articles in the current newsgroup that can be processed.")
1210
1211 (defvar gnus-newsgroup-downloadable nil
1212   "Sorted list of articles in the current newsgroup that can be processed.")
1213
1214 (defvar gnus-newsgroup-undownloaded nil
1215   "List of articles in the current newsgroup that haven't been downloaded..")
1216
1217 (defvar gnus-newsgroup-unsendable nil
1218   "List of articles in the current newsgroup that won't be sent.")
1219
1220 (defvar gnus-newsgroup-bookmarks nil
1221   "List of articles in the current newsgroup that have bookmarks.")
1222
1223 (defvar gnus-newsgroup-dormant nil
1224   "Sorted list of dormant articles in the current newsgroup.")
1225
1226 (defvar gnus-newsgroup-unseen nil
1227   "List of unseen articles in the current newsgroup.")
1228
1229 (defvar gnus-newsgroup-seen nil
1230   "Range of seen articles in the current newsgroup.")
1231
1232 (defvar gnus-newsgroup-articles nil
1233   "List of articles in the current newsgroup.")
1234
1235 (defvar gnus-newsgroup-scored nil
1236   "List of scored articles in the current newsgroup.")
1237
1238 (defvar gnus-newsgroup-headers nil
1239   "List of article headers in the current newsgroup.")
1240
1241 (defvar gnus-newsgroup-threads nil)
1242
1243 (defvar gnus-newsgroup-prepared nil
1244   "Whether the current group has been prepared properly.")
1245
1246 (defvar gnus-newsgroup-ancient nil
1247   "List of `gnus-fetch-old-headers' articles in the current newsgroup.")
1248
1249 (defvar gnus-newsgroup-sparse nil)
1250
1251 (defvar gnus-current-article nil)
1252 (defvar gnus-article-current nil)
1253 (defvar gnus-current-headers nil)
1254 (defvar gnus-have-all-headers nil)
1255 (defvar gnus-last-article nil)
1256 (defvar gnus-newsgroup-history nil)
1257 (defvar gnus-newsgroup-charset nil)
1258 (defvar gnus-newsgroup-ephemeral-charset nil)
1259 (defvar gnus-newsgroup-ephemeral-ignored-charsets nil)
1260
1261 (defvar gnus-article-before-search nil)
1262
1263 (defconst gnus-summary-local-variables
1264   '(gnus-newsgroup-name
1265     gnus-newsgroup-begin gnus-newsgroup-end
1266     gnus-newsgroup-last-rmail gnus-newsgroup-last-mail
1267     gnus-newsgroup-last-folder gnus-newsgroup-last-file
1268     gnus-newsgroup-auto-expire gnus-newsgroup-unreads
1269     gnus-newsgroup-unselected gnus-newsgroup-marked
1270     gnus-newsgroup-spam-marked
1271     gnus-newsgroup-reads gnus-newsgroup-saved
1272     gnus-newsgroup-replied gnus-newsgroup-forwarded
1273     gnus-newsgroup-recent
1274     gnus-newsgroup-expirable
1275     gnus-newsgroup-processable gnus-newsgroup-killed
1276     gnus-newsgroup-downloadable gnus-newsgroup-undownloaded
1277     gnus-newsgroup-unsendable gnus-newsgroup-unseen
1278     gnus-newsgroup-seen gnus-newsgroup-articles
1279     gnus-newsgroup-bookmarks gnus-newsgroup-dormant
1280     gnus-newsgroup-headers gnus-newsgroup-threads
1281     gnus-newsgroup-prepared gnus-summary-highlight-line-function
1282     gnus-current-article gnus-current-headers gnus-have-all-headers
1283     gnus-last-article gnus-article-internal-prepare-hook
1284     gnus-newsgroup-dependencies gnus-newsgroup-selected-overlay
1285     gnus-newsgroup-scored gnus-newsgroup-kill-headers
1286     gnus-thread-expunge-below
1287     gnus-score-alist gnus-current-score-file
1288     (gnus-summary-expunge-below . global)
1289     (gnus-summary-mark-below . global)
1290     (gnus-orphan-score . global)
1291     gnus-newsgroup-active gnus-scores-exclude-files
1292     gnus-newsgroup-history gnus-newsgroup-ancient
1293     gnus-newsgroup-sparse gnus-newsgroup-process-stack
1294     (gnus-newsgroup-adaptive . gnus-use-adaptive-scoring)
1295     gnus-newsgroup-adaptive-score-file (gnus-reffed-article-number . -1)
1296     (gnus-newsgroup-expunged-tally . 0)
1297     gnus-cache-removable-articles gnus-newsgroup-cached
1298     gnus-newsgroup-data gnus-newsgroup-data-reverse
1299     gnus-newsgroup-limit gnus-newsgroup-limits
1300     gnus-newsgroup-charset gnus-newsgroup-display)
1301   "Variables that are buffer-local to the summary buffers.")
1302
1303 (defvar gnus-newsgroup-variables nil
1304   "A list of variables that have separate values in different newsgroups.
1305 A list of newsgroup (summary buffer) local variables, or cons of
1306 variables and their default values (when the default values are not
1307 nil), that should be made global while the summary buffer is active.
1308 These variables can be used to set variables in the group parameters
1309 while still allowing them to affect operations done in other
1310 buffers. For example:
1311
1312 \(setq gnus-newsgroup-variables
1313      '(message-use-followup-to
1314        (gnus-visible-headers .
1315          \"^From:\\\\|^Newsgroups:\\\\|^Subject:\\\\|^Date:\\\\|^To:\")))
1316 ")
1317
1318 ;; Byte-compiler warning.
1319 (eval-when-compile (defvar gnus-article-mode-map))
1320
1321 ;; MIME stuff.
1322
1323 (defvar gnus-decode-encoded-word-methods
1324   '(mail-decode-encoded-word-string)
1325   "List of methods used to decode encoded words.
1326
1327 This variable is a list of FUNCTION or (REGEXP . FUNCTION).  If item
1328 is FUNCTION, FUNCTION will be apply to all newsgroups.  If item is a
1329 \(REGEXP . FUNCTION), FUNCTION will be only apply to thes newsgroups
1330 whose names match REGEXP.
1331
1332 For example:
1333 \((\"chinese\" . gnus-decode-encoded-word-string-by-guess)
1334  mail-decode-encoded-word-string
1335  (\"chinese\" . rfc1843-decode-string))")
1336
1337 (defvar gnus-decode-encoded-word-methods-cache nil)
1338
1339 (defun gnus-multi-decode-encoded-word-string (string)
1340   "Apply the functions from `gnus-encoded-word-methods' that match."
1341   (unless (and gnus-decode-encoded-word-methods-cache
1342                (eq gnus-newsgroup-name
1343                    (car gnus-decode-encoded-word-methods-cache)))
1344     (setq gnus-decode-encoded-word-methods-cache (list gnus-newsgroup-name))
1345     (mapcar (lambda (x)
1346               (if (symbolp x)
1347                   (nconc gnus-decode-encoded-word-methods-cache (list x))
1348                 (if (and gnus-newsgroup-name
1349                          (string-match (car x) gnus-newsgroup-name))
1350                     (nconc gnus-decode-encoded-word-methods-cache
1351                            (list (cdr x))))))
1352             gnus-decode-encoded-word-methods))
1353   (let ((xlist gnus-decode-encoded-word-methods-cache))
1354     (pop xlist)
1355     (while xlist
1356       (setq string (funcall (pop xlist) string))))
1357   string)
1358
1359 ;; Subject simplification.
1360
1361 (defun gnus-simplify-whitespace (str)
1362   "Remove excessive whitespace from STR."
1363   (let ((mystr str))
1364     ;; Multiple spaces.
1365     (while (string-match "[ \t][ \t]+" mystr)
1366       (setq mystr (concat (substring mystr 0 (match-beginning 0))
1367                           " "
1368                           (substring mystr (match-end 0)))))
1369     ;; Leading spaces.
1370     (when (string-match "^[ \t]+" mystr)
1371       (setq mystr (substring mystr (match-end 0))))
1372     ;; Trailing spaces.
1373     (when (string-match "[ \t]+$" mystr)
1374       (setq mystr (substring mystr 0 (match-beginning 0))))
1375     mystr))
1376
1377 (defun gnus-simplify-all-whitespace (str)
1378   "Remove all whitespace from STR."
1379   (let ((mystr str))
1380     (while (string-match "[ \t\n]+" mystr)
1381       (setq mystr (replace-match "" nil nil mystr)))
1382     mystr))
1383
1384 (defsubst gnus-simplify-subject-re (subject)
1385   "Remove \"Re:\" from subject lines."
1386   (if (string-match message-subject-re-regexp subject)
1387       (substring subject (match-end 0))
1388     subject))
1389
1390 (defun gnus-simplify-subject (subject &optional re-only)
1391   "Remove `Re:' and words in parentheses.
1392 If RE-ONLY is non-nil, strip leading `Re:'s only."
1393   (let ((case-fold-search t))           ;Ignore case.
1394     ;; Remove `Re:', `Re^N:', `Re(n)', and `Re[n]:'.
1395     (when (string-match "\\`\\(re\\([[(^][0-9]+[])]?\\)?:[ \t]*\\)+" subject)
1396       (setq subject (substring subject (match-end 0))))
1397     ;; Remove uninteresting prefixes.
1398     (when (and (not re-only)
1399                gnus-simplify-ignored-prefixes
1400                (string-match gnus-simplify-ignored-prefixes subject))
1401       (setq subject (substring subject (match-end 0))))
1402     ;; Remove words in parentheses from end.
1403     (unless re-only
1404       (while (string-match "[ \t\n]*([^()]*)[ \t\n]*\\'" subject)
1405         (setq subject (substring subject 0 (match-beginning 0)))))
1406     ;; Return subject string.
1407     subject))
1408
1409 ;; Remove any leading "re:"s, any trailing paren phrases, and simplify
1410 ;; all whitespace.
1411 (defsubst gnus-simplify-buffer-fuzzy-step (regexp &optional newtext)
1412   (goto-char (point-min))
1413   (while (re-search-forward regexp nil t)
1414     (replace-match (or newtext ""))))
1415
1416 (defun gnus-simplify-buffer-fuzzy ()
1417   "Simplify string in the buffer fuzzily.
1418 The string in the accessible portion of the current buffer is simplified.
1419 It is assumed to be a single-line subject.
1420 Whitespace is generally cleaned up, and miscellaneous leading/trailing
1421 matter is removed.  Additional things can be deleted by setting
1422 `gnus-simplify-subject-fuzzy-regexp'."
1423   (let ((case-fold-search t)
1424         (modified-tick))
1425     (gnus-simplify-buffer-fuzzy-step "\t" " ")
1426
1427     (while (not (eq modified-tick (buffer-modified-tick)))
1428       (setq modified-tick (buffer-modified-tick))
1429       (cond
1430        ((listp gnus-simplify-subject-fuzzy-regexp)
1431         (mapcar 'gnus-simplify-buffer-fuzzy-step
1432                 gnus-simplify-subject-fuzzy-regexp))
1433        (gnus-simplify-subject-fuzzy-regexp
1434         (gnus-simplify-buffer-fuzzy-step gnus-simplify-subject-fuzzy-regexp)))
1435       (gnus-simplify-buffer-fuzzy-step "^ *\\[[-+?*!][-+?*!]\\] *")
1436       (gnus-simplify-buffer-fuzzy-step
1437        "^ *\\(re\\|fw\\|fwd\\)[[{(^0-9]*[])}]?[:;] *")
1438       (gnus-simplify-buffer-fuzzy-step "^[[].*:\\( .*\\)[]]$" "\\1"))
1439
1440     (gnus-simplify-buffer-fuzzy-step " *[[{(][^()\n]*[]})] *$")
1441     (gnus-simplify-buffer-fuzzy-step "  +" " ")
1442     (gnus-simplify-buffer-fuzzy-step " $")
1443     (gnus-simplify-buffer-fuzzy-step "^ +")))
1444
1445 (defun gnus-simplify-subject-fuzzy (subject)
1446   "Simplify a subject string fuzzily.
1447 See `gnus-simplify-buffer-fuzzy' for details."
1448   (save-excursion
1449     (gnus-set-work-buffer)
1450     (let ((case-fold-search t))
1451       ;; Remove uninteresting prefixes.
1452       (when (and gnus-simplify-ignored-prefixes
1453                  (string-match gnus-simplify-ignored-prefixes subject))
1454         (setq subject (substring subject (match-end 0))))
1455       (insert subject)
1456       (inline (gnus-simplify-buffer-fuzzy))
1457       (buffer-string))))
1458
1459 (defsubst gnus-simplify-subject-fully (subject)
1460   "Simplify a subject string according to gnus-summary-gather-subject-limit."
1461   (cond
1462    (gnus-simplify-subject-functions
1463     (gnus-map-function gnus-simplify-subject-functions subject))
1464    ((null gnus-summary-gather-subject-limit)
1465     (gnus-simplify-subject-re subject))
1466    ((eq gnus-summary-gather-subject-limit 'fuzzy)
1467     (gnus-simplify-subject-fuzzy subject))
1468    ((numberp gnus-summary-gather-subject-limit)
1469     (gnus-limit-string (gnus-simplify-subject-re subject)
1470                        gnus-summary-gather-subject-limit))
1471    (t
1472     subject)))
1473
1474 (defsubst gnus-subject-equal (s1 s2 &optional simple-first)
1475   "Check whether two subjects are equal.
1476 If optional argument simple-first is t, first argument is already
1477 simplified."
1478   (cond
1479    ((null simple-first)
1480     (equal (gnus-simplify-subject-fully s1)
1481            (gnus-simplify-subject-fully s2)))
1482    (t
1483     (equal s1
1484            (gnus-simplify-subject-fully s2)))))
1485
1486 (defun gnus-summary-bubble-group ()
1487   "Increase the score of the current group.
1488 This is a handy function to add to `gnus-summary-exit-hook' to
1489 increase the score of each group you read."
1490   (gnus-group-add-score gnus-newsgroup-name))
1491
1492 \f
1493 ;;;
1494 ;;; Gnus summary mode
1495 ;;;
1496
1497 (put 'gnus-summary-mode 'mode-class 'special)
1498
1499 (defvar gnus-article-commands-menu)
1500
1501 (when t
1502   ;; Non-orthogonal keys
1503
1504   (gnus-define-keys gnus-summary-mode-map
1505     " " gnus-summary-next-page
1506     "\177" gnus-summary-prev-page
1507     [delete] gnus-summary-prev-page
1508     [backspace] gnus-summary-prev-page
1509     "\r" gnus-summary-scroll-up
1510     "\M-\r" gnus-summary-scroll-down
1511     "n" gnus-summary-next-unread-article
1512     "p" gnus-summary-prev-unread-article
1513     "N" gnus-summary-next-article
1514     "P" gnus-summary-prev-article
1515     "\M-\C-n" gnus-summary-next-same-subject
1516     "\M-\C-p" gnus-summary-prev-same-subject
1517     "\M-n" gnus-summary-next-unread-subject
1518     "\M-p" gnus-summary-prev-unread-subject
1519     "." gnus-summary-first-unread-article
1520     "," gnus-summary-best-unread-article
1521     "\M-s" gnus-summary-search-article-forward
1522     "\M-r" gnus-summary-search-article-backward
1523     "<" gnus-summary-beginning-of-article
1524     ">" gnus-summary-end-of-article
1525     "j" gnus-summary-goto-article
1526     "^" gnus-summary-refer-parent-article
1527     "\M-^" gnus-summary-refer-article
1528     "u" gnus-summary-tick-article-forward
1529     "!" gnus-summary-tick-article-forward
1530     "U" gnus-summary-tick-article-backward
1531     "d" gnus-summary-mark-as-read-forward
1532     "D" gnus-summary-mark-as-read-backward
1533     "E" gnus-summary-mark-as-expirable
1534     "\M-u" gnus-summary-clear-mark-forward
1535     "\M-U" gnus-summary-clear-mark-backward
1536     "k" gnus-summary-kill-same-subject-and-select
1537     "\C-k" gnus-summary-kill-same-subject
1538     "\M-\C-k" gnus-summary-kill-thread
1539     "\M-\C-l" gnus-summary-lower-thread
1540     "e" gnus-summary-edit-article
1541     "#" gnus-summary-mark-as-processable
1542     "\M-#" gnus-summary-unmark-as-processable
1543     "\M-\C-t" gnus-summary-toggle-threads
1544     "\M-\C-s" gnus-summary-show-thread
1545     "\M-\C-h" gnus-summary-hide-thread
1546     "\M-\C-f" gnus-summary-next-thread
1547     "\M-\C-b" gnus-summary-prev-thread
1548     [(meta down)] gnus-summary-next-thread
1549     [(meta up)] gnus-summary-prev-thread
1550     "\M-\C-u" gnus-summary-up-thread
1551     "\M-\C-d" gnus-summary-down-thread
1552     "&" gnus-summary-execute-command
1553     "c" gnus-summary-catchup-and-exit
1554     "\C-w" gnus-summary-mark-region-as-read
1555     "\C-t" gnus-summary-toggle-truncation
1556     "?" gnus-summary-mark-as-dormant
1557     "\C-c\M-\C-s" gnus-summary-limit-include-expunged
1558     "\C-c\C-s\C-n" gnus-summary-sort-by-number
1559     "\C-c\C-s\C-l" gnus-summary-sort-by-lines
1560     "\C-c\C-s\C-c" gnus-summary-sort-by-chars
1561     "\C-c\C-s\C-a" gnus-summary-sort-by-author
1562     "\C-c\C-s\C-s" gnus-summary-sort-by-subject
1563     "\C-c\C-s\C-d" gnus-summary-sort-by-date
1564     "\C-c\C-s\C-i" gnus-summary-sort-by-score
1565     "\C-c\C-s\C-o" gnus-summary-sort-by-original
1566     "\C-c\C-s\C-r" gnus-summary-sort-by-random
1567     "=" gnus-summary-expand-window
1568     "\C-x\C-s" gnus-summary-reselect-current-group
1569     "\M-g" gnus-summary-rescan-group
1570     "w" gnus-summary-stop-page-breaking
1571     "\C-c\C-r" gnus-summary-caesar-message
1572     "f" gnus-summary-followup
1573     "F" gnus-summary-followup-with-original
1574     "C" gnus-summary-cancel-article
1575     "r" gnus-summary-reply
1576     "R" gnus-summary-reply-with-original
1577     "\C-c\C-f" gnus-summary-mail-forward
1578     "o" gnus-summary-save-article
1579     "\C-o" gnus-summary-save-article-mail
1580     "|" gnus-summary-pipe-output
1581     "\M-k" gnus-summary-edit-local-kill
1582     "\M-K" gnus-summary-edit-global-kill
1583     ;; "V" gnus-version
1584     "\C-c\C-d" gnus-summary-describe-group
1585     "q" gnus-summary-exit
1586     "Q" gnus-summary-exit-no-update
1587     "\C-c\C-i" gnus-info-find-node
1588     gnus-mouse-2 gnus-mouse-pick-article
1589     "m" gnus-summary-mail-other-window
1590     "a" gnus-summary-post-news
1591     "i" gnus-summary-news-other-window
1592     "x" gnus-summary-limit-to-unread
1593     "s" gnus-summary-isearch-article
1594     "t" gnus-summary-toggle-header
1595     "g" gnus-summary-show-article
1596     "l" gnus-summary-goto-last-article
1597     "\C-c\C-v\C-v" gnus-uu-decode-uu-view
1598     "\C-d" gnus-summary-enter-digest-group
1599     "\M-\C-d" gnus-summary-read-document
1600     "\M-\C-e" gnus-summary-edit-parameters
1601     "\M-\C-a" gnus-summary-customize-parameters
1602     "\C-c\C-b" gnus-bug
1603     "*" gnus-cache-enter-article
1604     "\M-*" gnus-cache-remove-article
1605     "\M-&" gnus-summary-universal-argument
1606     "\C-l" gnus-recenter
1607     "I" gnus-summary-increase-score
1608     "L" gnus-summary-lower-score
1609     "\M-i" gnus-symbolic-argument
1610     "h" gnus-summary-select-article-buffer
1611
1612     "b" gnus-article-view-part
1613     "\M-t" gnus-summary-toggle-display-buttonized
1614
1615     "V" gnus-summary-score-map
1616     "X" gnus-uu-extract-map
1617     "S" gnus-summary-send-map)
1618
1619   ;; Sort of orthogonal keymap
1620   (gnus-define-keys (gnus-summary-mark-map "M" gnus-summary-mode-map)
1621     "t" gnus-summary-tick-article-forward
1622     "!" gnus-summary-tick-article-forward
1623     "d" gnus-summary-mark-as-read-forward
1624     "r" gnus-summary-mark-as-read-forward
1625     "c" gnus-summary-clear-mark-forward
1626     " " gnus-summary-clear-mark-forward
1627     "e" gnus-summary-mark-as-expirable
1628     "x" gnus-summary-mark-as-expirable
1629     "?" gnus-summary-mark-as-dormant
1630     "b" gnus-summary-set-bookmark
1631     "B" gnus-summary-remove-bookmark
1632     "#" gnus-summary-mark-as-processable
1633     "\M-#" gnus-summary-unmark-as-processable
1634     "S" gnus-summary-limit-include-expunged
1635     "C" gnus-summary-catchup
1636     "H" gnus-summary-catchup-to-here
1637     "h" gnus-summary-catchup-from-here
1638     "\C-c" gnus-summary-catchup-all
1639     "k" gnus-summary-kill-same-subject-and-select
1640     "K" gnus-summary-kill-same-subject
1641     "P" gnus-uu-mark-map)
1642
1643   (gnus-define-keys (gnus-summary-mscore-map "V" gnus-summary-mark-map)
1644     "c" gnus-summary-clear-above
1645     "u" gnus-summary-tick-above
1646     "m" gnus-summary-mark-above
1647     "k" gnus-summary-kill-below)
1648
1649   (gnus-define-keys (gnus-summary-limit-map "/" gnus-summary-mode-map)
1650     "/" gnus-summary-limit-to-subject
1651     "n" gnus-summary-limit-to-articles
1652     "w" gnus-summary-pop-limit
1653     "s" gnus-summary-limit-to-subject
1654     "a" gnus-summary-limit-to-author
1655     "u" gnus-summary-limit-to-unread
1656     "m" gnus-summary-limit-to-marks
1657     "M" gnus-summary-limit-exclude-marks
1658     "v" gnus-summary-limit-to-score
1659     "*" gnus-summary-limit-include-cached
1660     "D" gnus-summary-limit-include-dormant
1661     "T" gnus-summary-limit-include-thread
1662     "d" gnus-summary-limit-exclude-dormant
1663     "t" gnus-summary-limit-to-age
1664     "." gnus-summary-limit-to-unseen
1665     "x" gnus-summary-limit-to-extra
1666     "p" gnus-summary-limit-to-display-predicate
1667     "E" gnus-summary-limit-include-expunged
1668     "c" gnus-summary-limit-exclude-childless-dormant
1669     "C" gnus-summary-limit-mark-excluded-as-read
1670     "o" gnus-summary-insert-old-articles
1671     "N" gnus-summary-insert-new-articles)
1672
1673   (gnus-define-keys (gnus-summary-goto-map "G" gnus-summary-mode-map)
1674     "n" gnus-summary-next-unread-article
1675     "p" gnus-summary-prev-unread-article
1676     "N" gnus-summary-next-article
1677     "P" gnus-summary-prev-article
1678     "\C-n" gnus-summary-next-same-subject
1679     "\C-p" gnus-summary-prev-same-subject
1680     "\M-n" gnus-summary-next-unread-subject
1681     "\M-p" gnus-summary-prev-unread-subject
1682     "f" gnus-summary-first-unread-article
1683     "b" gnus-summary-best-unread-article
1684     "j" gnus-summary-goto-article
1685     "g" gnus-summary-goto-subject
1686     "l" gnus-summary-goto-last-article
1687     "o" gnus-summary-pop-article)
1688
1689   (gnus-define-keys (gnus-summary-thread-map "T" gnus-summary-mode-map)
1690     "k" gnus-summary-kill-thread
1691     "l" gnus-summary-lower-thread
1692     "i" gnus-summary-raise-thread
1693     "T" gnus-summary-toggle-threads
1694     "t" gnus-summary-rethread-current
1695     "^" gnus-summary-reparent-thread
1696     "s" gnus-summary-show-thread
1697     "S" gnus-summary-show-all-threads
1698     "h" gnus-summary-hide-thread
1699     "H" gnus-summary-hide-all-threads
1700     "n" gnus-summary-next-thread
1701     "p" gnus-summary-prev-thread
1702     "u" gnus-summary-up-thread
1703     "o" gnus-summary-top-thread
1704     "d" gnus-summary-down-thread
1705     "#" gnus-uu-mark-thread
1706     "\M-#" gnus-uu-unmark-thread)
1707
1708   (gnus-define-keys (gnus-summary-buffer-map "Y" gnus-summary-mode-map)
1709     "g" gnus-summary-prepare
1710     "c" gnus-summary-insert-cached-articles)
1711
1712   (gnus-define-keys (gnus-summary-exit-map "Z" gnus-summary-mode-map)
1713     "c" gnus-summary-catchup-and-exit
1714     "C" gnus-summary-catchup-all-and-exit
1715     "E" gnus-summary-exit-no-update
1716     "Q" gnus-summary-exit
1717     "Z" gnus-summary-exit
1718     "n" gnus-summary-catchup-and-goto-next-group
1719     "R" gnus-summary-reselect-current-group
1720     "G" gnus-summary-rescan-group
1721     "N" gnus-summary-next-group
1722     "s" gnus-summary-save-newsrc
1723     "P" gnus-summary-prev-group)
1724
1725   (gnus-define-keys (gnus-summary-article-map "A" gnus-summary-mode-map)
1726     " " gnus-summary-next-page
1727     "n" gnus-summary-next-page
1728     "\177" gnus-summary-prev-page
1729     [delete] gnus-summary-prev-page
1730     "p" gnus-summary-prev-page
1731     "\r" gnus-summary-scroll-up
1732     "\M-\r" gnus-summary-scroll-down
1733     "<" gnus-summary-beginning-of-article
1734     ">" gnus-summary-end-of-article
1735     "b" gnus-summary-beginning-of-article
1736     "e" gnus-summary-end-of-article
1737     "^" gnus-summary-refer-parent-article
1738     "r" gnus-summary-refer-parent-article
1739     "D" gnus-summary-enter-digest-group
1740     "R" gnus-summary-refer-references
1741     "T" gnus-summary-refer-thread
1742     "g" gnus-summary-show-article
1743     "s" gnus-summary-isearch-article
1744     "P" gnus-summary-print-article
1745     "M" gnus-mailing-list-insinuate
1746     "t" gnus-article-babel)
1747
1748   (gnus-define-keys (gnus-summary-wash-map "W" gnus-summary-mode-map)
1749     "b" gnus-article-add-buttons
1750     "B" gnus-article-add-buttons-to-head
1751     "o" gnus-article-treat-overstrike
1752     "e" gnus-article-emphasize
1753     "w" gnus-article-fill-cited-article
1754     "Q" gnus-article-fill-long-lines
1755     "C" gnus-article-capitalize-sentences
1756     "c" gnus-article-remove-cr
1757     "q" gnus-article-de-quoted-unreadable
1758     "6" gnus-article-de-base64-unreadable
1759     "Z" gnus-article-decode-HZ
1760     "h" gnus-article-wash-html
1761     "u" gnus-article-unsplit-urls
1762     "s" gnus-summary-force-verify-and-decrypt
1763     "f" gnus-article-display-x-face
1764     "l" gnus-summary-stop-page-breaking
1765     "r" gnus-summary-caesar-message
1766     "m" gnus-summary-morse-message
1767     "t" gnus-summary-toggle-header
1768     "g" gnus-treat-smiley
1769     "v" gnus-summary-verbose-headers
1770     "a" gnus-article-strip-headers-in-body ;; mnemonic: wash archive
1771     "p" gnus-article-verify-x-pgp-sig
1772     "d" gnus-article-treat-dumbquotes
1773     "k" gnus-article-outlook-deuglify-article)
1774
1775   (gnus-define-keys (gnus-summary-wash-hide-map "W" gnus-summary-wash-map)
1776     "a" gnus-article-hide
1777     "h" gnus-article-hide-headers
1778     "b" gnus-article-hide-boring-headers
1779     "s" gnus-article-hide-signature
1780     "c" gnus-article-hide-citation
1781     "C" gnus-article-hide-citation-in-followups
1782     "l" gnus-article-hide-list-identifiers
1783     "p" gnus-article-hide-pgp
1784     "B" gnus-article-strip-banner
1785     "P" gnus-article-hide-pem
1786     "\C-c" gnus-article-hide-citation-maybe)
1787
1788   (gnus-define-keys (gnus-summary-wash-highlight-map "H" gnus-summary-wash-map)
1789     "a" gnus-article-highlight
1790     "h" gnus-article-highlight-headers
1791     "c" gnus-article-highlight-citation
1792     "s" gnus-article-highlight-signature)
1793
1794   (gnus-define-keys (gnus-summary-wash-header-map "G" gnus-summary-wash-map)
1795     "f" gnus-article-treat-fold-headers
1796     "u" gnus-article-treat-unfold-headers
1797     "n" gnus-article-treat-fold-newsgroups)
1798
1799   (gnus-define-keys (gnus-summary-wash-display-map "D" gnus-summary-wash-map)
1800     "x" gnus-article-display-x-face
1801     "s" gnus-treat-smiley
1802     "D" gnus-article-remove-images
1803     "f" gnus-treat-from-picon
1804     "m" gnus-treat-mail-picon
1805     "n" gnus-treat-newsgroups-picon)
1806
1807   (gnus-define-keys (gnus-summary-wash-mime-map "M" gnus-summary-wash-map)
1808     "w" gnus-article-decode-mime-words
1809     "c" gnus-article-decode-charset
1810     "v" gnus-mime-view-all-parts
1811     "b" gnus-article-view-part)
1812
1813   (gnus-define-keys (gnus-summary-wash-time-map "T" gnus-summary-wash-map)
1814     "z" gnus-article-date-ut
1815     "u" gnus-article-date-ut
1816     "l" gnus-article-date-local
1817     "p" gnus-article-date-english
1818     "e" gnus-article-date-lapsed
1819     "o" gnus-article-date-original
1820     "i" gnus-article-date-iso8601
1821     "s" gnus-article-date-user)
1822
1823   (gnus-define-keys (gnus-summary-wash-empty-map "E" gnus-summary-wash-map)
1824     "t" gnus-article-remove-trailing-blank-lines
1825     "l" gnus-article-strip-leading-blank-lines
1826     "m" gnus-article-strip-multiple-blank-lines
1827     "a" gnus-article-strip-blank-lines
1828     "A" gnus-article-strip-all-blank-lines
1829     "s" gnus-article-strip-leading-space
1830     "e" gnus-article-strip-trailing-space
1831     "w" gnus-article-remove-leading-whitespace)
1832
1833   (gnus-define-keys (gnus-summary-help-map "H" gnus-summary-mode-map)
1834     "v" gnus-version
1835     "f" gnus-summary-fetch-faq
1836     "d" gnus-summary-describe-group
1837     "h" gnus-summary-describe-briefly
1838     "i" gnus-info-find-node
1839     "c" gnus-group-fetch-charter
1840     "C" gnus-group-fetch-control)
1841
1842   (gnus-define-keys (gnus-summary-backend-map "B" gnus-summary-mode-map)
1843     "e" gnus-summary-expire-articles
1844     "\M-\C-e" gnus-summary-expire-articles-now
1845     "\177" gnus-summary-delete-article
1846     [delete] gnus-summary-delete-article
1847     [backspace] gnus-summary-delete-article
1848     "m" gnus-summary-move-article
1849     "r" gnus-summary-respool-article
1850     "w" gnus-summary-edit-article
1851     "c" gnus-summary-copy-article
1852     "B" gnus-summary-crosspost-article
1853     "q" gnus-summary-respool-query
1854     "t" gnus-summary-respool-trace
1855     "i" gnus-summary-import-article
1856     "I" gnus-summary-create-article
1857     "p" gnus-summary-article-posted-p)
1858
1859   (gnus-define-keys (gnus-summary-save-map "O" gnus-summary-mode-map)
1860     "o" gnus-summary-save-article
1861     "m" gnus-summary-save-article-mail
1862     "F" gnus-summary-write-article-file
1863     "r" gnus-summary-save-article-rmail
1864     "f" gnus-summary-save-article-file
1865     "b" gnus-summary-save-article-body-file
1866     "h" gnus-summary-save-article-folder
1867     "v" gnus-summary-save-article-vm
1868     "p" gnus-summary-pipe-output
1869     "P" gnus-summary-muttprint
1870     "s" gnus-soup-add-article)
1871
1872   (gnus-define-keys (gnus-summary-mime-map "K" gnus-summary-mode-map)
1873     "b" gnus-summary-display-buttonized
1874     "m" gnus-summary-repair-multipart
1875     "v" gnus-article-view-part
1876     "o" gnus-article-save-part
1877     "c" gnus-article-copy-part
1878     "C" gnus-article-view-part-as-charset
1879     "e" gnus-article-view-part-externally
1880     "E" gnus-article-encrypt-body
1881     "i" gnus-article-inline-part
1882     "|" gnus-article-pipe-part)
1883
1884   (gnus-define-keys (gnus-uu-mark-map "P" gnus-summary-mark-map)
1885     "p" gnus-summary-mark-as-processable
1886     "u" gnus-summary-unmark-as-processable
1887     "U" gnus-summary-unmark-all-processable
1888     "v" gnus-uu-mark-over
1889     "s" gnus-uu-mark-series
1890     "r" gnus-uu-mark-region
1891     "g" gnus-uu-unmark-region
1892     "R" gnus-uu-mark-by-regexp
1893     "G" gnus-uu-unmark-by-regexp
1894     "t" gnus-uu-mark-thread
1895     "T" gnus-uu-unmark-thread
1896     "a" gnus-uu-mark-all
1897     "b" gnus-uu-mark-buffer
1898     "S" gnus-uu-mark-sparse
1899     "k" gnus-summary-kill-process-mark
1900     "y" gnus-summary-yank-process-mark
1901     "w" gnus-summary-save-process-mark
1902     "i" gnus-uu-invert-processable)
1903
1904   (gnus-define-keys (gnus-uu-extract-map "X" gnus-summary-mode-map)
1905     ;;"x" gnus-uu-extract-any
1906     "m" gnus-summary-save-parts
1907     "u" gnus-uu-decode-uu
1908     "U" gnus-uu-decode-uu-and-save
1909     "s" gnus-uu-decode-unshar
1910     "S" gnus-uu-decode-unshar-and-save
1911     "o" gnus-uu-decode-save
1912     "O" gnus-uu-decode-save
1913     "b" gnus-uu-decode-binhex
1914     "B" gnus-uu-decode-binhex
1915     "p" gnus-uu-decode-postscript
1916     "P" gnus-uu-decode-postscript-and-save)
1917
1918   (gnus-define-keys
1919       (gnus-uu-extract-view-map "v" gnus-uu-extract-map)
1920     "u" gnus-uu-decode-uu-view
1921     "U" gnus-uu-decode-uu-and-save-view
1922     "s" gnus-uu-decode-unshar-view
1923     "S" gnus-uu-decode-unshar-and-save-view
1924     "o" gnus-uu-decode-save-view
1925     "O" gnus-uu-decode-save-view
1926     "b" gnus-uu-decode-binhex-view
1927     "B" gnus-uu-decode-binhex-view
1928     "p" gnus-uu-decode-postscript-view
1929     "P" gnus-uu-decode-postscript-and-save-view))
1930
1931 (defvar gnus-article-post-menu nil)
1932
1933 (defconst gnus-summary-menu-maxlen 20)
1934
1935 (defun gnus-summary-menu-split (menu)
1936   ;; If we have lots of elements, divide them into groups of 20
1937   ;; and make a pane (or submenu) for each one.
1938   (if (> (length menu) (/ (* gnus-summary-menu-maxlen 3) 2))
1939       (let ((menu menu) sublists next
1940             (i 1))
1941         (while menu
1942           ;; Pull off the next gnus-summary-menu-maxlen elements
1943           ;; and make them the next element of sublist.
1944           (setq next (nthcdr gnus-summary-menu-maxlen menu))
1945           (if next
1946               (setcdr (nthcdr (1- gnus-summary-menu-maxlen) menu)
1947                       nil))
1948           (setq sublists (cons (cons (format "%s ... %s" (aref (car menu) 0)
1949                                              (aref (car (last menu)) 0)) menu)
1950                                sublists))
1951           (setq i (1+ i))
1952           (setq menu next))
1953         (nreverse sublists))
1954     ;; Few elements--put them all in one pane.
1955     menu))
1956
1957 (defun gnus-summary-make-menu-bar ()
1958   (gnus-turn-off-edit-menu 'summary)
1959
1960   (unless (boundp 'gnus-summary-misc-menu)
1961
1962     (easy-menu-define
1963       gnus-summary-kill-menu gnus-summary-mode-map ""
1964       (cons
1965        "Score"
1966        (nconc
1967         (list
1968          ["Customize" gnus-score-customize t])
1969         (gnus-make-score-map 'increase)
1970         (gnus-make-score-map 'lower)
1971         '(("Mark"
1972            ["Kill below" gnus-summary-kill-below t]
1973            ["Mark above" gnus-summary-mark-above t]
1974            ["Tick above" gnus-summary-tick-above t]
1975            ["Clear above" gnus-summary-clear-above t])
1976           ["Current score" gnus-summary-current-score t]
1977           ["Set score" gnus-summary-set-score t]
1978           ["Switch current score file..." gnus-score-change-score-file t]
1979           ["Set mark below..." gnus-score-set-mark-below t]
1980           ["Set expunge below..." gnus-score-set-expunge-below t]
1981           ["Edit current score file" gnus-score-edit-current-scores t]
1982           ["Edit score file" gnus-score-edit-file t]
1983           ["Trace score" gnus-score-find-trace t]
1984           ["Find words" gnus-score-find-favourite-words t]
1985           ["Rescore buffer" gnus-summary-rescore t]
1986           ["Increase score..." gnus-summary-increase-score t]
1987           ["Lower score..." gnus-summary-lower-score t]))))
1988
1989     ;; Define both the Article menu in the summary buffer and the
1990     ;; equivalent Commands menu in the article buffer here for
1991     ;; consistency.
1992     (let ((innards
1993            `(("Hide"
1994               ["All" gnus-article-hide t]
1995               ["Headers" gnus-article-hide-headers t]
1996               ["Signature" gnus-article-hide-signature t]
1997               ["Citation" gnus-article-hide-citation t]
1998               ["List identifiers" gnus-article-hide-list-identifiers t]
1999               ["PGP" gnus-article-hide-pgp t]
2000               ["Banner" gnus-article-strip-banner t]
2001               ["Boring headers" gnus-article-hide-boring-headers t])
2002              ("Highlight"
2003               ["All" gnus-article-highlight t]
2004               ["Headers" gnus-article-highlight-headers t]
2005               ["Signature" gnus-article-highlight-signature t]
2006               ["Citation" gnus-article-highlight-citation t])
2007              ("MIME"
2008               ["Words" gnus-article-decode-mime-words t]
2009               ["Charset" gnus-article-decode-charset t]
2010               ["QP" gnus-article-de-quoted-unreadable t]
2011               ["Base64" gnus-article-de-base64-unreadable t]
2012               ["View MIME buttons" gnus-summary-display-buttonized t]
2013               ["View all" gnus-mime-view-all-parts t]
2014               ["Verify and Decrypt" gnus-summary-force-verify-and-decrypt t]
2015               ["Encrypt body" gnus-article-encrypt-body t]
2016               ["Extract all parts" gnus-summary-save-parts t])
2017              ("Date"
2018               ["Local" gnus-article-date-local t]
2019               ["ISO8601" gnus-article-date-iso8601 t]
2020               ["UT" gnus-article-date-ut t]
2021               ["Original" gnus-article-date-original t]
2022               ["Lapsed" gnus-article-date-lapsed t]
2023               ["User-defined" gnus-article-date-user t])
2024              ("Display"
2025               ["Remove images" gnus-article-remove-images t]
2026               ["Toggle smiley" gnus-treat-smiley t]
2027               ["Show X-Face" gnus-article-display-x-face t]
2028               ["Show picons in From" gnus-treat-from-picon t]
2029               ["Show picons in mail headers" gnus-treat-mail-picon t]
2030               ["Show picons in news headers" gnus-treat-newsgroups-picon t]
2031               ("View as different encoding"
2032                ,@(gnus-summary-menu-split
2033                   (mapcar
2034                    (lambda (cs)
2035                      ;; Since easymenu under FSF Emacs doesn't allow lambda
2036                      ;; forms for menu commands, we should provide intern'ed
2037                      ;; function symbols.
2038                      (let ((command (intern (format "\
2039 gnus-summary-show-article-from-menu-as-charset-%s" cs))))
2040                        (fset command
2041                              `(lambda ()
2042                                 (interactive)
2043                                 (let ((gnus-summary-show-article-charset-alist
2044                                        '((1 . ,cs))))
2045                                   (gnus-summary-show-article 1))))
2046                        `[,(symbol-name cs) ,command t]))
2047                    (sort (if (fboundp 'coding-system-list)
2048                              (coding-system-list)
2049                            (mapcar 'car mm-mime-mule-charset-alist))
2050                          'string<)))))
2051              ("Washing"
2052               ("Remove Blanks"
2053                ["Leading" gnus-article-strip-leading-blank-lines t]
2054                ["Multiple" gnus-article-strip-multiple-blank-lines t]
2055                ["Trailing" gnus-article-remove-trailing-blank-lines t]
2056                ["All of the above" gnus-article-strip-blank-lines t]
2057                ["All" gnus-article-strip-all-blank-lines t]
2058                ["Leading space" gnus-article-strip-leading-space t]
2059                ["Trailing space" gnus-article-strip-trailing-space t]
2060                ["Leading space in headers"
2061                 gnus-article-remove-leading-whitespace t])
2062               ["Overstrike" gnus-article-treat-overstrike t]
2063               ["Dumb quotes" gnus-article-treat-dumbquotes t]
2064               ["Emphasis" gnus-article-emphasize t]
2065               ["Word wrap" gnus-article-fill-cited-article t]
2066               ["Fill long lines" gnus-article-fill-long-lines t]
2067               ["Capitalize sentences" gnus-article-capitalize-sentences t]
2068               ["CR" gnus-article-remove-cr t]
2069               ["Quoted-Printable" gnus-article-de-quoted-unreadable t]
2070               ["Base64" gnus-article-de-base64-unreadable t]
2071               ["Rot 13" gnus-summary-caesar-message
2072                ,@(if (featurep 'xemacs) '(t)
2073                    '(:help "\"Caesar rotate\" article by 13"))]
2074               ["Morse decode" gnus-summary-morse-message t]
2075               ["Unix pipe..." gnus-summary-pipe-message t]
2076               ["Add buttons" gnus-article-add-buttons t]
2077               ["Add buttons to head" gnus-article-add-buttons-to-head t]
2078               ["Stop page breaking" gnus-summary-stop-page-breaking t]
2079               ["Verbose header" gnus-summary-verbose-headers t]
2080               ["Toggle header" gnus-summary-toggle-header t]
2081               ["Unfold headers" gnus-article-treat-unfold-headers t]
2082               ["Fold newsgroups" gnus-article-treat-fold-newsgroups t]
2083               ["Html" gnus-article-wash-html t]
2084               ["URLs" gnus-article-unsplit-urls t]
2085               ["Verify X-PGP-Sig" gnus-article-verify-x-pgp-sig t]
2086               ["HZ" gnus-article-decode-HZ t]
2087               ["OutlooK deuglify" gnus-article-outlook-deuglify-article t]
2088               )
2089              ("Output"
2090               ["Save in default format" gnus-summary-save-article
2091                ,@(if (featurep 'xemacs) '(t)
2092                    '(:help "Save article using default method"))]
2093               ["Save in file" gnus-summary-save-article-file
2094                ,@(if (featurep 'xemacs) '(t)
2095                    '(:help "Save article in file"))]
2096               ["Save in Unix mail format" gnus-summary-save-article-mail t]
2097               ["Save in MH folder" gnus-summary-save-article-folder t]
2098               ["Save in VM folder" gnus-summary-save-article-vm t]
2099               ["Save in RMAIL mbox" gnus-summary-save-article-rmail t]
2100               ["Save body in file" gnus-summary-save-article-body-file t]
2101               ["Pipe through a filter" gnus-summary-pipe-output t]
2102               ["Add to SOUP packet" gnus-soup-add-article t]
2103               ["Print with Muttprint" gnus-summary-muttprint t]
2104               ["Print" gnus-summary-print-article t])
2105              ("Backend"
2106               ["Respool article..." gnus-summary-respool-article t]
2107               ["Move article..." gnus-summary-move-article
2108                (gnus-check-backend-function
2109                 'request-move-article gnus-newsgroup-name)]
2110               ["Copy article..." gnus-summary-copy-article t]
2111               ["Crosspost article..." gnus-summary-crosspost-article
2112                (gnus-check-backend-function
2113                 'request-replace-article gnus-newsgroup-name)]
2114               ["Import file..." gnus-summary-import-article t]
2115               ["Create article..." gnus-summary-create-article t]
2116               ["Check if posted" gnus-summary-article-posted-p t]
2117               ["Edit article" gnus-summary-edit-article
2118                (not (gnus-group-read-only-p))]
2119               ["Delete article" gnus-summary-delete-article
2120                (gnus-check-backend-function
2121                 'request-expire-articles gnus-newsgroup-name)]
2122               ["Query respool" gnus-summary-respool-query t]
2123               ["Trace respool" gnus-summary-respool-trace t]
2124               ["Delete expirable articles" gnus-summary-expire-articles-now
2125                (gnus-check-backend-function
2126                 'request-expire-articles gnus-newsgroup-name)])
2127              ("Extract"
2128               ["Uudecode" gnus-uu-decode-uu
2129                ,@(if (featurep 'xemacs) '(t)
2130                    '(:help "Decode uuencoded article(s)"))]
2131               ["Uudecode and save" gnus-uu-decode-uu-and-save t]
2132               ["Unshar" gnus-uu-decode-unshar t]
2133               ["Unshar and save" gnus-uu-decode-unshar-and-save t]
2134               ["Save" gnus-uu-decode-save t]
2135               ["Binhex" gnus-uu-decode-binhex t]
2136               ["Postscript" gnus-uu-decode-postscript t]
2137               ["All MIME parts" gnus-summary-save-parts t])
2138              ("Cache"
2139               ["Enter article" gnus-cache-enter-article t]
2140               ["Remove article" gnus-cache-remove-article t])
2141              ["Translate" gnus-article-babel t]
2142              ["Select article buffer" gnus-summary-select-article-buffer t]
2143              ["Enter digest buffer" gnus-summary-enter-digest-group t]
2144              ["Isearch article..." gnus-summary-isearch-article t]
2145              ["Beginning of the article" gnus-summary-beginning-of-article t]
2146              ["End of the article" gnus-summary-end-of-article t]
2147              ["Fetch parent of article" gnus-summary-refer-parent-article t]
2148              ["Fetch referenced articles" gnus-summary-refer-references t]
2149              ["Fetch current thread" gnus-summary-refer-thread t]
2150              ["Fetch article with id..." gnus-summary-refer-article t]
2151              ["Setup Mailing List Params" gnus-mailing-list-insinuate t]
2152              ["Redisplay" gnus-summary-show-article t]
2153              ["Raw article" gnus-summary-show-raw-article :keys "C-u g"])))
2154       (easy-menu-define
2155         gnus-summary-article-menu gnus-summary-mode-map ""
2156         (cons "Article" innards))
2157
2158       (if (not (keymapp gnus-summary-article-menu))
2159           (easy-menu-define
2160             gnus-article-commands-menu gnus-article-mode-map ""
2161             (cons "Commands" innards))
2162         ;; in Emacs, don't share menu.
2163         (setq gnus-article-commands-menu
2164               (copy-keymap gnus-summary-article-menu))
2165         (define-key gnus-article-mode-map [menu-bar commands]
2166           (cons "Commands" gnus-article-commands-menu))))
2167
2168     (easy-menu-define
2169       gnus-summary-thread-menu gnus-summary-mode-map ""
2170       '("Threads"
2171         ["Toggle threading" gnus-summary-toggle-threads t]
2172         ["Hide threads" gnus-summary-hide-all-threads t]
2173         ["Show threads" gnus-summary-show-all-threads t]
2174         ["Hide thread" gnus-summary-hide-thread t]
2175         ["Show thread" gnus-summary-show-thread t]
2176         ["Go to next thread" gnus-summary-next-thread t]
2177         ["Go to previous thread" gnus-summary-prev-thread t]
2178         ["Go down thread" gnus-summary-down-thread t]
2179         ["Go up thread" gnus-summary-up-thread t]
2180         ["Top of thread" gnus-summary-top-thread t]
2181         ["Mark thread as read" gnus-summary-kill-thread t]
2182         ["Lower thread score" gnus-summary-lower-thread t]
2183         ["Raise thread score" gnus-summary-raise-thread t]
2184         ["Rethread current" gnus-summary-rethread-current t]))
2185
2186     (easy-menu-define
2187       gnus-summary-post-menu gnus-summary-mode-map ""
2188       `("Post"
2189         ["Send a message (mail or news)" gnus-summary-post-news
2190          ,@(if (featurep 'xemacs) '(t)
2191              '(:help "Post an article"))]
2192         ["Followup" gnus-summary-followup
2193          ,@(if (featurep 'xemacs) '(t)
2194              '(:help "Post followup to this article"))]
2195         ["Followup and yank" gnus-summary-followup-with-original
2196          ,@(if (featurep 'xemacs) '(t)
2197              '(:help "Post followup to this article, quoting its contents"))]
2198         ["Supersede article" gnus-summary-supersede-article t]
2199         ["Cancel article" gnus-summary-cancel-article
2200          ,@(if (featurep 'xemacs) '(t)
2201              '(:help "Cancel an article you posted"))]
2202         ["Reply" gnus-summary-reply t]
2203         ["Reply and yank" gnus-summary-reply-with-original t]
2204         ["Wide reply" gnus-summary-wide-reply t]
2205         ["Wide reply and yank" gnus-summary-wide-reply-with-original
2206          ,@(if (featurep 'xemacs) '(t)
2207              '(:help "Mail a reply, quoting this article"))]
2208         ["Very wide reply" gnus-summary-very-wide-reply t]
2209         ["Very wide reply and yank" gnus-summary-very-wide-reply-with-original
2210          ,@(if (featurep 'xemacs) '(t)
2211              '(:help "Mail a very wide reply, quoting this article"))]
2212         ["Mail forward" gnus-summary-mail-forward t]
2213         ["Post forward" gnus-summary-post-forward t]
2214         ["Digest and mail" gnus-uu-digest-mail-forward t]
2215         ["Digest and post" gnus-uu-digest-post-forward t]
2216         ["Resend message" gnus-summary-resend-message t]
2217         ["Resend message edit" gnus-summary-resend-message-edit t]
2218         ["Send bounced mail" gnus-summary-resend-bounced-mail t]
2219         ["Send a mail" gnus-summary-mail-other-window t]
2220         ["Create a local message" gnus-summary-news-other-window t]
2221         ["Uuencode and post" gnus-uu-post-news
2222          ,@(if (featurep 'xemacs) '(t)
2223              '(:help "Post a uuencoded article"))]
2224         ["Followup via news" gnus-summary-followup-to-mail t]
2225         ["Followup via news and yank"
2226          gnus-summary-followup-to-mail-with-original t]
2227         ;;("Draft"
2228         ;;["Send" gnus-summary-send-draft t]
2229         ;;["Send bounced" gnus-resend-bounced-mail t])
2230         ))
2231
2232     (cond
2233      ((not (keymapp gnus-summary-post-menu))
2234       (setq gnus-article-post-menu gnus-summary-post-menu))
2235      ((not gnus-article-post-menu)
2236       ;; Don't share post menu.
2237       (setq gnus-article-post-menu
2238             (copy-keymap gnus-summary-post-menu))))
2239     (define-key gnus-article-mode-map [menu-bar post]
2240       (cons "Post" gnus-article-post-menu))
2241
2242     (easy-menu-define
2243       gnus-summary-misc-menu gnus-summary-mode-map ""
2244       `("Gnus"
2245         ("Mark Read"
2246          ["Mark as read" gnus-summary-mark-as-read-forward t]
2247          ["Mark same subject and select"
2248           gnus-summary-kill-same-subject-and-select t]
2249          ["Mark same subject" gnus-summary-kill-same-subject t]
2250          ["Catchup" gnus-summary-catchup
2251           ,@(if (featurep 'xemacs) '(t)
2252               '(:help "Mark unread articles in this group as read"))]
2253          ["Catchup all" gnus-summary-catchup-all t]
2254          ["Catchup to here" gnus-summary-catchup-to-here t]
2255          ["Catchup from here" gnus-summary-catchup-from-here t]
2256          ["Catchup region" gnus-summary-mark-region-as-read t]
2257          ["Mark excluded" gnus-summary-limit-mark-excluded-as-read t])
2258         ("Mark Various"
2259          ["Tick" gnus-summary-tick-article-forward t]
2260          ["Mark as dormant" gnus-summary-mark-as-dormant t]
2261          ["Remove marks" gnus-summary-clear-mark-forward t]
2262          ["Set expirable mark" gnus-summary-mark-as-expirable t]
2263          ["Set bookmark" gnus-summary-set-bookmark t]
2264          ["Remove bookmark" gnus-summary-remove-bookmark t])
2265         ("Limit to"
2266          ["Marks..." gnus-summary-limit-to-marks t]
2267          ["Subject..." gnus-summary-limit-to-subject t]
2268          ["Author..." gnus-summary-limit-to-author t]
2269          ["Age..." gnus-summary-limit-to-age t]
2270          ["Extra..." gnus-summary-limit-to-extra t]
2271          ["Score..." gnus-summary-limit-to-score t]
2272          ["Display Predicate" gnus-summary-limit-to-display-predicate t]
2273          ["Unread" gnus-summary-limit-to-unread t]
2274          ["Unseen" gnus-summary-limit-to-unseen t]
2275          ["Non-dormant" gnus-summary-limit-exclude-dormant t]
2276          ["Articles" gnus-summary-limit-to-articles t]
2277          ["Pop limit" gnus-summary-pop-limit t]
2278          ["Show dormant" gnus-summary-limit-include-dormant t]
2279          ["Hide childless dormant"
2280           gnus-summary-limit-exclude-childless-dormant t]
2281          ;;["Hide thread" gnus-summary-limit-exclude-thread t]
2282          ["Hide marked" gnus-summary-limit-exclude-marks t]
2283          ["Show expunged" gnus-summary-limit-include-expunged t])
2284         ("Process Mark"
2285          ["Set mark" gnus-summary-mark-as-processable t]
2286          ["Remove mark" gnus-summary-unmark-as-processable t]
2287          ["Remove all marks" gnus-summary-unmark-all-processable t]
2288          ["Mark above" gnus-uu-mark-over t]
2289          ["Mark series" gnus-uu-mark-series t]
2290          ["Mark region" gnus-uu-mark-region t]
2291          ["Unmark region" gnus-uu-unmark-region t]
2292          ["Mark by regexp..." gnus-uu-mark-by-regexp t]
2293          ["Unmark by regexp..." gnus-uu-unmark-by-regexp t]
2294          ["Mark all" gnus-uu-mark-all t]
2295          ["Mark buffer" gnus-uu-mark-buffer t]
2296          ["Mark sparse" gnus-uu-mark-sparse t]
2297          ["Mark thread" gnus-uu-mark-thread t]
2298          ["Unmark thread" gnus-uu-unmark-thread t]
2299          ("Process Mark Sets"
2300           ["Kill" gnus-summary-kill-process-mark t]
2301           ["Yank" gnus-summary-yank-process-mark
2302            gnus-newsgroup-process-stack]
2303           ["Save" gnus-summary-save-process-mark t]))
2304         ("Scroll article"
2305          ["Page forward" gnus-summary-next-page
2306           ,@(if (featurep 'xemacs) '(t)
2307               '(:help "Show next page of article"))]
2308          ["Page backward" gnus-summary-prev-page
2309           ,@(if (featurep 'xemacs) '(t)
2310               '(:help "Show previous page of article"))]
2311          ["Line forward" gnus-summary-scroll-up t])
2312         ("Move"
2313          ["Next unread article" gnus-summary-next-unread-article t]
2314          ["Previous unread article" gnus-summary-prev-unread-article t]
2315          ["Next article" gnus-summary-next-article t]
2316          ["Previous article" gnus-summary-prev-article t]
2317          ["Next unread subject" gnus-summary-next-unread-subject t]
2318          ["Previous unread subject" gnus-summary-prev-unread-subject t]
2319          ["Next article same subject" gnus-summary-next-same-subject t]
2320          ["Previous article same subject" gnus-summary-prev-same-subject t]
2321          ["First unread article" gnus-summary-first-unread-article t]
2322          ["Best unread article" gnus-summary-best-unread-article t]
2323          ["Go to subject number..." gnus-summary-goto-subject t]
2324          ["Go to article number..." gnus-summary-goto-article t]
2325          ["Go to the last article" gnus-summary-goto-last-article t]
2326          ["Pop article off history" gnus-summary-pop-article t])
2327         ("Sort"
2328          ["Sort by number" gnus-summary-sort-by-number t]
2329          ["Sort by author" gnus-summary-sort-by-author t]
2330          ["Sort by subject" gnus-summary-sort-by-subject t]
2331          ["Sort by date" gnus-summary-sort-by-date t]
2332          ["Sort by score" gnus-summary-sort-by-score t]
2333          ["Sort by lines" gnus-summary-sort-by-lines t]
2334          ["Sort by characters" gnus-summary-sort-by-chars t]
2335          ["Randomize" gnus-summary-sort-by-random t]
2336          ["Original sort" gnus-summary-sort-by-original t])
2337         ("Help"
2338          ["Fetch group FAQ" gnus-summary-fetch-faq t]
2339          ["Describe group" gnus-summary-describe-group t]
2340          ["Fetch charter" gnus-group-fetch-charter
2341           ,@(if (featurep 'xemacs) nil
2342               '(:help "Display the charter of the current group"))]
2343          ["Fetch control message" gnus-group-fetch-control
2344           ,@(if (featurep 'xemacs) nil
2345               '(:help "Display the archived control message for the current group"))]
2346          ["Read manual" gnus-info-find-node t])
2347         ("Modes"
2348          ["Pick and read" gnus-pick-mode t]
2349          ["Binary" gnus-binary-mode t])
2350         ("Regeneration"
2351          ["Regenerate" gnus-summary-prepare t]
2352          ["Insert cached articles" gnus-summary-insert-cached-articles t]
2353          ["Toggle threading" gnus-summary-toggle-threads t])
2354         ["See old articles" gnus-summary-insert-old-articles t]
2355         ["See new articles" gnus-summary-insert-new-articles t]
2356         ["Filter articles..." gnus-summary-execute-command t]
2357         ["Run command on subjects..." gnus-summary-universal-argument t]
2358         ["Search articles forward..." gnus-summary-search-article-forward t]
2359         ["Search articles backward..." gnus-summary-search-article-backward t]
2360         ["Toggle line truncation" gnus-summary-toggle-truncation t]
2361         ["Expand window" gnus-summary-expand-window t]
2362         ["Expire expirable articles" gnus-summary-expire-articles
2363          (gnus-check-backend-function
2364           'request-expire-articles gnus-newsgroup-name)]
2365         ["Edit local kill file" gnus-summary-edit-local-kill t]
2366         ["Edit main kill file" gnus-summary-edit-global-kill t]
2367         ["Edit group parameters" gnus-summary-edit-parameters t]
2368         ["Customize group parameters" gnus-summary-customize-parameters t]
2369         ["Send a bug report" gnus-bug t]
2370         ("Exit"
2371          ["Catchup and exit" gnus-summary-catchup-and-exit
2372           ,@(if (featurep 'xemacs) '(t)
2373               '(:help "Mark unread articles in this group as read, then exit"))]
2374          ["Catchup all and exit" gnus-summary-catchup-all-and-exit t]
2375          ["Catchup and goto next" gnus-summary-catchup-and-goto-next-group t]
2376          ["Exit group" gnus-summary-exit
2377           ,@(if (featurep 'xemacs) '(t)
2378               '(:help "Exit current group, return to group selection mode"))]
2379          ["Exit group without updating" gnus-summary-exit-no-update t]
2380          ["Exit and goto next group" gnus-summary-next-group t]
2381          ["Exit and goto prev group" gnus-summary-prev-group t]
2382          ["Reselect group" gnus-summary-reselect-current-group t]
2383          ["Rescan group" gnus-summary-rescan-group t]
2384          ["Update dribble" gnus-summary-save-newsrc t])))
2385
2386     (gnus-run-hooks 'gnus-summary-menu-hook)))
2387
2388 (defvar gnus-summary-tool-bar-map nil)
2389
2390 ;; Emacs 21 tool bar.  Should be no-op otherwise.
2391 (defun gnus-summary-make-tool-bar ()
2392   (if (and (fboundp 'tool-bar-add-item-from-menu)
2393            (default-value 'tool-bar-mode)
2394            (not gnus-summary-tool-bar-map))
2395       (setq gnus-summary-tool-bar-map
2396             (let ((tool-bar-map (make-sparse-keymap))
2397                   (load-path (mm-image-load-path)))
2398               (tool-bar-add-item-from-menu
2399                'gnus-summary-prev-unread "prev-ur" gnus-summary-mode-map)
2400               (tool-bar-add-item-from-menu
2401                'gnus-summary-next-unread "next-ur" gnus-summary-mode-map)
2402               (tool-bar-add-item-from-menu
2403                'gnus-summary-post-news "post" gnus-summary-mode-map)
2404               (tool-bar-add-item-from-menu
2405                'gnus-summary-followup-with-original "fuwo" gnus-summary-mode-map)
2406               (tool-bar-add-item-from-menu
2407                'gnus-summary-followup "followup" gnus-summary-mode-map)
2408               (tool-bar-add-item-from-menu
2409                'gnus-summary-reply-with-original "reply-wo" gnus-summary-mode-map)
2410               (tool-bar-add-item-from-menu
2411                'gnus-summary-reply "reply" gnus-summary-mode-map)
2412               (tool-bar-add-item-from-menu
2413                'gnus-summary-caesar-message "rot13" gnus-summary-mode-map)
2414               (tool-bar-add-item-from-menu
2415                'gnus-uu-decode-uu "uu-decode" gnus-summary-mode-map)
2416               (tool-bar-add-item-from-menu
2417                'gnus-summary-save-article-file "save-aif" gnus-summary-mode-map)
2418               (tool-bar-add-item-from-menu
2419                'gnus-summary-save-article "save-art" gnus-summary-mode-map)
2420               (tool-bar-add-item-from-menu
2421                'gnus-uu-post-news "uu-post" gnus-summary-mode-map)
2422               (tool-bar-add-item-from-menu
2423                'gnus-summary-catchup "catchup" gnus-summary-mode-map)
2424               (tool-bar-add-item-from-menu
2425                'gnus-summary-catchup-and-exit "cu-exit" gnus-summary-mode-map)
2426               (tool-bar-add-item-from-menu
2427                'gnus-summary-exit "exit-summ" gnus-summary-mode-map)
2428               tool-bar-map)))
2429   (if gnus-summary-tool-bar-map
2430       (set (make-local-variable 'tool-bar-map) gnus-summary-tool-bar-map)))
2431
2432 (defun gnus-score-set-default (var value)
2433   "A version of set that updates the GNU Emacs menu-bar."
2434   (set var value)
2435   ;; It is the message that forces the active status to be updated.
2436   (message ""))
2437
2438 (defun gnus-make-score-map (type)
2439   "Make a summary score map of type TYPE."
2440   (if t
2441       nil
2442     (let ((headers '(("author" "from" string)
2443                      ("subject" "subject" string)
2444                      ("article body" "body" string)
2445                      ("article head" "head" string)
2446                      ("xref" "xref" string)
2447                      ("extra header" "extra" string)
2448                      ("lines" "lines" number)
2449                      ("followups to author" "followup" string)))
2450           (types '((number ("less than" <)
2451                            ("greater than" >)
2452                            ("equal" =))
2453                    (string ("substring" s)
2454                            ("exact string" e)
2455                            ("fuzzy string" f)
2456                            ("regexp" r))))
2457           (perms '(("temporary" (current-time-string))
2458                    ("permanent" nil)
2459                    ("immediate" now)))
2460           header)
2461       (list
2462        (apply
2463         'nconc
2464         (list
2465          (if (eq type 'lower)
2466              "Lower score"
2467            "Increase score"))
2468         (let (outh)
2469           (while headers
2470             (setq header (car headers))
2471             (setq outh
2472                   (cons
2473                    (apply
2474                     'nconc
2475                     (list (car header))
2476                     (let ((ts (cdr (assoc (nth 2 header) types)))
2477                           outt)
2478                       (while ts
2479                         (setq outt
2480                               (cons
2481                                (apply
2482                                 'nconc
2483                                 (list (caar ts))
2484                                 (let ((ps perms)
2485                                       outp)
2486                                   (while ps
2487                                     (setq outp
2488                                           (cons
2489                                            (vector
2490                                             (caar ps)
2491                                             (list
2492                                              'gnus-summary-score-entry
2493                                              (nth 1 header)
2494                                              (if (or (string= (nth 1 header)
2495                                                               "head")
2496                                                      (string= (nth 1 header)
2497                                                               "body"))
2498                                                  ""
2499                                                (list 'gnus-summary-header
2500                                                      (nth 1 header)))
2501                                              (list 'quote (nth 1 (car ts)))
2502                                              (list 'gnus-score-delta-default
2503                                                    nil)
2504                                              (nth 1 (car ps))
2505                                              t)
2506                                             t)
2507                                            outp))
2508                                     (setq ps (cdr ps)))
2509                                   (list (nreverse outp))))
2510                                outt))
2511                         (setq ts (cdr ts)))
2512                       (list (nreverse outt))))
2513                    outh))
2514             (setq headers (cdr headers)))
2515           (list (nreverse outh))))))))
2516
2517 \f
2518
2519 (defun gnus-summary-mode (&optional group)
2520   "Major mode for reading articles.
2521
2522 All normal editing commands are switched off.
2523 \\<gnus-summary-mode-map>
2524 Each line in this buffer represents one article.  To read an
2525 article, you can, for instance, type `\\[gnus-summary-next-page]'.  To move forwards
2526 and backwards while displaying articles, type `\\[gnus-summary-next-unread-article]' and `\\[gnus-summary-prev-unread-article]',
2527 respectively.
2528
2529 You can also post articles and send mail from this buffer.  To
2530 follow up an article, type `\\[gnus-summary-followup]'.  To mail a reply to the author
2531 of an article, type `\\[gnus-summary-reply]'.
2532
2533 There are approx. one gazillion commands you can execute in this
2534 buffer; read the info pages for more information (`\\[gnus-info-find-node]').
2535
2536 The following commands are available:
2537
2538 \\{gnus-summary-mode-map}"
2539   (interactive)
2540   (kill-all-local-variables)
2541   (when (gnus-visual-p 'summary-menu 'menu)
2542     (gnus-summary-make-menu-bar)
2543     (gnus-summary-make-tool-bar))
2544   (gnus-summary-make-local-variables)
2545   (let ((gnus-summary-local-variables gnus-newsgroup-variables))
2546     (gnus-summary-make-local-variables))
2547   (gnus-make-thread-indent-array)
2548   (gnus-simplify-mode-line)
2549   (setq major-mode 'gnus-summary-mode)
2550   (setq mode-name "Summary")
2551   (make-local-variable 'minor-mode-alist)
2552   (use-local-map gnus-summary-mode-map)
2553   (buffer-disable-undo)
2554   (setq buffer-read-only t)             ;Disable modification
2555   (setq truncate-lines t)
2556   (setq selective-display t)
2557   (setq selective-display-ellipses t)   ;Display `...'
2558   (gnus-summary-set-display-table)
2559   (gnus-set-default-directory)
2560   (setq gnus-newsgroup-name group)
2561   (make-local-variable 'gnus-summary-line-format)
2562   (make-local-variable 'gnus-summary-line-format-spec)
2563   (make-local-variable 'gnus-summary-dummy-line-format)
2564   (make-local-variable 'gnus-summary-dummy-line-format-spec)
2565   (make-local-variable 'gnus-summary-mark-positions)
2566   (make-local-hook 'pre-command-hook)
2567   (add-hook 'pre-command-hook 'gnus-set-global-variables nil t)
2568   (gnus-run-hooks 'gnus-summary-mode-hook)
2569   (turn-on-gnus-mailing-list-mode)
2570   (mm-enable-multibyte)
2571   (gnus-update-format-specifications nil 'summary 'summary-mode 'summary-dummy)
2572   (gnus-update-summary-mark-positions))
2573
2574 (defun gnus-summary-make-local-variables ()
2575   "Make all the local summary buffer variables."
2576   (let (global)
2577     (dolist (local gnus-summary-local-variables)
2578       (if (consp local)
2579           (progn
2580             (if (eq (cdr local) 'global)
2581                 ;; Copy the global value of the variable.
2582                 (setq global (symbol-value (car local)))
2583               ;; Use the value from the list.
2584               (setq global (eval (cdr local))))
2585             (set (make-local-variable (car local)) global))
2586         ;; Simple nil-valued local variable.
2587         (set (make-local-variable local) nil)))))
2588
2589 (defun gnus-summary-clear-local-variables ()
2590   (let ((locals gnus-summary-local-variables))
2591     (while locals
2592       (if (consp (car locals))
2593           (and (vectorp (caar locals))
2594                (set (caar locals) nil))
2595         (and (vectorp (car locals))
2596              (set (car locals) nil)))
2597       (setq locals (cdr locals)))))
2598
2599 ;; Summary data functions.
2600
2601 (defmacro gnus-data-number (data)
2602   `(car ,data))
2603
2604 (defmacro gnus-data-set-number (data number)
2605   `(setcar ,data ,number))
2606
2607 (defmacro gnus-data-mark (data)
2608   `(nth 1 ,data))
2609
2610 (defmacro gnus-data-set-mark (data mark)
2611   `(setcar (nthcdr 1 ,data) ,mark))
2612
2613 (defmacro gnus-data-pos (data)
2614   `(nth 2 ,data))
2615
2616 (defmacro gnus-data-set-pos (data pos)
2617   `(setcar (nthcdr 2 ,data) ,pos))
2618
2619 (defmacro gnus-data-header (data)
2620   `(nth 3 ,data))
2621
2622 (defmacro gnus-data-set-header (data header)
2623   `(setf (nth 3 ,data) ,header))
2624
2625 (defmacro gnus-data-level (data)
2626   `(nth 4 ,data))
2627
2628 (defmacro gnus-data-unread-p (data)
2629   `(= (nth 1 ,data) gnus-unread-mark))
2630
2631 (defmacro gnus-data-read-p (data)
2632   `(/= (nth 1 ,data) gnus-unread-mark))
2633
2634 (defmacro gnus-data-pseudo-p (data)
2635   `(consp (nth 3 ,data)))
2636
2637 (defmacro gnus-data-find (number)
2638   `(assq ,number gnus-newsgroup-data))
2639
2640 (defmacro gnus-data-find-list (number &optional data)
2641   `(let ((bdata ,(or data 'gnus-newsgroup-data)))
2642      (memq (assq ,number bdata)
2643            bdata)))
2644
2645 (defmacro gnus-data-make (number mark pos header level)
2646   `(list ,number ,mark ,pos ,header ,level))
2647
2648 (defun gnus-data-enter (after-article number mark pos header level offset)
2649   (let ((data (gnus-data-find-list after-article)))
2650     (unless data
2651       (error "No such article: %d" after-article))
2652     (setcdr data (cons (gnus-data-make number mark pos header level)
2653                        (cdr data)))
2654     (setq gnus-newsgroup-data-reverse nil)
2655     (gnus-data-update-list (cddr data) offset)))
2656
2657 (defun gnus-data-enter-list (after-article list &optional offset)
2658   (when list
2659     (let ((data (and after-article (gnus-data-find-list after-article)))
2660           (ilist list))
2661       (if (not (or data
2662                    after-article))
2663           (let ((odata gnus-newsgroup-data))
2664             (setq gnus-newsgroup-data (nconc list gnus-newsgroup-data))
2665             (when offset
2666               (gnus-data-update-list odata offset)))
2667       ;; Find the last element in the list to be spliced into the main
2668         ;; list.
2669         (while (cdr list)
2670           (setq list (cdr list)))
2671         (if (not data)
2672             (progn
2673               (setcdr list gnus-newsgroup-data)
2674               (setq gnus-newsgroup-data ilist)
2675               (when offset
2676                 (gnus-data-update-list (cdr list) offset)))
2677           (setcdr list (cdr data))
2678           (setcdr data ilist)
2679           (when offset
2680             (gnus-data-update-list (cdr list) offset))))
2681       (setq gnus-newsgroup-data-reverse nil))))
2682
2683 (defun gnus-data-remove (article &optional offset)
2684   (let ((data gnus-newsgroup-data))
2685     (if (= (gnus-data-number (car data)) article)
2686         (progn
2687           (setq gnus-newsgroup-data (cdr gnus-newsgroup-data)
2688                 gnus-newsgroup-data-reverse nil)
2689           (when offset
2690             (gnus-data-update-list gnus-newsgroup-data offset)))
2691       (while (cdr data)
2692         (when (= (gnus-data-number (cadr data)) article)
2693           (setcdr data (cddr data))
2694           (when offset
2695             (gnus-data-update-list (cdr data) offset))
2696           (setq data nil
2697                 gnus-newsgroup-data-reverse nil))
2698         (setq data (cdr data))))))
2699
2700 (defmacro gnus-data-list (backward)
2701   `(if ,backward
2702        (or gnus-newsgroup-data-reverse
2703            (setq gnus-newsgroup-data-reverse
2704                  (reverse gnus-newsgroup-data)))
2705      gnus-newsgroup-data))
2706
2707 (defun gnus-data-update-list (data offset)
2708   "Add OFFSET to the POS of all data entries in DATA."
2709   (setq gnus-newsgroup-data-reverse nil)
2710   (while data
2711     (setcar (nthcdr 2 (car data)) (+ offset (nth 2 (car data))))
2712     (setq data (cdr data))))
2713
2714 (defun gnus-summary-article-pseudo-p (article)
2715   "Say whether this article is a pseudo article or not."
2716   (not (vectorp (gnus-data-header (gnus-data-find article)))))
2717
2718 (defmacro gnus-summary-article-sparse-p (article)
2719   "Say whether this article is a sparse article or not."
2720   `(memq ,article gnus-newsgroup-sparse))
2721
2722 (defmacro gnus-summary-article-ancient-p (article)
2723   "Say whether this article is a sparse article or not."
2724   `(memq ,article gnus-newsgroup-ancient))
2725
2726 (defun gnus-article-parent-p (number)
2727   "Say whether this article is a parent or not."
2728   (let ((data (gnus-data-find-list number)))
2729     (and (cdr data)              ; There has to be an article after...
2730          (< (gnus-data-level (car data)) ; And it has to have a higher level.
2731             (gnus-data-level (nth 1 data))))))
2732
2733 (defun gnus-article-children (number)
2734   "Return a list of all children to NUMBER."
2735   (let* ((data (gnus-data-find-list number))
2736          (level (gnus-data-level (car data)))
2737          children)
2738     (setq data (cdr data))
2739     (while (and data
2740                 (= (gnus-data-level (car data)) (1+ level)))
2741       (push (gnus-data-number (car data)) children)
2742       (setq data (cdr data)))
2743     children))
2744
2745 (defmacro gnus-summary-skip-intangible ()
2746   "If the current article is intangible, then jump to a different article."
2747   '(let ((to (get-text-property (point) 'gnus-intangible)))
2748      (and to (gnus-summary-goto-subject to))))
2749
2750 (defmacro gnus-summary-article-intangible-p ()
2751   "Say whether this article is intangible or not."
2752   '(get-text-property (point) 'gnus-intangible))
2753
2754 (defun gnus-article-read-p (article)
2755   "Say whether ARTICLE is read or not."
2756   (not (or (memq article gnus-newsgroup-marked)
2757            (memq article gnus-newsgroup-spam-marked)
2758            (memq article gnus-newsgroup-unreads)
2759            (memq article gnus-newsgroup-unselected)
2760            (memq article gnus-newsgroup-dormant))))
2761
2762 ;; Some summary mode macros.
2763
2764 (defmacro gnus-summary-article-number ()
2765   "The article number of the article on the current line.
2766 If there isn't an article number here, then we return the current
2767 article number."
2768   '(progn
2769      (gnus-summary-skip-intangible)
2770      (or (get-text-property (point) 'gnus-number)
2771          (gnus-summary-last-subject))))
2772
2773 (defmacro gnus-summary-article-header (&optional number)
2774   "Return the header of article NUMBER."
2775   `(gnus-data-header (gnus-data-find
2776                       ,(or number '(gnus-summary-article-number)))))
2777
2778 (defmacro gnus-summary-thread-level (&optional number)
2779   "Return the level of thread that starts with article NUMBER."
2780   `(if (and (eq gnus-summary-make-false-root 'dummy)
2781             (get-text-property (point) 'gnus-intangible))
2782        0
2783      (gnus-data-level (gnus-data-find
2784                        ,(or number '(gnus-summary-article-number))))))
2785
2786 (defmacro gnus-summary-article-mark (&optional number)
2787   "Return the mark of article NUMBER."
2788   `(gnus-data-mark (gnus-data-find
2789                     ,(or number '(gnus-summary-article-number)))))
2790
2791 (defmacro gnus-summary-article-pos (&optional number)
2792   "Return the position of the line of article NUMBER."
2793   `(gnus-data-pos (gnus-data-find
2794                    ,(or number '(gnus-summary-article-number)))))
2795
2796 (defalias 'gnus-summary-subject-string 'gnus-summary-article-subject)
2797 (defmacro gnus-summary-article-subject (&optional number)
2798   "Return current subject string or nil if nothing."
2799   `(let ((headers
2800           ,(if number
2801                `(gnus-data-header (assq ,number gnus-newsgroup-data))
2802              '(gnus-data-header (assq (gnus-summary-article-number)
2803                                       gnus-newsgroup-data)))))
2804      (and headers
2805           (vectorp headers)
2806           (mail-header-subject headers))))
2807
2808 (defmacro gnus-summary-article-score (&optional number)
2809   "Return current article score."
2810   `(or (cdr (assq ,(or number '(gnus-summary-article-number))
2811                   gnus-newsgroup-scored))
2812        gnus-summary-default-score 0))
2813
2814 (defun gnus-summary-article-children (&optional number)
2815   "Return a list of article numbers that are children of article NUMBER."
2816   (let* ((data (gnus-data-find-list (or number (gnus-summary-article-number))))
2817          (level (gnus-data-level (car data)))
2818          l children)
2819     (while (and (setq data (cdr data))
2820                 (> (setq l (gnus-data-level (car data))) level))
2821       (and (= (1+ level) l)
2822            (push (gnus-data-number (car data))
2823                  children)))
2824     (nreverse children)))
2825
2826 (defun gnus-summary-article-parent (&optional number)
2827   "Return the article number of the parent of article NUMBER."
2828   (let* ((data (gnus-data-find-list (or number (gnus-summary-article-number))
2829                                     (gnus-data-list t)))
2830          (level (gnus-data-level (car data))))
2831     (if (zerop level)
2832         ()                              ; This is a root.
2833       ;; We search until we find an article with a level less than
2834       ;; this one.  That function has to be the parent.
2835       (while (and (setq data (cdr data))
2836                   (not (< (gnus-data-level (car data)) level))))
2837       (and data (gnus-data-number (car data))))))
2838
2839 (defun gnus-unread-mark-p (mark)
2840   "Say whether MARK is the unread mark."
2841   (= mark gnus-unread-mark))
2842
2843 (defun gnus-read-mark-p (mark)
2844   "Say whether MARK is one of the marks that mark as read.
2845 This is all marks except unread, ticked, dormant, and expirable."
2846   (not (or (= mark gnus-unread-mark)
2847            (= mark gnus-ticked-mark)
2848            (= mark gnus-dormant-mark)
2849            (= mark gnus-expirable-mark))))
2850
2851 (defmacro gnus-article-mark (number)
2852   "Return the MARK of article NUMBER.
2853 This macro should only be used when computing the mark the \"first\"
2854 time; i.e., when generating the summary lines.  After that,
2855 `gnus-summary-article-mark' should be used to examine the
2856 marks of articles."
2857   `(cond
2858     ((memq ,number gnus-newsgroup-unsendable) gnus-unsendable-mark)
2859 ;;;;    ((memq ,number gnus-newsgroup-undownloaded) gnus-undownloaded-mark)
2860     ((memq ,number gnus-newsgroup-downloadable) gnus-downloadable-mark)
2861     ((memq ,number gnus-newsgroup-unreads) gnus-unread-mark)
2862     ((memq ,number gnus-newsgroup-marked) gnus-ticked-mark)
2863     ((memq ,number gnus-newsgroup-spam-marked) gnus-spam-mark)
2864     ((memq ,number gnus-newsgroup-dormant) gnus-dormant-mark)
2865     ((memq ,number gnus-newsgroup-expirable) gnus-expirable-mark)
2866     (t (or (cdr (assq ,number gnus-newsgroup-reads))
2867            gnus-ancient-mark))))
2868
2869 ;; Saving hidden threads.
2870
2871 (defmacro gnus-save-hidden-threads (&rest forms)
2872   "Save hidden threads, eval FORMS, and restore the hidden threads."
2873   (let ((config (make-symbol "config")))
2874     `(let ((,config (gnus-hidden-threads-configuration)))
2875        (unwind-protect
2876            (save-excursion
2877              ,@forms)
2878          (gnus-restore-hidden-threads-configuration ,config)))))
2879 (put 'gnus-save-hidden-threads 'lisp-indent-function 0)
2880 (put 'gnus-save-hidden-threads 'edebug-form-spec '(body))
2881
2882 (defun gnus-data-compute-positions ()
2883   "Compute the positions of all articles."
2884   (setq gnus-newsgroup-data-reverse nil)
2885   (let ((data gnus-newsgroup-data))
2886     (save-excursion
2887       (gnus-save-hidden-threads
2888         (gnus-summary-show-all-threads)
2889         (goto-char (point-min))
2890         (while data
2891           (while (get-text-property (point) 'gnus-intangible)
2892             (forward-line 1))
2893           (gnus-data-set-pos (car data) (+ (point) 3))
2894           (setq data (cdr data))
2895           (forward-line 1))))))
2896
2897 (defun gnus-hidden-threads-configuration ()
2898   "Return the current hidden threads configuration."
2899   (save-excursion
2900     (let (config)
2901       (goto-char (point-min))
2902       (while (search-forward "\r" nil t)
2903         (push (1- (point)) config))
2904       config)))
2905
2906 (defun gnus-restore-hidden-threads-configuration (config)
2907   "Restore hidden threads configuration from CONFIG."
2908   (save-excursion
2909     (let (point buffer-read-only)
2910       (while (setq point (pop config))
2911         (when (and (< point (point-max))
2912                    (goto-char point)
2913                    (eq (char-after) ?\n))
2914           (subst-char-in-region point (1+ point) ?\n ?\r))))))
2915
2916 ;; Various summary mode internalish functions.
2917
2918 (defun gnus-mouse-pick-article (e)
2919   (interactive "e")
2920   (mouse-set-point e)
2921   (gnus-summary-next-page nil t))
2922
2923 (defun gnus-summary-set-display-table ()
2924   "Change the display table.
2925 Odd characters have a tendency to mess
2926 up nicely formatted displays - we make all possible glyphs
2927 display only a single character."
2928
2929   ;; We start from the standard display table, if any.
2930   (let ((table (or (copy-sequence standard-display-table)
2931                    (make-display-table)))
2932         (i 32))
2933     ;; Nix out all the control chars...
2934     (while (>= (setq i (1- i)) 0)
2935       (aset table i [??]))
2936    ;; ... but not newline and cr, of course.  (cr is necessary for the
2937     ;; selective display).
2938     (aset table ?\n nil)
2939     (aset table ?\r nil)
2940     ;; We keep TAB as well.
2941     (aset table ?\t nil)
2942     ;; We nix out any glyphs over 126 that are not set already.
2943     (let ((i 256))
2944       (while (>= (setq i (1- i)) 127)
2945         ;; Only modify if the entry is nil.
2946         (unless (aref table i)
2947           (aset table i [??]))))
2948     (setq buffer-display-table table)))
2949
2950 (defun gnus-summary-set-article-display-arrow (pos)
2951   "Update the overlay arrow to point to line at position POS."
2952   (when (and gnus-summary-display-arrow
2953              (boundp 'overlay-arrow-position)
2954              (boundp 'overlay-arrow-string))
2955     (save-excursion
2956       (goto-char pos)
2957       (beginning-of-line)
2958       (unless overlay-arrow-position
2959         (setq overlay-arrow-position (make-marker)))
2960       (setq overlay-arrow-string "=>"
2961             overlay-arrow-position (set-marker overlay-arrow-position
2962                                                (point)
2963                                                (current-buffer))))))
2964
2965 (defun gnus-summary-buffer-name (group)
2966   "Return the summary buffer name of GROUP."
2967   (concat "*Summary " (gnus-group-decoded-name group) "*"))
2968
2969 (defun gnus-summary-setup-buffer (group)
2970   "Initialize summary buffer."
2971   (let ((buffer (gnus-summary-buffer-name group))
2972         (dead-name (concat "*Dead Summary "
2973                            (gnus-group-decoded-name group) "*")))
2974     ;; If a dead summary buffer exists, we kill it.
2975     (when (gnus-buffer-live-p dead-name)
2976       (gnus-kill-buffer dead-name))
2977     (if (get-buffer buffer)
2978         (progn
2979           (set-buffer buffer)
2980           (setq gnus-summary-buffer (current-buffer))
2981           (not gnus-newsgroup-prepared))
2982       ;; Fix by Sudish Joseph <joseph@cis.ohio-state.edu>
2983       (setq gnus-summary-buffer (set-buffer (gnus-get-buffer-create buffer)))
2984       (gnus-summary-mode group)
2985       (when gnus-carpal
2986         (gnus-carpal-setup-buffer 'summary))
2987       (unless gnus-single-article-buffer
2988         (make-local-variable 'gnus-article-buffer)
2989         (make-local-variable 'gnus-article-current)
2990         (make-local-variable 'gnus-original-article-buffer))
2991       (setq gnus-newsgroup-name group)
2992       ;; Set any local variables in the group parameters.
2993       (gnus-summary-set-local-parameters gnus-newsgroup-name)
2994       t)))
2995
2996 (defun gnus-set-global-variables ()
2997   "Set the global equivalents of the buffer-local variables.
2998 They are set to the latest values they had.  These reflect the summary
2999 buffer that was in action when the last article was fetched."
3000   (when (eq major-mode 'gnus-summary-mode)
3001     (setq gnus-summary-buffer (current-buffer))
3002     (let ((name gnus-newsgroup-name)
3003           (marked gnus-newsgroup-marked)
3004           (spam gnus-newsgroup-spam-marked)
3005           (unread gnus-newsgroup-unreads)
3006           (headers gnus-current-headers)
3007           (data gnus-newsgroup-data)
3008           (summary gnus-summary-buffer)
3009           (article-buffer gnus-article-buffer)
3010           (original gnus-original-article-buffer)
3011           (gac gnus-article-current)
3012           (reffed gnus-reffed-article-number)
3013           (score-file gnus-current-score-file)
3014           (default-charset gnus-newsgroup-charset)
3015           vlist)
3016       (let ((locals gnus-newsgroup-variables))
3017         (while locals
3018           (if (consp (car locals))
3019               (push (eval (caar locals)) vlist)
3020             (push (eval (car locals)) vlist))
3021           (setq locals (cdr locals)))
3022         (setq vlist (nreverse vlist)))
3023       (save-excursion
3024         (set-buffer gnus-group-buffer)
3025         (setq gnus-newsgroup-name name
3026               gnus-newsgroup-marked marked
3027               gnus-newsgroup-spam-marked spam
3028               gnus-newsgroup-unreads unread
3029               gnus-current-headers headers
3030               gnus-newsgroup-data data
3031               gnus-article-current gac
3032               gnus-summary-buffer summary
3033               gnus-article-buffer article-buffer
3034               gnus-original-article-buffer original
3035               gnus-reffed-article-number reffed
3036               gnus-current-score-file score-file
3037               gnus-newsgroup-charset default-charset)
3038         (let ((locals gnus-newsgroup-variables))
3039           (while locals
3040             (if (consp (car locals))
3041                 (set (caar locals) (pop vlist))
3042               (set (car locals) (pop vlist)))
3043             (setq locals (cdr locals))))
3044         ;; The article buffer also has local variables.
3045         (when (gnus-buffer-live-p gnus-article-buffer)
3046           (set-buffer gnus-article-buffer)
3047           (setq gnus-summary-buffer summary))))))
3048
3049 (defun gnus-summary-article-unread-p (article)
3050   "Say whether ARTICLE is unread or not."
3051   (memq article gnus-newsgroup-unreads))
3052
3053 (defun gnus-summary-first-article-p (&optional article)
3054   "Return whether ARTICLE is the first article in the buffer."
3055   (if (not (setq article (or article (gnus-summary-article-number))))
3056       nil
3057     (eq article (caar gnus-newsgroup-data))))
3058
3059 (defun gnus-summary-last-article-p (&optional article)
3060   "Return whether ARTICLE is the last article in the buffer."
3061   (if (not (setq article (or article (gnus-summary-article-number))))
3062       ;; All non-existent numbers are the last article.  :-)
3063       t
3064     (not (cdr (gnus-data-find-list article)))))
3065
3066 (defun gnus-make-thread-indent-array ()
3067   (let ((n 200))
3068     (unless (and gnus-thread-indent-array
3069                  (= gnus-thread-indent-level gnus-thread-indent-array-level))
3070       (setq gnus-thread-indent-array (make-vector 201 "")
3071             gnus-thread-indent-array-level gnus-thread-indent-level)
3072       (while (>= n 0)
3073         (aset gnus-thread-indent-array n
3074               (make-string (* n gnus-thread-indent-level) ? ))
3075         (setq n (1- n))))))
3076
3077 (defun gnus-update-summary-mark-positions ()
3078   "Compute where the summary marks are to go."
3079   (save-excursion
3080     (when (gnus-buffer-exists-p gnus-summary-buffer)
3081       (set-buffer gnus-summary-buffer))
3082     (let ((gnus-replied-mark 129)
3083           (gnus-score-below-mark 130)
3084           (gnus-score-over-mark 130)
3085           (gnus-download-mark 131)
3086           (spec gnus-summary-line-format-spec)
3087           gnus-visual pos)
3088       (save-excursion
3089         (gnus-set-work-buffer)
3090         (let ((gnus-summary-line-format-spec spec)
3091               (gnus-newsgroup-downloadable '((0 . t))))
3092           (gnus-summary-insert-line
3093            [0 "" "" "05 Apr 2001 23:33:09 +0400" "" "" 0 0 "" nil]
3094            0 nil 128 t nil "" nil 1)
3095           (goto-char (point-min))
3096           (setq pos (list (cons 'unread (and (search-forward "\200" nil t)
3097                                              (- (point) (point-min) 1)))))
3098           (goto-char (point-min))
3099           (push (cons 'replied (and (search-forward "\201" nil t)
3100                                     (- (point) (point-min) 1)))
3101                 pos)
3102           (goto-char (point-min))
3103           (push (cons 'score (and (search-forward "\202" nil t)
3104                                   (- (point) (point-min) 1)))
3105                 pos)
3106           (goto-char (point-min))
3107           (push (cons 'download
3108                       (and (search-forward "\203" nil t)
3109                            (- (point) (point-min) 1)))
3110                 pos)))
3111       (setq gnus-summary-mark-positions pos))))
3112
3113 (defun gnus-summary-insert-dummy-line (gnus-tmp-subject gnus-tmp-number)
3114   "Insert a dummy root in the summary buffer."
3115   (beginning-of-line)
3116   (gnus-add-text-properties
3117    (point) (progn (eval gnus-summary-dummy-line-format-spec) (point))
3118    (list 'gnus-number gnus-tmp-number 'gnus-intangible gnus-tmp-number)))
3119
3120 (defun gnus-summary-extract-address-component (from)
3121   (or (car (funcall gnus-extract-address-components from))
3122       from))
3123
3124 (defun gnus-summary-from-or-to-or-newsgroups (header gnus-tmp-from)
3125   (let ((mail-parse-charset gnus-newsgroup-charset)
3126         ; Is it really necessary to do this next part for each summary line?
3127         ; Luckily, doesn't seem to slow things down much.
3128         (mail-parse-ignored-charsets
3129          (save-excursion (set-buffer gnus-summary-buffer)
3130                          gnus-newsgroup-ignored-charsets)))
3131     (or
3132      (and gnus-ignored-from-addresses
3133           (string-match gnus-ignored-from-addresses gnus-tmp-from)
3134           (let ((extra-headers (mail-header-extra header))
3135                 to
3136                 newsgroups)
3137             (cond
3138              ((setq to (cdr (assq 'To extra-headers)))
3139               (concat "-> "
3140                       (inline
3141                         (gnus-summary-extract-address-component
3142                          (funcall gnus-decode-encoded-word-function to)))))
3143              ((setq newsgroups (cdr (assq 'Newsgroups extra-headers)))
3144               (concat "=> " newsgroups)))))
3145      (inline (gnus-summary-extract-address-component gnus-tmp-from)))))
3146
3147 (defun gnus-summary-insert-line (gnus-tmp-header
3148                                  gnus-tmp-level gnus-tmp-current
3149                                  gnus-tmp-unread gnus-tmp-replied
3150                                  gnus-tmp-expirable gnus-tmp-subject-or-nil
3151                                  &optional gnus-tmp-dummy gnus-tmp-score
3152                                  gnus-tmp-process)
3153   (let* ((gnus-tmp-indentation (aref gnus-thread-indent-array gnus-tmp-level))
3154          (gnus-tmp-lines (mail-header-lines gnus-tmp-header))
3155          (gnus-tmp-score (or gnus-tmp-score gnus-summary-default-score 0))
3156          (gnus-tmp-score-char
3157           (if (or (null gnus-summary-default-score)
3158                   (<= (abs (- gnus-tmp-score gnus-summary-default-score))
3159                       gnus-summary-zcore-fuzz))
3160               ?                         ;Whitespace
3161             (if (< gnus-tmp-score gnus-summary-default-score)
3162                 gnus-score-below-mark gnus-score-over-mark)))
3163          (gnus-tmp-number (mail-header-number gnus-tmp-header))
3164          (gnus-tmp-replied
3165           (cond (gnus-tmp-process gnus-process-mark)
3166                 ((memq gnus-tmp-current gnus-newsgroup-cached)
3167                  gnus-cached-mark)
3168                 (gnus-tmp-replied gnus-replied-mark)
3169                 ((memq gnus-tmp-current gnus-newsgroup-forwarded)
3170                  gnus-forwarded-mark)
3171                 ((memq gnus-tmp-current gnus-newsgroup-saved)
3172                  gnus-saved-mark)
3173                 ((memq gnus-tmp-number gnus-newsgroup-recent)
3174                  gnus-recent-mark)
3175                 ((memq gnus-tmp-number gnus-newsgroup-unseen)
3176                  gnus-unseen-mark)
3177                 (t gnus-no-mark)))
3178          (gnus-tmp-from (mail-header-from gnus-tmp-header))
3179          (gnus-tmp-name
3180           (cond
3181            ((string-match "<[^>]+> *$" gnus-tmp-from)
3182             (let ((beg (match-beginning 0)))
3183               (or (and (string-match "^\".+\"" gnus-tmp-from)
3184                        (substring gnus-tmp-from 1 (1- (match-end 0))))
3185                   (substring gnus-tmp-from 0 beg))))
3186            ((string-match "(.+)" gnus-tmp-from)
3187             (substring gnus-tmp-from
3188                        (1+ (match-beginning 0)) (1- (match-end 0))))
3189            (t gnus-tmp-from)))
3190          (gnus-tmp-subject (mail-header-subject gnus-tmp-header))
3191          (gnus-tmp-opening-bracket (if gnus-tmp-dummy ?\< ?\[))
3192          (gnus-tmp-closing-bracket (if gnus-tmp-dummy ?\> ?\]))
3193          (buffer-read-only nil))
3194     (when (string= gnus-tmp-name "")
3195       (setq gnus-tmp-name gnus-tmp-from))
3196     (unless (numberp gnus-tmp-lines)
3197       (setq gnus-tmp-lines -1))
3198     (if (= gnus-tmp-lines -1)
3199         (setq gnus-tmp-lines "?")
3200       (setq gnus-tmp-lines (number-to-string gnus-tmp-lines)))
3201     (gnus-put-text-property
3202      (point)
3203      (progn (eval gnus-summary-line-format-spec) (point))
3204      'gnus-number gnus-tmp-number)
3205     (when (gnus-visual-p 'summary-highlight 'highlight)
3206       (forward-line -1)
3207       (gnus-run-hooks 'gnus-summary-update-hook)
3208       (forward-line 1))))
3209
3210 (defun gnus-summary-update-line (&optional dont-update)
3211   "Update summary line after change."
3212   (when (and gnus-summary-default-score
3213              (not gnus-summary-inhibit-highlight))
3214     (let* ((gnus-summary-inhibit-highlight t) ; Prevent recursion.
3215            (article (gnus-summary-article-number))
3216            (score (gnus-summary-article-score article)))
3217       (unless dont-update
3218         (if (and gnus-summary-mark-below
3219                  (< (gnus-summary-article-score)
3220                     gnus-summary-mark-below))
3221             ;; This article has a low score, so we mark it as read.
3222             (when (memq article gnus-newsgroup-unreads)
3223               (gnus-summary-mark-article-as-read gnus-low-score-mark))
3224           (when (eq (gnus-summary-article-mark) gnus-low-score-mark)
3225             ;; This article was previously marked as read on account
3226             ;; of a low score, but now it has risen, so we mark it as
3227             ;; unread.
3228             (gnus-summary-mark-article-as-unread gnus-unread-mark)))
3229         (gnus-summary-update-mark
3230          (if (or (null gnus-summary-default-score)
3231                  (<= (abs (- score gnus-summary-default-score))
3232                      gnus-summary-zcore-fuzz))
3233              ?                          ;Whitespace
3234            (if (< score gnus-summary-default-score)
3235                gnus-score-below-mark gnus-score-over-mark))
3236          'score))
3237       ;; Do visual highlighting.
3238       (when (gnus-visual-p 'summary-highlight 'highlight)
3239         (gnus-run-hooks 'gnus-summary-update-hook)))))
3240
3241 (defvar gnus-tmp-new-adopts nil)
3242
3243 (defun gnus-summary-number-of-articles-in-thread (thread &optional level char)
3244   "Return the number of articles in THREAD.
3245 This may be 0 in some cases -- if none of the articles in
3246 the thread are to be displayed."
3247   (let* ((number
3248          ;; Fix by Luc Van Eycken <Luc.VanEycken@esat.kuleuven.ac.be>.
3249           (cond
3250            ((not (listp thread))
3251             1)
3252            ((and (consp thread) (cdr thread))
3253             (apply
3254              '+ 1 (mapcar
3255                    'gnus-summary-number-of-articles-in-thread (cdr thread))))
3256            ((null thread)
3257             1)
3258            ((memq (mail-header-number (car thread)) gnus-newsgroup-limit)
3259             1)
3260            (t 0))))
3261     (when (and level (zerop level) gnus-tmp-new-adopts)
3262       (incf number
3263             (apply '+ (mapcar
3264                        'gnus-summary-number-of-articles-in-thread
3265                        gnus-tmp-new-adopts))))
3266     (if char
3267         (if (> number 1) gnus-not-empty-thread-mark
3268           gnus-empty-thread-mark)
3269       number)))
3270
3271 (defsubst gnus-summary-line-message-size (head)
3272   "Return pretty-printed version of message size.
3273 This function is intended to be used in
3274 `gnus-summary-line-format-alist', which see."
3275   (let ((c (or (mail-header-chars head) -1)))
3276     (cond ((< c 0) "n/a")               ; chars not available
3277           ((< c (* 1000 10)) (format "%1.1fk" (/ c 1024.0)))
3278           ((< c (* 1000 100)) (format "%dk" (/ c 1024.0)))
3279           ((< c (* 1000 10000)) (format "%1.1fM" (/ c (* 1024.0 1024))))
3280           (t (format "%dM" (/ c (* 1024.0 1024)))))))
3281
3282
3283 (defun gnus-summary-set-local-parameters (group)
3284   "Go through the local params of GROUP and set all variable specs in that list."
3285   (let ((params (gnus-group-find-parameter group))
3286         (vars '(quit-config))           ; Ignore quit-config.
3287         elem)
3288     (while params
3289       (setq elem (car params)
3290             params (cdr params))
3291       (and (consp elem)                 ; Has to be a cons.
3292            (consp (cdr elem))           ; The cdr has to be a list.
3293            (symbolp (car elem))         ; Has to be a symbol in there.
3294            (not (memq (car elem) vars))
3295            (ignore-errors               ; So we set it.
3296              (push (car elem) vars)
3297              (make-local-variable (car elem))
3298              (set (car elem) (eval (nth 1 elem))))))))
3299
3300 (defun gnus-summary-read-group (group &optional show-all no-article
3301                                       kill-buffer no-display backward
3302                                       select-articles)
3303   "Start reading news in newsgroup GROUP.
3304 If SHOW-ALL is non-nil, already read articles are also listed.
3305 If NO-ARTICLE is non-nil, no article is selected initially.
3306 If NO-DISPLAY, don't generate a summary buffer."
3307   (let (result)
3308     (while (and group
3309                 (null (setq result
3310                             (let ((gnus-auto-select-next nil))
3311                               (or (gnus-summary-read-group-1
3312                                    group show-all no-article
3313                                    kill-buffer no-display
3314                                    select-articles)
3315                                   (setq show-all nil
3316                                         select-articles nil)))))
3317                 (eq gnus-auto-select-next 'quietly))
3318       (set-buffer gnus-group-buffer)
3319       ;; The entry function called above goes to the next
3320       ;; group automatically, so we go two groups back
3321       ;; if we are searching for the previous group.
3322       (when backward
3323         (gnus-group-prev-unread-group 2))
3324       (if (not (equal group (gnus-group-group-name)))
3325           (setq group (gnus-group-group-name))
3326         (setq group nil)))
3327     result))
3328
3329 (defun gnus-summary-read-group-1 (group show-all no-article
3330                                         kill-buffer no-display
3331                                         &optional select-articles)
3332   ;; Killed foreign groups can't be entered.
3333   ;;  (when (and (not (gnus-group-native-p group))
3334   ;;         (not (gnus-gethash group gnus-newsrc-hashtb)))
3335   ;;    (error "Dead non-native groups can't be entered"))
3336   (gnus-message 5 "Retrieving newsgroup: %s..."
3337                 (gnus-group-decoded-name group))
3338   (let* ((new-group (gnus-summary-setup-buffer group))
3339          (quit-config (gnus-group-quit-config group))
3340          (did-select (and new-group (gnus-select-newsgroup
3341                                      group show-all select-articles))))
3342     (cond
3343      ;; This summary buffer exists already, so we just select it.
3344      ((not new-group)
3345       (gnus-set-global-variables)
3346       (when kill-buffer
3347         (gnus-kill-or-deaden-summary kill-buffer))
3348       (gnus-configure-windows 'summary 'force)
3349       (gnus-set-mode-line 'summary)
3350       (gnus-summary-position-point)
3351       (message "")
3352       t)
3353      ;; We couldn't select this group.
3354      ((null did-select)
3355       (when (and (eq major-mode 'gnus-summary-mode)
3356                  (not (equal (current-buffer) kill-buffer)))
3357         (kill-buffer (current-buffer))
3358         (if (not quit-config)
3359             (progn
3360               ;; Update the info -- marks might need to be removed,
3361               ;; for instance.
3362               (gnus-summary-update-info)
3363               (set-buffer gnus-group-buffer)
3364               (gnus-group-jump-to-group group)
3365               (gnus-group-next-unread-group 1))
3366           (gnus-handle-ephemeral-exit quit-config)))
3367       (let ((grpinfo (gnus-get-info group)))
3368         (if (null (gnus-info-read grpinfo))
3369             (gnus-message 3 "Group %s contains no messages"
3370                           (gnus-group-decoded-name group))
3371           (gnus-message 3 "Can't select group")))
3372       nil)
3373      ;; The user did a `C-g' while prompting for number of articles,
3374      ;; so we exit this group.
3375      ((eq did-select 'quit)
3376       (and (eq major-mode 'gnus-summary-mode)
3377            (not (equal (current-buffer) kill-buffer))
3378            (kill-buffer (current-buffer)))
3379       (when kill-buffer
3380         (gnus-kill-or-deaden-summary kill-buffer))
3381       (if (not quit-config)
3382           (progn
3383             (set-buffer gnus-group-buffer)
3384             (gnus-group-jump-to-group group)
3385             (gnus-group-next-unread-group 1)
3386             (gnus-configure-windows 'group 'force))
3387         (gnus-handle-ephemeral-exit quit-config))
3388       ;; Finally signal the quit.
3389       (signal 'quit nil))
3390      ;; The group was successfully selected.
3391      (t
3392       (gnus-set-global-variables)
3393       ;; Save the active value in effect when the group was entered.
3394       (setq gnus-newsgroup-active
3395             (gnus-copy-sequence
3396              (gnus-active gnus-newsgroup-name)))
3397       ;; You can change the summary buffer in some way with this hook.
3398       (gnus-run-hooks 'gnus-select-group-hook)
3399       (gnus-update-format-specifications
3400        nil 'summary 'summary-mode 'summary-dummy)
3401       (gnus-update-summary-mark-positions)
3402       ;; Do score processing.
3403       (when gnus-use-scoring
3404         (gnus-possibly-score-headers))
3405       ;; Check whether to fill in the gaps in the threads.
3406       (when gnus-build-sparse-threads
3407         (gnus-build-sparse-threads))
3408       ;; Find the initial limit.
3409       (if gnus-show-threads
3410           (if show-all
3411               (let ((gnus-newsgroup-dormant nil))
3412                 (gnus-summary-initial-limit show-all))
3413             (gnus-summary-initial-limit show-all))
3414         ;; When unthreaded, all articles are always shown.
3415         (setq gnus-newsgroup-limit
3416               (mapcar
3417                (lambda (header) (mail-header-number header))
3418                gnus-newsgroup-headers)))
3419       ;; Generate the summary buffer.
3420       (unless no-display
3421         (gnus-summary-prepare))
3422       (when gnus-use-trees
3423         (gnus-tree-open group)
3424         (setq gnus-summary-highlight-line-function
3425               'gnus-tree-highlight-article))
3426       ;; If the summary buffer is empty, but there are some low-scored
3427       ;; articles or some excluded dormants, we include these in the
3428       ;; buffer.
3429       (when (and (zerop (buffer-size))
3430                  (not no-display))
3431         (cond (gnus-newsgroup-dormant
3432                (gnus-summary-limit-include-dormant))
3433               ((and gnus-newsgroup-scored show-all)
3434                (gnus-summary-limit-include-expunged t))))
3435       ;; Function `gnus-apply-kill-file' must be called in this hook.
3436       (gnus-run-hooks 'gnus-apply-kill-hook)
3437       (if (and (zerop (buffer-size))
3438                (not no-display))
3439           (progn
3440             ;; This newsgroup is empty.
3441             (gnus-summary-catchup-and-exit nil t)
3442             (gnus-message 6 "No unread news")
3443             (when kill-buffer
3444               (gnus-kill-or-deaden-summary kill-buffer))
3445             ;; Return nil from this function.
3446             nil)
3447         ;; Hide conversation thread subtrees.  We cannot do this in
3448         ;; gnus-summary-prepare-hook since kill processing may not
3449         ;; work with hidden articles.
3450         (gnus-summary-maybe-hide-threads)
3451         (when kill-buffer
3452           (gnus-kill-or-deaden-summary kill-buffer))
3453         (gnus-summary-auto-select-subject)
3454         ;; Show first unread article if requested.
3455         (if (and (not no-article)
3456                  (not no-display)
3457                  gnus-newsgroup-unreads
3458                  gnus-auto-select-first)
3459             (progn
3460               (gnus-configure-windows 'summary)
3461               (let ((art (gnus-summary-article-number)))
3462                 (unless (or (memq art gnus-newsgroup-undownloaded)
3463                             (memq art gnus-newsgroup-downloadable))
3464                   (gnus-summary-goto-article art))))
3465           ;; Don't select any articles.
3466           (gnus-summary-position-point)
3467           (gnus-configure-windows 'summary 'force)
3468           (gnus-set-mode-line 'summary))
3469         (when (get-buffer-window gnus-group-buffer t)
3470           ;; Gotta use windows, because recenter does weird stuff if
3471           ;; the current buffer ain't the displayed window.
3472           (let ((owin (selected-window)))
3473             (select-window (get-buffer-window gnus-group-buffer t))
3474             (when (gnus-group-goto-group group)
3475               (recenter))
3476             (select-window owin)))
3477         ;; Mark this buffer as "prepared".
3478         (setq gnus-newsgroup-prepared t)
3479         (gnus-run-hooks 'gnus-summary-prepared-hook)
3480         t)))))
3481
3482 (defun gnus-summary-auto-select-subject ()
3483   "Select the subject line on initial group entry."
3484   (goto-char (point-min))
3485   (cond
3486    ((eq gnus-auto-select-subject 'best)
3487     (gnus-summary-best-unread-subject))
3488    ((eq gnus-auto-select-subject 'unread)
3489     (gnus-summary-first-unread-subject))
3490    ((eq gnus-auto-select-subject 'unseen)
3491     (gnus-summary-first-unseen-subject))
3492    ((eq gnus-auto-select-subject 'unseen-or-unread)
3493     (gnus-summary-first-unseen-or-unread-subject))
3494    ((eq gnus-auto-select-subject 'first)
3495     ;; Do nothing.
3496     )
3497    ((gnus-functionp gnus-auto-select-subject)
3498     (funcall gnus-auto-select-subject))))
3499
3500 (defun gnus-summary-prepare ()
3501   "Generate the summary buffer."
3502   (interactive)
3503   (let ((buffer-read-only nil))
3504     (erase-buffer)
3505     (setq gnus-newsgroup-data nil
3506           gnus-newsgroup-data-reverse nil)
3507     (gnus-run-hooks 'gnus-summary-generate-hook)
3508     ;; Generate the buffer, either with threads or without.
3509     (when gnus-newsgroup-headers
3510       (gnus-summary-prepare-threads
3511        (if gnus-show-threads
3512            (gnus-sort-gathered-threads
3513             (funcall gnus-summary-thread-gathering-function
3514                      (gnus-sort-threads
3515                       (gnus-cut-threads (gnus-make-threads)))))
3516          ;; Unthreaded display.
3517          (gnus-sort-articles gnus-newsgroup-headers))))
3518     (setq gnus-newsgroup-data (nreverse gnus-newsgroup-data))
3519     ;; Call hooks for modifying summary buffer.
3520     (goto-char (point-min))
3521     (gnus-run-hooks 'gnus-summary-prepare-hook)))
3522
3523 (defsubst gnus-general-simplify-subject (subject)
3524   "Simply subject by the same rules as gnus-gather-threads-by-subject."
3525   (setq subject
3526         (cond
3527          ;; Truncate the subject.
3528          (gnus-simplify-subject-functions
3529           (gnus-map-function gnus-simplify-subject-functions subject))
3530          ((numberp gnus-summary-gather-subject-limit)
3531           (setq subject (gnus-simplify-subject-re subject))
3532           (if (> (length subject) gnus-summary-gather-subject-limit)
3533               (substring subject 0 gnus-summary-gather-subject-limit)
3534             subject))
3535          ;; Fuzzily simplify it.
3536          ((eq 'fuzzy gnus-summary-gather-subject-limit)
3537           (gnus-simplify-subject-fuzzy subject))
3538          ;; Just remove the leading "Re:".
3539          (t
3540           (gnus-simplify-subject-re subject))))
3541
3542   (if (and gnus-summary-gather-exclude-subject
3543            (string-match gnus-summary-gather-exclude-subject subject))
3544       nil                         ; This article shouldn't be gathered
3545     subject))
3546
3547 (defun gnus-summary-simplify-subject-query ()
3548   "Query where the respool algorithm would put this article."
3549   (interactive)
3550   (gnus-summary-select-article)
3551   (message (gnus-general-simplify-subject (gnus-summary-article-subject))))
3552
3553 (defun gnus-gather-threads-by-subject (threads)
3554   "Gather threads by looking at Subject headers."
3555   (if (not gnus-summary-make-false-root)
3556       threads
3557     (let ((hashtb (gnus-make-hashtable 1024))
3558           (prev threads)
3559           (result threads)
3560           subject hthread whole-subject)
3561       (while threads
3562         (setq subject (gnus-general-simplify-subject
3563                        (setq whole-subject (mail-header-subject
3564                                             (caar threads)))))
3565         (when subject
3566           (if (setq hthread (gnus-gethash subject hashtb))
3567               (progn
3568                 ;; We enter a dummy root into the thread, if we
3569                 ;; haven't done that already.
3570                 (unless (stringp (caar hthread))
3571                   (setcar hthread (list whole-subject (car hthread))))
3572                 ;; We add this new gathered thread to this gathered
3573                 ;; thread.
3574                 (setcdr (car hthread)
3575                         (nconc (cdar hthread) (list (car threads))))
3576                 ;; Remove it from the list of threads.
3577                 (setcdr prev (cdr threads))
3578                 (setq threads prev))
3579             ;; Enter this thread into the hash table.
3580             (gnus-sethash subject threads hashtb)))
3581         (setq prev threads)
3582         (setq threads (cdr threads)))
3583       result)))
3584
3585 (defun gnus-gather-threads-by-references (threads)
3586   "Gather threads by looking at References headers."
3587   (let ((idhashtb (gnus-make-hashtable 1024))
3588         (thhashtb (gnus-make-hashtable 1024))
3589         (prev threads)
3590         (result threads)
3591         ids references id gthread gid entered ref)
3592     (while threads
3593       (when (setq references (mail-header-references (caar threads)))
3594         (setq id (mail-header-id (caar threads))
3595               ids (inline (gnus-split-references references))
3596               entered nil)
3597         (while (setq ref (pop ids))
3598           (setq ids (delete ref ids))
3599           (if (not (setq gid (gnus-gethash ref idhashtb)))
3600               (progn
3601                 (gnus-sethash ref id idhashtb)
3602                 (gnus-sethash id threads thhashtb))
3603             (setq gthread (gnus-gethash gid thhashtb))
3604             (unless entered
3605               ;; We enter a dummy root into the thread, if we
3606               ;; haven't done that already.
3607               (unless (stringp (caar gthread))
3608                 (setcar gthread (list (mail-header-subject (caar gthread))
3609                                       (car gthread))))
3610               ;; We add this new gathered thread to this gathered
3611               ;; thread.
3612               (setcdr (car gthread)
3613                       (nconc (cdar gthread) (list (car threads)))))
3614             ;; Add it into the thread hash table.
3615             (gnus-sethash id gthread thhashtb)
3616             (setq entered t)
3617             ;; Remove it from the list of threads.
3618             (setcdr prev (cdr threads))
3619             (setq threads prev))))
3620       (setq prev threads)
3621       (setq threads (cdr threads)))
3622     result))
3623
3624 (defun gnus-sort-gathered-threads (threads)
3625   "Sort subtreads inside each gathered thread by `gnus-sort-gathered-threads-function'."
3626   (let ((result threads))
3627     (while threads
3628       (when (stringp (caar threads))
3629         (setcdr (car threads)
3630                 (sort (cdar threads) gnus-sort-gathered-threads-function)))
3631       (setq threads (cdr threads)))
3632     result))
3633
3634 (defun gnus-thread-loop-p (root thread)
3635   "Say whether ROOT is in THREAD."
3636   (let ((stack (list thread))
3637         (infloop 0)
3638         th)
3639     (while (setq thread (pop stack))
3640       (setq th (cdr thread))
3641       (while (and th
3642                   (not (eq (caar th) root)))
3643         (pop th))
3644       (if th
3645           ;; We have found a loop.
3646           (let (ref-dep)
3647             (setcdr thread (delq (car th) (cdr thread)))
3648             (if (boundp (setq ref-dep (intern "none"
3649                                               gnus-newsgroup-dependencies)))
3650                 (setcdr (symbol-value ref-dep)
3651                         (nconc (cdr (symbol-value ref-dep))
3652                                (list (car th))))
3653               (set ref-dep (list nil (car th))))
3654             (setq infloop 1
3655                   stack nil))
3656         ;; Push all the subthreads onto the stack.
3657         (push (cdr thread) stack)))
3658     infloop))
3659
3660 (defun gnus-make-threads ()
3661   "Go through the dependency hashtb and find the roots.  Return all threads."
3662   (let (threads)
3663     (while (catch 'infloop
3664              (mapatoms
3665               (lambda (refs)
3666                 ;; Deal with self-referencing References loops.
3667                 (when (and (car (symbol-value refs))
3668                            (not (zerop
3669                                  (apply
3670                                   '+
3671                                   (mapcar
3672                                    (lambda (thread)
3673                                      (gnus-thread-loop-p
3674                                       (car (symbol-value refs)) thread))
3675                                    (cdr (symbol-value refs)))))))
3676                   (setq threads nil)
3677                   (throw 'infloop t))
3678                 (unless (car (symbol-value refs))
3679                   ;; These threads do not refer back to any other
3680                   ;; articles, so they're roots.
3681                   (setq threads (append (cdr (symbol-value refs)) threads))))
3682               gnus-newsgroup-dependencies)))
3683     threads))
3684
3685 ;; Build the thread tree.
3686 (defsubst gnus-dependencies-add-header (header dependencies force-new)
3687   "Enter HEADER into the DEPENDENCIES table if it is not already there.
3688
3689 If FORCE-NEW is not nil, enter HEADER into the DEPENDENCIES table even
3690 if it was already present.
3691
3692 If `gnus-summary-ignore-duplicates' is nil then duplicate Message-IDs
3693 will not be entered in the DEPENDENCIES table.  Otherwise duplicate
3694 Message-IDs will be renamed to a unique Message-ID before being
3695 entered.
3696
3697 Returns HEADER if it was entered in the DEPENDENCIES.  Returns nil otherwise."
3698   (let* ((id (mail-header-id header))
3699          (id-dep (and id (intern id dependencies)))
3700          parent-id ref ref-dep ref-header replaced)
3701     ;; Enter this `header' in the `dependencies' table.
3702     (cond
3703      ((not id-dep)
3704       (setq header nil))
3705      ;; The first two cases do the normal part: enter a new `header'
3706      ;; in the `dependencies' table.
3707      ((not (boundp id-dep))
3708       (set id-dep (list header)))
3709      ((null (car (symbol-value id-dep)))
3710       (setcar (symbol-value id-dep) header))
3711
3712      ;; From here the `header' was already present in the
3713      ;; `dependencies' table.
3714      (force-new
3715       ;; Overrides an existing entry;
3716       ;; just set the header part of the entry.
3717       (setcar (symbol-value id-dep) header)
3718       (setq replaced t))
3719
3720      ;; Renames the existing `header' to a unique Message-ID.
3721      ((not gnus-summary-ignore-duplicates)
3722       ;; An article with this Message-ID has already been seen.
3723       ;; We rename the Message-ID.
3724       (set (setq id-dep (intern (setq id (nnmail-message-id)) dependencies))
3725            (list header))
3726       (mail-header-set-id header id))
3727
3728      ;; The last case ignores an existing entry, except it adds any
3729      ;; additional Xrefs (in case the two articles came from different
3730      ;; servers.
3731      ;; Also sets `header' to `nil' meaning that the `dependencies'
3732      ;; table was *not* modified.
3733      (t
3734       (mail-header-set-xref
3735        (car (symbol-value id-dep))
3736        (concat (or (mail-header-xref (car (symbol-value id-dep)))
3737                    "")
3738                (or (mail-header-xref header) "")))
3739       (setq header nil)))
3740
3741     (when (and header (not replaced))
3742       ;; First check that we are not creating a References loop.
3743       (setq parent-id (gnus-parent-id (mail-header-references header)))
3744       (setq ref parent-id)
3745       (while (and ref
3746                   (setq ref-dep (intern-soft ref dependencies))
3747                   (boundp ref-dep)
3748                   (setq ref-header (car (symbol-value ref-dep))))
3749         (if (string= id ref)
3750             ;; Yuk!  This is a reference loop.  Make the article be a
3751             ;; root article.
3752             (progn
3753               (mail-header-set-references (car (symbol-value id-dep)) "none")
3754               (setq ref nil)
3755               (setq parent-id nil))
3756           (setq ref (gnus-parent-id (mail-header-references ref-header)))))
3757       (setq ref-dep (intern (or parent-id "none") dependencies))
3758       (if (boundp ref-dep)
3759           (setcdr (symbol-value ref-dep)
3760                   (nconc (cdr (symbol-value ref-dep))
3761                          (list (symbol-value id-dep))))
3762         (set ref-dep (list nil (symbol-value id-dep)))))
3763     header))
3764
3765 (defun gnus-extract-message-id-from-in-reply-to (string)
3766   (if (string-match "<[^>]+>" string)
3767       (substring string (match-beginning 0) (match-end 0))
3768     nil))
3769
3770 (defun gnus-build-sparse-threads ()
3771   (let ((headers gnus-newsgroup-headers)
3772         (mail-parse-charset gnus-newsgroup-charset)
3773         (gnus-summary-ignore-duplicates t)
3774         header references generation relations
3775         subject child end new-child date)
3776     ;; First we create an alist of generations/relations, where
3777     ;; generations is how much we trust the relation, and the relation
3778     ;; is parent/child.
3779     (gnus-message 7 "Making sparse threads...")
3780     (save-excursion
3781       (nnheader-set-temp-buffer " *gnus sparse threads*")
3782       (while (setq header (pop headers))
3783         (when (and (setq references (mail-header-references header))
3784                    (not (string= references "")))
3785           (insert references)
3786           (setq child (mail-header-id header)
3787                 subject (mail-header-subject header)
3788                 date (mail-header-date header)
3789                 generation 0)
3790           (while (search-backward ">" nil t)
3791             (setq end (1+ (point)))
3792             (when (search-backward "<" nil t)
3793               (setq new-child (buffer-substring (point) end))
3794               (push (list (incf generation)
3795                           child (setq child new-child)
3796                           subject date)
3797                     relations)))
3798           (when child
3799             (push (list (1+ generation) child nil subject) relations))
3800           (erase-buffer)))
3801       (kill-buffer (current-buffer)))
3802     ;; Sort over trustworthiness.
3803     (mapcar
3804      (lambda (relation)
3805        (when (gnus-dependencies-add-header
3806               (make-full-mail-header
3807                gnus-reffed-article-number
3808                (nth 3 relation) "" (or (nth 4 relation) "")
3809                (nth 1 relation)
3810                (or (nth 2 relation) "") 0 0 "")
3811               gnus-newsgroup-dependencies nil)
3812          (push gnus-reffed-article-number gnus-newsgroup-limit)
3813          (push gnus-reffed-article-number gnus-newsgroup-sparse)
3814          (push (cons gnus-reffed-article-number gnus-sparse-mark)
3815                gnus-newsgroup-reads)
3816          (decf gnus-reffed-article-number)))
3817      (sort relations 'car-less-than-car))
3818     (gnus-message 7 "Making sparse threads...done")))
3819
3820 (defun gnus-build-old-threads ()
3821   ;; Look at all the articles that refer back to old articles, and
3822   ;; fetch the headers for the articles that aren't there.  This will
3823   ;; build complete threads - if the roots haven't been expired by the
3824   ;; server, that is.
3825   (let ((mail-parse-charset gnus-newsgroup-charset)
3826         id heads)
3827     (mapatoms
3828      (lambda (refs)
3829        (when (not (car (symbol-value refs)))
3830          (setq heads (cdr (symbol-value refs)))
3831          (while heads
3832            (if (memq (mail-header-number (caar heads))
3833                      gnus-newsgroup-dormant)
3834                (setq heads (cdr heads))
3835              (setq id (symbol-name refs))
3836              (while (and (setq id (gnus-build-get-header id))
3837                          (not (car (gnus-id-to-thread id)))))
3838              (setq heads nil)))))
3839      gnus-newsgroup-dependencies)))
3840
3841 ;; This function has to be called with point after the article number
3842 ;; on the beginning of the line.
3843 (defsubst gnus-nov-parse-line (number dependencies &optional force-new)
3844   (let ((eol (gnus-point-at-eol))
3845         (buffer (current-buffer))
3846         header references in-reply-to)
3847
3848     ;; overview: [num subject from date id refs chars lines misc]
3849     (unwind-protect
3850         (let (x)
3851           (narrow-to-region (point) eol)
3852           (unless (eobp)
3853             (forward-char))
3854
3855           (setq header
3856                 (make-full-mail-header
3857                  number                 ; number
3858                  (condition-case ()     ; subject
3859                      (funcall gnus-decode-encoded-word-function
3860                               (setq x (nnheader-nov-field)))
3861                    (error x))
3862                  (condition-case ()     ; from
3863                      (funcall gnus-decode-encoded-word-function
3864                               (setq x (nnheader-nov-field)))
3865                    (error x))
3866                  (nnheader-nov-field)   ; date
3867                  (nnheader-nov-read-message-id) ; id
3868                  (setq references (nnheader-nov-field)) ; refs
3869                  (nnheader-nov-read-integer) ; chars
3870                  (nnheader-nov-read-integer) ; lines
3871                  (unless (eobp)
3872                    (if (looking-at "Xref: ")
3873                        (goto-char (match-end 0)))
3874                    (nnheader-nov-field)) ; Xref
3875                  (nnheader-nov-parse-extra)))) ; extra
3876
3877       (widen))
3878
3879     (when (and (string= references "")
3880                (setq in-reply-to (mail-header-extra header))
3881                (setq in-reply-to (cdr (assq 'In-Reply-To in-reply-to))))
3882       (mail-header-set-references
3883        header (gnus-extract-message-id-from-in-reply-to in-reply-to)))
3884
3885     (when gnus-alter-header-function
3886       (funcall gnus-alter-header-function header))
3887     (gnus-dependencies-add-header header dependencies force-new)))
3888
3889 (defun gnus-build-get-header (id)
3890   "Look through the buffer of NOV lines and find the header to ID.
3891 Enter this line into the dependencies hash table, and return
3892 the id of the parent article (if any)."
3893   (let ((deps gnus-newsgroup-dependencies)
3894         found header)
3895     (prog1
3896         (save-excursion
3897           (set-buffer nntp-server-buffer)
3898           (let ((case-fold-search nil))
3899             (goto-char (point-min))
3900             (while (and (not found)
3901                         (search-forward id nil t))
3902               (beginning-of-line)
3903               (setq found (looking-at
3904                            (format "^[^\t]*\t[^\t]*\t[^\t]*\t[^\t]*\t%s"
3905                                    (regexp-quote id))))
3906               (or found (beginning-of-line 2)))
3907             (when found
3908               (beginning-of-line)
3909               (and
3910                (setq header (gnus-nov-parse-line
3911                              (read (current-buffer)) deps))
3912                (gnus-parent-id (mail-header-references header))))))
3913       (when header
3914         (let ((number (mail-header-number header)))
3915           (push number gnus-newsgroup-limit)
3916           (push header gnus-newsgroup-headers)
3917           (if (memq number gnus-newsgroup-unselected)
3918               (progn
3919                 (setq gnus-newsgroup-unreads
3920                       (gnus-add-to-sorted-list gnus-newsgroup-unreads
3921                                                number))
3922                 (setq gnus-newsgroup-unselected
3923                       (delq number gnus-newsgroup-unselected)))
3924             (push number gnus-newsgroup-ancient)))))))
3925
3926 (defun gnus-build-all-threads ()
3927   "Read all the headers."
3928   (let ((gnus-summary-ignore-duplicates t)
3929         (mail-parse-charset gnus-newsgroup-charset)
3930         (dependencies gnus-newsgroup-dependencies)
3931         header article)
3932     (save-excursion
3933       (set-buffer nntp-server-buffer)
3934       (let ((case-fold-search nil))
3935         (goto-char (point-min))
3936         (while (not (eobp))
3937           (ignore-errors
3938             (setq article (read (current-buffer))
3939                   header (gnus-nov-parse-line article dependencies)))
3940           (when header
3941             (save-excursion
3942               (set-buffer gnus-summary-buffer)
3943               (push header gnus-newsgroup-headers)
3944               (if (memq (setq article (mail-header-number header))
3945                         gnus-newsgroup-unselected)
3946                   (progn
3947                     (setq gnus-newsgroup-unreads
3948                           (gnus-add-to-sorted-list
3949                            gnus-newsgroup-unreads article))
3950                     (setq gnus-newsgroup-unselected
3951                           (delq article gnus-newsgroup-unselected)))
3952                 (push article gnus-newsgroup-ancient)))
3953             (forward-line 1)))))))
3954
3955 (defun gnus-summary-update-article-line (article header)
3956   "Update the line for ARTICLE using HEADERS."
3957   (let* ((id (mail-header-id header))
3958          (thread (gnus-id-to-thread id)))
3959     (unless thread
3960       (error "Article in no thread"))
3961     ;; Update the thread.
3962     (setcar thread header)
3963     (gnus-summary-goto-subject article)
3964     (let* ((datal (gnus-data-find-list article))
3965            (data (car datal))
3966            (length (when (cdr datal)
3967                      (- (gnus-data-pos data)
3968                         (gnus-data-pos (cadr datal)))))
3969            (buffer-read-only nil)
3970            (level (gnus-summary-thread-level)))
3971       (gnus-delete-line)
3972       (gnus-summary-insert-line
3973        header level nil (gnus-article-mark article)
3974        (memq article gnus-newsgroup-replied)
3975        (memq article gnus-newsgroup-expirable)
3976        ;; Only insert the Subject string when it's different
3977        ;; from the previous Subject string.
3978        (if (and
3979             gnus-show-threads
3980             (gnus-subject-equal
3981              (condition-case ()
3982                  (mail-header-subject
3983                   (gnus-data-header
3984                    (cadr
3985                     (gnus-data-find-list
3986                      article
3987                      (gnus-data-list t)))))
3988                ;; Error on the side of excessive subjects.
3989                (error ""))
3990              (mail-header-subject header)))
3991            ""
3992          (mail-header-subject header))
3993        nil (cdr (assq article gnus-newsgroup-scored))
3994        (memq article gnus-newsgroup-processable))
3995       (when length
3996         (gnus-data-update-list
3997          (cdr datal) (- length (- (gnus-data-pos data) (point))))))))
3998
3999 (defun gnus-summary-update-article (article &optional iheader)
4000   "Update ARTICLE in the summary buffer."
4001   (set-buffer gnus-summary-buffer)
4002   (let* ((header (gnus-summary-article-header article))
4003          (id (mail-header-id header))
4004          (data (gnus-data-find article))
4005          (thread (gnus-id-to-thread id))
4006          (references (mail-header-references header))
4007          (parent
4008           (gnus-id-to-thread
4009            (or (gnus-parent-id
4010                 (when (and references
4011                            (not (equal "" references)))
4012                   references))
4013                "none")))
4014          (buffer-read-only nil)
4015          (old (car thread)))
4016     (when thread
4017       (unless iheader
4018         (setcar thread nil)
4019         (when parent
4020           (delq thread parent)))
4021       (if (gnus-summary-insert-subject id header)
4022        ;; Set the (possibly) new article number in the data structure.
4023           (gnus-data-set-number data (gnus-id-to-article id))
4024         (setcar thread old)
4025         nil))))
4026
4027 (defun gnus-rebuild-thread (id &optional line)
4028   "Rebuild the thread containing ID.
4029 If LINE, insert the rebuilt thread starting on line LINE."
4030   (let ((buffer-read-only nil)
4031         old-pos current thread data)
4032     (if (not gnus-show-threads)
4033         (setq thread (list (car (gnus-id-to-thread id))))
4034       ;; Get the thread this article is part of.
4035       (setq thread (gnus-remove-thread id)))
4036     (setq old-pos (gnus-point-at-bol))
4037     (setq current (save-excursion
4038                     (and (re-search-backward "[\r\n]" nil t)
4039                          (gnus-summary-article-number))))
4040     ;; If this is a gathered thread, we have to go some re-gathering.
4041     (when (stringp (car thread))
4042       (let ((subject (car thread))
4043             roots thr)
4044         (setq thread (cdr thread))
4045         (while thread
4046           (unless (memq (setq thr (gnus-id-to-thread
4047                                    (gnus-root-id
4048                                     (mail-header-id (caar thread)))))
4049                         roots)
4050             (push thr roots))
4051           (setq thread (cdr thread)))
4052         ;; We now have all (unique) roots.
4053         (if (= (length roots) 1)
4054             ;; All the loose roots are now one solid root.
4055             (setq thread (car roots))
4056           (setq thread (cons subject (gnus-sort-threads roots))))))
4057     (let (threads)
4058       ;; We then insert this thread into the summary buffer.
4059       (when line
4060         (goto-char (point-min))
4061         (forward-line (1- line)))
4062       (let (gnus-newsgroup-data gnus-newsgroup-threads)
4063         (if gnus-show-threads
4064             (gnus-summary-prepare-threads (gnus-cut-threads (list thread)))
4065           (gnus-summary-prepare-unthreaded thread))
4066         (setq data (nreverse gnus-newsgroup-data))
4067         (setq threads gnus-newsgroup-threads))
4068       ;; We splice the new data into the data structure.
4069       ;;!!! This is kinda bogus.  We assume that in LINE is non-nil,
4070       ;;!!! then we want to insert at the beginning of the buffer.
4071       ;;!!! That happens to be true with Gnus now, but that may
4072       ;;!!! change in the future.  Perhaps.
4073       (gnus-data-enter-list
4074        (if line nil current) data (- (point) old-pos))
4075       (setq gnus-newsgroup-threads
4076             (nconc threads gnus-newsgroup-threads))
4077       (gnus-data-compute-positions))))
4078
4079 (defun gnus-number-to-header (number)
4080   "Return the header for article NUMBER."
4081   (let ((headers gnus-newsgroup-headers))
4082     (while (and headers
4083                 (not (= number (mail-header-number (car headers)))))
4084       (pop headers))
4085     (when headers
4086       (car headers))))
4087
4088 (defun gnus-parent-headers (in-headers &optional generation)
4089   "Return the headers of the GENERATIONeth parent of HEADERS."
4090   (unless generation
4091     (setq generation 1))
4092   (let ((parent t)
4093         (headers in-headers)
4094         references)
4095     (while (and parent
4096                 (not (zerop generation))
4097                 (setq references (mail-header-references headers)))
4098       (setq headers (if (and references
4099                              (setq parent (gnus-parent-id references)))
4100                         (car (gnus-id-to-thread parent))
4101                       nil))
4102       (decf generation))
4103     (and (not (eq headers in-headers))
4104          headers)))
4105
4106 (defun gnus-id-to-thread (id)
4107   "Return the (sub-)thread where ID appears."
4108   (gnus-gethash id gnus-newsgroup-dependencies))
4109
4110 (defun gnus-id-to-article (id)
4111   "Return the article number of ID."
4112   (let ((thread (gnus-id-to-thread id)))
4113     (when (and thread
4114                (car thread))
4115       (mail-header-number (car thread)))))
4116
4117 (defun gnus-id-to-header (id)
4118   "Return the article headers of ID."
4119   (car (gnus-id-to-thread id)))
4120
4121 (defun gnus-article-displayed-root-p (article)
4122   "Say whether ARTICLE is a root(ish) article."
4123   (let ((level (gnus-summary-thread-level article))
4124         (refs (mail-header-references  (gnus-summary-article-header article)))
4125         particle)
4126     (cond
4127      ((null level) nil)
4128      ((zerop level) t)
4129      ((null refs) t)
4130      ((null (gnus-parent-id refs)) t)
4131      ((and (= 1 level)
4132            (null (setq particle (gnus-id-to-article
4133                                  (gnus-parent-id refs))))
4134            (null (gnus-summary-thread-level particle)))))))
4135
4136 (defun gnus-root-id (id)
4137   "Return the id of the root of the thread where ID appears."
4138   (let (last-id prev)
4139     (while (and id (setq prev (car (gnus-id-to-thread id))))
4140       (setq last-id id
4141             id (gnus-parent-id (mail-header-references prev))))
4142     last-id))
4143
4144 (defun gnus-articles-in-thread (thread)
4145   "Return the list of articles in THREAD."
4146   (cons (mail-header-number (car thread))
4147         (apply 'nconc (mapcar 'gnus-articles-in-thread (cdr thread)))))
4148
4149 (defun gnus-remove-thread (id &optional dont-remove)
4150   "Remove the thread that has ID in it."
4151   (let (headers thread last-id)
4152     ;; First go up in this thread until we find the root.
4153     (setq last-id (gnus-root-id id)
4154           headers (message-flatten-list (gnus-id-to-thread last-id)))
4155     ;; We have now found the real root of this thread.  It might have
4156     ;; been gathered into some loose thread, so we have to search
4157     ;; through the threads to find the thread we wanted.
4158     (let ((threads gnus-newsgroup-threads)
4159           sub)
4160       (while threads
4161         (setq sub (car threads))
4162         (if (stringp (car sub))
4163             ;; This is a gathered thread, so we look at the roots
4164             ;; below it to find whether this article is in this
4165             ;; gathered root.
4166             (progn
4167               (setq sub (cdr sub))
4168               (while sub
4169                 (when (member (caar sub) headers)
4170                   (setq thread (car threads)
4171                         threads nil
4172                         sub nil))
4173                 (setq sub (cdr sub))))
4174           ;; It's an ordinary thread, so we check it.
4175           (when (eq (car sub) (car headers))
4176             (setq thread sub
4177                   threads nil)))
4178         (setq threads (cdr threads)))
4179       ;; If this article is in no thread, then it's a root.
4180       (if thread
4181           (unless dont-remove
4182             (setq gnus-newsgroup-threads (delq thread gnus-newsgroup-threads)))
4183         (setq thread (gnus-id-to-thread last-id)))
4184       (when thread
4185         (prog1
4186             thread                      ; We return this thread.
4187           (unless dont-remove
4188             (if (stringp (car thread))
4189                 (progn
4190                   ;; If we use dummy roots, then we have to remove the
4191                   ;; dummy root as well.
4192                   (when (eq gnus-summary-make-false-root 'dummy)
4193                     ;; We go to the dummy root by going to
4194                     ;; the first sub-"thread", and then one line up.
4195                     (gnus-summary-goto-article
4196                      (mail-header-number (caadr thread)))
4197                     (forward-line -1)
4198                     (gnus-delete-line)
4199                     (gnus-data-compute-positions))
4200                   (setq thread (cdr thread))
4201                   (while thread
4202                     (gnus-remove-thread-1 (car thread))
4203                     (setq thread (cdr thread))))
4204               (gnus-remove-thread-1 thread))))))))
4205
4206 (defun gnus-remove-thread-1 (thread)
4207   "Remove the thread THREAD recursively."
4208   (let ((number (mail-header-number (pop thread)))
4209         d)
4210     (setq thread (reverse thread))
4211     (while thread
4212       (gnus-remove-thread-1 (pop thread)))
4213     (when (setq d (gnus-data-find number))
4214       (goto-char (gnus-data-pos d))
4215       (gnus-summary-show-thread)
4216       (gnus-data-remove
4217        number
4218        (- (gnus-point-at-bol)
4219           (prog1
4220               (1+ (gnus-point-at-eol))
4221             (gnus-delete-line)))))))
4222
4223 (defun gnus-sort-threads-1 (threads func)
4224   (sort (mapcar (lambda (thread)
4225                   (cons (car thread)
4226                         (and (cdr thread)
4227                              (gnus-sort-threads-1 (cdr thread) func))))
4228                 threads) func))
4229
4230 (defun gnus-sort-threads (threads)
4231   "Sort THREADS."
4232   (if (not gnus-thread-sort-functions)
4233       threads
4234     (gnus-message 8 "Sorting threads...")
4235     (prog1
4236         (gnus-sort-threads-1
4237          threads
4238          (gnus-make-sort-function gnus-thread-sort-functions))
4239       (gnus-message 8 "Sorting threads...done"))))
4240
4241 (defun gnus-sort-articles (articles)
4242   "Sort ARTICLES."
4243   (when gnus-article-sort-functions
4244     (gnus-message 7 "Sorting articles...")
4245     (prog1
4246         (setq gnus-newsgroup-headers
4247               (sort articles (gnus-make-sort-function
4248                               gnus-article-sort-functions)))
4249       (gnus-message 7 "Sorting articles...done"))))
4250
4251 ;; Written by Hallvard B Furuseth <h.b.furuseth@usit.uio.no>.
4252 (defmacro gnus-thread-header (thread)
4253   "Return header of first article in THREAD.
4254 Note that THREAD must never, ever be anything else than a variable -
4255 using some other form will lead to serious barfage."
4256   (or (symbolp thread) (signal 'wrong-type-argument '(symbolp thread)))
4257   ;; (8% speedup to gnus-summary-prepare, just for fun :-)
4258   (list 'byte-code "\10\211:\203\17\0\211@;\203\16\0A@@\207"
4259         (vector thread) 2))
4260
4261 (defsubst gnus-article-sort-by-number (h1 h2)
4262   "Sort articles by article number."
4263   (< (mail-header-number h1)
4264      (mail-header-number h2)))
4265
4266 (defun gnus-thread-sort-by-number (h1 h2)
4267   "Sort threads by root article number."
4268   (gnus-article-sort-by-number
4269    (gnus-thread-header h1) (gnus-thread-header h2)))
4270
4271 (defsubst gnus-article-sort-by-random (h1 h2)
4272   "Sort articles by article number."
4273   (zerop (random 2)))
4274
4275 (defun gnus-thread-sort-by-random (h1 h2)
4276   "Sort threads by root article number."
4277   (gnus-article-sort-by-random
4278    (gnus-thread-header h1) (gnus-thread-header h2)))
4279
4280 (defsubst gnus-article-sort-by-lines (h1 h2)
4281   "Sort articles by article Lines header."
4282   (< (mail-header-lines h1)
4283      (mail-header-lines h2)))
4284
4285 (defun gnus-thread-sort-by-lines (h1 h2)
4286   "Sort threads by root article Lines header."
4287   (gnus-article-sort-by-lines
4288    (gnus-thread-header h1) (gnus-thread-header h2)))
4289
4290 (defsubst gnus-article-sort-by-chars (h1 h2)
4291   "Sort articles by octet length."
4292   (< (mail-header-chars h1)
4293      (mail-header-chars h2)))
4294
4295 (defun gnus-thread-sort-by-chars (h1 h2)
4296   "Sort threads by root article octet length."
4297   (gnus-article-sort-by-chars
4298    (gnus-thread-header h1) (gnus-thread-header h2)))
4299
4300 (defsubst gnus-article-sort-by-author (h1 h2)
4301   "Sort articles by root author."
4302   (string-lessp
4303    (let ((extract (funcall
4304                    gnus-extract-address-components
4305                    (mail-header-from h1))))
4306      (or (car extract) (cadr extract) ""))
4307    (let ((extract (funcall
4308                    gnus-extract-address-components
4309                    (mail-header-from h2))))
4310      (or (car extract) (cadr extract) ""))))
4311
4312 (defun gnus-thread-sort-by-author (h1 h2)
4313   "Sort threads by root author."
4314   (gnus-article-sort-by-author
4315    (gnus-thread-header h1)  (gnus-thread-header h2)))
4316
4317 (defsubst gnus-article-sort-by-subject (h1 h2)
4318   "Sort articles by root subject."
4319   (string-lessp
4320    (downcase (gnus-simplify-subject-re (mail-header-subject h1)))
4321    (downcase (gnus-simplify-subject-re (mail-header-subject h2)))))
4322
4323 (defun gnus-thread-sort-by-subject (h1 h2)
4324   "Sort threads by root subject."
4325   (gnus-article-sort-by-subject
4326    (gnus-thread-header h1) (gnus-thread-header h2)))
4327
4328 (defsubst gnus-article-sort-by-date (h1 h2)
4329   "Sort articles by root article date."
4330   (time-less-p
4331    (gnus-date-get-time (mail-header-date h1))
4332    (gnus-date-get-time (mail-header-date h2))))
4333
4334 (defun gnus-thread-sort-by-date (h1 h2)
4335   "Sort threads by root article date."
4336   (gnus-article-sort-by-date
4337    (gnus-thread-header h1) (gnus-thread-header h2)))
4338
4339 (defsubst gnus-article-sort-by-score (h1 h2)
4340   "Sort articles by root article score.
4341 Unscored articles will be counted as having a score of zero."
4342   (> (or (cdr (assq (mail-header-number h1)
4343                     gnus-newsgroup-scored))
4344          gnus-summary-default-score 0)
4345      (or (cdr (assq (mail-header-number h2)
4346                     gnus-newsgroup-scored))
4347          gnus-summary-default-score 0)))
4348
4349 (defun gnus-thread-sort-by-score (h1 h2)
4350   "Sort threads by root article score."
4351   (gnus-article-sort-by-score
4352    (gnus-thread-header h1) (gnus-thread-header h2)))
4353
4354 (defun gnus-thread-sort-by-total-score (h1 h2)
4355   "Sort threads by the sum of all scores in the thread.
4356 Unscored articles will be counted as having a score of zero."
4357   (> (gnus-thread-total-score h1) (gnus-thread-total-score h2)))
4358
4359 (defun gnus-thread-total-score (thread)
4360   ;; This function find the total score of THREAD.
4361   (cond
4362    ((null thread)
4363     0)
4364    ((consp thread)
4365     (if (stringp (car thread))
4366         (apply gnus-thread-score-function 0
4367                (mapcar 'gnus-thread-total-score-1 (cdr thread)))
4368       (gnus-thread-total-score-1 thread)))
4369    (t
4370     (gnus-thread-total-score-1 (list thread)))))
4371
4372 (defun gnus-thread-sort-by-most-recent-number (h1 h2)
4373   "Sort threads such that the thread with the most recently arrived article comes first."
4374   (> (gnus-thread-highest-number h1) (gnus-thread-highest-number h2)))
4375
4376 (defun gnus-thread-highest-number (thread)
4377   "Return the highest article number in THREAD."
4378   (apply 'max (mapcar (lambda (header)
4379                         (mail-header-number header))
4380                       (message-flatten-list thread))))
4381
4382 (defun gnus-thread-sort-by-most-recent-date (h1 h2)
4383   "Sort threads such that the thread with the most recently dated article comes first."
4384   (> (gnus-thread-latest-date h1) (gnus-thread-latest-date h2)))
4385
4386 (defun gnus-thread-latest-date (thread)
4387   "Return the highest article date in THREAD."
4388   (let ((previous-time 0))
4389     (apply 'max (mapcar
4390                  (lambda (header)
4391                    (setq previous-time
4392                          (time-to-seconds
4393                           (mail-header-parse-date
4394                            (condition-case ()
4395                                (mail-header-date header)
4396                              (error previous-time))))))
4397                  (sort
4398                   (message-flatten-list thread)
4399                   (lambda (h1 h2)
4400                     (< (mail-header-number h1)
4401                        (mail-header-number h2))))))))
4402
4403 (defun gnus-thread-total-score-1 (root)
4404   ;; This function find the total score of the thread below ROOT.
4405   (setq root (car root))
4406   (apply gnus-thread-score-function
4407          (or (append
4408               (mapcar 'gnus-thread-total-score
4409                       (cdr (gnus-id-to-thread (mail-header-id root))))
4410               (when (> (mail-header-number root) 0)
4411                 (list (or (cdr (assq (mail-header-number root)
4412                                      gnus-newsgroup-scored))
4413                           gnus-summary-default-score 0))))
4414              (list gnus-summary-default-score)
4415              '(0))))
4416
4417 ;; Added by Per Abrahamsen <amanda@iesd.auc.dk>.
4418 (defvar gnus-tmp-prev-subject nil)
4419 (defvar gnus-tmp-false-parent nil)
4420 (defvar gnus-tmp-root-expunged nil)
4421 (defvar gnus-tmp-dummy-line nil)
4422
4423 (eval-when-compile (defvar gnus-tmp-header))
4424 (defun gnus-extra-header (type &optional header)
4425   "Return the extra header of TYPE."
4426   (or (cdr (assq type (mail-header-extra (or header gnus-tmp-header))))
4427       ""))
4428
4429 (defvar gnus-tmp-thread-tree-header-string "")
4430
4431 (defcustom gnus-sum-thread-tree-root "> "
4432   "With %B spec, used for the root of a thread.
4433 If nil, use subject instead."
4434   :type 'string
4435   :group 'gnus-thread)
4436 (defcustom gnus-sum-thread-tree-single-indent ""
4437   "With %B spec, used for a thread with just one message.
4438 If nil, use subject instead."
4439   :type 'string
4440   :group 'gnus-thread)
4441 (defcustom gnus-sum-thread-tree-vertical "| "
4442   "With %B spec, used for drawing a vertical line."
4443   :type 'string
4444   :group 'gnus-thread)
4445 (defcustom gnus-sum-thread-tree-indent "  "
4446   "With %B spec, used for indenting."
4447   :type 'string
4448   :group 'gnus-thread)
4449 (defcustom gnus-sum-thread-tree-leaf-with-other "+-> "
4450   "With %B spec, used for a leaf with brothers."
4451   :type 'string
4452   :group 'gnus-thread)
4453 (defcustom gnus-sum-thread-tree-single-leaf "\\-> "
4454   "With %B spec, used for a leaf without brothers."
4455   :type 'string
4456   :group 'gnus-thread)
4457
4458 (defun gnus-summary-prepare-threads (threads)
4459   "Prepare summary buffer from THREADS and indentation LEVEL.
4460 THREADS is either a list of `(PARENT [(CHILD1 [(GRANDCHILD ...]...) ...])'
4461 or a straight list of headers."
4462   (gnus-message 7 "Generating summary...")
4463
4464   (setq gnus-newsgroup-threads threads)
4465   (beginning-of-line)
4466
4467   (let ((gnus-tmp-level 0)
4468         (default-score (or gnus-summary-default-score 0))
4469         (gnus-visual-p (gnus-visual-p 'summary-highlight 'highlight))
4470         thread number subject stack state gnus-tmp-gathered beg-match
4471         new-roots gnus-tmp-new-adopts thread-end simp-subject
4472         gnus-tmp-header gnus-tmp-unread
4473         gnus-tmp-replied gnus-tmp-subject-or-nil
4474         gnus-tmp-dummy gnus-tmp-indentation gnus-tmp-lines gnus-tmp-score
4475         gnus-tmp-score-char gnus-tmp-from gnus-tmp-name
4476         gnus-tmp-number gnus-tmp-opening-bracket gnus-tmp-closing-bracket
4477         tree-stack)
4478
4479     (setq gnus-tmp-prev-subject nil)
4480
4481     (if (vectorp (car threads))
4482         ;; If this is a straight (sic) list of headers, then a
4483         ;; threaded summary display isn't required, so we just create
4484         ;; an unthreaded one.
4485         (gnus-summary-prepare-unthreaded threads)
4486
4487       ;; Do the threaded display.
4488
4489       (while (or threads stack gnus-tmp-new-adopts new-roots)
4490
4491         (if (and (= gnus-tmp-level 0)
4492                  (or (not stack)
4493                      (= (caar stack) 0))
4494                  (not gnus-tmp-false-parent)
4495                  (or gnus-tmp-new-adopts new-roots))
4496             (if gnus-tmp-new-adopts
4497                 (setq gnus-tmp-level (if gnus-tmp-root-expunged 0 1)
4498                       thread (list (car gnus-tmp-new-adopts))
4499                       gnus-tmp-header (caar thread)
4500                       gnus-tmp-new-adopts (cdr gnus-tmp-new-adopts))
4501               (when new-roots
4502                 (setq thread (list (car new-roots))
4503                       gnus-tmp-header (caar thread)
4504                       new-roots (cdr new-roots))))
4505
4506           (if threads
4507               ;; If there are some threads, we do them before the
4508               ;; threads on the stack.
4509               (setq thread threads
4510                     gnus-tmp-header (caar thread))
4511             ;; There were no current threads, so we pop something off
4512             ;; the stack.
4513             (setq state (car stack)
4514                   gnus-tmp-level (car state)
4515                   tree-stack (cadr state)
4516                   thread (caddr state)
4517                   stack (cdr stack)
4518                   gnus-tmp-header (caar thread))))
4519
4520         (setq gnus-tmp-false-parent nil)
4521         (setq gnus-tmp-root-expunged nil)
4522         (setq thread-end nil)
4523
4524         (if (stringp gnus-tmp-header)
4525             ;; The header is a dummy root.
4526             (cond
4527              ((eq gnus-summary-make-false-root 'adopt)
4528               ;; We let the first article adopt the rest.
4529               (setq gnus-tmp-new-adopts (nconc gnus-tmp-new-adopts
4530                                                (cddar thread)))
4531               (setq gnus-tmp-gathered
4532                     (nconc (mapcar
4533                             (lambda (h) (mail-header-number (car h)))
4534                             (cddar thread))
4535                            gnus-tmp-gathered))
4536               (setq thread (cons (list (caar thread)
4537                                        (cadar thread))
4538                                  (cdr thread)))
4539               (setq gnus-tmp-level -1
4540                     gnus-tmp-false-parent t))
4541              ((eq gnus-summary-make-false-root 'empty)
4542               ;; We print adopted articles with empty subject fields.
4543               (setq gnus-tmp-gathered
4544                     (nconc (mapcar
4545                             (lambda (h) (mail-header-number (car h)))
4546                             (cddar thread))
4547                            gnus-tmp-gathered))
4548               (setq gnus-tmp-level -1))
4549              ((eq gnus-summary-make-false-root 'dummy)
4550               ;; We remember that we probably want to output a dummy
4551               ;; root.
4552               (setq gnus-tmp-dummy-line gnus-tmp-header)
4553               (setq gnus-tmp-prev-subject gnus-tmp-header))
4554              (t
4555               ;; We do not make a root for the gathered
4556               ;; sub-threads at all.
4557               (setq gnus-tmp-level -1)))
4558
4559           (setq number (mail-header-number gnus-tmp-header)
4560                 subject (mail-header-subject gnus-tmp-header)
4561                 simp-subject (gnus-simplify-subject-fully subject))
4562
4563           (cond
4564            ;; If the thread has changed subject, we might want to make
4565            ;; this subthread into a root.
4566            ((and (null gnus-thread-ignore-subject)
4567                  (not (zerop gnus-tmp-level))
4568                  gnus-tmp-prev-subject
4569                  (not (string= gnus-tmp-prev-subject simp-subject)))
4570             (setq new-roots (nconc new-roots (list (car thread)))
4571                   thread-end t
4572                   gnus-tmp-header nil))
4573            ;; If the article lies outside the current limit,
4574            ;; then we do not display it.
4575            ((not (memq number gnus-newsgroup-limit))
4576             (setq gnus-tmp-gathered
4577                   (nconc (mapcar
4578                           (lambda (h) (mail-header-number (car h)))
4579                           (cdar thread))
4580                          gnus-tmp-gathered))
4581             (setq gnus-tmp-new-adopts (if (cdar thread)
4582                                           (append gnus-tmp-new-adopts
4583                                                   (cdar thread))
4584                                         gnus-tmp-new-adopts)
4585                   thread-end t
4586                   gnus-tmp-header nil)
4587             (when (zerop gnus-tmp-level)
4588               (setq gnus-tmp-root-expunged t)))
4589            ;; Perhaps this article is to be marked as read?
4590            ((and gnus-summary-mark-below
4591                  (< (or (cdr (assq number gnus-newsgroup-scored))
4592                         default-score)
4593                     gnus-summary-mark-below)
4594                  ;; Don't touch sparse articles.
4595                  (not (gnus-summary-article-sparse-p number))
4596                  (not (gnus-summary-article-ancient-p number)))
4597             (setq gnus-newsgroup-unreads
4598                   (delq number gnus-newsgroup-unreads))
4599             (if gnus-newsgroup-auto-expire
4600                 (setq gnus-newsgroup-expirable
4601                       (gnus-add-to-sorted-list
4602                        gnus-newsgroup-expirable number))
4603               (push (cons number gnus-low-score-mark)
4604                     gnus-newsgroup-reads))))
4605
4606           (when gnus-tmp-header
4607             ;; We may have an old dummy line to output before this
4608             ;; article.
4609             (when (and gnus-tmp-dummy-line
4610                        (gnus-subject-equal
4611                         gnus-tmp-dummy-line
4612                         (mail-header-subject gnus-tmp-header)))
4613               (gnus-summary-insert-dummy-line
4614                gnus-tmp-dummy-line (mail-header-number gnus-tmp-header))
4615               (setq gnus-tmp-dummy-line nil))
4616
4617             ;; Compute the mark.
4618             (setq gnus-tmp-unread (gnus-article-mark number))
4619
4620             (push (gnus-data-make number gnus-tmp-unread (1+ (point))
4621                                   gnus-tmp-header gnus-tmp-level)
4622                   gnus-newsgroup-data)
4623
4624             ;; Actually insert the line.
4625             (setq
4626              gnus-tmp-subject-or-nil
4627              (cond
4628               ((and gnus-thread-ignore-subject
4629                     gnus-tmp-prev-subject
4630                     (not (string= gnus-tmp-prev-subject simp-subject)))
4631                subject)
4632               ((zerop gnus-tmp-level)
4633                (if (and (eq gnus-summary-make-false-root 'empty)
4634                         (memq number gnus-tmp-gathered)
4635                         gnus-tmp-prev-subject
4636                         (string= gnus-tmp-prev-subject simp-subject))
4637                    gnus-summary-same-subject
4638                  subject))
4639               (t gnus-summary-same-subject)))
4640             (if (and (eq gnus-summary-make-false-root 'adopt)
4641                      (= gnus-tmp-level 1)
4642                      (memq number gnus-tmp-gathered))
4643                 (setq gnus-tmp-opening-bracket ?\<
4644                       gnus-tmp-closing-bracket ?\>)
4645               (setq gnus-tmp-opening-bracket ?\[
4646                     gnus-tmp-closing-bracket ?\]))
4647             (setq
4648              gnus-tmp-indentation
4649              (aref gnus-thread-indent-array gnus-tmp-level)
4650              gnus-tmp-lines (mail-header-lines gnus-tmp-header)
4651              gnus-tmp-score (or (cdr (assq number gnus-newsgroup-scored))
4652                                 gnus-summary-default-score 0)
4653              gnus-tmp-score-char
4654              (if (or (null gnus-summary-default-score)
4655                      (<= (abs (- gnus-tmp-score gnus-summary-default-score))
4656                          gnus-summary-zcore-fuzz))
4657                  ?                      ;Whitespace
4658                (if (< gnus-tmp-score gnus-summary-default-score)
4659                    gnus-score-below-mark gnus-score-over-mark))
4660              gnus-tmp-replied
4661              (cond ((memq number gnus-newsgroup-processable)
4662                     gnus-process-mark)
4663                    ((memq number gnus-newsgroup-cached)
4664                     gnus-cached-mark)
4665                    ((memq number gnus-newsgroup-replied)
4666                     gnus-replied-mark)
4667                    ((memq number gnus-newsgroup-forwarded)
4668                     gnus-forwarded-mark)
4669                    ((memq number gnus-newsgroup-saved)
4670                     gnus-saved-mark)
4671                    ((memq number gnus-newsgroup-recent)
4672                     gnus-recent-mark)
4673                    ((memq number gnus-newsgroup-unseen)
4674                     gnus-unseen-mark)
4675                    (t gnus-no-mark))
4676              gnus-tmp-from (mail-header-from gnus-tmp-header)
4677              gnus-tmp-name
4678              (cond
4679               ((string-match "<[^>]+> *$" gnus-tmp-from)
4680                (setq beg-match (match-beginning 0))
4681                (or (and (string-match "^\".+\"" gnus-tmp-from)
4682                         (substring gnus-tmp-from 1 (1- (match-end 0))))
4683                    (substring gnus-tmp-from 0 beg-match)))
4684               ((string-match "(.+)" gnus-tmp-from)
4685                (substring gnus-tmp-from
4686                           (1+ (match-beginning 0)) (1- (match-end 0))))
4687               (t gnus-tmp-from))
4688              gnus-tmp-thread-tree-header-string
4689              (cond
4690               ((not gnus-show-threads) "")
4691               ((zerop gnus-tmp-level)
4692                (if (cdar thread)
4693                    (or gnus-sum-thread-tree-root subject)
4694                  (or gnus-sum-thread-tree-single-indent subject)))
4695               (t
4696                (concat (apply 'concat
4697                               (mapcar (lambda (item)
4698                                         (if (= item 1)
4699                                             gnus-sum-thread-tree-vertical
4700                                           gnus-sum-thread-tree-indent))
4701                                       (cdr (reverse tree-stack))))
4702                        (if (nth 1 thread)
4703                            gnus-sum-thread-tree-leaf-with-other
4704                          gnus-sum-thread-tree-single-leaf)))))
4705             (when (string= gnus-tmp-name "")
4706               (setq gnus-tmp-name gnus-tmp-from))
4707             (unless (numberp gnus-tmp-lines)
4708               (setq gnus-tmp-lines -1))
4709             (if (= gnus-tmp-lines -1)
4710                 (setq gnus-tmp-lines "?")
4711               (setq gnus-tmp-lines (number-to-string gnus-tmp-lines)))
4712             (gnus-put-text-property
4713              (point)
4714              (progn (eval gnus-summary-line-format-spec) (point))
4715              'gnus-number number)
4716             (when gnus-visual-p
4717               (forward-line -1)
4718               (gnus-run-hooks 'gnus-summary-update-hook)
4719               (forward-line 1))
4720
4721             (setq gnus-tmp-prev-subject simp-subject)))
4722
4723         (when (nth 1 thread)
4724           (push (list (max 0 gnus-tmp-level)
4725                       (copy-list tree-stack)
4726                       (nthcdr 1 thread))
4727                 stack))
4728         (push (if (nth 1 thread) 1 0) tree-stack)
4729         (incf gnus-tmp-level)
4730         (setq threads (if thread-end nil (cdar thread)))
4731         (unless threads
4732           (setq gnus-tmp-level 0)))))
4733   (gnus-message 7 "Generating summary...done"))
4734
4735 (defun gnus-summary-prepare-unthreaded (headers)
4736   "Generate an unthreaded summary buffer based on HEADERS."
4737   (let (header number mark)
4738
4739     (beginning-of-line)
4740
4741     (while headers
4742       ;; We may have to root out some bad articles...
4743       (when (memq (setq number (mail-header-number
4744                                 (setq header (pop headers))))
4745                   gnus-newsgroup-limit)
4746         ;; Mark article as read when it has a low score.
4747         (when (and gnus-summary-mark-below
4748                    (< (or (cdr (assq number gnus-newsgroup-scored))
4749                           gnus-summary-default-score 0)
4750                       gnus-summary-mark-below)
4751                    (not (gnus-summary-article-ancient-p number)))
4752           (setq gnus-newsgroup-unreads
4753                 (delq number gnus-newsgroup-unreads))
4754           (if gnus-newsgroup-auto-expire
4755               (push number gnus-newsgroup-expirable)
4756             (push (cons number gnus-low-score-mark)
4757                   gnus-newsgroup-reads)))
4758
4759         (setq mark (gnus-article-mark number))
4760         (push (gnus-data-make number mark (1+ (point)) header 0)
4761               gnus-newsgroup-data)
4762         (gnus-summary-insert-line
4763          header 0 number
4764          mark (memq number gnus-newsgroup-replied)
4765          (memq number gnus-newsgroup-expirable)
4766          (mail-header-subject header) nil
4767          (cdr (assq number gnus-newsgroup-scored))
4768          (memq number gnus-newsgroup-processable))))))
4769
4770 (defun gnus-summary-remove-list-identifiers ()
4771   "Remove list identifiers in `gnus-list-identifiers' from articles in the current group."
4772   (let ((regexp (if (consp gnus-list-identifiers)
4773                     (mapconcat 'identity gnus-list-identifiers " *\\|")
4774                   gnus-list-identifiers))
4775         changed subject)
4776     (when regexp
4777       (dolist (header gnus-newsgroup-headers)
4778         (setq subject (mail-header-subject header)
4779               changed nil)
4780         (while (string-match
4781                 (concat "^\\(R[Ee]: +\\)*\\(" regexp " *\\)")
4782                 subject)
4783           (setq subject
4784                 (concat (substring subject 0 (match-beginning 2))
4785                         (substring subject (match-end 0)))
4786                 changed t))
4787         (when (and changed
4788                    (string-match
4789                     "^\\(\\(R[Ee]: +\\)+\\)R[Ee]: +" subject))
4790           (setq subject
4791                 (concat (substring subject 0 (match-beginning 1))
4792                         (substring subject (match-end 1)))))
4793         (when changed
4794           (mail-header-set-subject header subject))))))
4795
4796 (defun gnus-fetch-headers (articles)
4797   "Fetch headers of ARTICLES."
4798   (let ((name (gnus-group-decoded-name gnus-newsgroup-name)))
4799     (gnus-message 5 "Fetching headers for %s..." name)
4800     (prog1
4801         (if (eq 'nov
4802                 (setq gnus-headers-retrieved-by
4803                       (gnus-retrieve-headers
4804                        articles gnus-newsgroup-name
4805                        ;; We might want to fetch old headers, but
4806                        ;; not if there is only 1 article.
4807                        (and (or (and
4808                                  (not (eq gnus-fetch-old-headers 'some))
4809                                  (not (numberp gnus-fetch-old-headers)))
4810                                 (> (length articles) 1))
4811                             gnus-fetch-old-headers))))
4812             (gnus-get-newsgroup-headers-xover
4813              articles nil nil gnus-newsgroup-name t)
4814           (gnus-get-newsgroup-headers))
4815       (gnus-message 5 "Fetching headers for %s...done" name))))
4816
4817 (defun gnus-select-newsgroup (group &optional read-all select-articles)
4818   "Select newsgroup GROUP.
4819 If READ-ALL is non-nil, all articles in the group are selected.
4820 If SELECT-ARTICLES, only select those articles from GROUP."
4821   (let* ((entry (gnus-gethash group gnus-newsrc-hashtb))
4822          ;;!!! Dirty hack; should be removed.
4823          (gnus-summary-ignore-duplicates
4824           (if (eq (car (gnus-find-method-for-group group)) 'nnvirtual)
4825               t
4826             gnus-summary-ignore-duplicates))
4827          (info (nth 2 entry))
4828          articles fetched-articles cached)
4829
4830     (unless (gnus-check-server
4831              (set (make-local-variable 'gnus-current-select-method)
4832                   (gnus-find-method-for-group group)))
4833       (error "Couldn't open server"))
4834
4835     (or (and entry (not (eq (car entry) t))) ; Either it's active...
4836         (gnus-activate-group group)     ; Or we can activate it...
4837         (progn                          ; Or we bug out.
4838           (when (equal major-mode 'gnus-summary-mode)
4839             (kill-buffer (current-buffer)))
4840           (error "Couldn't activate group %s: %s"
4841                  group (gnus-status-message group))))
4842
4843     (unless (gnus-request-group group t)
4844       (when (equal major-mode 'gnus-summary-mode)
4845         (kill-buffer (current-buffer)))
4846       (error "Couldn't request group %s: %s"
4847              group (gnus-status-message group)))
4848
4849     (setq gnus-newsgroup-name group
4850           gnus-newsgroup-unselected nil
4851           gnus-newsgroup-unreads (gnus-list-of-unread-articles group))
4852
4853     (let ((display (gnus-group-find-parameter group 'display)))
4854       (setq gnus-newsgroup-display
4855             (cond
4856              ((not (zerop (or (car-safe read-all) 0)))
4857               ;; The user entered the group with C-u SPC/RET, let's show
4858               ;; all articles.
4859               'gnus-not-ignore)
4860              ((eq display 'all)
4861               'gnus-not-ignore)
4862              ((arrayp display)
4863               (gnus-summary-display-make-predicate (mapcar 'identity display)))
4864              ((numberp display)
4865               ;; The following is probably the "correct" solution, but
4866               ;; it makes Gnus fetch all headers and then limit the
4867               ;; articles (which is slow), so instead we hack the
4868               ;; select-articles parameter instead. -- Simon Josefsson
4869               ;; <jas@kth.se>
4870               ;;
4871               ;; (gnus-byte-compile
4872               ;;  `(lambda () (> number ,(- (cdr (gnus-active group))
4873               ;;                         display)))))
4874               (setq select-articles
4875                     (gnus-uncompress-range
4876                      (cons (let ((tmp (- (cdr (gnus-active group)) display)))
4877                              (if (> tmp 0)
4878                                  tmp
4879                                1))
4880                            (cdr (gnus-active group)))))
4881               nil)
4882              (t
4883               nil))))
4884
4885     (gnus-summary-setup-default-charset)
4886
4887     ;; Kludge to avoid having cached articles nixed out in virtual groups.
4888     (when (gnus-virtual-group-p group)
4889       (setq cached gnus-newsgroup-cached))
4890
4891     (setq gnus-newsgroup-unreads
4892           (gnus-sorted-ndifference
4893            (gnus-sorted-ndifference gnus-newsgroup-unreads
4894                                     gnus-newsgroup-marked)
4895            gnus-newsgroup-dormant))
4896
4897     (setq gnus-newsgroup-processable nil)
4898
4899     (gnus-update-read-articles group gnus-newsgroup-unreads)
4900
4901     ;; Adjust and set lists of article marks.
4902     (when info
4903       (gnus-adjust-marked-articles info))
4904     (if (setq articles select-articles)
4905         (setq gnus-newsgroup-unselected
4906               (gnus-sorted-difference gnus-newsgroup-unreads articles))
4907       (setq articles (gnus-articles-to-read group read-all)))
4908
4909     (cond
4910      ((null articles)
4911       ;;(gnus-message 3 "Couldn't select newsgroup -- no articles to display")
4912       'quit)
4913      ((eq articles 0) nil)
4914      (t
4915       ;; Init the dependencies hash table.
4916       (setq gnus-newsgroup-dependencies
4917             (gnus-make-hashtable (length articles)))
4918       (gnus-set-global-variables)
4919       ;; Retrieve the headers and read them in.
4920
4921       (setq gnus-newsgroup-headers (gnus-fetch-headers articles))
4922
4923       ;; Kludge to avoid having cached articles nixed out in virtual groups.
4924       (when cached
4925         (setq gnus-newsgroup-cached cached))
4926
4927       ;; Suppress duplicates?
4928       (when gnus-suppress-duplicates
4929         (gnus-dup-suppress-articles))
4930
4931       ;; Set the initial limit.
4932       (setq gnus-newsgroup-limit (copy-sequence articles))
4933       ;; Remove canceled articles from the list of unread articles.
4934       (setq fetched-articles
4935             (mapcar (lambda (headers) (mail-header-number headers))
4936                     gnus-newsgroup-headers))
4937       (setq gnus-newsgroup-articles fetched-articles)
4938       (setq gnus-newsgroup-unreads
4939             (gnus-sorted-nintersection
4940              gnus-newsgroup-unreads fetched-articles))
4941       (gnus-compute-unseen-list)
4942
4943       ;; Removed marked articles that do not exist.
4944       (gnus-update-missing-marks
4945        (gnus-sorted-difference articles fetched-articles))
4946       ;; We might want to build some more threads first.
4947       (when (and gnus-fetch-old-headers
4948                  (eq gnus-headers-retrieved-by 'nov))
4949         (if (eq gnus-fetch-old-headers 'invisible)
4950             (gnus-build-all-threads)
4951           (gnus-build-old-threads)))
4952       ;; Let the Gnus agent mark articles as read.
4953       (when gnus-agent
4954         (gnus-agent-get-undownloaded-list))
4955       ;; Remove list identifiers from subject
4956       (when gnus-list-identifiers
4957         (gnus-summary-remove-list-identifiers))
4958       ;; Check whether auto-expire is to be done in this group.
4959       (setq gnus-newsgroup-auto-expire
4960             (gnus-group-auto-expirable-p group))
4961       ;; Set up the article buffer now, if necessary.
4962       (unless gnus-single-article-buffer
4963         (gnus-article-setup-buffer))
4964       ;; First and last article in this newsgroup.
4965       (when gnus-newsgroup-headers
4966         (setq gnus-newsgroup-begin
4967               (mail-header-number (car gnus-newsgroup-headers))
4968               gnus-newsgroup-end
4969               (mail-header-number
4970                (gnus-last-element gnus-newsgroup-headers))))
4971       ;; GROUP is successfully selected.
4972       (or gnus-newsgroup-headers t)))))
4973
4974 (defun gnus-compute-unseen-list ()
4975   ;; The `seen' marks are treated specially.
4976   (if (not gnus-newsgroup-seen)
4977       (setq gnus-newsgroup-unseen gnus-newsgroup-articles)
4978     (setq gnus-newsgroup-unseen
4979           (gnus-inverse-list-range-intersection
4980            gnus-newsgroup-articles gnus-newsgroup-seen))))
4981
4982 (defun gnus-summary-display-make-predicate (display)
4983   (require 'gnus-agent)
4984   (when (= (length display) 1)
4985     (setq display (car display)))
4986   (unless gnus-summary-display-cache
4987     (dolist (elem (append '((unread . unread)
4988                             (read . read)
4989                             (unseen . unseen))
4990                           gnus-article-mark-lists))
4991       (push (cons (cdr elem)
4992                   (gnus-byte-compile
4993                    `(lambda () (gnus-article-marked-p ',(cdr elem)))))
4994             gnus-summary-display-cache)))
4995   (let ((gnus-category-predicate-alist gnus-summary-display-cache)
4996         (gnus-category-predicate-cache gnus-summary-display-cache))
4997     (gnus-get-predicate display)))
4998
4999 ;; Uses the dynamically bound `number' variable.
5000 (defvar number)
5001 (defun gnus-article-marked-p (type &optional article)
5002   (let ((article (or article number)))
5003     (cond
5004      ((eq type 'tick)
5005       (memq article gnus-newsgroup-marked))
5006      ((eq type 'spam)
5007       (memq article gnus-newsgroup-spam-marked))
5008      ((eq type 'unsend)
5009       (memq article gnus-newsgroup-unsendable))
5010      ((eq type 'undownload)
5011       (memq article gnus-newsgroup-undownloaded))
5012      ((eq type 'download)
5013       (memq article gnus-newsgroup-downloadable))
5014      ((eq type 'unread)
5015       (memq article gnus-newsgroup-unreads))
5016      ((eq type 'read)
5017       (memq article gnus-newsgroup-reads))
5018      ((eq type 'dormant)
5019       (memq article gnus-newsgroup-dormant) )
5020      ((eq type 'expire)
5021       (memq article gnus-newsgroup-expirable))
5022      ((eq type 'reply)
5023       (memq article gnus-newsgroup-replied))
5024      ((eq type 'killed)
5025       (memq article gnus-newsgroup-killed))
5026      ((eq type 'bookmark)
5027       (assq article gnus-newsgroup-bookmarks))
5028      ((eq type 'score)
5029       (assq article gnus-newsgroup-scored))
5030      ((eq type 'save)
5031       (memq article gnus-newsgroup-saved))
5032      ((eq type 'cache)
5033       (memq article gnus-newsgroup-cached))
5034      ((eq type 'forward)
5035       (memq article gnus-newsgroup-forwarded))
5036      ((eq type 'seen)
5037       (not (memq article gnus-newsgroup-unseen)))
5038      ((eq type 'recent)
5039       (memq article gnus-newsgroup-recent))
5040      (t t))))
5041
5042 (defun gnus-articles-to-read (group &optional read-all)
5043   "Find out what articles the user wants to read."
5044   (let* ((articles
5045           ;; Select all articles if `read-all' is non-nil, or if there
5046           ;; are no unread articles.
5047           (if (or read-all
5048                   (and (zerop (length gnus-newsgroup-marked))
5049                        (zerop (length gnus-newsgroup-unreads)))
5050                   ;; Fetch all if the predicate is non-nil.
5051                   gnus-newsgroup-display)
5052               ;; We want to select the headers for all the articles in
5053               ;; the group, so we select either all the active
5054               ;; articles in the group, or (if that's nil), the
5055               ;; articles in the cache.
5056               (or
5057                (gnus-uncompress-range (gnus-active group))
5058                (gnus-cache-articles-in-group group))
5059             ;; Select only the "normal" subset of articles.
5060             (gnus-sorted-nunion
5061              (gnus-sorted-union gnus-newsgroup-dormant gnus-newsgroup-marked)
5062              gnus-newsgroup-unreads)))
5063          (scored-list (gnus-killed-articles gnus-newsgroup-killed articles))
5064          (scored (length scored-list))
5065          (number (length articles))
5066          (marked (+ (length gnus-newsgroup-marked)
5067                     (length gnus-newsgroup-dormant)))
5068          (select
5069           (cond
5070            ((numberp read-all)
5071             read-all)
5072            ((numberp gnus-newsgroup-display)
5073             gnus-newsgroup-display)
5074            (t
5075             (condition-case ()
5076                 (cond
5077                  ((and (or (<= scored marked) (= scored number))
5078                        (numberp gnus-large-newsgroup)
5079                        (> number gnus-large-newsgroup))
5080                   (let* ((cursor-in-echo-area nil)
5081                          (initial (gnus-parameter-large-newsgroup-initial
5082                                    gnus-newsgroup-name))
5083                          (input
5084                           (read-string
5085                            (format
5086                             "How many articles from %s (%s %d): "
5087                             (gnus-limit-string
5088                              (gnus-group-decoded-name gnus-newsgroup-name)
5089                              35)
5090                             (if initial "max" "default")
5091                             number)
5092                            (if initial
5093                                (cons (number-to-string initial)
5094                                      0)))))
5095                     (if (string-match "^[ \t]*$" input) number input)))
5096                  ((and (> scored marked) (< scored number)
5097                        (> (- scored number) 20))
5098                   (let ((input
5099                          (read-string
5100                           (format "%s %s (%d scored, %d total): "
5101                                   "How many articles from"
5102                                   (gnus-group-decoded-name group)
5103                                   scored number))))
5104                     (if (string-match "^[ \t]*$" input)
5105                         number input)))
5106                  (t number))
5107               (quit
5108                (message "Quit getting the articles to read")
5109                nil))))))
5110     (setq select (if (stringp select) (string-to-number select) select))
5111     (if (or (null select) (zerop select))
5112         select
5113       (if (and (not (zerop scored)) (<= (abs select) scored))
5114           (progn
5115             (setq articles (sort scored-list '<))
5116             (setq number (length articles)))
5117         (setq articles (copy-sequence articles)))
5118
5119       (when (< (abs select) number)
5120         (if (< select 0)
5121             ;; Select the N oldest articles.
5122             (setcdr (nthcdr (1- (abs select)) articles) nil)
5123           ;; Select the N most recent articles.
5124           (setq articles (nthcdr (- number select) articles))))
5125       (setq gnus-newsgroup-unselected
5126             (gnus-sorted-difference gnus-newsgroup-unreads articles))
5127       (when gnus-alter-articles-to-read-function
5128         (setq gnus-newsgroup-unreads
5129               (sort
5130                (funcall gnus-alter-articles-to-read-function
5131                         gnus-newsgroup-name gnus-newsgroup-unreads)
5132                '<)))
5133       articles)))
5134
5135 (defun gnus-killed-articles (killed articles)
5136   (let (out)
5137     (while articles
5138       (when (inline (gnus-member-of-range (car articles) killed))
5139         (push (car articles) out))
5140       (setq articles (cdr articles)))
5141     out))
5142
5143 (defun gnus-uncompress-marks (marks)
5144   "Uncompress the mark ranges in MARKS."
5145   (let ((uncompressed '(score bookmark))
5146         out)
5147     (while marks
5148       (if (memq (caar marks) uncompressed)
5149           (push (car marks) out)
5150         (push (cons (caar marks) (gnus-uncompress-range (cdar marks))) out))
5151       (setq marks (cdr marks)))
5152     out))
5153
5154 (defun gnus-article-mark-to-type (mark)
5155   "Return the type of MARK."
5156   (or (cadr (assq mark gnus-article-special-mark-lists))
5157       'list))
5158
5159 (defun gnus-article-unpropagatable-p (mark)
5160   "Return whether MARK should be propagated to backend."
5161   (memq mark gnus-article-unpropagated-mark-lists))
5162
5163 (defun gnus-adjust-marked-articles (info)
5164   "Set all article lists and remove all marks that are no longer valid."
5165   (let* ((marked-lists (gnus-info-marks info))
5166          (active (gnus-active (gnus-info-group info)))
5167          (min (car active))
5168          (max (cdr active))
5169          (types gnus-article-mark-lists)
5170          marks var articles article mark mark-type)
5171
5172     (dolist (marks marked-lists)
5173       (setq mark (car marks)
5174             mark-type (gnus-article-mark-to-type mark)
5175             var (intern (format "gnus-newsgroup-%s" (car (rassq mark types)))))
5176
5177       ;; We set the variable according to the type of the marks list,
5178       ;; and then adjust the marks to a subset of the active articles.
5179       (cond
5180        ;; Adjust "simple" lists.
5181        ((eq mark-type 'list)
5182         (set var (setq articles (gnus-uncompress-range (cdr marks))))
5183         (when (memq mark '(tick dormant expire reply save))
5184           (while articles
5185             (when (or (< (setq article (pop articles)) min) (> article max))
5186               (set var (delq article (symbol-value var)))))))
5187        ;; Adjust assocs.
5188        ((eq mark-type 'tuple)
5189         (set var (setq articles (cdr marks)))
5190         (when (not (listp (cdr (symbol-value var))))
5191           (set var (list (symbol-value var))))
5192         (when (not (listp (cdr articles)))
5193           (setq articles (list articles)))
5194         (while articles
5195           (when (or (not (consp (setq article (pop articles))))
5196                     (< (car article) min)
5197                     (> (car article) max))
5198             (set var (delq article (symbol-value var))))))
5199        ;; Adjust ranges (sloppily).
5200        ((eq mark-type 'range)
5201         (cond
5202          ((eq mark 'seen)
5203           ;; Fix the record for `seen' if it looks like (seen NUM1 . NUM2).
5204           ;; It should be (seen (NUM1 . NUM2)).
5205           (when (numberp (cddr marks))
5206             (setcdr marks (list (cdr marks))))
5207           (setq articles (cdr marks))
5208           (while (and articles
5209                       (or (and (consp (car articles))
5210                                (> min (cdar articles)))
5211                           (and (numberp (car articles))
5212                                (> min (car articles)))))
5213             (pop articles))
5214           (set var articles))))))))
5215
5216 (defun gnus-update-missing-marks (missing)
5217   "Go through the list of MISSING articles and remove them from the mark lists."
5218   (when missing
5219     (let (var m)
5220       ;; Go through all types.
5221       (dolist (elem gnus-article-mark-lists)
5222         (when (eq (gnus-article-mark-to-type (cdr elem)) 'list)
5223           (setq var (intern (format "gnus-newsgroup-%s" (car elem))))
5224           (when (symbol-value var)
5225             ;; This list has articles.  So we delete all missing
5226             ;; articles from it.
5227             (setq m missing)
5228             (while m
5229               (set var (delq (pop m) (symbol-value var))))))))))
5230
5231 (defun gnus-update-marks ()
5232   "Enter the various lists of marked articles into the newsgroup info list."
5233   (let ((types gnus-article-mark-lists)
5234         (info (gnus-get-info gnus-newsgroup-name))
5235         type list newmarked symbol delta-marks)
5236     (when info
5237       ;; Add all marks lists to the list of marks lists.
5238       (while (setq type (pop types))
5239         (setq list (symbol-value
5240                     (setq symbol
5241                           (intern (format "gnus-newsgroup-%s" (car type))))))
5242
5243         (when list
5244           ;; Get rid of the entries of the articles that have the
5245           ;; default score.
5246           (when (and (eq (cdr type) 'score)
5247                      gnus-save-score
5248                      list)
5249             (let* ((arts list)
5250                    (prev (cons nil list))
5251                    (all prev))
5252               (while arts
5253                 (if (or (not (consp (car arts)))
5254                         (= (cdar arts) gnus-summary-default-score))
5255                     (setcdr prev (cdr arts))
5256                   (setq prev arts))
5257                 (setq arts (cdr arts)))
5258               (setq list (cdr all)))))
5259
5260         (when (eq (cdr type) 'seen)
5261           (setq list (gnus-range-add list gnus-newsgroup-unseen)))
5262
5263         (when (eq (gnus-article-mark-to-type (cdr type)) 'list)
5264           (setq list (gnus-compress-sequence (set symbol (sort list '<)) t)))
5265
5266         (when (and (gnus-check-backend-function
5267                     'request-set-mark gnus-newsgroup-name)
5268                    (not (gnus-article-unpropagatable-p (cdr type))))
5269           (let* ((old (cdr (assq (cdr type) (gnus-info-marks info))))
5270                  (del (gnus-remove-from-range (gnus-copy-sequence old) list))
5271                  (add (gnus-remove-from-range
5272                        (gnus-copy-sequence list) old)))
5273             (when add
5274               (push (list add 'add (list (cdr type))) delta-marks))
5275             (when del
5276               (push (list del 'del (list (cdr type))) delta-marks))))
5277
5278         (when list
5279           (push (cons (cdr type) list) newmarked)))
5280
5281       (when delta-marks
5282         (unless (gnus-check-group gnus-newsgroup-name)
5283           (error "Can't open server for %s" gnus-newsgroup-name))
5284         (gnus-request-set-mark gnus-newsgroup-name delta-marks))
5285
5286       ;; Enter these new marks into the info of the group.
5287       (if (nthcdr 3 info)
5288           (setcar (nthcdr 3 info) newmarked)
5289         ;; Add the marks lists to the end of the info.
5290         (when newmarked
5291           (setcdr (nthcdr 2 info) (list newmarked))))
5292
5293       ;; Cut off the end of the info if there's nothing else there.
5294       (let ((i 5))
5295         (while (and (> i 2)
5296                     (not (nth i info)))
5297           (when (nthcdr (decf i) info)
5298             (setcdr (nthcdr i info) nil)))))))
5299
5300 (defun gnus-set-mode-line (where)
5301   "Set the mode line of the article or summary buffers.
5302 If WHERE is `summary', the summary mode line format will be used."
5303   ;; Is this mode line one we keep updated?
5304   (when (and (memq where gnus-updated-mode-lines)
5305              (symbol-value
5306               (intern (format "gnus-%s-mode-line-format-spec" where))))
5307     (let (mode-string)
5308       (save-excursion
5309         ;; We evaluate this in the summary buffer since these
5310         ;; variables are buffer-local to that buffer.
5311         (set-buffer gnus-summary-buffer)
5312        ;; We bind all these variables that are used in the `eval' form
5313         ;; below.
5314         (let* ((mformat (symbol-value
5315                          (intern
5316                           (format "gnus-%s-mode-line-format-spec" where))))
5317                (gnus-tmp-group-name (gnus-group-decoded-name
5318                                      gnus-newsgroup-name))
5319                (gnus-tmp-article-number (or gnus-current-article 0))
5320                (gnus-tmp-unread gnus-newsgroup-unreads)
5321                (gnus-tmp-unread-and-unticked (length gnus-newsgroup-unreads))
5322                (gnus-tmp-unselected (length gnus-newsgroup-unselected))
5323                (gnus-tmp-unread-and-unselected
5324                 (cond ((and (zerop gnus-tmp-unread-and-unticked)
5325                             (zerop gnus-tmp-unselected))
5326                        "")
5327                       ((zerop gnus-tmp-unselected)
5328                        (format "{%d more}" gnus-tmp-unread-and-unticked))
5329                       (t (format "{%d(+%d) more}"
5330                                  gnus-tmp-unread-and-unticked
5331                                  gnus-tmp-unselected))))
5332                (gnus-tmp-subject
5333                 (if (and gnus-current-headers
5334                          (vectorp gnus-current-headers))
5335                     (gnus-mode-string-quote
5336                      (mail-header-subject gnus-current-headers))
5337                   ""))
5338                bufname-length max-len
5339                gnus-tmp-header) ;; passed as argument to any user-format-funcs
5340           (setq mode-string (eval mformat))
5341           (setq bufname-length (if (string-match "%b" mode-string)
5342                                    (- (length
5343                                        (buffer-name
5344                                         (if (eq where 'summary)
5345                                             nil
5346                                           (get-buffer gnus-article-buffer))))
5347                                       2)
5348                                  0))
5349           (setq max-len (max 4 (if gnus-mode-non-string-length
5350                                    (- (window-width)
5351                                       gnus-mode-non-string-length
5352                                       bufname-length)
5353                                  (length mode-string))))
5354           ;; We might have to chop a bit of the string off...
5355           (when (> (length mode-string) max-len)
5356             (setq mode-string
5357                   (concat (truncate-string-to-width mode-string (- max-len 3))
5358                           "...")))
5359           ;; Pad the mode string a bit.
5360           (setq mode-string (format (format "%%-%ds" max-len) mode-string))))
5361       ;; Update the mode line.
5362       (setq mode-line-buffer-identification
5363             (gnus-mode-line-buffer-identification (list mode-string)))
5364       (set-buffer-modified-p t))))
5365
5366 (defun gnus-create-xref-hashtb (from-newsgroup headers unreads)
5367   "Go through the HEADERS list and add all Xrefs to a hash table.
5368 The resulting hash table is returned, or nil if no Xrefs were found."
5369   (let* ((virtual (gnus-virtual-group-p from-newsgroup))
5370          (prefix (if virtual "" (gnus-group-real-prefix from-newsgroup)))
5371          (xref-hashtb (gnus-make-hashtable))
5372          start group entry number xrefs header)
5373     (while headers
5374       (setq header (pop headers))
5375       (when (and (setq xrefs (mail-header-xref header))
5376                  (not (memq (setq number (mail-header-number header))
5377                             unreads)))
5378         (setq start 0)
5379         (while (string-match "\\([^ ]+\\)[:/]\\([0-9]+\\)" xrefs start)
5380           (setq start (match-end 0))
5381           (setq group (if prefix
5382                           (concat prefix (substring xrefs (match-beginning 1)
5383                                                     (match-end 1)))
5384                         (substring xrefs (match-beginning 1) (match-end 1))))
5385           (setq number
5386                 (string-to-int (substring xrefs (match-beginning 2)
5387                                           (match-end 2))))
5388           (if (setq entry (gnus-gethash group xref-hashtb))
5389               (setcdr entry (cons number (cdr entry)))
5390             (gnus-sethash group (cons number nil) xref-hashtb)))))
5391     (and start xref-hashtb)))
5392
5393 (defun gnus-mark-xrefs-as-read (from-newsgroup headers unreads)
5394   "Look through all the headers and mark the Xrefs as read."
5395   (let ((virtual (gnus-virtual-group-p from-newsgroup))
5396         name entry info xref-hashtb idlist method nth4)
5397     (save-excursion
5398       (set-buffer gnus-group-buffer)
5399       (when (setq xref-hashtb
5400                   (gnus-create-xref-hashtb from-newsgroup headers unreads))
5401         (mapatoms
5402          (lambda (group)
5403            (unless (string= from-newsgroup (setq name (symbol-name group)))
5404              (setq idlist (symbol-value group))
5405              ;; Dead groups are not updated.
5406              (and (prog1
5407                       (setq entry (gnus-gethash name gnus-newsrc-hashtb)
5408                             info (nth 2 entry))
5409                     (when (stringp (setq nth4 (gnus-info-method info)))
5410                       (setq nth4 (gnus-server-to-method nth4))))
5411                   ;; Only do the xrefs if the group has the same
5412                   ;; select method as the group we have just read.
5413                   (or (gnus-methods-equal-p
5414                        nth4 (gnus-find-method-for-group from-newsgroup))
5415                       virtual
5416                       (equal nth4 (setq method (gnus-find-method-for-group
5417                                                 from-newsgroup)))
5418                       (and (equal (car nth4) (car method))
5419                            (equal (nth 1 nth4) (nth 1 method))))
5420                   gnus-use-cross-reference
5421                   (or (not (eq gnus-use-cross-reference t))
5422                       virtual
5423                       ;; Only do cross-references on subscribed
5424                       ;; groups, if that is what is wanted.
5425                       (<= (gnus-info-level info) gnus-level-subscribed))
5426                   (gnus-group-make-articles-read name idlist))))
5427          xref-hashtb)))))
5428
5429 (defun gnus-compute-read-articles (group articles)
5430   (let* ((entry (gnus-gethash group gnus-newsrc-hashtb))
5431          (info (nth 2 entry))
5432          (active (gnus-active group))
5433          ninfo)
5434     (when entry
5435       ;; First peel off all invalid article numbers.
5436       (when active
5437         (let ((ids articles)
5438               id first)
5439           (while (setq id (pop ids))
5440             (when (and first (> id (cdr active)))
5441               ;; We'll end up in this situation in one particular
5442               ;; obscure situation.  If you re-scan a group and get
5443               ;; a new article that is cross-posted to a different
5444               ;; group that has not been re-scanned, you might get
5445               ;; crossposted article that has a higher number than
5446               ;; Gnus believes possible.  So we re-activate this
5447               ;; group as well.  This might mean doing the
5448               ;; crossposting thingy will *increase* the number
5449               ;; of articles in some groups.  Tsk, tsk.
5450               (setq active (or (gnus-activate-group group) active)))
5451             (when (or (> id (cdr active))
5452                       (< id (car active)))
5453               (setq articles (delq id articles))))))
5454       ;; If the read list is nil, we init it.
5455       (if (and active
5456                (null (gnus-info-read info))
5457                (> (car active) 1))
5458           (setq ninfo (cons 1 (1- (car active))))
5459         (setq ninfo (gnus-info-read info)))
5460       ;; Then we add the read articles to the range.
5461       (gnus-add-to-range
5462        ninfo (setq articles (sort articles '<))))))
5463
5464 (defun gnus-group-make-articles-read (group articles)
5465   "Update the info of GROUP to say that ARTICLES are read."
5466   (let* ((num 0)
5467          (entry (gnus-gethash group gnus-newsrc-hashtb))
5468          (info (nth 2 entry))
5469          (active (gnus-active group))
5470          range)
5471     (when entry
5472       (setq range (gnus-compute-read-articles group articles))
5473       (save-excursion
5474         (set-buffer gnus-group-buffer)
5475         (gnus-undo-register
5476           `(progn
5477              (gnus-info-set-marks ',info ',(gnus-info-marks info) t)
5478              (gnus-info-set-read ',info ',(gnus-info-read info))
5479              (gnus-get-unread-articles-in-group ',info (gnus-active ,group))
5480              (gnus-request-set-mark ,group (list (list ',range 'del '(read))))
5481              (gnus-group-update-group ,group t))))
5482       ;; Add the read articles to the range.
5483       (gnus-info-set-read info range)
5484       (gnus-request-set-mark group (list (list range 'add '(read))))
5485       ;; Then we have to re-compute how many unread
5486       ;; articles there are in this group.
5487       (when active
5488         (cond
5489          ((not range)
5490           (setq num (- (1+ (cdr active)) (car active))))
5491          ((not (listp (cdr range)))
5492           (setq num (- (cdr active) (- (1+ (cdr range))
5493                                        (car range)))))
5494          (t
5495           (while range
5496             (if (numberp (car range))
5497                 (setq num (1+ num))
5498               (setq num (+ num (- (1+ (cdar range)) (caar range)))))
5499             (setq range (cdr range)))
5500           (setq num (- (cdr active) num))))
5501         ;; Update the number of unread articles.
5502         (setcar entry num)
5503         ;; Update the group buffer.
5504         (gnus-group-update-group group t)))))
5505
5506 (defvar gnus-newsgroup-none-id 0)
5507
5508 (defun gnus-get-newsgroup-headers (&optional dependencies force-new)
5509   (let ((cur nntp-server-buffer)
5510         (dependencies
5511          (or dependencies
5512              (save-excursion (set-buffer gnus-summary-buffer)
5513                              gnus-newsgroup-dependencies)))
5514         headers id end ref
5515         (mail-parse-charset gnus-newsgroup-charset)
5516         (mail-parse-ignored-charsets
5517          (save-excursion (condition-case nil
5518                              (set-buffer gnus-summary-buffer)
5519                            (error))
5520                          gnus-newsgroup-ignored-charsets)))
5521     (save-excursion
5522       (set-buffer nntp-server-buffer)
5523       ;; Translate all TAB characters into SPACE characters.
5524       (subst-char-in-region (point-min) (point-max) ?\t ?  t)
5525       (subst-char-in-region (point-min) (point-max) ?\r ?  t)
5526       (gnus-run-hooks 'gnus-parse-headers-hook)
5527       (let ((case-fold-search t)
5528             in-reply-to header p lines chars)
5529         (goto-char (point-min))
5530         ;; Search to the beginning of the next header.  Error messages
5531         ;; do not begin with 2 or 3.
5532         (while (re-search-forward "^[23][0-9]+ " nil t)
5533           (setq id nil
5534                 ref nil)
5535           ;; This implementation of this function, with nine
5536           ;; search-forwards instead of the one re-search-forward and
5537           ;; a case (which basically was the old function) is actually
5538           ;; about twice as fast, even though it looks messier.  You
5539           ;; can't have everything, I guess.  Speed and elegance
5540           ;; doesn't always go hand in hand.
5541           (setq
5542            header
5543            (vector
5544             ;; Number.
5545             (prog1
5546                 (read cur)
5547               (end-of-line)
5548               (setq p (point))
5549               (narrow-to-region (point)
5550                                 (or (and (search-forward "\n.\n" nil t)
5551                                          (- (point) 2))
5552                                     (point))))
5553             ;; Subject.
5554             (progn
5555               (goto-char p)
5556               (if (search-forward "\nsubject:" nil t)
5557                   (funcall gnus-decode-encoded-word-function
5558                            (nnheader-header-value))
5559                 "(none)"))
5560             ;; From.
5561             (progn
5562               (goto-char p)
5563               (if (search-forward "\nfrom:" nil t)
5564                   (funcall gnus-decode-encoded-word-function
5565                            (nnheader-header-value))
5566                 "(nobody)"))
5567             ;; Date.
5568             (progn
5569               (goto-char p)
5570               (if (search-forward "\ndate:" nil t)
5571                   (nnheader-header-value) ""))
5572             ;; Message-ID.
5573             (progn
5574               (goto-char p)
5575               (setq id (if (re-search-forward
5576                             "^message-id: *\\(<[^\n\t> ]+>\\)" nil t)
5577                            ;; We do it this way to make sure the Message-ID
5578                            ;; is (somewhat) syntactically valid.
5579                            (buffer-substring (match-beginning 1)
5580                                              (match-end 1))
5581                          ;; If there was no message-id, we just fake one
5582                          ;; to make subsequent routines simpler.
5583                          (nnheader-generate-fake-message-id))))
5584             ;; References.
5585             (progn
5586               (goto-char p)
5587               (if (search-forward "\nreferences:" nil t)
5588                   (progn
5589                     (setq end (point))
5590                     (prog1
5591                         (nnheader-header-value)
5592                       (setq ref
5593                             (buffer-substring
5594                              (progn
5595                                (end-of-line)
5596                                (search-backward ">" end t)
5597                                (1+ (point)))
5598                              (progn
5599                                (search-backward "<" end t)
5600                                (point))))))
5601                 ;; Get the references from the in-reply-to header if there
5602                 ;; were no references and the in-reply-to header looks
5603                 ;; promising.
5604                 (if (and (search-forward "\nin-reply-to:" nil t)
5605                          (setq in-reply-to (nnheader-header-value))
5606                          (string-match "<[^>]+>" in-reply-to))
5607                     (let (ref2)
5608                       (setq ref (substring in-reply-to (match-beginning 0)
5609                                            (match-end 0)))
5610                       (while (string-match "<[^>]+>" in-reply-to (match-end 0))
5611                         (setq ref2 (substring in-reply-to (match-beginning 0)
5612                                               (match-end 0)))
5613                         (when (> (length ref2) (length ref))
5614                           (setq ref ref2)))
5615                       ref)
5616                   (setq ref nil))))
5617             ;; Chars.
5618             (progn
5619               (goto-char p)
5620               (if (search-forward "\nchars: " nil t)
5621                   (if (numberp (setq chars (ignore-errors (read cur))))
5622                       chars -1)
5623                 -1))
5624             ;; Lines.
5625             (progn
5626               (goto-char p)
5627               (if (search-forward "\nlines: " nil t)
5628                   (if (numberp (setq lines (ignore-errors (read cur))))
5629                       lines -1)
5630                 -1))
5631             ;; Xref.
5632             (progn
5633               (goto-char p)
5634               (and (search-forward "\nxref:" nil t)
5635                    (nnheader-header-value)))
5636             ;; Extra.
5637             (when gnus-extra-headers
5638               (let ((extra gnus-extra-headers)
5639                     out)
5640                 (while extra
5641                   (goto-char p)
5642                   (when (search-forward
5643                          (concat "\n" (symbol-name (car extra)) ":") nil t)
5644                     (push (cons (car extra) (nnheader-header-value))
5645                           out))
5646                   (pop extra))
5647                 out))))
5648           (when (equal id ref)
5649             (setq ref nil))
5650
5651           (when gnus-alter-header-function
5652             (funcall gnus-alter-header-function header)
5653             (setq id (mail-header-id header)
5654                   ref (gnus-parent-id (mail-header-references header))))
5655
5656           (when (setq header
5657                       (gnus-dependencies-add-header
5658                        header dependencies force-new))
5659             (push header headers))
5660           (goto-char (point-max))
5661           (widen))
5662         (nreverse headers)))))
5663
5664 ;; Goes through the xover lines and returns a list of vectors
5665 (defun gnus-get-newsgroup-headers-xover (sequence &optional
5666                                                   force-new dependencies
5667                                                   group also-fetch-heads)
5668   "Parse the news overview data in the server buffer.
5669 Return a list of headers that match SEQUENCE (see
5670 `nntp-retrieve-headers')."
5671   ;; Get the Xref when the users reads the articles since most/some
5672   ;; NNTP servers do not include Xrefs when using XOVER.
5673   (setq gnus-article-internal-prepare-hook '(gnus-article-get-xrefs))
5674   (let ((mail-parse-charset gnus-newsgroup-charset)
5675         (mail-parse-ignored-charsets gnus-newsgroup-ignored-charsets)
5676         (cur nntp-server-buffer)
5677         (dependencies (or dependencies gnus-newsgroup-dependencies))
5678         (allp (cond
5679                ((eq gnus-read-all-available-headers t)
5680                 t)
5681                ((stringp gnus-read-all-available-headers)
5682                 (string-match gnus-read-all-available-headers group))
5683                (t
5684                 nil)))
5685         number headers header)
5686     (save-excursion
5687       (set-buffer nntp-server-buffer)
5688       (subst-char-in-region (point-min) (point-max) ?\r ?  t)
5689       ;; Allow the user to mangle the headers before parsing them.
5690       (gnus-run-hooks 'gnus-parse-headers-hook)
5691       (goto-char (point-min))
5692       (gnus-parse-without-error
5693         (while (and (or sequence allp)
5694                     (not (eobp)))
5695           (setq number (read cur))
5696           (when (not allp)
5697             (while (and sequence
5698                         (< (car sequence) number))
5699               (setq sequence (cdr sequence))))
5700           (when (and (or allp
5701                          (and sequence
5702                               (eq number (car sequence))))
5703                      (progn
5704                        (setq sequence (cdr sequence))
5705                        (setq header (inline
5706                                       (gnus-nov-parse-line
5707                                        number dependencies force-new)))))
5708             (push header headers))
5709           (forward-line 1)))
5710       ;; A common bug in inn is that if you have posted an article and
5711       ;; then retrieves the active file, it will answer correctly --
5712       ;; the new article is included.  However, a NOV entry for the
5713       ;; article may not have been generated yet, so this may fail.
5714       ;; We work around this problem by retrieving the last few
5715       ;; headers using HEAD.
5716       (if (or (not also-fetch-heads)
5717               (not sequence))
5718           ;; We (probably) got all the headers.
5719           (nreverse headers)
5720         (let ((gnus-nov-is-evil t))
5721           (nconc
5722            (nreverse headers)
5723            (when (eq (gnus-retrieve-headers sequence group) 'headers)
5724              (gnus-get-newsgroup-headers))))))))
5725
5726 (defun gnus-article-get-xrefs ()
5727   "Fill in the Xref value in `gnus-current-headers', if necessary.
5728 This is meant to be called in `gnus-article-internal-prepare-hook'."
5729   (let ((headers (save-excursion (set-buffer gnus-summary-buffer)
5730                                  gnus-current-headers)))
5731     (or (not gnus-use-cross-reference)
5732         (not headers)
5733         (and (mail-header-xref headers)
5734              (not (string= (mail-header-xref headers) "")))
5735         (let ((case-fold-search t)
5736               xref)
5737           (save-restriction
5738             (nnheader-narrow-to-headers)
5739             (goto-char (point-min))
5740             (when (or (and (not (eobp))
5741                            (eq (downcase (char-after)) ?x)
5742                            (looking-at "Xref:"))
5743                       (search-forward "\nXref:" nil t))
5744               (goto-char (1+ (match-end 0)))
5745               (setq xref (buffer-substring (point)
5746                                            (progn (end-of-line) (point))))
5747               (mail-header-set-xref headers xref)))))))
5748
5749 (defun gnus-summary-insert-subject (id &optional old-header use-old-header)
5750   "Find article ID and insert the summary line for that article.
5751 OLD-HEADER can either be a header or a line number to insert
5752 the subject line on."
5753   (let* ((line (and (numberp old-header) old-header))
5754          (old-header (and (vectorp old-header) old-header))
5755          (header (cond ((and old-header use-old-header)
5756                         old-header)
5757                        ((and (numberp id)
5758                              (gnus-number-to-header id))
5759                         (gnus-number-to-header id))
5760                        (t
5761                         (gnus-read-header id))))
5762          (number (and (numberp id) id))
5763          d)
5764     (when header
5765       ;; Rebuild the thread that this article is part of and go to the
5766       ;; article we have fetched.
5767       (when (and (not gnus-show-threads)
5768                  old-header)
5769         (when (and number
5770                    (setq d (gnus-data-find (mail-header-number old-header))))
5771           (goto-char (gnus-data-pos d))
5772           (gnus-data-remove
5773            number
5774            (- (gnus-point-at-bol)
5775               (prog1
5776                   (1+ (gnus-point-at-eol))
5777                 (gnus-delete-line))))))
5778       (when old-header
5779         (mail-header-set-number header (mail-header-number old-header)))
5780       (setq gnus-newsgroup-sparse
5781             (delq (setq number (mail-header-number header))
5782                   gnus-newsgroup-sparse))
5783       (setq gnus-newsgroup-ancient (delq number gnus-newsgroup-ancient))
5784       (push number gnus-newsgroup-limit)
5785       (gnus-rebuild-thread (mail-header-id header) line)
5786       (gnus-summary-goto-subject number nil t))
5787     (when (and (numberp number)
5788                (> number 0))
5789       ;; We have to update the boundaries even if we can't fetch the
5790       ;; article if ID is a number -- so that the next `P' or `N'
5791       ;; command will fetch the previous (or next) article even
5792       ;; if the one we tried to fetch this time has been canceled.
5793       (when (> number gnus-newsgroup-end)
5794         (setq gnus-newsgroup-end number))
5795       (when (< number gnus-newsgroup-begin)
5796         (setq gnus-newsgroup-begin number))
5797       (setq gnus-newsgroup-unselected
5798             (delq number gnus-newsgroup-unselected)))
5799     ;; Report back a success?
5800     (and header (mail-header-number header))))
5801
5802 ;;; Process/prefix in the summary buffer
5803
5804 (defun gnus-summary-work-articles (n)
5805   "Return a list of articles to be worked upon.
5806 The prefix argument, the list of process marked articles, and the
5807 current article will be taken into consideration."
5808   (save-excursion
5809     (set-buffer gnus-summary-buffer)
5810     (cond
5811      (n
5812       ;; A numerical prefix has been given.
5813       (setq n (prefix-numeric-value n))
5814       (let ((backward (< n 0))
5815             (n (abs (prefix-numeric-value n)))
5816             articles article)
5817         (save-excursion
5818           (while
5819               (and (> n 0)
5820                    (push (setq article (gnus-summary-article-number))
5821                          articles)
5822                    (if backward
5823                        (gnus-summary-find-prev nil article)
5824                      (gnus-summary-find-next nil article)))
5825             (decf n)))
5826         (nreverse articles)))
5827      ((and (gnus-region-active-p) (mark))
5828       (message "region active")
5829       ;; Work on the region between point and mark.
5830       (let ((max (max (point) (mark)))
5831             articles article)
5832         (save-excursion
5833           (goto-char (min (min (point) (mark))))
5834           (while
5835               (and
5836                (push (setq article (gnus-summary-article-number)) articles)
5837                (gnus-summary-find-next nil article)
5838                (< (point) max)))
5839           (nreverse articles))))
5840      (gnus-newsgroup-processable
5841       ;; There are process-marked articles present.
5842       ;; Save current state.
5843       (gnus-summary-save-process-mark)
5844       ;; Return the list.
5845       (reverse gnus-newsgroup-processable))
5846      (t
5847       ;; Just return the current article.
5848       (list (gnus-summary-article-number))))))
5849
5850 (defmacro gnus-summary-iterate (arg &rest forms)
5851   "Iterate over the process/prefixed articles and do FORMS.
5852 ARG is the interactive prefix given to the command.  FORMS will be
5853 executed with point over the summary line of the articles."
5854   (let ((articles (make-symbol "gnus-summary-iterate-articles")))
5855     `(let ((,articles (gnus-summary-work-articles ,arg)))
5856        (while ,articles
5857          (gnus-summary-goto-subject (car ,articles))
5858          ,@forms
5859          (pop ,articles)))))
5860
5861 (put 'gnus-summary-iterate 'lisp-indent-function 1)
5862 (put 'gnus-summary-iterate 'edebug-form-spec '(form body))
5863
5864 (defun gnus-summary-save-process-mark ()
5865   "Push the current set of process marked articles on the stack."
5866   (interactive)
5867   (push (copy-sequence gnus-newsgroup-processable)
5868         gnus-newsgroup-process-stack))
5869
5870 (defun gnus-summary-kill-process-mark ()
5871   "Push the current set of process marked articles on the stack and unmark."
5872   (interactive)
5873   (gnus-summary-save-process-mark)
5874   (gnus-summary-unmark-all-processable))
5875
5876 (defun gnus-summary-yank-process-mark ()
5877   "Pop the last process mark state off the stack and restore it."
5878   (interactive)
5879   (unless gnus-newsgroup-process-stack
5880     (error "Empty mark stack"))
5881   (gnus-summary-process-mark-set (pop gnus-newsgroup-process-stack)))
5882
5883 (defun gnus-summary-process-mark-set (set)
5884   "Make SET into the current process marked articles."
5885   (gnus-summary-unmark-all-processable)
5886   (while set
5887     (gnus-summary-set-process-mark (pop set))))
5888
5889 ;;; Searching and stuff
5890
5891 (defun gnus-summary-search-group (&optional backward use-level)
5892   "Search for next unread newsgroup.
5893 If optional argument BACKWARD is non-nil, search backward instead."
5894   (save-excursion
5895     (set-buffer gnus-group-buffer)
5896     (when (gnus-group-search-forward
5897            backward nil (if use-level (gnus-group-group-level) nil))
5898       (gnus-group-group-name))))
5899
5900 (defun gnus-summary-best-group (&optional exclude-group)
5901   "Find the name of the best unread group.
5902 If EXCLUDE-GROUP, do not go to this group."
5903   (save-excursion
5904     (set-buffer gnus-group-buffer)
5905     (save-excursion
5906       (gnus-group-best-unread-group exclude-group))))
5907
5908 (defun gnus-summary-find-next (&optional unread article backward undownloaded)
5909   (if backward (gnus-summary-find-prev)
5910     (let* ((dummy (gnus-summary-article-intangible-p))
5911            (article (or article (gnus-summary-article-number)))
5912            (arts (gnus-data-find-list article))
5913            result)
5914       (when (and (not dummy)
5915                  (or (not gnus-summary-check-current)
5916                      (not unread)
5917                      (not (gnus-data-unread-p (car arts)))))
5918         (setq arts (cdr arts)))
5919       (when (setq result
5920                   (if unread
5921                       (progn
5922                         (while arts
5923                           (when (or (and undownloaded
5924                                          (eq gnus-undownloaded-mark
5925                                              (gnus-data-mark (car arts))))
5926                                     (gnus-data-unread-p (car arts)))
5927                             (setq result (car arts)
5928                                   arts nil))
5929                           (setq arts (cdr arts)))
5930                         result)
5931                     (car arts)))
5932         (goto-char (gnus-data-pos result))
5933         (gnus-data-number result)))))
5934
5935 (defun gnus-summary-find-prev (&optional unread article)
5936   (let* ((eobp (eobp))
5937          (article (or article (gnus-summary-article-number)))
5938          (arts (gnus-data-find-list article (gnus-data-list 'rev)))
5939          result)
5940     (when (and (not eobp)
5941                (or (not gnus-summary-check-current)
5942                    (not unread)
5943                    (not (gnus-data-unread-p (car arts)))))
5944       (setq arts (cdr arts)))
5945     (when (setq result
5946                 (if unread
5947                     (progn
5948                       (while arts
5949                         (when (gnus-data-unread-p (car arts))
5950                           (setq result (car arts)
5951                                 arts nil))
5952                         (setq arts (cdr arts)))
5953                       result)
5954                   (car arts)))
5955       (goto-char (gnus-data-pos result))
5956       (gnus-data-number result))))
5957
5958 (defun gnus-summary-find-subject (subject &optional unread backward article)
5959   (let* ((simp-subject (gnus-simplify-subject-fully subject))
5960          (article (or article (gnus-summary-article-number)))
5961          (articles (gnus-data-list backward))
5962          (arts (gnus-data-find-list article articles))
5963          result)
5964     (when (or (not gnus-summary-check-current)
5965               (not unread)
5966               (not (gnus-data-unread-p (car arts))))
5967       (setq arts (cdr arts)))
5968     (while arts
5969       (and (or (not unread)
5970                (gnus-data-unread-p (car arts)))
5971            (vectorp (gnus-data-header (car arts)))
5972            (gnus-subject-equal
5973             simp-subject (mail-header-subject (gnus-data-header (car arts))) t)
5974            (setq result (car arts)
5975                  arts nil))
5976       (setq arts (cdr arts)))
5977     (and result
5978          (goto-char (gnus-data-pos result))
5979          (gnus-data-number result))))
5980
5981 (defun gnus-summary-search-forward (&optional unread subject backward)
5982   "Search forward for an article.
5983 If UNREAD, look for unread articles.  If SUBJECT, look for
5984 articles with that subject.  If BACKWARD, search backward instead."
5985   (cond (subject (gnus-summary-find-subject subject unread backward))
5986         (backward (gnus-summary-find-prev unread))
5987         (t (gnus-summary-find-next unread))))
5988
5989 (defun gnus-recenter (&optional n)
5990   "Center point in window and redisplay frame.
5991 Also do horizontal recentering."
5992   (interactive "P")
5993   (when (and gnus-auto-center-summary
5994              (not (eq gnus-auto-center-summary 'vertical)))
5995     (gnus-horizontal-recenter))
5996   (recenter n))
5997
5998 (defun gnus-summary-recenter ()
5999   "Center point in the summary window.
6000 If `gnus-auto-center-summary' is nil, or the article buffer isn't
6001 displayed, no centering will be performed."
6002   ;; Suggested by earle@mahendo.JPL.NASA.GOV (Greg Earle).
6003 ;; Recenter only when requested.  Suggested by popovich@park.cs.columbia.edu.
6004   (interactive)
6005   (let* ((top (cond ((< (window-height) 4) 0)
6006                     ((< (window-height) 7) 1)
6007                     (t (if (numberp gnus-auto-center-summary)
6008                            gnus-auto-center-summary
6009                          2))))
6010          (height (1- (window-height)))
6011          (bottom (save-excursion (goto-char (point-max))
6012                                  (forward-line (- height))
6013                                  (point)))
6014          (window (get-buffer-window (current-buffer))))
6015     ;; The user has to want it.
6016     (when gnus-auto-center-summary
6017       (when (get-buffer-window gnus-article-buffer)
6018         ;; Only do recentering when the article buffer is displayed,
6019       ;; Set the window start to either `bottom', which is the biggest
6020         ;; possible valid number, or the second line from the top,
6021         ;; whichever is the least.
6022         (let ((top-pos (save-excursion (forward-line (- top)) (point))))
6023           (if (> bottom top-pos)
6024               ;; Keep the second line from the top visible
6025               (set-window-start window top-pos t)
6026             ;; Try to keep the bottom line visible; if it's partially
6027             ;; obscured, either scroll one more line to make it fully
6028             ;; visible, or revert to using TOP-POS.
6029             (save-excursion
6030               (goto-char (point-max))
6031               (forward-line -1)
6032               (let ((last-line-start (point)))
6033                 (goto-char bottom)
6034                 (set-window-start window (point) t)
6035                 (when (not (pos-visible-in-window-p last-line-start window))
6036                   (forward-line 1)
6037                   (set-window-start window (min (point) top-pos) t)))))))
6038       ;; Do horizontal recentering while we're at it.
6039       (when (and (get-buffer-window (current-buffer) t)
6040                  (not (eq gnus-auto-center-summary 'vertical)))
6041         (let ((selected (selected-window)))
6042           (select-window (get-buffer-window (current-buffer) t))
6043           (gnus-summary-position-point)
6044           (gnus-horizontal-recenter)
6045           (select-window selected))))))
6046
6047 (defun gnus-summary-jump-to-group (newsgroup)
6048   "Move point to NEWSGROUP in group mode buffer."
6049   ;; Keep update point of group mode buffer if visible.
6050   (if (eq (current-buffer) (get-buffer gnus-group-buffer))
6051       (save-window-excursion
6052         ;; Take care of tree window mode.
6053         (when (get-buffer-window gnus-group-buffer)
6054           (pop-to-buffer gnus-group-buffer))
6055         (gnus-group-jump-to-group newsgroup))
6056     (save-excursion
6057       ;; Take care of tree window mode.
6058       (if (get-buffer-window gnus-group-buffer)
6059           (pop-to-buffer gnus-group-buffer)
6060         (set-buffer gnus-group-buffer))
6061       (gnus-group-jump-to-group newsgroup))))
6062
6063 ;; This function returns a list of article numbers based on the
6064 ;; difference between the ranges of read articles in this group and
6065 ;; the range of active articles.
6066 (defun gnus-list-of-unread-articles (group)
6067   (let* ((read (gnus-info-read (gnus-get-info group)))
6068          (active (or (gnus-active group) (gnus-activate-group group)))
6069          (last (cdr active))
6070          first nlast unread)
6071     ;; If none are read, then all are unread.
6072     (if (not read)
6073         (setq first (car active))
6074       ;; If the range of read articles is a single range, then the
6075       ;; first unread article is the article after the last read
6076       ;; article.  Sounds logical, doesn't it?
6077       (if (and (not (listp (cdr read)))
6078                (or (< (car read) (car active))
6079                    (progn (setq read (list read))
6080                           nil)))
6081           (setq first (max (car active) (1+ (cdr read))))
6082         ;; `read' is a list of ranges.
6083         (when (/= (setq nlast (or (and (numberp (car read)) (car read))
6084                                   (caar read)))
6085                   1)
6086           (setq first (car active)))
6087         (while read
6088           (when first
6089             (while (< first nlast)
6090               (push first unread)
6091               (setq first (1+ first))))
6092           (setq first (1+ (if (atom (car read)) (car read) (cdar read))))
6093           (setq nlast (if (atom (cadr read)) (cadr read) (caadr read)))
6094           (setq read (cdr read)))))
6095     ;; And add the last unread articles.
6096     (while (<= first last)
6097       (push first unread)
6098       (setq first (1+ first)))
6099     ;; Return the list of unread articles.
6100     (delq 0 (nreverse unread))))
6101
6102 (defun gnus-list-of-read-articles (group)
6103   "Return a list of unread, unticked and non-dormant articles."
6104   (let* ((info (gnus-get-info group))
6105          (marked (gnus-info-marks info))
6106          (active (gnus-active group)))
6107     (and info active
6108          (gnus-list-range-difference
6109           (gnus-list-range-difference
6110            (gnus-sorted-complement
6111             (gnus-uncompress-range active)
6112             (gnus-list-of-unread-articles group))
6113            (cdr (assq 'dormant marked)))
6114           (cdr (assq 'tick marked))))))
6115
6116 ;; Various summary commands
6117
6118 (defun gnus-summary-select-article-buffer ()
6119   "Reconfigure windows to show article buffer."
6120   (interactive)
6121   (if (not (gnus-buffer-live-p gnus-article-buffer))
6122       (error "There is no article buffer for this summary buffer")
6123     (gnus-configure-windows 'article)
6124     (select-window (get-buffer-window gnus-article-buffer))))
6125
6126 (defun gnus-summary-universal-argument (arg)
6127   "Perform any operation on all articles that are process/prefixed."
6128   (interactive "P")
6129   (let ((articles (gnus-summary-work-articles arg))
6130         func article)
6131     (if (eq
6132          (setq
6133           func
6134           (key-binding
6135            (read-key-sequence
6136             (substitute-command-keys
6137              "\\<gnus-summary-mode-map>\\[gnus-summary-universal-argument]"))))
6138          'undefined)
6139         (gnus-error 1 "Undefined key")
6140       (save-excursion
6141         (while articles
6142           (gnus-summary-goto-subject (setq article (pop articles)))
6143           (let (gnus-newsgroup-processable)
6144             (command-execute func))
6145           (gnus-summary-remove-process-mark article)))))
6146   (gnus-summary-position-point))
6147
6148 (defun gnus-summary-toggle-truncation (&optional arg)
6149   "Toggle truncation of summary lines.
6150 With arg, turn line truncation on if arg is positive."
6151   (interactive "P")
6152   (setq truncate-lines
6153         (if (null arg) (not truncate-lines)
6154           (> (prefix-numeric-value arg) 0)))
6155   (redraw-display))
6156
6157 (defun gnus-summary-find-uncancelled ()
6158   "Return the number of an uncancelled article.
6159 The current article is considered, then following articles, then previous
6160 articles.  If all articles are cancelled then return a dummy 0."
6161   (let (found)
6162     (dolist (rev '(nil t))
6163       (unless found      ; don't demand the reverse list if we don't need it
6164         (let ((data (gnus-data-find-list
6165                      (gnus-summary-article-number) (gnus-data-list rev))))
6166           (while (and data (not found))
6167             (if (not (eq gnus-canceled-mark (gnus-data-mark (car data))))
6168                 (setq found (gnus-data-number (car data))))
6169             (setq data (cdr data))))))
6170     (or found 0)))
6171
6172 (defun gnus-summary-reselect-current-group (&optional all rescan)
6173   "Exit and then reselect the current newsgroup.
6174 The prefix argument ALL means to select all articles."
6175   (interactive "P")
6176   (when (gnus-ephemeral-group-p gnus-newsgroup-name)
6177     (error "Ephemeral groups can't be reselected"))
6178   (let ((current-subject (gnus-summary-find-uncancelled))
6179         (group gnus-newsgroup-name))
6180     (setq gnus-newsgroup-begin nil)
6181     (gnus-summary-exit)
6182     ;; We have to adjust the point of group mode buffer because
6183     ;; point was moved to the next unread newsgroup by exiting.
6184     (gnus-summary-jump-to-group group)
6185     (when rescan
6186       (save-excursion
6187         (gnus-group-get-new-news-this-group 1)))
6188     (gnus-group-read-group all t)
6189     (gnus-summary-goto-subject current-subject nil t)))
6190
6191 (defun gnus-summary-rescan-group (&optional all)
6192   "Exit the newsgroup, ask for new articles, and select the newsgroup."
6193   (interactive "P")
6194   (gnus-summary-reselect-current-group all t))
6195
6196 (defun gnus-summary-update-info (&optional non-destructive)
6197   (save-excursion
6198     (let ((group gnus-newsgroup-name))
6199       (when group
6200         (when gnus-newsgroup-kill-headers
6201           (setq gnus-newsgroup-killed
6202                 (gnus-compress-sequence
6203                  (gnus-sorted-union
6204                   (gnus-list-range-intersection
6205                    gnus-newsgroup-unselected gnus-newsgroup-killed)
6206                   gnus-newsgroup-unreads)
6207                  t)))
6208         (unless (listp (cdr gnus-newsgroup-killed))
6209           (setq gnus-newsgroup-killed (list gnus-newsgroup-killed)))
6210         (let ((headers gnus-newsgroup-headers))
6211           ;; Set the new ranges of read articles.
6212           (save-excursion
6213             (set-buffer gnus-group-buffer)
6214             (gnus-undo-force-boundary))
6215           (gnus-update-read-articles
6216            group (gnus-sorted-union
6217                   gnus-newsgroup-unreads gnus-newsgroup-unselected))
6218           ;; Set the current article marks.
6219           (let ((gnus-newsgroup-scored
6220                  (if (and (not gnus-save-score)
6221                           (not non-destructive))
6222                      nil
6223                    gnus-newsgroup-scored)))
6224             (save-excursion
6225               (gnus-update-marks)))
6226           ;; Do the cross-ref thing.
6227           (when gnus-use-cross-reference
6228             (gnus-mark-xrefs-as-read group headers gnus-newsgroup-unreads))
6229           ;; Do not switch windows but change the buffer to work.
6230           (set-buffer gnus-group-buffer)
6231           (unless (gnus-ephemeral-group-p group)
6232             (gnus-group-update-group group)))))))
6233
6234 (defun gnus-summary-save-newsrc (&optional force)
6235   "Save the current number of read/marked articles in the dribble buffer.
6236 The dribble buffer will then be saved.
6237 If FORCE (the prefix), also save the .newsrc file(s)."
6238   (interactive "P")
6239   (gnus-summary-update-info t)
6240   (if force
6241       (gnus-save-newsrc-file)
6242     (gnus-dribble-save)))
6243
6244 (defun gnus-summary-exit (&optional temporary)
6245   "Exit reading current newsgroup, and then return to group selection mode.
6246 `gnus-exit-group-hook' is called with no arguments if that value is non-nil."
6247   (interactive)
6248   (gnus-set-global-variables)
6249   (when (gnus-buffer-live-p gnus-article-buffer)
6250     (save-excursion
6251       (set-buffer gnus-article-buffer)
6252       (mm-destroy-parts gnus-article-mime-handles)
6253       ;; Set it to nil for safety reason.
6254       (setq gnus-article-mime-handle-alist nil)
6255       (setq gnus-article-mime-handles nil)))
6256   (gnus-kill-save-kill-buffer)
6257   (gnus-async-halt-prefetch)
6258   (let* ((group gnus-newsgroup-name)
6259          (quit-config (gnus-group-quit-config gnus-newsgroup-name))
6260          (mode major-mode)
6261          (group-point nil)
6262          (buf (current-buffer)))
6263     (unless quit-config
6264       ;; Do adaptive scoring, and possibly save score files.
6265       (when gnus-newsgroup-adaptive
6266         (gnus-score-adaptive))
6267       (when gnus-use-scoring
6268         (gnus-score-save)))
6269     (gnus-run-hooks 'gnus-summary-prepare-exit-hook)
6270     ;; If we have several article buffers, we kill them at exit.
6271     (unless gnus-single-article-buffer
6272       (gnus-kill-buffer gnus-original-article-buffer)
6273       (setq gnus-article-current nil))
6274     (when gnus-use-cache
6275       (gnus-cache-possibly-remove-articles)
6276       (gnus-cache-save-buffers))
6277     (gnus-async-prefetch-remove-group group)
6278     (when gnus-suppress-duplicates
6279       (gnus-dup-enter-articles))
6280     (when gnus-use-trees
6281       (gnus-tree-close group))
6282     (when gnus-use-cache
6283       (gnus-cache-write-active))
6284     ;; Remove entries for this group.
6285     (nnmail-purge-split-history (gnus-group-real-name group))
6286     ;; Make all changes in this group permanent.
6287     (unless quit-config
6288       (gnus-run-hooks 'gnus-exit-group-hook)
6289       (gnus-summary-update-info))
6290     (gnus-close-group group)
6291     ;; Make sure where we were, and go to next newsgroup.
6292     (set-buffer gnus-group-buffer)
6293     (unless quit-config
6294       (gnus-group-jump-to-group group))
6295     (gnus-run-hooks 'gnus-summary-exit-hook)
6296     (unless (or quit-config
6297                 ;; If this group has disappeared from the summary
6298                 ;; buffer, don't skip forwards.
6299                 (not (string= group (gnus-group-group-name))))
6300       (gnus-group-next-unread-group 1))
6301     (setq group-point (point))
6302     (if temporary
6303         nil                             ;Nothing to do.
6304       ;; If we have several article buffers, we kill them at exit.
6305       (unless gnus-single-article-buffer
6306         (gnus-kill-buffer gnus-article-buffer)
6307         (gnus-kill-buffer gnus-original-article-buffer)
6308         (setq gnus-article-current nil))
6309       (set-buffer buf)
6310       (if (not gnus-kill-summary-on-exit)
6311           (progn
6312             (gnus-deaden-summary)
6313             (setq mode nil))
6314        ;; We set all buffer-local variables to nil.  It is unclear why
6315         ;; this is needed, but if we don't, buffer-local variables are
6316         ;; not garbage-collected, it seems.  This would the lead to en
6317         ;; ever-growing Emacs.
6318         (gnus-summary-clear-local-variables)
6319         (let ((gnus-summary-local-variables gnus-newsgroup-variables))
6320           (gnus-summary-clear-local-variables))
6321         (when (get-buffer gnus-article-buffer)
6322           (bury-buffer gnus-article-buffer))
6323         ;; We clear the global counterparts of the buffer-local
6324         ;; variables as well, just to be on the safe side.
6325         (set-buffer gnus-group-buffer)
6326         (gnus-summary-clear-local-variables)
6327         (let ((gnus-summary-local-variables gnus-newsgroup-variables))
6328           (gnus-summary-clear-local-variables)))
6329       (setq gnus-current-select-method gnus-select-method)
6330       (pop-to-buffer gnus-group-buffer)
6331       (if (not quit-config)
6332           (progn
6333             (goto-char group-point)
6334             (gnus-configure-windows 'group 'force))
6335         (gnus-handle-ephemeral-exit quit-config))
6336       ;; Return to group mode buffer.
6337       (when (eq mode 'gnus-summary-mode)
6338         (gnus-kill-buffer buf))
6339       ;; Clear the current group name.
6340       (unless quit-config
6341         (setq gnus-newsgroup-name nil)))))
6342
6343 (defalias 'gnus-summary-quit 'gnus-summary-exit-no-update)
6344 (defun gnus-summary-exit-no-update (&optional no-questions)
6345   "Quit reading current newsgroup without updating read article info."
6346   (interactive)
6347   (let* ((group gnus-newsgroup-name)
6348          (quit-config (gnus-group-quit-config group)))
6349     (when (or no-questions
6350               gnus-expert-user
6351               (gnus-y-or-n-p "Discard changes to this group and exit? "))
6352       (gnus-async-halt-prefetch)
6353       (mapcar 'funcall
6354               (delq 'gnus-summary-expire-articles
6355                     (copy-sequence gnus-summary-prepare-exit-hook)))
6356       (when (gnus-buffer-live-p gnus-article-buffer)
6357         (save-excursion
6358           (set-buffer gnus-article-buffer)
6359           (mm-destroy-parts gnus-article-mime-handles)
6360           ;; Set it to nil for safety reason.
6361           (setq gnus-article-mime-handle-alist nil)
6362           (setq gnus-article-mime-handles nil)))
6363       ;; If we have several article buffers, we kill them at exit.
6364       (unless gnus-single-article-buffer
6365         (gnus-kill-buffer gnus-article-buffer)
6366         (gnus-kill-buffer gnus-original-article-buffer)
6367         (setq gnus-article-current nil))
6368       (if (not gnus-kill-summary-on-exit)
6369           (gnus-deaden-summary)
6370         (gnus-close-group group)
6371         (gnus-summary-clear-local-variables)
6372         (let ((gnus-summary-local-variables gnus-newsgroup-variables))
6373           (gnus-summary-clear-local-variables))
6374         (set-buffer gnus-group-buffer)
6375         (gnus-summary-clear-local-variables)
6376         (let ((gnus-summary-local-variables gnus-newsgroup-variables))
6377           (gnus-summary-clear-local-variables))
6378         (when (get-buffer gnus-summary-buffer)
6379           (kill-buffer gnus-summary-buffer)))
6380       (unless gnus-single-article-buffer
6381         (setq gnus-article-current nil))
6382       (when gnus-use-trees
6383         (gnus-tree-close group))
6384       (gnus-async-prefetch-remove-group group)
6385       (when (get-buffer gnus-article-buffer)
6386         (bury-buffer gnus-article-buffer))
6387       ;; Return to the group buffer.
6388       (gnus-configure-windows 'group 'force)
6389       ;; Clear the current group name.
6390       (setq gnus-newsgroup-name nil)
6391       (when (equal (gnus-group-group-name) group)
6392         (gnus-group-next-unread-group 1))
6393       (when quit-config
6394         (gnus-handle-ephemeral-exit quit-config)))))
6395
6396 (defun gnus-handle-ephemeral-exit (quit-config)
6397   "Handle movement when leaving an ephemeral group.
6398 The state which existed when entering the ephemeral is reset."
6399   (if (not (buffer-name (car quit-config)))
6400       (gnus-configure-windows 'group 'force)
6401     (set-buffer (car quit-config))
6402     (cond ((eq major-mode 'gnus-summary-mode)
6403            (gnus-set-global-variables))
6404           ((eq major-mode 'gnus-article-mode)
6405            (save-excursion
6406              ;; The `gnus-summary-buffer' variable may point
6407              ;; to the old summary buffer when using a single
6408              ;; article buffer.
6409              (unless (gnus-buffer-live-p gnus-summary-buffer)
6410                (set-buffer gnus-group-buffer))
6411              (set-buffer gnus-summary-buffer)
6412              (gnus-set-global-variables))))
6413     (if (or (eq (cdr quit-config) 'article)
6414             (eq (cdr quit-config) 'pick))
6415         (progn
6416           ;; The current article may be from the ephemeral group
6417           ;; thus it is best that we reload this article
6418           (gnus-summary-show-article)
6419           (if (and (boundp 'gnus-pick-mode) (symbol-value 'gnus-pick-mode))
6420               (gnus-configure-windows 'pick 'force)
6421             (gnus-configure-windows (cdr quit-config) 'force)))
6422       (gnus-configure-windows (cdr quit-config) 'force))
6423     (when (eq major-mode 'gnus-summary-mode)
6424       (gnus-summary-next-subject 1 nil t)
6425       (gnus-summary-recenter)
6426       (gnus-summary-position-point))))
6427
6428 ;;; Dead summaries.
6429
6430 (defvar gnus-dead-summary-mode-map nil)
6431
6432 (unless gnus-dead-summary-mode-map
6433   (setq gnus-dead-summary-mode-map (make-keymap))
6434   (suppress-keymap gnus-dead-summary-mode-map)
6435   (substitute-key-definition
6436    'undefined 'gnus-summary-wake-up-the-dead gnus-dead-summary-mode-map)
6437   (dolist (key '("\C-d" "\r" "\177" [delete]))
6438     (define-key gnus-dead-summary-mode-map
6439       key 'gnus-summary-wake-up-the-dead))
6440   (dolist (key '("q" "Q"))
6441     (define-key gnus-dead-summary-mode-map key 'bury-buffer)))
6442
6443 (defvar gnus-dead-summary-mode nil
6444   "Minor mode for Gnus summary buffers.")
6445
6446 (defun gnus-dead-summary-mode (&optional arg)
6447   "Minor mode for Gnus summary buffers."
6448   (interactive "P")
6449   (when (eq major-mode 'gnus-summary-mode)
6450     (make-local-variable 'gnus-dead-summary-mode)
6451     (setq gnus-dead-summary-mode
6452           (if (null arg) (not gnus-dead-summary-mode)
6453             (> (prefix-numeric-value arg) 0)))
6454     (when gnus-dead-summary-mode
6455       (gnus-add-minor-mode
6456        'gnus-dead-summary-mode " Dead" gnus-dead-summary-mode-map))))
6457
6458 (defun gnus-deaden-summary ()
6459   "Make the current summary buffer into a dead summary buffer."
6460   ;; Kill any previous dead summary buffer.
6461   (when (and gnus-dead-summary
6462              (buffer-name gnus-dead-summary))
6463     (save-excursion
6464       (set-buffer gnus-dead-summary)
6465       (when gnus-dead-summary-mode
6466         (kill-buffer (current-buffer)))))
6467   ;; Make this the current dead summary.
6468   (setq gnus-dead-summary (current-buffer))
6469   (gnus-dead-summary-mode 1)
6470   (let ((name (buffer-name)))
6471     (when (string-match "Summary" name)
6472       (rename-buffer
6473        (concat (substring name 0 (match-beginning 0)) "Dead "
6474                (substring name (match-beginning 0)))
6475        t)
6476       (bury-buffer))))
6477
6478 (defun gnus-kill-or-deaden-summary (buffer)
6479   "Kill or deaden the summary BUFFER."
6480   (save-excursion
6481     (when (and (buffer-name buffer)
6482                (not gnus-single-article-buffer))
6483       (save-excursion
6484         (set-buffer buffer)
6485         (gnus-kill-buffer gnus-article-buffer)
6486         (gnus-kill-buffer gnus-original-article-buffer)))
6487     (cond
6488      ;; Kill the buffer.
6489      (gnus-kill-summary-on-exit
6490       (when (and gnus-use-trees
6491                  (gnus-buffer-exists-p buffer))
6492         (save-excursion
6493           (set-buffer buffer)
6494           (gnus-tree-close gnus-newsgroup-name)))
6495       (gnus-kill-buffer buffer))
6496      ;; Deaden the buffer.
6497      ((gnus-buffer-exists-p buffer)
6498       (save-excursion
6499         (set-buffer buffer)
6500         (gnus-deaden-summary))))))
6501
6502 (defun gnus-summary-wake-up-the-dead (&rest args)
6503   "Wake up the dead summary buffer."
6504   (interactive)
6505   (gnus-dead-summary-mode -1)
6506   (let ((name (buffer-name)))
6507     (when (string-match "Dead " name)
6508       (rename-buffer
6509        (concat (substring name 0 (match-beginning 0))
6510                (substring name (match-end 0)))
6511        t)))
6512   (gnus-message 3 "This dead summary is now alive again"))
6513
6514 ;; Suggested by Andrew Eskilsson <pi92ae@pt.hk-r.se>.
6515 (defun gnus-summary-fetch-faq (&optional faq-dir)
6516   "Fetch the FAQ for the current group.
6517 If FAQ-DIR (the prefix), prompt for a directory to search for the faq
6518 in."
6519   (interactive
6520    (list
6521     (when current-prefix-arg
6522       (completing-read
6523        "FAQ dir: " (and (listp gnus-group-faq-directory)
6524                         (mapcar (lambda (file) (list file))
6525                                 gnus-group-faq-directory))))))
6526   (let (gnus-faq-buffer)
6527     (when (setq gnus-faq-buffer
6528                 (gnus-group-fetch-faq gnus-newsgroup-name faq-dir))
6529       (gnus-configure-windows 'summary-faq))))
6530
6531 ;; Suggested by Per Abrahamsen <amanda@iesd.auc.dk>.
6532 (defun gnus-summary-describe-group (&optional force)
6533   "Describe the current newsgroup."
6534   (interactive "P")
6535   (gnus-group-describe-group force gnus-newsgroup-name))
6536
6537 (defun gnus-summary-describe-briefly ()
6538   "Describe summary mode commands briefly."
6539   (interactive)
6540   (gnus-message 6 (substitute-command-keys "\\<gnus-summary-mode-map>\\[gnus-summary-next-page]:Select  \\[gnus-summary-next-unread-article]:Forward  \\[gnus-summary-prev-unread-article]:Backward  \\[gnus-summary-exit]:Exit  \\[gnus-info-find-node]:Run Info        \\[gnus-summary-describe-briefly]:This help")))
6541
6542 ;; Walking around group mode buffer from summary mode.
6543
6544 (defun gnus-summary-next-group (&optional no-article target-group backward)
6545   "Exit current newsgroup and then select next unread newsgroup.
6546 If prefix argument NO-ARTICLE is non-nil, no article is selected
6547 initially.  If NEXT-GROUP, go to this group.  If BACKWARD, go to
6548 previous group instead."
6549   (interactive "P")
6550   ;; Stop pre-fetching.
6551   (gnus-async-halt-prefetch)
6552   (let ((current-group gnus-newsgroup-name)
6553         (current-buffer (current-buffer))
6554         entered)
6555     (while (not entered)
6556       ;; Then we find what group we are supposed to enter.
6557       (set-buffer gnus-group-buffer)
6558       (gnus-group-jump-to-group current-group)
6559       (setq target-group
6560             (or target-group
6561                 (if (eq gnus-keep-same-level 'best)
6562                     (gnus-summary-best-group gnus-newsgroup-name)
6563                   (gnus-summary-search-group backward gnus-keep-same-level))))
6564       (if (not target-group)
6565           ;; There are no further groups, so we return to the group
6566           ;; buffer.
6567           (progn
6568             (gnus-message 5 "Returning to the group buffer")
6569             (setq entered t)
6570             (when (gnus-buffer-live-p current-buffer)
6571               (set-buffer current-buffer)
6572               (gnus-summary-exit))
6573             (gnus-run-hooks 'gnus-group-no-more-groups-hook))
6574         ;; We try to enter the target group.
6575         (gnus-group-jump-to-group target-group)
6576         (let ((unreads (gnus-group-group-unread)))
6577           (if (and (or (eq t unreads)
6578                        (and unreads (not (zerop unreads))))
6579                    (progn
6580                      ;; Now we semi-exit this group to update Xrefs
6581                      ;; and all variables.  We can't do a real exit,
6582                      ;; because the window conf must remain the same
6583                      ;; in case the user is prompted for info, and we
6584                      ;; don't want the window conf to change before
6585                      ;; that...
6586                      (when (gnus-buffer-live-p current-buffer)
6587                        (set-buffer current-buffer)
6588                        (gnus-summary-exit t))
6589                      (gnus-summary-read-group
6590                       target-group nil no-article
6591                       (and (buffer-name current-buffer) current-buffer)
6592                       nil backward)))
6593               (setq entered t)
6594             (setq current-group target-group
6595                   target-group nil)))))))
6596
6597 (defun gnus-summary-prev-group (&optional no-article)
6598   "Exit current newsgroup and then select previous unread newsgroup.
6599 If prefix argument NO-ARTICLE is non-nil, no article is selected initially."
6600   (interactive "P")
6601   (gnus-summary-next-group no-article nil t))
6602
6603 ;; Walking around summary lines.
6604
6605 (defun gnus-summary-first-subject (&optional unread undownloaded unseen)
6606   "Go to the first unread subject.
6607 If UNREAD is non-nil, go to the first unread article.
6608 Returns the article selected or nil if there are no unread articles."
6609   (interactive "P")
6610   (prog1
6611       (cond
6612        ;; Empty summary.
6613        ((null gnus-newsgroup-data)
6614         (gnus-message 3 "No articles in the group")
6615         nil)
6616        ;; Pick the first article.
6617        ((not unread)
6618         (goto-char (gnus-data-pos (car gnus-newsgroup-data)))
6619         (gnus-data-number (car gnus-newsgroup-data)))
6620        ;; No unread articles.
6621        ((null gnus-newsgroup-unreads)
6622         (gnus-message 3 "No more unread articles")
6623         nil)
6624        ;; Find the first unread article.
6625        (t
6626         (let ((data gnus-newsgroup-data))
6627           (while (and data
6628                       (and (not (and undownloaded
6629                                      (eq gnus-undownloaded-mark
6630                                          (gnus-data-mark (car data)))))
6631                            (if unseen
6632                                (or (not (memq
6633                                          (gnus-data-number (car data))
6634                                          gnus-newsgroup-unseen))
6635                                    (not (gnus-data-unread-p (car data))))
6636                              (not (gnus-data-unread-p (car data))))))
6637             (setq data (cdr data)))
6638           (when data
6639             (goto-char (gnus-data-pos (car data)))
6640             (gnus-data-number (car data))))))
6641     (gnus-summary-position-point)))
6642
6643 (defun gnus-summary-next-subject (n &optional unread dont-display)
6644   "Go to next N'th summary line.
6645 If N is negative, go to the previous N'th subject line.
6646 If UNREAD is non-nil, only unread articles are selected.
6647 The difference between N and the actual number of steps taken is
6648 returned."
6649   (interactive "p")
6650   (let ((backward (< n 0))
6651         (n (abs n)))
6652     (while (and (> n 0)
6653                 (if backward
6654                     (gnus-summary-find-prev unread)
6655                   (gnus-summary-find-next unread)))
6656       (unless (zerop (setq n (1- n)))
6657         (gnus-summary-show-thread)))
6658     (when (/= 0 n)
6659       (gnus-message 7 "No more%s articles"
6660                     (if unread " unread" "")))
6661     (unless dont-display
6662       (gnus-summary-recenter)
6663       (gnus-summary-position-point))
6664     n))
6665
6666 (defun gnus-summary-next-unread-subject (n)
6667   "Go to next N'th unread summary line."
6668   (interactive "p")
6669   (gnus-summary-next-subject n t))
6670
6671 (defun gnus-summary-prev-subject (n &optional unread)
6672   "Go to previous N'th summary line.
6673 If optional argument UNREAD is non-nil, only unread article is selected."
6674   (interactive "p")
6675   (gnus-summary-next-subject (- n) unread))
6676
6677 (defun gnus-summary-prev-unread-subject (n)
6678   "Go to previous N'th unread summary line."
6679   (interactive "p")
6680   (gnus-summary-next-subject (- n) t))
6681
6682 (defun gnus-summary-goto-subject (article &optional force silent)
6683   "Go the subject line of ARTICLE.
6684 If FORCE, also allow jumping to articles not currently shown."
6685   (interactive "nArticle number: ")
6686   (unless (numberp article)
6687     (error "Article %s is not a number" article))
6688   (let ((b (point))
6689         (data (gnus-data-find article)))
6690     ;; We read in the article if we have to.
6691     (and (not data)
6692          force
6693          (gnus-summary-insert-subject
6694           article
6695           (if (or (numberp force) (vectorp force)) force)
6696           t)
6697          (setq data (gnus-data-find article)))
6698     (goto-char b)
6699     (if (not data)
6700         (progn
6701           (unless silent
6702             (gnus-message 3 "Can't find article %d" article))
6703           nil)
6704       (let ((pt (gnus-data-pos data)))
6705         (goto-char pt)
6706         (gnus-summary-set-article-display-arrow pt))
6707       (gnus-summary-position-point)
6708       article)))
6709
6710 ;; Walking around summary lines with displaying articles.
6711
6712 (defun gnus-summary-expand-window (&optional arg)
6713   "Make the summary buffer take up the entire Emacs frame.
6714 Given a prefix, will force an `article' buffer configuration."
6715   (interactive "P")
6716   (if arg
6717       (gnus-configure-windows 'article 'force)
6718     (gnus-configure-windows 'summary 'force)))
6719
6720 (defun gnus-summary-display-article (article &optional all-header)
6721   "Display ARTICLE in article buffer."
6722   (when (gnus-buffer-live-p gnus-article-buffer)
6723     (with-current-buffer gnus-article-buffer
6724       (mm-enable-multibyte)))
6725   (gnus-set-global-variables)
6726   (when (gnus-buffer-live-p gnus-article-buffer)
6727     (with-current-buffer gnus-article-buffer
6728       (setq gnus-article-charset gnus-newsgroup-charset)
6729       (setq gnus-article-ignored-charsets gnus-newsgroup-ignored-charsets)
6730       (mm-enable-multibyte)))
6731   (if (null article)
6732       nil
6733     (prog1
6734         (if gnus-summary-display-article-function
6735             (funcall gnus-summary-display-article-function article all-header)
6736           (gnus-article-prepare article all-header))
6737       (gnus-run-hooks 'gnus-select-article-hook)
6738       (when (and gnus-current-article
6739                  (not (zerop gnus-current-article)))
6740         (gnus-summary-goto-subject gnus-current-article))
6741       (gnus-summary-recenter)
6742       (when (and gnus-use-trees gnus-show-threads)
6743         (gnus-possibly-generate-tree article)
6744         (gnus-highlight-selected-tree article))
6745       ;; Successfully display article.
6746       (gnus-article-set-window-start
6747        (cdr (assq article gnus-newsgroup-bookmarks))))))
6748
6749 (defun gnus-summary-select-article (&optional all-headers force pseudo article)
6750   "Select the current article.
6751 If ALL-HEADERS is non-nil, show all header fields.  If FORCE is
6752 non-nil, the article will be re-fetched even if it already present in
6753 the article buffer.  If PSEUDO is non-nil, pseudo-articles will also
6754 be displayed."
6755   ;; Make sure we are in the summary buffer to work around bbdb bug.
6756   (unless (eq major-mode 'gnus-summary-mode)
6757     (set-buffer gnus-summary-buffer))
6758   (let ((article (or article (gnus-summary-article-number)))
6759         (all-headers (not (not all-headers))) ;Must be t or nil.
6760         gnus-summary-display-article-function)
6761     (and (not pseudo)
6762          (gnus-summary-article-pseudo-p article)
6763          (error "This is a pseudo-article"))
6764     (save-excursion
6765       (set-buffer gnus-summary-buffer)
6766       (if (or (and gnus-single-article-buffer
6767                    (or (null gnus-current-article)
6768                        (null gnus-article-current)
6769                        (null (get-buffer gnus-article-buffer))
6770                        (not (eq article (cdr gnus-article-current)))
6771                        (not (equal (car gnus-article-current)
6772                                    gnus-newsgroup-name))))
6773               (and (not gnus-single-article-buffer)
6774                    (or (null gnus-current-article)
6775                        (not (eq gnus-current-article article))))
6776               force)
6777           ;; The requested article is different from the current article.
6778           (progn
6779             (gnus-summary-display-article article all-headers)
6780             (when (gnus-buffer-live-p gnus-article-buffer)
6781               (with-current-buffer gnus-article-buffer
6782                 (if (not gnus-article-decoded-p) ;; a local variable
6783                     (mm-disable-multibyte))))
6784             (gnus-article-set-window-start
6785              (cdr (assq article gnus-newsgroup-bookmarks)))
6786             article)
6787         'old))))
6788
6789 (defun gnus-summary-force-verify-and-decrypt ()
6790   (interactive)
6791   (let ((mm-verify-option 'known)
6792         (mm-decrypt-option 'known)
6793         (gnus-buttonized-mime-types (append (list "multipart/signed"
6794                                                   "multipart/encrypted")
6795                                             gnus-buttonized-mime-types)))
6796     (gnus-summary-select-article nil 'force)))
6797
6798 (defun gnus-summary-set-current-mark (&optional current-mark)
6799   "Obsolete function."
6800   nil)
6801
6802 (defun gnus-summary-next-article (&optional unread subject backward push)
6803   "Select the next article.
6804 If UNREAD, only unread articles are selected.
6805 If SUBJECT, only articles with SUBJECT are selected.
6806 If BACKWARD, the previous article is selected instead of the next."
6807   (interactive "P")
6808   (cond
6809    ;; Is there such an article?
6810    ((and (gnus-summary-search-forward unread subject backward)
6811          (or (gnus-summary-display-article (gnus-summary-article-number))
6812              (eq (gnus-summary-article-mark) gnus-canceled-mark)))
6813     (gnus-summary-position-point))
6814    ;; If not, we try the first unread, if that is wanted.
6815    ((and subject
6816          gnus-auto-select-same
6817          (gnus-summary-first-unread-article))
6818     (gnus-summary-position-point)
6819     (gnus-message 6 "Wrapped"))
6820    ;; Try to get next/previous article not displayed in this group.
6821    ((and gnus-auto-extend-newsgroup
6822          (not unread) (not subject))
6823     (gnus-summary-goto-article
6824      (if backward (1- gnus-newsgroup-begin) (1+ gnus-newsgroup-end))
6825      nil (count-lines (point-min) (point))))
6826    ;; Go to next/previous group.
6827    (t
6828     (unless (gnus-ephemeral-group-p gnus-newsgroup-name)
6829       (gnus-summary-jump-to-group gnus-newsgroup-name))
6830     (let ((cmd last-command-char)
6831           (point
6832            (save-excursion
6833              (set-buffer gnus-group-buffer)
6834              (point)))
6835           (group
6836            (if (eq gnus-keep-same-level 'best)
6837                (gnus-summary-best-group gnus-newsgroup-name)
6838              (gnus-summary-search-group backward gnus-keep-same-level))))
6839       ;; For some reason, the group window gets selected.  We change
6840       ;; it back.
6841       (select-window (get-buffer-window (current-buffer)))
6842       ;; Select next unread newsgroup automagically.
6843       (cond
6844        ((or (not gnus-auto-select-next)
6845             (not cmd))
6846         (gnus-message 7 "No more%s articles" (if unread " unread" "")))
6847        ((or (eq gnus-auto-select-next 'quietly)
6848             (and (eq gnus-auto-select-next 'slightly-quietly)
6849                  push)
6850             (and (eq gnus-auto-select-next 'almost-quietly)
6851                  (gnus-summary-last-article-p)))
6852         ;; Select quietly.
6853         (if (gnus-ephemeral-group-p gnus-newsgroup-name)
6854             (gnus-summary-exit)
6855           (gnus-message 7 "No more%s articles (%s)..."
6856                         (if unread " unread" "")
6857                         (if group (concat "selecting " group)
6858                           "exiting"))
6859           (gnus-summary-next-group nil group backward)))
6860        (t
6861         (when (gnus-key-press-event-p last-input-event)
6862           (gnus-summary-walk-group-buffer
6863            gnus-newsgroup-name cmd unread backward point))))))))
6864
6865 (defun gnus-summary-walk-group-buffer (from-group cmd unread backward start)
6866   (let ((keystrokes '((?\C-n (gnus-group-next-unread-group 1))
6867                       (?\C-p (gnus-group-prev-unread-group 1))))
6868         (cursor-in-echo-area t)
6869         keve key group ended)
6870     (save-excursion
6871       (set-buffer gnus-group-buffer)
6872       (goto-char start)
6873       (setq group
6874             (if (eq gnus-keep-same-level 'best)
6875                 (gnus-summary-best-group gnus-newsgroup-name)
6876               (gnus-summary-search-group backward gnus-keep-same-level))))
6877     (while (not ended)
6878       (gnus-message
6879        5 "No more%s articles%s" (if unread " unread" "")
6880        (if (and group
6881                 (not (gnus-ephemeral-group-p gnus-newsgroup-name)))
6882            (format " (Type %s for %s [%s])"
6883                    (single-key-description cmd) group
6884                    (car (gnus-gethash group gnus-newsrc-hashtb)))
6885          (format " (Type %s to exit %s)"
6886                  (single-key-description cmd)
6887                  gnus-newsgroup-name)))
6888       ;; Confirm auto selection.
6889       (setq key (car (setq keve (gnus-read-event-char))))
6890       (setq ended t)
6891       (cond
6892        ((assq key keystrokes)
6893         (let ((obuf (current-buffer)))
6894           (switch-to-buffer gnus-group-buffer)
6895           (when group
6896             (gnus-group-jump-to-group group))
6897           (eval (cadr (assq key keystrokes)))
6898           (setq group (gnus-group-group-name))
6899           (switch-to-buffer obuf))
6900         (setq ended nil))
6901        ((equal key cmd)
6902         (if (or (not group)
6903                 (gnus-ephemeral-group-p gnus-newsgroup-name))
6904             (gnus-summary-exit)
6905           (gnus-summary-next-group nil group backward)))
6906        (t
6907         (push (cdr keve) unread-command-events))))))
6908
6909 (defun gnus-summary-next-unread-article ()
6910   "Select unread article after current one."
6911   (interactive)
6912   (gnus-summary-next-article
6913    (or (not (eq gnus-summary-goto-unread 'never))
6914        (gnus-summary-last-article-p (gnus-summary-article-number)))
6915    (and gnus-auto-select-same
6916         (gnus-summary-article-subject))))
6917
6918 (defun gnus-summary-prev-article (&optional unread subject)
6919   "Select the article after the current one.
6920 If UNREAD is non-nil, only unread articles are selected."
6921   (interactive "P")
6922   (gnus-summary-next-article unread subject t))
6923
6924 (defun gnus-summary-prev-unread-article ()
6925   "Select unread article before current one."
6926   (interactive)
6927   (gnus-summary-prev-article
6928    (or (not (eq gnus-summary-goto-unread 'never))
6929        (gnus-summary-first-article-p (gnus-summary-article-number)))
6930    (and gnus-auto-select-same
6931         (gnus-summary-article-subject))))
6932
6933 (defun gnus-summary-next-page (&optional lines circular)
6934   "Show next page of the selected article.
6935 If at the end of the current article, select the next article.
6936 LINES says how many lines should be scrolled up.
6937
6938 If CIRCULAR is non-nil, go to the start of the article instead of
6939 selecting the next article when reaching the end of the current
6940 article."
6941   (interactive "P")
6942   (setq gnus-summary-buffer (current-buffer))
6943   (gnus-set-global-variables)
6944   (let ((article (gnus-summary-article-number))
6945         (article-window (get-buffer-window gnus-article-buffer t))
6946         endp)
6947     ;; If the buffer is empty, we have no article.
6948     (unless article
6949       (error "No article to select"))
6950     (gnus-configure-windows 'article)
6951     (if (eq (cdr (assq article gnus-newsgroup-reads)) gnus-canceled-mark)
6952         (if (and (eq gnus-summary-goto-unread 'never)
6953                  (not (gnus-summary-last-article-p article)))
6954             (gnus-summary-next-article)
6955           (gnus-summary-next-unread-article))
6956       (if (or (null gnus-current-article)
6957               (null gnus-article-current)
6958               (/= article (cdr gnus-article-current))
6959               (not (equal (car gnus-article-current) gnus-newsgroup-name)))
6960           ;; Selected subject is different from current article's.
6961           (gnus-summary-display-article article)
6962         (when article-window
6963           (gnus-eval-in-buffer-window gnus-article-buffer
6964             (setq endp (gnus-article-next-page lines)))
6965           (when endp
6966             (cond (circular
6967                    (gnus-summary-beginning-of-article))
6968                   (lines
6969                    (gnus-message 3 "End of message"))
6970                   ((null lines)
6971                    (if (and (eq gnus-summary-goto-unread 'never)
6972                             (not (gnus-summary-last-article-p article)))
6973                        (gnus-summary-next-article)
6974                      (gnus-summary-next-unread-article))))))))
6975     (gnus-summary-recenter)
6976     (gnus-summary-position-point)))
6977
6978 (defun gnus-summary-prev-page (&optional lines move)
6979   "Show previous page of selected article.
6980 Argument LINES specifies lines to be scrolled down.
6981 If MOVE, move to the previous unread article if point is at
6982 the beginning of the buffer."
6983   (interactive "P")
6984   (let ((article (gnus-summary-article-number))
6985         (article-window (get-buffer-window gnus-article-buffer t))
6986         endp)
6987     (gnus-configure-windows 'article)
6988     (if (or (null gnus-current-article)
6989             (null gnus-article-current)
6990             (/= article (cdr gnus-article-current))
6991             (not (equal (car gnus-article-current) gnus-newsgroup-name)))
6992         ;; Selected subject is different from current article's.
6993         (gnus-summary-display-article article)
6994       (gnus-summary-recenter)
6995       (when article-window
6996         (gnus-eval-in-buffer-window gnus-article-buffer
6997           (setq endp (gnus-article-prev-page lines)))
6998         (when (and move endp)
6999           (cond (lines
7000                  (gnus-message 3 "Beginning of message"))
7001                 ((null lines)
7002                  (if (and (eq gnus-summary-goto-unread 'never)
7003                           (not (gnus-summary-first-article-p article)))
7004                      (gnus-summary-prev-article)
7005                    (gnus-summary-prev-unread-article))))))))
7006   (gnus-summary-position-point))
7007
7008 (defun gnus-summary-prev-page-or-article (&optional lines)
7009   "Show previous page of selected article.
7010 Argument LINES specifies lines to be scrolled down.
7011 If at the beginning of the article, go to the next article."
7012   (interactive "P")
7013   (gnus-summary-prev-page lines t))
7014
7015 (defun gnus-summary-scroll-up (lines)
7016   "Scroll up (or down) one line current article.
7017 Argument LINES specifies lines to be scrolled up (or down if negative)."
7018   (interactive "p")
7019   (gnus-configure-windows 'article)
7020   (gnus-summary-show-thread)
7021   (when (eq (gnus-summary-select-article nil nil 'pseudo) 'old)
7022     (gnus-eval-in-buffer-window gnus-article-buffer
7023       (cond ((> lines 0)
7024              (when (gnus-article-next-page lines)
7025                (gnus-message 3 "End of message")))
7026             ((< lines 0)
7027              (gnus-article-prev-page (- lines))))))
7028   (gnus-summary-recenter)
7029   (gnus-summary-position-point))
7030
7031 (defun gnus-summary-scroll-down (lines)
7032   "Scroll down (or up) one line current article.
7033 Argument LINES specifies lines to be scrolled down (or up if negative)."
7034   (interactive "p")
7035   (gnus-summary-scroll-up (- lines)))
7036
7037 (defun gnus-summary-next-same-subject ()
7038   "Select next article which has the same subject as current one."
7039   (interactive)
7040   (gnus-summary-next-article nil (gnus-summary-article-subject)))
7041
7042 (defun gnus-summary-prev-same-subject ()
7043   "Select previous article which has the same subject as current one."
7044   (interactive)
7045   (gnus-summary-prev-article nil (gnus-summary-article-subject)))
7046
7047 (defun gnus-summary-next-unread-same-subject ()
7048   "Select next unread article which has the same subject as current one."
7049   (interactive)
7050   (gnus-summary-next-article t (gnus-summary-article-subject)))
7051
7052 (defun gnus-summary-prev-unread-same-subject ()
7053   "Select previous unread article which has the same subject as current one."
7054   (interactive)
7055   (gnus-summary-prev-article t (gnus-summary-article-subject)))
7056
7057 (defun gnus-summary-first-unread-article ()
7058   "Select the first unread article.
7059 Return nil if there are no unread articles."
7060   (interactive)
7061   (prog1
7062       (when (gnus-summary-first-subject t)
7063         (gnus-summary-show-thread)
7064         (gnus-summary-first-subject t)
7065         (gnus-summary-display-article (gnus-summary-article-number)))
7066     (gnus-summary-position-point)))
7067
7068 (defun gnus-summary-first-unread-subject ()
7069   "Place the point on the subject line of the first unread article.
7070 Return nil if there are no unread articles."
7071   (interactive)
7072   (prog1
7073       (when (gnus-summary-first-subject t)
7074         (gnus-summary-show-thread)
7075         (gnus-summary-first-subject t))
7076     (gnus-summary-position-point)))
7077
7078 (defun gnus-summary-first-unseen-subject ()
7079   "Place the point on the subject line of the first unseen article.
7080 Return nil if there are no unseen articles."
7081   (interactive)
7082   (prog1
7083       (when (gnus-summary-first-subject t t t)
7084         (gnus-summary-show-thread)
7085         (gnus-summary-first-subject t t t))
7086     (gnus-summary-position-point)))
7087
7088 (defun gnus-summary-first-unseen-or-unread-subject ()
7089   "Place the point on the subject line of the first unseen article.
7090 Return nil if there are no unseen articles."
7091   (interactive)
7092   (prog1
7093       (unless (when (gnus-summary-first-subject t t t)
7094                 (gnus-summary-show-thread)
7095                 (gnus-summary-first-subject t t t))
7096         (when (gnus-summary-first-subject t)
7097           (gnus-summary-show-thread)
7098           (gnus-summary-first-subject t)))
7099     (gnus-summary-position-point)))
7100
7101 (defun gnus-summary-first-article ()
7102   "Select the first article.
7103 Return nil if there are no articles."
7104   (interactive)
7105   (prog1
7106       (when (gnus-summary-first-subject)
7107         (gnus-summary-show-thread)
7108         (gnus-summary-first-subject)
7109         (gnus-summary-display-article (gnus-summary-article-number)))
7110     (gnus-summary-position-point)))
7111
7112 (defun gnus-summary-best-unread-article (&optional arg)
7113   "Select the unread article with the highest score.
7114 If given a prefix argument, select the next unread article that has a
7115 score higher than the default score."
7116   (interactive "P")
7117   (let ((article (if arg
7118                      (gnus-summary-better-unread-subject)
7119                    (gnus-summary-best-unread-subject))))
7120     (if article
7121         (gnus-summary-goto-article article)
7122       (error "No unread articles"))))
7123
7124 (defun gnus-summary-best-unread-subject ()
7125   "Select the unread subject with the highest score."
7126   (interactive)
7127   (let ((best -1000000)
7128         (data gnus-newsgroup-data)
7129         article score)
7130     (while data
7131       (and (gnus-data-unread-p (car data))
7132            (> (setq score
7133                     (gnus-summary-article-score (gnus-data-number (car data))))
7134               best)
7135            (setq best score
7136                  article (gnus-data-number (car data))))
7137       (setq data (cdr data)))
7138     (when article
7139       (gnus-summary-goto-subject article))
7140     (gnus-summary-position-point)
7141     article))
7142
7143 (defun gnus-summary-better-unread-subject ()
7144   "Select the first unread subject that has a score over the default score."
7145   (interactive)
7146   (let ((data gnus-newsgroup-data)
7147         article score)
7148     (while (and (setq article (gnus-data-number (car data)))
7149                 (or (gnus-data-read-p (car data))
7150                     (not (> (gnus-summary-article-score article)
7151                             gnus-summary-default-score))))
7152       (setq data (cdr data)))
7153     (when article
7154       (gnus-summary-goto-subject article))
7155     (gnus-summary-position-point)
7156     article))
7157
7158 (defun gnus-summary-last-subject ()
7159   "Go to the last displayed subject line in the group."
7160   (let ((article (gnus-data-number (car (gnus-data-list t)))))
7161     (when article
7162       (gnus-summary-goto-subject article))))
7163
7164 (defun gnus-summary-goto-article (article &optional all-headers force)
7165   "Fetch ARTICLE (article number or Message-ID) and display it if it exists.
7166 If ALL-HEADERS is non-nil, no header lines are hidden.
7167 If FORCE, go to the article even if it isn't displayed.  If FORCE
7168 is a number, it is the line the article is to be displayed on."
7169   (interactive
7170    (list
7171     (completing-read
7172      "Article number or Message-ID: "
7173      (mapcar (lambda (number) (list (int-to-string number)))
7174              gnus-newsgroup-limit))
7175     current-prefix-arg
7176     t))
7177   (prog1
7178       (if (and (stringp article)
7179                (string-match "@" article))
7180           (gnus-summary-refer-article article)
7181         (when (stringp article)
7182           (setq article (string-to-number article)))
7183         (if (gnus-summary-goto-subject article force)
7184             (gnus-summary-display-article article all-headers)
7185           (gnus-message 4 "Couldn't go to article %s" article) nil))
7186     (gnus-summary-position-point)))
7187
7188 (defun gnus-summary-goto-last-article ()
7189   "Go to the previously read article."
7190   (interactive)
7191   (prog1
7192       (when gnus-last-article
7193         (gnus-summary-goto-article gnus-last-article nil t))
7194     (gnus-summary-position-point)))
7195
7196 (defun gnus-summary-pop-article (number)
7197   "Pop one article off the history and go to the previous.
7198 NUMBER articles will be popped off."
7199   (interactive "p")
7200   (let (to)
7201     (setq gnus-newsgroup-history
7202           (cdr (setq to (nthcdr number gnus-newsgroup-history))))
7203     (if to
7204         (gnus-summary-goto-article (car to) nil t)
7205       (error "Article history empty")))
7206   (gnus-summary-position-point))
7207
7208 ;; Summary commands and functions for limiting the summary buffer.
7209
7210 (defun gnus-summary-limit-to-articles (n)
7211   "Limit the summary buffer to the next N articles.
7212 If not given a prefix, use the process marked articles instead."
7213   (interactive "P")
7214   (prog1
7215       (let ((articles (gnus-summary-work-articles n)))
7216         (setq gnus-newsgroup-processable nil)
7217         (gnus-summary-limit articles))
7218     (gnus-summary-position-point)))
7219
7220 (defun gnus-summary-pop-limit (&optional total)
7221   "Restore the previous limit.
7222 If given a prefix, remove all limits."
7223   (interactive "P")
7224   (when total
7225     (setq gnus-newsgroup-limits
7226           (list (mapcar (lambda (h) (mail-header-number h))
7227                         gnus-newsgroup-headers))))
7228   (unless gnus-newsgroup-limits
7229     (error "No limit to pop"))
7230   (prog1
7231       (gnus-summary-limit nil 'pop)
7232     (gnus-summary-position-point)))
7233
7234 (defun gnus-summary-limit-to-subject (subject &optional header not-matching)
7235   "Limit the summary buffer to articles that have subjects that match a regexp.
7236 If NOT-MATCHING, excluding articles that have subjects that match a regexp."
7237   (interactive
7238    (list (read-string (if current-prefix-arg
7239                           "Exclude subject (regexp): "
7240                         "Limit to subject (regexp): "))
7241          nil current-prefix-arg))
7242   (unless header
7243     (setq header "subject"))
7244   (when (not (equal "" subject))
7245     (prog1
7246         (let ((articles (gnus-summary-find-matching
7247                          (or header "subject") subject 'all nil nil
7248                          not-matching)))
7249           (unless articles
7250             (error "Found no matches for \"%s\"" subject))
7251           (gnus-summary-limit articles))
7252       (gnus-summary-position-point))))
7253
7254 (defun gnus-summary-limit-to-author (from &optional not-matching)
7255   "Limit the summary buffer to articles that have authors that match a regexp.
7256 If NOT-MATCHING, excluding articles that have authors that match a regexp."
7257   (interactive
7258    (list (read-string (if current-prefix-arg
7259                           "Exclude author (regexp): "
7260                         "Limit to author (regexp): "))
7261          current-prefix-arg))
7262   (gnus-summary-limit-to-subject from "from" not-matching))
7263
7264 (defun gnus-summary-limit-to-age (age &optional younger-p)
7265   "Limit the summary buffer to articles that are older than (or equal) AGE days.
7266 If YOUNGER-P (the prefix) is non-nil, limit the summary buffer to
7267 articles that are younger than AGE days."
7268   (interactive
7269    (let ((younger current-prefix-arg)
7270          (days-got nil)
7271          days)
7272      (while (not days-got)
7273        (setq days (if younger
7274                       (read-string "Limit to articles within (in days): ")
7275                     (read-string "Limit to articles older than (in days): ")))
7276        (when (> (length days) 0)
7277          (setq days (read days)))
7278        (if (numberp days)
7279            (progn
7280              (setq days-got t)
7281              (if (< days 0)
7282                  (progn
7283                    (setq younger (not younger))
7284                    (setq days (* days -1)))))
7285          (message "Please enter a number.")
7286          (sleep-for 1)))
7287      (list days younger)))
7288   (prog1
7289       (let ((data gnus-newsgroup-data)
7290             (cutoff (days-to-time age))
7291             articles d date is-younger)
7292         (while (setq d (pop data))
7293           (when (and (vectorp (gnus-data-header d))
7294                      (setq date (mail-header-date (gnus-data-header d))))
7295             (setq is-younger (time-less-p
7296                               (time-since (condition-case ()
7297                                               (date-to-time date)
7298                                             (error '(0 0))))
7299                               cutoff))
7300             (when (if younger-p
7301                       is-younger
7302                     (not is-younger))
7303               (push (gnus-data-number d) articles))))
7304         (gnus-summary-limit (nreverse articles)))
7305     (gnus-summary-position-point)))
7306
7307 (defun gnus-summary-limit-to-extra (header regexp &optional not-matching)
7308   "Limit the summary buffer to articles that match an 'extra' header."
7309   (interactive
7310    (let ((header
7311           (intern
7312            (gnus-completing-read-with-default
7313             (symbol-name (car gnus-extra-headers))
7314             (if current-prefix-arg
7315                 "Exclude extra header:"
7316               "Limit extra header:")
7317             (mapcar (lambda (x)
7318                       (cons (symbol-name x) x))
7319                     gnus-extra-headers)
7320             nil
7321             t))))
7322      (list header
7323            (read-string (format "%s header %s (regexp): "
7324                                 (if current-prefix-arg "Exclude" "Limit to")
7325                                 header))
7326            current-prefix-arg)))
7327   (when (not (equal "" regexp))
7328     (prog1
7329         (let ((articles (gnus-summary-find-matching
7330                          (cons 'extra header) regexp 'all nil nil
7331                          not-matching)))
7332           (unless articles
7333             (error "Found no matches for \"%s\"" regexp))
7334           (gnus-summary-limit articles))
7335       (gnus-summary-position-point))))
7336
7337 (defun gnus-summary-limit-to-display-predicate ()
7338   "Limit the summary buffer to the predicated in the `display' group parameter."
7339   (interactive)
7340   (unless gnus-newsgroup-display
7341     (error "There is no `display' group parameter"))
7342   (let (articles)
7343     (dolist (number gnus-newsgroup-articles)
7344       (when (funcall gnus-newsgroup-display)
7345         (push number articles)))
7346     (gnus-summary-limit articles))
7347   (gnus-summary-position-point))
7348
7349 (defalias 'gnus-summary-delete-marked-as-read 'gnus-summary-limit-to-unread)
7350 (make-obsolete
7351  'gnus-summary-delete-marked-as-read 'gnus-summary-limit-to-unread)
7352
7353 (defun gnus-summary-limit-to-unread (&optional all)
7354   "Limit the summary buffer to articles that are not marked as read.
7355 If ALL is non-nil, limit strictly to unread articles."
7356   (interactive "P")
7357   (if all
7358       (gnus-summary-limit-to-marks (char-to-string gnus-unread-mark))
7359     (gnus-summary-limit-to-marks
7360      ;; Concat all the marks that say that an article is read and have
7361      ;; those removed.
7362      (list gnus-del-mark gnus-read-mark gnus-ancient-mark
7363            gnus-killed-mark gnus-spam-mark gnus-kill-file-mark
7364            gnus-low-score-mark gnus-expirable-mark
7365            gnus-canceled-mark gnus-catchup-mark gnus-sparse-mark
7366            gnus-duplicate-mark gnus-souped-mark)
7367      'reverse)))
7368
7369 (defalias 'gnus-summary-delete-marked-with 'gnus-summary-limit-exclude-marks)
7370 (make-obsolete 'gnus-summary-delete-marked-with
7371                'gnus-summary-limit-exclude-marks)
7372
7373 (defun gnus-summary-limit-exclude-marks (marks &optional reverse)
7374   "Exclude articles that are marked with MARKS (e.g. \"DK\").
7375 If REVERSE, limit the summary buffer to articles that are marked
7376 with MARKS.  MARKS can either be a string of marks or a list of marks.
7377 Returns how many articles were removed."
7378   (interactive "sMarks: ")
7379   (gnus-summary-limit-to-marks marks t))
7380
7381 (defun gnus-summary-limit-to-marks (marks &optional reverse)
7382   "Limit the summary buffer to articles that are marked with MARKS (e.g. \"DK\").
7383 If REVERSE (the prefix), limit the summary buffer to articles that are
7384 not marked with MARKS.  MARKS can either be a string of marks or a
7385 list of marks.
7386 Returns how many articles were removed."
7387   (interactive "sMarks: \nP")
7388   (prog1
7389       (let ((data gnus-newsgroup-data)
7390             (marks (if (listp marks) marks
7391                      (append marks nil))) ; Transform to list.
7392             articles)
7393         (while data
7394           (when (if reverse (not (memq (gnus-data-mark (car data)) marks))
7395                   (memq (gnus-data-mark (car data)) marks))
7396             (push (gnus-data-number (car data)) articles))
7397           (setq data (cdr data)))
7398         (gnus-summary-limit articles))
7399     (gnus-summary-position-point)))
7400
7401 (defun gnus-summary-limit-to-score (score)
7402   "Limit to articles with score at or above SCORE."
7403   (interactive "NLimit to articles with score of at least: ")
7404   (let ((data gnus-newsgroup-data)
7405         articles)
7406     (while data
7407       (when (>= (gnus-summary-article-score (gnus-data-number (car data)))
7408                 score)
7409         (push (gnus-data-number (car data)) articles))
7410       (setq data (cdr data)))
7411     (prog1
7412         (gnus-summary-limit articles)
7413       (gnus-summary-position-point))))
7414
7415 (defun gnus-summary-limit-to-unseen ()
7416   "Limit to unseen articles."
7417   (interactive)
7418   (prog1
7419       (gnus-summary-limit gnus-newsgroup-unseen)
7420     (gnus-summary-position-point)))
7421
7422 (defun gnus-summary-limit-include-thread (id)
7423   "Display all the hidden articles that is in the thread with ID in it.
7424 When called interactively, ID is the Message-ID of the current
7425 article."
7426   (interactive (list (mail-header-id (gnus-summary-article-header))))
7427   (let ((articles (gnus-articles-in-thread
7428                    (gnus-id-to-thread (gnus-root-id id)))))
7429     (prog1
7430         (gnus-summary-limit (nconc articles gnus-newsgroup-limit))
7431       (gnus-summary-limit-include-matching-articles
7432        "subject"
7433        (regexp-quote (gnus-simplify-subject-re
7434                       (mail-header-subject (gnus-id-to-header id)))))
7435       (gnus-summary-position-point))))
7436
7437 (defun gnus-summary-limit-include-matching-articles (header regexp)
7438   "Display all the hidden articles that have HEADERs that match REGEXP."
7439   (interactive (list (read-string "Match on header: ")
7440                      (read-string "Regexp: ")))
7441   (let ((articles (gnus-find-matching-articles header regexp)))
7442     (prog1
7443         (gnus-summary-limit (nconc articles gnus-newsgroup-limit))
7444       (gnus-summary-position-point))))
7445
7446 (defun gnus-summary-limit-include-dormant ()
7447   "Display all the hidden articles that are marked as dormant.
7448 Note that this command only works on a subset of the articles currently
7449 fetched for this group."
7450   (interactive)
7451   (unless gnus-newsgroup-dormant
7452     (error "There are no dormant articles in this group"))
7453   (prog1
7454       (gnus-summary-limit (append gnus-newsgroup-dormant gnus-newsgroup-limit))
7455     (gnus-summary-position-point)))
7456
7457 (defun gnus-summary-limit-exclude-dormant ()
7458   "Hide all dormant articles."
7459   (interactive)
7460   (prog1
7461       (gnus-summary-limit-to-marks (list gnus-dormant-mark) 'reverse)
7462     (gnus-summary-position-point)))
7463
7464 (defun gnus-summary-limit-exclude-childless-dormant ()
7465   "Hide all dormant articles that have no children."
7466   (interactive)
7467   (let ((data (gnus-data-list t))
7468         articles d children)
7469     ;; Find all articles that are either not dormant or have
7470     ;; children.
7471     (while (setq d (pop data))
7472       (when (or (not (= (gnus-data-mark d) gnus-dormant-mark))
7473                 (and (setq children
7474                            (gnus-article-children (gnus-data-number d)))
7475                      (let (found)
7476                        (while children
7477                          (when (memq (car children) articles)
7478                            (setq children nil
7479                                  found t))
7480                          (pop children))
7481                        found)))
7482         (push (gnus-data-number d) articles)))
7483     ;; Do the limiting.
7484     (prog1
7485         (gnus-summary-limit articles)
7486       (gnus-summary-position-point))))
7487
7488 (defun gnus-summary-limit-mark-excluded-as-read (&optional all)
7489   "Mark all unread excluded articles as read.
7490 If ALL, mark even excluded ticked and dormants as read."
7491   (interactive "P")
7492   (setq gnus-newsgroup-limit (sort gnus-newsgroup-limit '<))
7493   (let ((articles (gnus-sorted-ndifference
7494                    (sort
7495                     (mapcar (lambda (h) (mail-header-number h))
7496                             gnus-newsgroup-headers)
7497                     '<)
7498                    gnus-newsgroup-limit))
7499         article)
7500     (setq gnus-newsgroup-unreads
7501           (gnus-sorted-intersection gnus-newsgroup-unreads
7502                                     gnus-newsgroup-limit))
7503     (if all
7504         (setq gnus-newsgroup-dormant nil
7505               gnus-newsgroup-marked nil
7506               gnus-newsgroup-reads
7507               (nconc
7508                (mapcar (lambda (n) (cons n gnus-catchup-mark)) articles)
7509                gnus-newsgroup-reads))
7510       (while (setq article (pop articles))
7511         (unless (or (memq article gnus-newsgroup-dormant)
7512                     (memq article gnus-newsgroup-marked))
7513           (push (cons article gnus-catchup-mark) gnus-newsgroup-reads))))))
7514
7515 (defun gnus-summary-limit (articles &optional pop)
7516   (if pop
7517       ;; We pop the previous limit off the stack and use that.
7518       (setq articles (car gnus-newsgroup-limits)
7519             gnus-newsgroup-limits (cdr gnus-newsgroup-limits))
7520     ;; We use the new limit, so we push the old limit on the stack.
7521     (push gnus-newsgroup-limit gnus-newsgroup-limits))
7522   ;; Set the limit.
7523   (setq gnus-newsgroup-limit articles)
7524   (let ((total (length gnus-newsgroup-data))
7525         (data (gnus-data-find-list (gnus-summary-article-number)))
7526         (gnus-summary-mark-below nil)   ; Inhibit this.
7527         found)
7528     ;; This will do all the work of generating the new summary buffer
7529     ;; according to the new limit.
7530     (gnus-summary-prepare)
7531     ;; Hide any threads, possibly.
7532     (gnus-summary-maybe-hide-threads)
7533     ;; Try to return to the article you were at, or one in the
7534     ;; neighborhood.
7535     (when data
7536       ;; We try to find some article after the current one.
7537       (while data
7538         (when (gnus-summary-goto-subject (gnus-data-number (car data)) nil t)
7539           (setq data nil
7540                 found t))
7541         (setq data (cdr data))))
7542     (unless found
7543       ;; If there is no data, that means that we were after the last
7544       ;; article.  The same goes when we can't find any articles
7545       ;; after the current one.
7546       (goto-char (point-max))
7547       (gnus-summary-find-prev))
7548     (gnus-set-mode-line 'summary)
7549     ;; We return how many articles were removed from the summary
7550     ;; buffer as a result of the new limit.
7551     (- total (length gnus-newsgroup-data))))
7552
7553 (defsubst gnus-invisible-cut-children (threads)
7554   (let ((num 0))
7555     (while threads
7556       (when (memq (mail-header-number (caar threads)) gnus-newsgroup-limit)
7557         (incf num))
7558       (pop threads))
7559     (< num 2)))
7560
7561 (defsubst gnus-cut-thread (thread)
7562   "Go forwards in the thread until we find an article that we want to display."
7563   (when (or (eq gnus-fetch-old-headers 'some)
7564             (eq gnus-fetch-old-headers 'invisible)
7565             (numberp gnus-fetch-old-headers)
7566             (eq gnus-build-sparse-threads 'some)
7567             (eq gnus-build-sparse-threads 'more))
7568     ;; Deal with old-fetched headers and sparse threads.
7569     (while (and
7570             thread
7571             (or
7572              (gnus-summary-article-sparse-p (mail-header-number (car thread)))
7573              (gnus-summary-article-ancient-p
7574               (mail-header-number (car thread))))
7575             (if (or (<= (length (cdr thread)) 1)
7576                     (eq gnus-fetch-old-headers 'invisible))
7577                 (setq gnus-newsgroup-limit
7578                       (delq (mail-header-number (car thread))
7579                             gnus-newsgroup-limit)
7580                       thread (cadr thread))
7581               (when (gnus-invisible-cut-children (cdr thread))
7582                 (let ((th (cdr thread)))
7583                   (while th
7584                     (if (memq (mail-header-number (caar th))
7585                               gnus-newsgroup-limit)
7586                         (setq thread (car th)
7587                               th nil)
7588                       (setq th (cdr th))))))))))
7589   thread)
7590
7591 (defun gnus-cut-threads (threads)
7592   "Cut off all uninteresting articles from the beginning of threads."
7593   (when (or (eq gnus-fetch-old-headers 'some)
7594             (eq gnus-fetch-old-headers 'invisible)
7595             (numberp gnus-fetch-old-headers)
7596             (eq gnus-build-sparse-threads 'some)
7597             (eq gnus-build-sparse-threads 'more))
7598     (let ((th threads))
7599       (while th
7600         (setcar th (gnus-cut-thread (car th)))
7601         (setq th (cdr th)))))
7602   ;; Remove nixed out threads.
7603   (delq nil threads))
7604
7605 (defun gnus-summary-initial-limit (&optional show-if-empty)
7606   "Figure out what the initial limit is supposed to be on group entry.
7607 This entails weeding out unwanted dormants, low-scored articles,
7608 fetch-old-headers verbiage, and so on."
7609   ;; Most groups have nothing to remove.
7610   (if (or gnus-inhibit-limiting
7611           (and (null gnus-newsgroup-dormant)
7612                (eq gnus-newsgroup-display 'gnus-not-ignore)
7613                (not (eq gnus-fetch-old-headers 'some))
7614                (not (numberp gnus-fetch-old-headers))
7615                (not (eq gnus-fetch-old-headers 'invisible))
7616                (null gnus-summary-expunge-below)
7617                (not (eq gnus-build-sparse-threads 'some))
7618                (not (eq gnus-build-sparse-threads 'more))
7619                (null gnus-thread-expunge-below)
7620                (not gnus-use-nocem)))
7621       ()                                ; Do nothing.
7622     (push gnus-newsgroup-limit gnus-newsgroup-limits)
7623     (setq gnus-newsgroup-limit nil)
7624     (mapatoms
7625      (lambda (node)
7626        (unless (car (symbol-value node))
7627          ;; These threads have no parents -- they are roots.
7628          (let ((nodes (cdr (symbol-value node)))
7629                thread)
7630            (while nodes
7631              (if (and gnus-thread-expunge-below
7632                       (< (gnus-thread-total-score (car nodes))
7633                          gnus-thread-expunge-below))
7634                  (gnus-expunge-thread (pop nodes))
7635                (setq thread (pop nodes))
7636                (gnus-summary-limit-children thread))))))
7637      gnus-newsgroup-dependencies)
7638     ;; If this limitation resulted in an empty group, we might
7639     ;; pop the previous limit and use it instead.
7640     (when (and (not gnus-newsgroup-limit)
7641                show-if-empty)
7642       (setq gnus-newsgroup-limit (pop gnus-newsgroup-limits)))
7643     gnus-newsgroup-limit))
7644
7645 (defun gnus-summary-limit-children (thread)
7646   "Return 1 if this subthread is visible and 0 if it is not."
7647   ;; First we get the number of visible children to this thread.  This
7648   ;; is done by recursing down the thread using this function, so this
7649   ;; will really go down to a leaf article first, before slowly
7650   ;; working its way up towards the root.
7651   (when thread
7652     (let ((children
7653            (if (cdr thread)
7654                (apply '+ (mapcar 'gnus-summary-limit-children
7655                                  (cdr thread)))
7656              0))
7657           (number (mail-header-number (car thread)))
7658           score)
7659       (if (and
7660            (not (memq number gnus-newsgroup-marked))
7661            (or
7662             ;; If this article is dormant and has absolutely no visible
7663             ;; children, then this article isn't visible.
7664             (and (memq number gnus-newsgroup-dormant)
7665                  (zerop children))
7666             ;; If this is "fetch-old-headered" and there is no
7667             ;; visible children, then we don't want this article.
7668             (and (or (eq gnus-fetch-old-headers 'some)
7669                      (numberp gnus-fetch-old-headers))
7670                  (gnus-summary-article-ancient-p number)
7671                  (zerop children))
7672             ;; If this is "fetch-old-headered" and `invisible', then
7673             ;; we don't want this article.
7674             (and (eq gnus-fetch-old-headers 'invisible)
7675                  (gnus-summary-article-ancient-p number))
7676             ;; If this is a sparsely inserted article with no children,
7677             ;; we don't want it.
7678             (and (eq gnus-build-sparse-threads 'some)
7679                  (gnus-summary-article-sparse-p number)
7680                  (zerop children))
7681             ;; If we use expunging, and this article is really
7682             ;; low-scored, then we don't want this article.
7683             (when (and gnus-summary-expunge-below
7684                        (< (setq score
7685                                 (or (cdr (assq number gnus-newsgroup-scored))
7686                                     gnus-summary-default-score))
7687                           gnus-summary-expunge-below))
7688               ;; We increase the expunge-tally here, but that has
7689               ;; nothing to do with the limits, really.
7690               (incf gnus-newsgroup-expunged-tally)
7691               ;; We also mark as read here, if that's wanted.
7692               (when (and gnus-summary-mark-below
7693                          (< score gnus-summary-mark-below))
7694                 (setq gnus-newsgroup-unreads
7695                       (delq number gnus-newsgroup-unreads))
7696                 (if gnus-newsgroup-auto-expire
7697                     (push number gnus-newsgroup-expirable)
7698                   (push (cons number gnus-low-score-mark)
7699                         gnus-newsgroup-reads)))
7700               t)
7701             ;; Do the `display' group parameter.
7702             (and gnus-newsgroup-display
7703                  (not (funcall gnus-newsgroup-display)))
7704             ;; Check NoCeM things.
7705             (if (and gnus-use-nocem
7706                      (gnus-nocem-unwanted-article-p
7707                       (mail-header-id (car thread))))
7708                 (progn
7709                   (setq gnus-newsgroup-unreads
7710                         (delq number gnus-newsgroup-unreads))
7711                   t))))
7712           ;; Nope, invisible article.
7713           0
7714         ;; Ok, this article is to be visible, so we add it to the limit
7715         ;; and return 1.
7716         (push number gnus-newsgroup-limit)
7717         1))))
7718
7719 (defun gnus-expunge-thread (thread)
7720   "Mark all articles in THREAD as read."
7721   (let* ((number (mail-header-number (car thread))))
7722     (incf gnus-newsgroup-expunged-tally)
7723     ;; We also mark as read here, if that's wanted.
7724     (setq gnus-newsgroup-unreads
7725           (delq number gnus-newsgroup-unreads))
7726     (if gnus-newsgroup-auto-expire
7727         (push number gnus-newsgroup-expirable)
7728       (push (cons number gnus-low-score-mark)
7729             gnus-newsgroup-reads)))
7730   ;; Go recursively through all subthreads.
7731   (mapcar 'gnus-expunge-thread (cdr thread)))
7732
7733 ;; Summary article oriented commands
7734
7735 (defun gnus-summary-refer-parent-article (n)
7736   "Refer parent article N times.
7737 If N is negative, go to ancestor -N instead.
7738 The difference between N and the number of articles fetched is returned."
7739   (interactive "p")
7740   (let ((skip 1)
7741         error header ref)
7742     (when (not (natnump n))
7743       (setq skip (abs n)
7744             n 1))
7745     (while (and (> n 0)
7746                 (not error))
7747       (setq header (gnus-summary-article-header))
7748       (if (and (eq (mail-header-number header)
7749                    (cdr gnus-article-current))
7750                (equal gnus-newsgroup-name
7751                       (car gnus-article-current)))
7752           ;; If we try to find the parent of the currently
7753           ;; displayed article, then we take a look at the actual
7754           ;; References header, since this is slightly more
7755           ;; reliable than the References field we got from the
7756           ;; server.
7757           (save-excursion
7758             (set-buffer gnus-original-article-buffer)
7759             (nnheader-narrow-to-headers)
7760             (unless (setq ref (message-fetch-field "references"))
7761               (setq ref (message-fetch-field "in-reply-to")))
7762             (widen))
7763         (setq ref
7764               ;; It's not the current article, so we take a bet on
7765               ;; the value we got from the server.
7766               (mail-header-references header)))
7767       (if (and ref
7768                (not (equal ref "")))
7769           (unless (gnus-summary-refer-article (gnus-parent-id ref skip))
7770             (gnus-message 1 "Couldn't find parent"))
7771         (gnus-message 1 "No references in article %d"
7772                       (gnus-summary-article-number))
7773         (setq error t))
7774       (decf n))
7775     (gnus-summary-position-point)
7776     n))
7777
7778 (defun gnus-summary-refer-references ()
7779   "Fetch all articles mentioned in the References header.
7780 Return the number of articles fetched."
7781   (interactive)
7782   (let ((ref (mail-header-references (gnus-summary-article-header)))
7783         (current (gnus-summary-article-number))
7784         (n 0))
7785     (if (or (not ref)
7786             (equal ref ""))
7787         (error "No References in the current article")
7788       ;; For each Message-ID in the References header...
7789       (while (string-match "<[^>]*>" ref)
7790         (incf n)
7791         ;; ... fetch that article.
7792         (gnus-summary-refer-article
7793          (prog1 (match-string 0 ref)
7794            (setq ref (substring ref (match-end 0))))))
7795       (gnus-summary-goto-subject current)
7796       (gnus-summary-position-point)
7797       n)))
7798
7799 (defun gnus-summary-refer-thread (&optional limit)
7800   "Fetch all articles in the current thread.
7801 If LIMIT (the numerical prefix), fetch that many old headers instead
7802 of what's specified by the `gnus-refer-thread-limit' variable."
7803   (interactive "P")
7804   (let ((id (mail-header-id (gnus-summary-article-header)))
7805         (limit (if limit (prefix-numeric-value limit)
7806                  gnus-refer-thread-limit)))
7807     ;; We want to fetch LIMIT *old* headers, but we also have to
7808     ;; re-fetch all the headers in the current buffer, because many of
7809     ;; them may be undisplayed.  So we adjust LIMIT.
7810     (when (numberp limit)
7811       (incf limit (- gnus-newsgroup-end gnus-newsgroup-begin)))
7812     (unless (eq gnus-fetch-old-headers 'invisible)
7813       (gnus-message 5 "Fetching headers for %s..." gnus-newsgroup-name)
7814       ;; Retrieve the headers and read them in.
7815       (if (eq (gnus-retrieve-headers
7816                (list gnus-newsgroup-end) gnus-newsgroup-name limit)
7817               'nov)
7818           (gnus-build-all-threads)
7819         (error "Can't fetch thread from backends that don't support NOV"))
7820       (gnus-message 5 "Fetching headers for %s...done" gnus-newsgroup-name))
7821     (gnus-summary-limit-include-thread id)))
7822
7823 (defun gnus-summary-refer-article (message-id)
7824   "Fetch an article specified by MESSAGE-ID."
7825   (interactive "sMessage-ID: ")
7826   (when (and (stringp message-id)
7827              (not (zerop (length message-id))))
7828     ;; Construct the correct Message-ID if necessary.
7829     ;; Suggested by tale@pawl.rpi.edu.
7830     (unless (string-match "^<" message-id)
7831       (setq message-id (concat "<" message-id)))
7832     (unless (string-match ">$" message-id)
7833       (setq message-id (concat message-id ">")))
7834     (let* ((header (gnus-id-to-header message-id))
7835            (sparse (and header
7836                         (gnus-summary-article-sparse-p
7837                          (mail-header-number header))
7838                         (memq (mail-header-number header)
7839                               gnus-newsgroup-limit)))
7840            number)
7841       (cond
7842        ;; If the article is present in the buffer we just go to it.
7843        ((and header
7844              (or (not (gnus-summary-article-sparse-p
7845                        (mail-header-number header)))
7846                  sparse))
7847         (prog1
7848             (gnus-summary-goto-article
7849              (mail-header-number header) nil t)
7850           (when sparse
7851             (gnus-summary-update-article (mail-header-number header)))))
7852        (t
7853         ;; We fetch the article.
7854         (catch 'found
7855           (dolist (gnus-override-method (gnus-refer-article-methods))
7856             (gnus-check-server gnus-override-method)
7857             ;; Fetch the header, and display the article.
7858             (when (setq number (gnus-summary-insert-subject message-id))
7859               (gnus-summary-select-article nil nil nil number)
7860               (throw 'found t)))
7861           (gnus-message 3 "Couldn't fetch article %s" message-id)))))))
7862
7863 (defun gnus-refer-article-methods ()
7864   "Return a list of referable methods."
7865   (cond
7866    ;; No method, so we default to current and native.
7867    ((null gnus-refer-article-method)
7868     (list gnus-current-select-method gnus-select-method))
7869    ;; Current.
7870    ((eq 'current gnus-refer-article-method)
7871     (list gnus-current-select-method))
7872    ;; List of select methods.
7873    ((not (and (symbolp (car gnus-refer-article-method))
7874               (assq (car gnus-refer-article-method) nnoo-definition-alist)))
7875     (let (out)
7876       (dolist (method gnus-refer-article-method)
7877         (push (if (eq 'current method)
7878                   gnus-current-select-method
7879                 method)
7880               out))
7881       (nreverse out)))
7882    ;; One single select method.
7883    (t
7884     (list gnus-refer-article-method))))
7885
7886 (defun gnus-summary-edit-parameters ()
7887   "Edit the group parameters of the current group."
7888   (interactive)
7889   (gnus-group-edit-group gnus-newsgroup-name 'params))
7890
7891 (defun gnus-summary-customize-parameters ()
7892   "Customize the group parameters of the current group."
7893   (interactive)
7894   (gnus-group-customize gnus-newsgroup-name))
7895
7896 (defun gnus-summary-enter-digest-group (&optional force)
7897   "Enter an nndoc group based on the current article.
7898 If FORCE, force a digest interpretation.  If not, try
7899 to guess what the document format is."
7900   (interactive "P")
7901   (let ((conf gnus-current-window-configuration))
7902     (save-excursion
7903       (gnus-summary-select-article))
7904     (setq gnus-current-window-configuration conf)
7905     (let* ((name (format "%s-%d"
7906                          (gnus-group-prefixed-name
7907                           gnus-newsgroup-name (list 'nndoc ""))
7908                          (save-excursion
7909                            (set-buffer gnus-summary-buffer)
7910                            gnus-current-article)))
7911            (ogroup gnus-newsgroup-name)
7912            (params (append (gnus-info-params (gnus-get-info ogroup))
7913                            (list (cons 'to-group ogroup))
7914                            (list (cons 'save-article-group ogroup))))
7915            (case-fold-search t)
7916            (buf (current-buffer))
7917            dig to-address)
7918       (save-excursion
7919         (set-buffer gnus-original-article-buffer)
7920         ;; Have the digest group inherit the main mail address of
7921         ;; the parent article.
7922         (when (setq to-address (or (gnus-fetch-field "reply-to")
7923                                    (gnus-fetch-field "from")))
7924           (setq params (append
7925                         (list (cons 'to-address
7926                                     (funcall gnus-decode-encoded-word-function
7927                                              to-address))))))
7928         (setq dig (nnheader-set-temp-buffer " *gnus digest buffer*"))
7929         (insert-buffer-substring gnus-original-article-buffer)
7930         ;; Remove lines that may lead nndoc to misinterpret the
7931         ;; document type.
7932         (narrow-to-region
7933          (goto-char (point-min))
7934          (or (search-forward "\n\n" nil t) (point)))
7935         (goto-char (point-min))
7936         (delete-matching-lines "^Path:\\|^From ")
7937         (widen))
7938       (unwind-protect
7939           (if (let ((gnus-newsgroup-ephemeral-charset gnus-newsgroup-charset)
7940                     (gnus-newsgroup-ephemeral-ignored-charsets
7941                      gnus-newsgroup-ignored-charsets))
7942                 (gnus-group-read-ephemeral-group
7943                  name `(nndoc ,name (nndoc-address ,(get-buffer dig))
7944                               (nndoc-article-type
7945                                ,(if force 'mbox 'guess)))
7946                  t nil nil nil
7947                  `((adapt-file . ,(gnus-score-file-name gnus-newsgroup-name
7948                                                         "ADAPT")))))
7949               ;; Make all postings to this group go to the parent group.
7950               (nconc (gnus-info-params (gnus-get-info name))
7951                      params)
7952             ;; Couldn't select this doc group.
7953             (switch-to-buffer buf)
7954             (gnus-set-global-variables)
7955             (gnus-configure-windows 'summary)
7956             (gnus-message 3 "Article couldn't be entered?"))
7957         (kill-buffer dig)))))
7958
7959 (defun gnus-summary-read-document (n)
7960   "Open a new group based on the current article(s).
7961 This will allow you to read digests and other similar
7962 documents as newsgroups.
7963 Obeys the standard process/prefix convention."
7964   (interactive "P")
7965   (let* ((articles (gnus-summary-work-articles n))
7966          (ogroup gnus-newsgroup-name)
7967          (params (append (gnus-info-params (gnus-get-info ogroup))
7968                          (list (cons 'to-group ogroup))))
7969          article group egroup groups vgroup)
7970     (while (setq article (pop articles))
7971       (setq group (format "%s-%d" gnus-newsgroup-name article))
7972       (gnus-summary-remove-process-mark article)
7973       (when (gnus-summary-display-article article)
7974         (save-excursion
7975           (with-temp-buffer
7976             (insert-buffer-substring gnus-original-article-buffer)
7977             ;; Remove some headers that may lead nndoc to make
7978             ;; the wrong guess.
7979             (message-narrow-to-head)
7980             (goto-char (point-min))
7981             (delete-matching-lines "^\\(Path\\):\\|^From ")
7982             (widen)
7983             (if (setq egroup
7984                       (gnus-group-read-ephemeral-group
7985                        group `(nndoc ,group (nndoc-address ,(current-buffer))
7986                                      (nndoc-article-type guess))
7987                        t nil t))
7988                 (progn
7989             ;; Make all postings to this group go to the parent group.
7990                   (nconc (gnus-info-params (gnus-get-info egroup))
7991                          params)
7992                   (push egroup groups))
7993               ;; Couldn't select this doc group.
7994               (gnus-error 3 "Article couldn't be entered"))))))
7995     ;; Now we have selected all the documents.
7996     (cond
7997      ((not groups)
7998       (error "None of the articles could be interpreted as documents"))
7999      ((gnus-group-read-ephemeral-group
8000        (setq vgroup (format
8001                      "nnvirtual:%s-%s" gnus-newsgroup-name
8002                      (format-time-string "%Y%m%dT%H%M%S" (current-time))))
8003        `(nnvirtual ,vgroup (nnvirtual-component-groups ,groups))
8004        t
8005        (cons (current-buffer) 'summary)))
8006      (t
8007       (error "Couldn't select virtual nndoc group")))))
8008
8009 (defun gnus-summary-isearch-article (&optional regexp-p)
8010   "Do incremental search forward on the current article.
8011 If REGEXP-P (the prefix) is non-nil, do regexp isearch."
8012   (interactive "P")
8013   (gnus-summary-select-article)
8014   (gnus-configure-windows 'article)
8015   (gnus-eval-in-buffer-window gnus-article-buffer
8016     (save-restriction
8017       (widen)
8018       (isearch-forward regexp-p))))
8019
8020 (defun gnus-summary-search-article-forward (regexp &optional backward)
8021   "Search for an article containing REGEXP forward.
8022 If BACKWARD, search backward instead."
8023   (interactive
8024    (list (read-string
8025           (format "Search article %s (regexp%s): "
8026                   (if current-prefix-arg "backward" "forward")
8027                   (if gnus-last-search-regexp
8028                       (concat ", default " gnus-last-search-regexp)
8029                     "")))
8030          current-prefix-arg))
8031   (if (string-equal regexp "")
8032       (setq regexp (or gnus-last-search-regexp ""))
8033     (setq gnus-last-search-regexp regexp)
8034     (setq gnus-article-before-search gnus-current-article))
8035   ;; Intentionally set gnus-last-article.
8036   (setq gnus-last-article gnus-article-before-search)
8037   (let ((gnus-last-article gnus-last-article))
8038     (if (gnus-summary-search-article regexp backward)
8039         (gnus-summary-show-thread)
8040       (error "Search failed: \"%s\"" regexp))))
8041
8042 (defun gnus-summary-search-article-backward (regexp)
8043   "Search for an article containing REGEXP backward."
8044   (interactive
8045    (list (read-string
8046           (format "Search article backward (regexp%s): "
8047                   (if gnus-last-search-regexp
8048                       (concat ", default " gnus-last-search-regexp)
8049                     "")))))
8050   (gnus-summary-search-article-forward regexp 'backward))
8051
8052 (defun gnus-summary-search-article (regexp &optional backward)
8053   "Search for an article containing REGEXP.
8054 Optional argument BACKWARD means do search for backward.
8055 `gnus-select-article-hook' is not called during the search."
8056   ;; We have to require this here to make sure that the following
8057   ;; dynamic binding isn't shadowed by autoloading.
8058   (require 'gnus-async)
8059   (require 'gnus-art)
8060   (let ((gnus-select-article-hook nil)  ;Disable hook.
8061         (gnus-article-prepare-hook nil)
8062         (gnus-mark-article-hook nil)    ;Inhibit marking as read.
8063         (gnus-use-article-prefetch nil)
8064         (gnus-xmas-force-redisplay nil) ;Inhibit XEmacs redisplay.
8065         (gnus-use-trees nil)            ;Inhibit updating tree buffer.
8066         (sum (current-buffer))
8067         (gnus-display-mime-function nil)
8068         (found nil)
8069         point)
8070     (gnus-save-hidden-threads
8071       (gnus-summary-select-article)
8072       (set-buffer gnus-article-buffer)
8073       (goto-char (window-point (get-buffer-window (current-buffer))))
8074       (when backward
8075         (forward-line -1))
8076       (while (not found)
8077         (gnus-message 7 "Searching article: %d..." (cdr gnus-article-current))
8078         (if (if backward
8079                 (re-search-backward regexp nil t)
8080               (re-search-forward regexp nil t))
8081             ;; We found the regexp.
8082             (progn
8083               (setq found 'found)
8084               (beginning-of-line)
8085               (set-window-start
8086                (get-buffer-window (current-buffer))
8087                (point))
8088               (forward-line 1)
8089               (set-window-point
8090                (get-buffer-window (current-buffer))
8091                (point))
8092               (set-buffer sum)
8093               (setq point (point)))
8094           ;; We didn't find it, so we go to the next article.
8095           (set-buffer sum)
8096           (setq found 'not)
8097           (while (eq found 'not)
8098             (if (not (if backward (gnus-summary-find-prev)
8099                        (gnus-summary-find-next)))
8100                 ;; No more articles.
8101                 (setq found t)
8102               ;; Select the next article and adjust point.
8103               (unless (gnus-summary-article-sparse-p
8104                        (gnus-summary-article-number))
8105                 (setq found nil)
8106                 (gnus-summary-select-article)
8107                 (set-buffer gnus-article-buffer)
8108                 (widen)
8109                 (goto-char (if backward (point-max) (point-min))))))))
8110       (gnus-message 7 ""))
8111     ;; Return whether we found the regexp.
8112     (when (eq found 'found)
8113       (goto-char point)
8114       (gnus-summary-show-thread)
8115       (gnus-summary-goto-subject gnus-current-article)
8116       (gnus-summary-position-point)
8117       t)))
8118
8119 (defun gnus-find-matching-articles (header regexp)
8120   "Return a list of all articles that match REGEXP on HEADER.
8121 This search includes all articles in the current group that Gnus has
8122 fetched headers for, whether they are displayed or not."
8123   (let ((articles nil)
8124         (func `(lambda (h) (,(intern (concat "mail-header-" header)) h)))
8125         (case-fold-search t))
8126     (dolist (header gnus-newsgroup-headers)
8127       (when (string-match regexp (funcall func header))
8128         (push (mail-header-number header) articles)))
8129     (nreverse articles)))
8130
8131 (defun gnus-summary-find-matching (header regexp &optional backward unread
8132                                           not-case-fold not-matching)
8133   "Return a list of all articles that match REGEXP on HEADER.
8134 The search stars on the current article and goes forwards unless
8135 BACKWARD is non-nil.  If BACKWARD is `all', do all articles.
8136 If UNREAD is non-nil, only unread articles will
8137 be taken into consideration.  If NOT-CASE-FOLD, case won't be folded
8138 in the comparisons. If NOT-MATCHING, return a list of all articles that
8139 not match REGEXP on HEADER."
8140   (let ((case-fold-search (not not-case-fold))
8141         articles d func)
8142     (if (consp header)
8143         (if (eq (car header) 'extra)
8144             (setq func
8145                   `(lambda (h)
8146                      (or (cdr (assq ',(cdr header) (mail-header-extra h)))
8147                          "")))
8148           (error "%s is an invalid header" header))
8149       (unless (fboundp (intern (concat "mail-header-" header)))
8150         (error "%s is not a valid header" header))
8151       (setq func `(lambda (h) (,(intern (concat "mail-header-" header)) h))))
8152     (dolist (d (if (eq backward 'all)
8153                    gnus-newsgroup-data
8154                  (gnus-data-find-list
8155                   (gnus-summary-article-number)
8156                   (gnus-data-list backward))))
8157       (when (and (or (not unread)       ; We want all articles...
8158                      (gnus-data-unread-p d)) ; Or just unreads.
8159                  (vectorp (gnus-data-header d)) ; It's not a pseudo.
8160                  (if not-matching
8161                      (not (string-match
8162                            regexp
8163                            (funcall func (gnus-data-header d))))
8164                    (string-match regexp
8165                                  (funcall func (gnus-data-header d)))))
8166         (push (gnus-data-number d) articles))) ; Success!
8167     (nreverse articles)))
8168
8169 (defun gnus-summary-execute-command (header regexp command &optional backward)
8170   "Search forward for an article whose HEADER matches REGEXP and execute COMMAND.
8171 If HEADER is an empty string (or nil), the match is done on the entire
8172 article.  If BACKWARD (the prefix) is non-nil, search backward instead."
8173   (interactive
8174    (list (let ((completion-ignore-case t))
8175            (completing-read
8176             "Header name: "
8177             (mapcar (lambda (header) (list (format "%s" header)))
8178                     (append
8179                      '("Number" "Subject" "From" "Lines" "Date"
8180                        "Message-ID" "Xref" "References" "Body")
8181                      gnus-extra-headers))
8182             nil 'require-match))
8183          (read-string "Regexp: ")
8184          (read-key-sequence "Command: ")
8185          current-prefix-arg))
8186   (when (equal header "Body")
8187     (setq header ""))
8188   ;; Hidden thread subtrees must be searched as well.
8189   (gnus-summary-show-all-threads)
8190   ;; We don't want to change current point nor window configuration.
8191   (save-excursion
8192     (save-window-excursion
8193       (let (gnus-visual
8194             gnus-treat-strip-trailing-blank-lines
8195             gnus-treat-strip-leading-blank-lines
8196             gnus-treat-strip-multiple-blank-lines
8197             gnus-treat-hide-boring-headers
8198             gnus-treat-fold-newsgroups
8199             gnus-article-prepare-hook)
8200         (gnus-message 6 "Executing %s..." (key-description command))
8201         ;; We'd like to execute COMMAND interactively so as to give arguments.
8202         (gnus-execute header regexp
8203                       `(call-interactively ',(key-binding command))
8204                       backward)
8205         (gnus-message 6 "Executing %s...done" (key-description command))))))
8206
8207 (defun gnus-summary-beginning-of-article ()
8208   "Scroll the article back to the beginning."
8209   (interactive)
8210   (gnus-summary-select-article)
8211   (gnus-configure-windows 'article)
8212   (gnus-eval-in-buffer-window gnus-article-buffer
8213     (widen)
8214     (goto-char (point-min))
8215     (when gnus-page-broken
8216       (gnus-narrow-to-page))))
8217
8218 (defun gnus-summary-end-of-article ()
8219   "Scroll to the end of the article."
8220   (interactive)
8221   (gnus-summary-select-article)
8222   (gnus-configure-windows 'article)
8223   (gnus-eval-in-buffer-window gnus-article-buffer
8224     (widen)
8225     (goto-char (point-max))
8226     (recenter -3)
8227     (when gnus-page-broken
8228       (gnus-narrow-to-page))))
8229
8230 (defun gnus-summary-print-truncate-and-quote (string &optional len)
8231   "Truncate to LEN and quote all \"(\"'s in STRING."
8232   (gnus-replace-in-string (if (and len (> (length string) len))
8233                               (substring string 0 len)
8234                             string)
8235                           "[()]" "\\\\\\&"))
8236
8237 (defun gnus-summary-print-article (&optional filename n)
8238   "Generate and print a PostScript image of the N next (mail) articles.
8239
8240 If N is negative, print the N previous articles.  If N is nil and articles
8241 have been marked with the process mark, print these instead.
8242
8243 If the optional first argument FILENAME is nil, send the image to the
8244 printer.  If FILENAME is a string, save the PostScript image in a file with
8245 that name.  If FILENAME is a number, prompt the user for the name of the file
8246 to save in."
8247   (interactive (list (ps-print-preprint current-prefix-arg)))
8248   (dolist (article (gnus-summary-work-articles n))
8249     (gnus-summary-select-article nil nil 'pseudo article)
8250     (gnus-eval-in-buffer-window gnus-article-buffer
8251       (gnus-print-buffer))
8252     (gnus-summary-remove-process-mark article))
8253   (ps-despool filename))
8254
8255 (defun gnus-print-buffer ()
8256   (let ((buffer (generate-new-buffer " *print*")))
8257     (unwind-protect
8258         (progn
8259           (copy-to-buffer buffer (point-min) (point-max))
8260           (set-buffer buffer)
8261           (gnus-article-delete-invisible-text)
8262           (gnus-remove-text-with-property 'gnus-decoration)
8263           (when (gnus-visual-p 'article-highlight 'highlight)
8264             ;; Copy-to-buffer doesn't copy overlay.  So redo
8265             ;; highlight.
8266             (let ((gnus-article-buffer buffer))
8267               (gnus-article-highlight-citation t)
8268               (gnus-article-highlight-signature)))
8269           (let ((ps-left-header
8270                  (list
8271                   (concat "("
8272                           (gnus-summary-print-truncate-and-quote
8273                            (mail-header-subject gnus-current-headers)
8274                            66) ")")
8275                   (concat "("
8276                           (gnus-summary-print-truncate-and-quote
8277                            (mail-header-from gnus-current-headers)
8278                            45) ")")))
8279                 (ps-right-header
8280                  (list
8281                   "/pagenumberstring load"
8282                   (concat "("
8283                           (mail-header-date gnus-current-headers) ")"))))
8284             (gnus-run-hooks 'gnus-ps-print-hook)
8285             (save-excursion
8286               (if window-system
8287                   (ps-spool-buffer-with-faces)
8288                 (ps-spool-buffer)))))
8289       (kill-buffer buffer))))
8290
8291 (defun gnus-summary-show-article (&optional arg)
8292   "Force redisplaying of the current article.
8293 If ARG (the prefix) is a number, show the article with the charset
8294 defined in `gnus-summary-show-article-charset-alist', or the charset
8295 input.
8296 If ARG (the prefix) is non-nil and not a number, show the raw article
8297 without any article massaging functions being run.  Normally, the key strokes
8298 are `C-u g'."
8299   (interactive "P")
8300   (cond
8301    ((numberp arg)
8302     (gnus-summary-show-article t)
8303     (let ((gnus-newsgroup-charset
8304            (or (cdr (assq arg gnus-summary-show-article-charset-alist))
8305                (mm-read-coding-system
8306                 "View as charset: " ;; actually it is coding system.
8307                 (save-excursion
8308                   (set-buffer gnus-article-buffer)
8309                   (mm-detect-coding-region (point) (point-max))))))
8310           (gnus-newsgroup-ignored-charsets 'gnus-all))
8311       (gnus-summary-select-article nil 'force)
8312       (let ((deps gnus-newsgroup-dependencies)
8313             head header lines)
8314         (save-excursion
8315           (set-buffer gnus-original-article-buffer)
8316           (save-restriction
8317             (message-narrow-to-head)
8318             (setq head (buffer-string))
8319             (goto-char (point-min))
8320             (unless (re-search-forward "^lines:[ \t]\\([0-9]+\\)" nil t)
8321               (goto-char (point-max))
8322               (widen)
8323               (setq lines (1- (count-lines (point) (point-max))))))
8324           (with-temp-buffer
8325             (insert (format "211 %d Article retrieved.\n"
8326                             (cdr gnus-article-current)))
8327             (insert head)
8328             (if lines (insert (format "Lines: %d\n" lines)))
8329             (insert ".\n")
8330             (let ((nntp-server-buffer (current-buffer)))
8331               (setq header (car (gnus-get-newsgroup-headers deps t))))))
8332         (gnus-data-set-header
8333          (gnus-data-find (cdr gnus-article-current))
8334          header)
8335         (gnus-summary-update-article-line
8336          (cdr gnus-article-current) header)
8337         (when (gnus-summary-goto-subject (cdr gnus-article-current) nil t)
8338           (gnus-summary-update-secondary-mark (cdr gnus-article-current))))))
8339    ((not arg)
8340     ;; Select the article the normal way.
8341     (gnus-summary-select-article nil 'force))
8342    (t
8343     ;; We have to require this here to make sure that the following
8344     ;; dynamic binding isn't shadowed by autoloading.
8345     (require 'gnus-async)
8346     (require 'gnus-art)
8347     ;; Bind the article treatment functions to nil.
8348     (let ((gnus-have-all-headers t)
8349           gnus-article-prepare-hook
8350           gnus-article-decode-hook
8351           gnus-display-mime-function
8352           gnus-break-pages)
8353       ;; Destroy any MIME parts.
8354       (when (gnus-buffer-live-p gnus-article-buffer)
8355         (save-excursion
8356           (set-buffer gnus-article-buffer)
8357           (mm-destroy-parts gnus-article-mime-handles)
8358           ;; Set it to nil for safety reason.
8359           (setq gnus-article-mime-handle-alist nil)
8360           (setq gnus-article-mime-handles nil)))
8361       (gnus-summary-select-article nil 'force))))
8362   (gnus-summary-goto-subject gnus-current-article)
8363   (gnus-summary-position-point))
8364
8365 (defun gnus-summary-show-raw-article ()
8366   "Show the raw article without any article massaging functions being run."
8367   (interactive)
8368   (gnus-summary-show-article t))
8369
8370 (defun gnus-summary-verbose-headers (&optional arg)
8371   "Toggle permanent full header display.
8372 If ARG is a positive number, turn header display on.
8373 If ARG is a negative number, turn header display off."
8374   (interactive "P")
8375   (setq gnus-show-all-headers
8376         (cond ((or (not (numberp arg))
8377                    (zerop arg))
8378                (not gnus-show-all-headers))
8379               ((natnump arg)
8380                t)))
8381   (gnus-summary-show-article))
8382
8383 (defun gnus-summary-toggle-header (&optional arg)
8384   "Show the headers if they are hidden, or hide them if they are shown.
8385 If ARG is a positive number, show the entire header.
8386 If ARG is a negative number, hide the unwanted header lines."
8387   (interactive "P")
8388   (let ((window (and (gnus-buffer-live-p gnus-article-buffer)
8389                      (get-buffer-window gnus-article-buffer t))))
8390     (with-current-buffer gnus-article-buffer
8391       (widen)
8392       (article-narrow-to-head)
8393       (let* ((buffer-read-only nil)
8394              (inhibit-point-motion-hooks t)
8395              (hidden (if (numberp arg)
8396                          (>= arg 0)
8397                        (gnus-article-hidden-text-p 'headers)))
8398              s e)
8399         (delete-region (point-min) (point-max))
8400         (with-current-buffer gnus-original-article-buffer
8401           (goto-char (setq s (point-min)))
8402           (setq e (if (search-forward "\n\n" nil t)
8403                       (1- (point))
8404                     (point-max))))
8405         (insert-buffer-substring gnus-original-article-buffer s e)
8406         (article-decode-encoded-words)
8407         (if hidden
8408             (let ((gnus-treat-hide-headers nil)
8409                   (gnus-treat-hide-boring-headers nil))
8410               (gnus-delete-wash-type 'headers)
8411               (gnus-treat-article 'head))
8412           (gnus-treat-article 'head))
8413         (widen)
8414         (if window
8415             (set-window-start window (goto-char (point-min))))
8416         (setq gnus-page-broken
8417               (when gnus-break-pages
8418                 (gnus-narrow-to-page)
8419                 t))
8420         (gnus-set-mode-line 'article)))))
8421
8422 (defun gnus-summary-show-all-headers ()
8423   "Make all header lines visible."
8424   (interactive)
8425   (gnus-summary-toggle-header 1))
8426
8427 (defun gnus-summary-caesar-message (&optional arg)
8428   "Caesar rotate the current article by 13.
8429 The numerical prefix specifies how many places to rotate each letter
8430 forward."
8431   (interactive "P")
8432   (gnus-summary-select-article)
8433   (let ((mail-header-separator ""))
8434     (gnus-eval-in-buffer-window gnus-article-buffer
8435       (save-restriction
8436         (widen)
8437         (let ((start (window-start))
8438               buffer-read-only)
8439           (message-caesar-buffer-body arg)
8440           (set-window-start (get-buffer-window (current-buffer)) start))))))
8441
8442 (autoload 'unmorse-region "morse"
8443   "Convert morse coded text in region to ordinary ASCII text."
8444   t)
8445
8446 (defun gnus-summary-morse-message (&optional arg)
8447   "Morse decode the current article."
8448   (interactive "P")
8449   (gnus-summary-select-article)
8450   (let ((mail-header-separator ""))
8451     (gnus-eval-in-buffer-window gnus-article-buffer
8452       (save-excursion
8453         (save-restriction
8454           (widen)
8455           (let ((pos (window-start))
8456                 buffer-read-only)
8457             (goto-char (point-min))
8458             (when (message-goto-body)
8459               (gnus-narrow-to-body))
8460             (goto-char (point-min))
8461             (while (re-search-forward "·" (point-max) t)
8462               (replace-match "."))
8463             (unmorse-region (point-min) (point-max))
8464             (widen)
8465             (set-window-start (get-buffer-window (current-buffer)) pos)))))))
8466
8467 (defun gnus-summary-stop-page-breaking ()
8468   "Stop page breaking in the current article."
8469   (interactive)
8470   (gnus-summary-select-article)
8471   (gnus-eval-in-buffer-window gnus-article-buffer
8472     (widen)
8473     (when (gnus-visual-p 'page-marker)
8474       (let ((buffer-read-only nil))
8475         (gnus-remove-text-with-property 'gnus-prev)
8476         (gnus-remove-text-with-property 'gnus-next))
8477       (setq gnus-page-broken nil))))
8478
8479 (defun gnus-summary-move-article (&optional n to-newsgroup
8480                                             select-method action)
8481   "Move the current article to a different newsgroup.
8482 If N is a positive number, move the N next articles.
8483 If N is a negative number, move the N previous articles.
8484 If N is nil and any articles have been marked with the process mark,
8485 move those articles instead.
8486 If TO-NEWSGROUP is string, do not prompt for a newsgroup to move to.
8487 If SELECT-METHOD is non-nil, do not move to a specific newsgroup, but
8488 re-spool using this method.
8489
8490 When called interactively with TO-NEWSGROUP being nil, the value of
8491 the variable `gnus-move-split-methods' is used for finding a default
8492 for the target newsgroup.
8493
8494 For this function to work, both the current newsgroup and the
8495 newsgroup that you want to move to have to support the `request-move'
8496 and `request-accept' functions.
8497
8498 ACTION can be either `move' (the default), `crosspost' or `copy'."
8499   (interactive "P")
8500   (unless action
8501     (setq action 'move))
8502   ;; Check whether the source group supports the required functions.
8503   (cond ((and (eq action 'move)
8504               (not (gnus-check-backend-function
8505                     'request-move-article gnus-newsgroup-name)))
8506          (error "The current group does not support article moving"))
8507         ((and (eq action 'crosspost)
8508               (not (gnus-check-backend-function
8509                     'request-replace-article gnus-newsgroup-name)))
8510          (error "The current group does not support article editing")))
8511   (let ((articles (gnus-summary-work-articles n))
8512         (prefix (if (gnus-check-backend-function
8513                      'request-move-article gnus-newsgroup-name)
8514                     (gnus-group-real-prefix gnus-newsgroup-name)
8515                   ""))
8516         (names '((move "Move" "Moving")
8517                  (copy "Copy" "Copying")
8518                  (crosspost "Crosspost" "Crossposting")))
8519         (copy-buf (save-excursion
8520                     (nnheader-set-temp-buffer " *copy article*")))
8521         art-group to-method new-xref article to-groups)
8522     (unless (assq action names)
8523       (error "Unknown action %s" action))
8524     ;; Read the newsgroup name.
8525     (when (and (not to-newsgroup)
8526                (not select-method))
8527       (if (and gnus-move-split-methods
8528                (not
8529                 (and (memq gnus-current-article articles)
8530                      (gnus-buffer-live-p gnus-original-article-buffer))))
8531           ;; When `gnus-move-split-methods' is non-nil, we have to
8532           ;; select an article to give `gnus-read-move-group-name' an
8533           ;; opportunity to suggest an appropriate default.  However,
8534           ;; we needn't render or mark the article.
8535           (let ((gnus-display-mime-function nil)
8536                 (gnus-article-prepare-hook nil)
8537                 (gnus-mark-article-hook nil))
8538             (gnus-summary-select-article nil nil nil (car articles))))
8539       (setq to-newsgroup
8540             (gnus-read-move-group-name
8541              (cadr (assq action names))
8542              (symbol-value (intern (format "gnus-current-%s-group" action)))
8543              articles prefix))
8544       (set (intern (format "gnus-current-%s-group" action)) to-newsgroup))
8545     (setq to-method (or select-method
8546                         (gnus-server-to-method
8547                          (gnus-group-method to-newsgroup))))
8548     ;; Check the method we are to move this article to...
8549     (unless (gnus-check-backend-function
8550              'request-accept-article (car to-method))
8551       (error "%s does not support article copying" (car to-method)))
8552     (unless (gnus-check-server to-method)
8553       (error "Can't open server %s" (car to-method)))
8554     (gnus-message 6 "%s to %s: %s..."
8555                   (caddr (assq action names))
8556                   (or (car select-method) to-newsgroup) articles)
8557     (while articles
8558       (setq article (pop articles))
8559       (setq
8560        art-group
8561        (cond
8562         ;; Move the article.
8563         ((eq action 'move)
8564          ;; Remove this article from future suppression.
8565          (gnus-dup-unsuppress-article article)
8566          (gnus-request-move-article
8567           article                       ; Article to move
8568           gnus-newsgroup-name           ; From newsgroup
8569           (nth 1 (gnus-find-method-for-group
8570                   gnus-newsgroup-name)) ; Server
8571           (list 'gnus-request-accept-article
8572                 to-newsgroup (list 'quote select-method)
8573                 (not articles) t)       ; Accept form
8574           (not articles)))              ; Only save nov last time
8575         ;; Copy the article.
8576         ((eq action 'copy)
8577          (save-excursion
8578            (set-buffer copy-buf)
8579            (when (gnus-request-article-this-buffer article gnus-newsgroup-name)
8580              (gnus-request-accept-article
8581               to-newsgroup select-method (not articles) t))))
8582         ;; Crosspost the article.
8583         ((eq action 'crosspost)
8584          (let ((xref (message-tokenize-header
8585                       (mail-header-xref (gnus-summary-article-header article))
8586                       " ")))
8587            (setq new-xref (concat (gnus-group-real-name gnus-newsgroup-name)
8588                                   ":" (number-to-string article)))
8589            (unless xref
8590              (setq xref (list (system-name))))
8591            (setq new-xref
8592                  (concat
8593                   (mapconcat 'identity
8594                              (delete "Xref:" (delete new-xref xref))
8595                              " ")
8596                   " " new-xref))
8597            (save-excursion
8598              (set-buffer copy-buf)
8599              ;; First put the article in the destination group.
8600              (gnus-request-article-this-buffer article gnus-newsgroup-name)
8601              (when (consp (setq art-group
8602                                 (gnus-request-accept-article
8603                                  to-newsgroup select-method (not articles))))
8604                (setq new-xref (concat new-xref " " (car art-group)
8605                                       ":"
8606                                       (number-to-string (cdr art-group))))
8607                ;; Now we have the new Xrefs header, so we insert
8608                ;; it and replace the new article.
8609                (nnheader-replace-header "Xref" new-xref)
8610                (gnus-request-replace-article
8611                 (cdr art-group) to-newsgroup (current-buffer))
8612                art-group))))))
8613       (cond
8614        ((not art-group)
8615         (gnus-message 1 "Couldn't %s article %s: %s"
8616                       (cadr (assq action names)) article
8617                       (nnheader-get-report (car to-method))))
8618        ((eq art-group 'junk)
8619         (when (eq action 'move)
8620           (gnus-summary-mark-article article gnus-canceled-mark)
8621           (gnus-message 4 "Deleted article %s" article)))
8622        (t
8623         (let* ((pto-group (gnus-group-prefixed-name
8624                            (car art-group) to-method))
8625                (entry
8626                 (gnus-gethash pto-group gnus-newsrc-hashtb))
8627                (info (nth 2 entry))
8628                (to-group (gnus-info-group info))
8629                to-marks)
8630           ;; Update the group that has been moved to.
8631           (when (and info
8632                      (memq action '(move copy)))
8633             (unless (member to-group to-groups)
8634               (push to-group to-groups))
8635
8636             (unless (memq article gnus-newsgroup-unreads)
8637               (push 'read to-marks)
8638               (gnus-info-set-read
8639                info (gnus-add-to-range (gnus-info-read info)
8640                                        (list (cdr art-group)))))
8641
8642             ;; See whether the article is to be put in the cache.
8643             (let ((marks gnus-article-mark-lists)
8644                   (to-article (cdr art-group)))
8645
8646               ;; Enter the article into the cache in the new group,
8647               ;; if that is required.
8648               (when gnus-use-cache
8649                 (gnus-cache-possibly-enter-article
8650                  to-group to-article
8651                  (memq article gnus-newsgroup-marked)
8652                  (memq article gnus-newsgroup-dormant)
8653                  (memq article gnus-newsgroup-unreads)))
8654
8655               (when gnus-preserve-marks
8656                 ;; Copy any marks over to the new group.
8657                 (when (and (equal to-group gnus-newsgroup-name)
8658                            (not (memq article gnus-newsgroup-unreads)))
8659                   ;; Mark this article as read in this group.
8660                   (push (cons to-article gnus-read-mark) gnus-newsgroup-reads)
8661                   (setcdr (gnus-active to-group) to-article)
8662                   (setcdr gnus-newsgroup-active to-article))
8663
8664                 (while marks
8665                   (when (eq (gnus-article-mark-to-type (cdar marks)) 'list)
8666                     (when (memq article (symbol-value
8667                                          (intern (format "gnus-newsgroup-%s"
8668                                                          (caar marks)))))
8669                       (push (cdar marks) to-marks)
8670                       ;; If the other group is the same as this group,
8671                       ;; then we have to add the mark to the list.
8672                       (when (equal to-group gnus-newsgroup-name)
8673                         (set (intern (format "gnus-newsgroup-%s" (caar marks)))
8674                              (cons to-article
8675                                    (symbol-value
8676                                     (intern (format "gnus-newsgroup-%s"
8677                                                     (caar marks)))))))
8678                       ;; Copy the marks to other group.
8679                       (gnus-add-marked-articles
8680                        to-group (cdar marks) (list to-article) info)))
8681                   (setq marks (cdr marks)))
8682
8683                 (gnus-request-set-mark to-group (list (list (list to-article)
8684                                                             'add
8685                                                             to-marks))))
8686
8687               (gnus-dribble-enter
8688                (concat "(gnus-group-set-info '"
8689                        (gnus-prin1-to-string (gnus-get-info to-group))
8690                        ")"))))
8691
8692           ;; Update the Xref header in this article to point to
8693           ;; the new crossposted article we have just created.
8694           (when (eq action 'crosspost)
8695             (save-excursion
8696               (set-buffer copy-buf)
8697               (gnus-request-article-this-buffer article gnus-newsgroup-name)
8698               (nnheader-replace-header "Xref" new-xref)
8699               (gnus-request-replace-article
8700                article gnus-newsgroup-name (current-buffer)))))
8701
8702         ;;;!!!Why is this necessary?
8703         (set-buffer gnus-summary-buffer)
8704
8705         (gnus-summary-goto-subject article)
8706         (when (eq action 'move)
8707           (gnus-summary-mark-article article gnus-canceled-mark))))
8708       (gnus-summary-remove-process-mark article))
8709     ;; Re-activate all groups that have been moved to.
8710     (save-excursion
8711       (set-buffer gnus-group-buffer)
8712       (let ((gnus-group-marked to-groups))
8713         (gnus-group-get-new-news-this-group nil t)))
8714
8715     (gnus-kill-buffer copy-buf)
8716     (gnus-summary-position-point)
8717     (gnus-set-mode-line 'summary)))
8718
8719 (defun gnus-summary-copy-article (&optional n to-newsgroup select-method)
8720   "Move the current article to a different newsgroup.
8721 If TO-NEWSGROUP is string, do not prompt for a newsgroup to move to.
8722 When called interactively, if TO-NEWSGROUP is nil, use the value of
8723 the variable `gnus-move-split-methods' for finding a default target
8724 newsgroup.
8725 If SELECT-METHOD is non-nil, do not move to a specific newsgroup, but
8726 re-spool using this method."
8727   (interactive "P")
8728   (gnus-summary-move-article n to-newsgroup select-method 'copy))
8729
8730 (defun gnus-summary-crosspost-article (&optional n)
8731   "Crosspost the current article to some other group."
8732   (interactive "P")
8733   (gnus-summary-move-article n nil nil 'crosspost))
8734
8735 (defcustom gnus-summary-respool-default-method nil
8736   "Default method type for respooling an article.
8737 If nil, use to the current newsgroup method."
8738   :type 'symbol
8739   :group 'gnus-summary-mail)
8740
8741 (defun gnus-summary-respool-article (&optional n method)
8742   "Respool the current article.
8743 The article will be squeezed through the mail spooling process again,
8744 which means that it will be put in some mail newsgroup or other
8745 depending on `nnmail-split-methods'.
8746 If N is a positive number, respool the N next articles.
8747 If N is a negative number, respool the N previous articles.
8748 If N is nil and any articles have been marked with the process mark,
8749 respool those articles instead.
8750
8751 Respooling can be done both from mail groups and \"real\" newsgroups.
8752 In the former case, the articles in question will be moved from the
8753 current group into whatever groups they are destined to.  In the
8754 latter case, they will be copied into the relevant groups."
8755   (interactive
8756    (list current-prefix-arg
8757          (let* ((methods (gnus-methods-using 'respool))
8758                 (methname
8759                  (symbol-name (or gnus-summary-respool-default-method
8760                                   (car (gnus-find-method-for-group
8761                                         gnus-newsgroup-name)))))
8762                 (method
8763                  (gnus-completing-read-with-default
8764                   methname "What backend do you want to use when respooling?"
8765                   methods nil t nil 'gnus-mail-method-history))
8766                 ms)
8767            (cond
8768             ((zerop (length (setq ms (gnus-servers-using-backend
8769                                       (intern method)))))
8770              (list (intern method) ""))
8771             ((= 1 (length ms))
8772              (car ms))
8773             (t
8774              (let ((ms-alist (mapcar (lambda (m) (cons (cadr m) m)) ms)))
8775                (cdr (assoc (completing-read "Server name: " ms-alist nil t)
8776                            ms-alist))))))))
8777   (unless method
8778     (error "No method given for respooling"))
8779   (if (assoc (symbol-name
8780               (car (gnus-find-method-for-group gnus-newsgroup-name)))
8781              (gnus-methods-using 'respool))
8782       (gnus-summary-move-article n nil method)
8783     (gnus-summary-copy-article n nil method)))
8784
8785 (defun gnus-summary-import-article (file &optional edit)
8786   "Import an arbitrary file into a mail newsgroup."
8787   (interactive "fImport file: \nP")
8788   (let ((group gnus-newsgroup-name)
8789         (now (current-time))
8790         atts lines group-art)
8791     (unless (gnus-check-backend-function 'request-accept-article group)
8792       (error "%s does not support article importing" group))
8793     (or (file-readable-p file)
8794         (not (file-regular-p file))
8795         (error "Can't read %s" file))
8796     (save-excursion
8797       (set-buffer (gnus-get-buffer-create " *import file*"))
8798       (erase-buffer)
8799       (nnheader-insert-file-contents file)
8800       (goto-char (point-min))
8801       (if (nnheader-article-p)
8802           (save-restriction
8803             (goto-char (point-min))
8804             (search-forward "\n\n" nil t)
8805             (narrow-to-region (point-min) (1- (point)))
8806             (goto-char (point-min))
8807             (unless (re-search-forward "^date:" nil t)
8808               (goto-char (point-max))
8809               (insert "Date: " (message-make-date (nth 5 atts)) "\n")))
8810        ;; This doesn't look like an article, so we fudge some headers.
8811         (setq atts (file-attributes file)
8812               lines (count-lines (point-min) (point-max)))
8813         (insert "From: " (read-string "From: ") "\n"
8814                 "Subject: " (read-string "Subject: ") "\n"
8815                 "Date: " (message-make-date (nth 5 atts)) "\n"
8816                 "Message-ID: " (message-make-message-id) "\n"
8817                 "Lines: " (int-to-string lines) "\n"
8818                 "Chars: " (int-to-string (nth 7 atts)) "\n\n"))
8819       (setq group-art (gnus-request-accept-article group nil t))
8820       (kill-buffer (current-buffer)))
8821     (setq gnus-newsgroup-active (gnus-activate-group group))
8822     (forward-line 1)
8823     (gnus-summary-goto-article (cdr group-art) nil t)
8824     (when edit
8825       (gnus-summary-edit-article))))
8826
8827 (defun gnus-summary-create-article ()
8828   "Create an article in a mail newsgroup."
8829   (interactive)
8830   (let ((group gnus-newsgroup-name)
8831         (now (current-time))
8832         group-art)
8833     (unless (gnus-check-backend-function 'request-accept-article group)
8834       (error "%s does not support article importing" group))
8835     (save-excursion
8836       (set-buffer (gnus-get-buffer-create " *import file*"))
8837       (erase-buffer)
8838       (goto-char (point-min))
8839       ;; This doesn't look like an article, so we fudge some headers.
8840       (insert "From: " (read-string "From: ") "\n"
8841               "Subject: " (read-string "Subject: ") "\n"
8842               "Date: " (message-make-date now) "\n"
8843               "Message-ID: " (message-make-message-id) "\n")
8844       (setq group-art (gnus-request-accept-article group nil t))
8845       (kill-buffer (current-buffer)))
8846     (setq gnus-newsgroup-active (gnus-activate-group group))
8847     (forward-line 1)
8848     (gnus-summary-goto-article (cdr group-art) nil t)
8849     (gnus-summary-edit-article)))
8850
8851 (defun gnus-summary-article-posted-p ()
8852   "Say whether the current (mail) article is available from news as well.
8853 This will be the case if the article has both been mailed and posted."
8854   (interactive)
8855   (let ((id (mail-header-references (gnus-summary-article-header)))
8856         (gnus-override-method (car (gnus-refer-article-methods))))
8857     (if (gnus-request-head id "")
8858         (gnus-message 2 "The current message was found on %s"
8859                       gnus-override-method)
8860       (gnus-message 2 "The current message couldn't be found on %s"
8861                     gnus-override-method)
8862       nil)))
8863
8864 (defun gnus-summary-expire-articles (&optional now)
8865   "Expire all articles that are marked as expirable in the current group."
8866   (interactive)
8867   (when (gnus-check-backend-function
8868          'request-expire-articles gnus-newsgroup-name)
8869     ;; This backend supports expiry.
8870     (let* ((total (gnus-group-total-expirable-p gnus-newsgroup-name))
8871            (expirable (if total
8872                           (progn
8873                             ;; We need to update the info for
8874                             ;; this group for `gnus-list-of-read-articles'
8875                             ;; to give us the right answer.
8876                             (gnus-run-hooks 'gnus-exit-group-hook)
8877                             (gnus-summary-update-info)
8878                             (gnus-list-of-read-articles gnus-newsgroup-name))
8879                         (setq gnus-newsgroup-expirable
8880                               (sort gnus-newsgroup-expirable '<))))
8881            (expiry-wait (if now 'immediate
8882                           (gnus-group-find-parameter
8883                            gnus-newsgroup-name 'expiry-wait)))
8884            (nnmail-expiry-target
8885             (or (gnus-group-find-parameter gnus-newsgroup-name 'expiry-target)
8886                 nnmail-expiry-target))
8887            es)
8888       (when expirable
8889         ;; There are expirable articles in this group, so we run them
8890         ;; through the expiry process.
8891         (gnus-message 6 "Expiring articles...")
8892         (unless (gnus-check-group gnus-newsgroup-name)
8893           (error "Can't open server for %s" gnus-newsgroup-name))
8894         ;; The list of articles that weren't expired is returned.
8895         (save-excursion
8896           (if expiry-wait
8897               (let ((nnmail-expiry-wait-function nil)
8898                     (nnmail-expiry-wait expiry-wait))
8899                 (setq es (gnus-request-expire-articles
8900                           expirable gnus-newsgroup-name)))
8901             (setq es (gnus-request-expire-articles
8902                       expirable gnus-newsgroup-name)))
8903           (unless total
8904             (setq gnus-newsgroup-expirable es))
8905           ;; We go through the old list of expirable, and mark all
8906           ;; really expired articles as nonexistent.
8907           (unless (eq es expirable) ;If nothing was expired, we don't mark.
8908             (let ((gnus-use-cache nil))
8909               (dolist (article expirable)
8910                 (when (and (not (memq article es))
8911                            (gnus-data-find article))
8912                   (gnus-summary-mark-article article gnus-canceled-mark))))))
8913         (gnus-message 6 "Expiring articles...done")))))
8914
8915 (defun gnus-summary-expire-articles-now ()
8916   "Expunge all expirable articles in the current group.
8917 This means that *all* articles that are marked as expirable will be
8918 deleted forever, right now."
8919   (interactive)
8920   (or gnus-expert-user
8921       (gnus-yes-or-no-p
8922        "Are you really, really, really sure you want to delete all these messages? ")
8923       (error "Phew!"))
8924   (gnus-summary-expire-articles t))
8925
8926 ;; Suggested by Jack Vinson <vinson@unagi.cis.upenn.edu>.
8927 (defun gnus-summary-delete-article (&optional n)
8928   "Delete the N next (mail) articles.
8929 This command actually deletes articles.  This is not a marking
8930 command.  The article will disappear forever from your life, never to
8931 return.
8932 If N is negative, delete backwards.
8933 If N is nil and articles have been marked with the process mark,
8934 delete these instead."
8935   (interactive "P")
8936   (unless (gnus-check-backend-function 'request-expire-articles
8937                                        gnus-newsgroup-name)
8938     (error "The current newsgroup does not support article deletion"))
8939   (unless (gnus-check-server (gnus-find-method-for-group gnus-newsgroup-name))
8940     (error "Couldn't open server"))
8941   ;; Compute the list of articles to delete.
8942   (let ((articles (sort (copy-sequence (gnus-summary-work-articles n)) '<))
8943         (nnmail-expiry-target 'delete)
8944         not-deleted)
8945     (if (and gnus-novice-user
8946              (not (gnus-yes-or-no-p
8947                    (format "Do you really want to delete %s forever? "
8948                            (if (> (length articles) 1)
8949                                (format "these %s articles" (length articles))
8950                              "this article")))))
8951         ()
8952       ;; Delete the articles.
8953       (setq not-deleted (gnus-request-expire-articles
8954                          articles gnus-newsgroup-name 'force))
8955       (while articles
8956         (gnus-summary-remove-process-mark (car articles))
8957         ;; The backend might not have been able to delete the article
8958         ;; after all.
8959         (unless (memq (car articles) not-deleted)
8960           (gnus-summary-mark-article (car articles) gnus-canceled-mark))
8961         (setq articles (cdr articles)))
8962       (when not-deleted
8963         (gnus-message 4 "Couldn't delete articles %s" not-deleted)))
8964     (gnus-summary-position-point)
8965     (gnus-set-mode-line 'summary)
8966     not-deleted))
8967
8968 (defun gnus-summary-edit-article (&optional arg)
8969   "Edit the current article.
8970 This will have permanent effect only in mail groups.
8971 If ARG is nil, edit the decoded articles.
8972 If ARG is 1, edit the raw articles.
8973 If ARG is 2, edit the raw articles even in read-only groups.
8974 If ARG is 3, edit the articles with the current handles.
8975 Otherwise, allow editing of articles even in read-only
8976 groups."
8977   (interactive "P")
8978   (let (force raw current-handles)
8979     (cond
8980      ((null arg))
8981      ((eq arg 1)
8982       (setq raw t))
8983      ((eq arg 2)
8984       (setq raw t
8985             force t))
8986      ((eq arg 3)
8987       (setq current-handles
8988             (and (gnus-buffer-live-p gnus-article-buffer)
8989                  (with-current-buffer gnus-article-buffer
8990                    (prog1
8991                        gnus-article-mime-handles
8992                      (setq gnus-article-mime-handles nil))))))
8993      (t
8994       (setq force t)))
8995     (when (and raw (not force)
8996                (member gnus-newsgroup-name '("nndraft:delayed"
8997                                              "nndraft:drafts"
8998                                              "nndraft:queue")))
8999       (error "Can't edit the raw article in group %s"
9000              gnus-newsgroup-name))
9001     (save-excursion
9002       (set-buffer gnus-summary-buffer)
9003       (let ((mail-parse-charset gnus-newsgroup-charset)
9004             (mail-parse-ignored-charsets gnus-newsgroup-ignored-charsets))
9005         (gnus-set-global-variables)
9006         (when (and (not force)
9007                    (gnus-group-read-only-p))
9008           (error "The current newsgroup does not support article editing"))
9009         (gnus-summary-show-article t)
9010         (when (and (not raw) (gnus-buffer-live-p gnus-article-buffer))
9011           (with-current-buffer gnus-article-buffer
9012             (mm-enable-multibyte)))
9013         (if (member gnus-newsgroup-name '("nndraft:delayed" "nndraft:drafts"))
9014             (setq raw t))
9015         (gnus-article-edit-article
9016          (if raw 'ignore
9017            `(lambda ()
9018               (let ((mbl mml-buffer-list))
9019                 (setq mml-buffer-list nil)
9020                 (mime-to-mml ,'current-handles)
9021                 (let ((mbl1 mml-buffer-list))
9022                   (setq mml-buffer-list mbl)
9023                   (set (make-local-variable 'mml-buffer-list) mbl1))
9024                 (make-local-hook 'kill-buffer-hook)
9025                 (add-hook 'kill-buffer-hook 'mml-destroy-buffers t t))))
9026          `(lambda (no-highlight)
9027             (let ((mail-parse-charset ',gnus-newsgroup-charset)
9028                   (message-options message-options)
9029                   (message-options-set-recipient)
9030                   (mail-parse-ignored-charsets
9031                    ',gnus-newsgroup-ignored-charsets))
9032               ,(if (not raw) '(progn
9033                                 (mml-to-mime)
9034                                 (mml-destroy-buffers)
9035                                 (remove-hook 'kill-buffer-hook
9036                                              'mml-destroy-buffers t)
9037                                 (kill-local-variable 'mml-buffer-list)))
9038               (gnus-summary-edit-article-done
9039                ,(or (mail-header-references gnus-current-headers) "")
9040                ,(gnus-group-read-only-p)
9041                ,gnus-summary-buffer no-highlight))))))))
9042
9043 (defalias 'gnus-summary-edit-article-postpone 'gnus-article-edit-exit)
9044
9045 (defun gnus-summary-edit-article-done (&optional references read-only buffer
9046                                                  no-highlight)
9047   "Make edits to the current article permanent."
9048   (interactive)
9049   (save-excursion
9050    ;; The buffer restriction contains the entire article if it exists.
9051     (when (article-goto-body)
9052       (let ((lines (count-lines (point) (point-max)))
9053             (length (- (point-max) (point)))
9054             (case-fold-search t)
9055             (body (copy-marker (point))))
9056         (goto-char (point-min))
9057         (when (re-search-forward "^content-length:[ \t]\\([0-9]+\\)" body t)
9058           (delete-region (match-beginning 1) (match-end 1))
9059           (insert (number-to-string length)))
9060         (goto-char (point-min))
9061         (when (re-search-forward
9062                "^x-content-length:[ \t]\\([0-9]+\\)" body t)
9063           (delete-region (match-beginning 1) (match-end 1))
9064           (insert (number-to-string length)))
9065         (goto-char (point-min))
9066         (when (re-search-forward "^lines:[ \t]\\([0-9]+\\)" body t)
9067           (delete-region (match-beginning 1) (match-end 1))
9068           (insert (number-to-string lines))))))
9069   ;; Replace the article.
9070   (let ((buf (current-buffer)))
9071     (with-temp-buffer
9072       (insert-buffer-substring buf)
9073
9074       (if (and (not read-only)
9075                (not (gnus-request-replace-article
9076                      (cdr gnus-article-current) (car gnus-article-current)
9077                      (current-buffer) t)))
9078           (error "Couldn't replace article")
9079         ;; Update the summary buffer.
9080         (if (and references
9081                  (equal (message-tokenize-header references " ")
9082                         (message-tokenize-header
9083                          (or (message-fetch-field "references") "") " ")))
9084             ;; We only have to update this line.
9085             (save-excursion
9086               (save-restriction
9087                 (message-narrow-to-head)
9088                 (let ((head (buffer-string))
9089                       header)
9090                   (with-temp-buffer
9091                     (insert (format "211 %d Article retrieved.\n"
9092                                     (cdr gnus-article-current)))
9093                     (insert head)
9094                     (insert ".\n")
9095                     (let ((nntp-server-buffer (current-buffer)))
9096                       (setq header (car (gnus-get-newsgroup-headers
9097                                          nil t))))
9098                     (save-excursion
9099                       (set-buffer gnus-summary-buffer)
9100                       (gnus-data-set-header
9101                        (gnus-data-find (cdr gnus-article-current))
9102                        header)
9103                       (gnus-summary-update-article-line
9104                        (cdr gnus-article-current) header)
9105                       (if (gnus-summary-goto-subject
9106                            (cdr gnus-article-current) nil t)
9107                           (gnus-summary-update-secondary-mark
9108                            (cdr gnus-article-current))))))))
9109           ;; Update threads.
9110           (set-buffer (or buffer gnus-summary-buffer))
9111           (gnus-summary-update-article (cdr gnus-article-current))
9112           (if (gnus-summary-goto-subject (cdr gnus-article-current) nil t)
9113               (gnus-summary-update-secondary-mark
9114                (cdr gnus-article-current))))
9115         ;; Prettify the article buffer again.
9116         (unless no-highlight
9117           (save-excursion
9118             (set-buffer gnus-article-buffer)
9119             ;;;!!! Fix this -- article should be rehighlighted.
9120             ;;;(gnus-run-hooks 'gnus-article-display-hook)
9121             (set-buffer gnus-original-article-buffer)
9122             (gnus-request-article
9123              (cdr gnus-article-current)
9124              (car gnus-article-current) (current-buffer))))
9125         ;; Prettify the summary buffer line.
9126         (when (gnus-visual-p 'summary-highlight 'highlight)
9127           (gnus-run-hooks 'gnus-visual-mark-article-hook))))))
9128
9129 (defun gnus-summary-edit-wash (key)
9130   "Perform editing command KEY in the article buffer."
9131   (interactive
9132    (list
9133     (progn
9134       (message "%s" (concat (this-command-keys) "- "))
9135       (read-char))))
9136   (message "")
9137   (gnus-summary-edit-article)
9138   (execute-kbd-macro (concat (this-command-keys) key))
9139   (gnus-article-edit-done))
9140
9141 ;;; Respooling
9142
9143 (defun gnus-summary-respool-query (&optional silent trace)
9144   "Query where the respool algorithm would put this article."
9145   (interactive)
9146   (let (gnus-mark-article-hook)
9147     (gnus-summary-select-article)
9148     (save-excursion
9149       (set-buffer gnus-original-article-buffer)
9150       (save-restriction
9151         (message-narrow-to-head)
9152         (let ((groups (nnmail-article-group 'identity trace)))
9153           (unless silent
9154             (if groups
9155                 (message "This message would go to %s"
9156                          (mapconcat 'car groups ", "))
9157               (message "This message would go to no groups"))
9158             groups))))))
9159
9160 (defun gnus-summary-respool-trace ()
9161   "Trace where the respool algorithm would put this article.
9162 Display a buffer showing all fancy splitting patterns which matched."
9163   (interactive)
9164   (gnus-summary-respool-query nil t))
9165
9166 ;; Summary marking commands.
9167
9168 (defun gnus-summary-kill-same-subject-and-select (&optional unmark)
9169   "Mark articles which has the same subject as read, and then select the next.
9170 If UNMARK is positive, remove any kind of mark.
9171 If UNMARK is negative, tick articles."
9172   (interactive "P")
9173   (when unmark
9174     (setq unmark (prefix-numeric-value unmark)))
9175   (let ((count
9176          (gnus-summary-mark-same-subject
9177           (gnus-summary-article-subject) unmark)))
9178     ;; Select next unread article.  If auto-select-same mode, should
9179     ;; select the first unread article.
9180     (gnus-summary-next-article t (and gnus-auto-select-same
9181                                       (gnus-summary-article-subject)))
9182     (gnus-message 7 "%d article%s marked as %s"
9183                   count (if (= count 1) " is" "s are")
9184                   (if unmark "unread" "read"))))
9185
9186 (defun gnus-summary-kill-same-subject (&optional unmark)
9187   "Mark articles which has the same subject as read.
9188 If UNMARK is positive, remove any kind of mark.
9189 If UNMARK is negative, tick articles."
9190   (interactive "P")
9191   (when unmark
9192     (setq unmark (prefix-numeric-value unmark)))
9193   (let ((count
9194          (gnus-summary-mark-same-subject
9195           (gnus-summary-article-subject) unmark)))
9196     ;; If marked as read, go to next unread subject.
9197     (when (null unmark)
9198       ;; Go to next unread subject.
9199       (gnus-summary-next-subject 1 t))
9200     (gnus-message 7 "%d articles are marked as %s"
9201                   count (if unmark "unread" "read"))))
9202
9203 (defun gnus-summary-mark-same-subject (subject &optional unmark)
9204   "Mark articles with same SUBJECT as read, and return marked number.
9205 If optional argument UNMARK is positive, remove any kinds of marks.
9206 If optional argument UNMARK is negative, mark articles as unread instead."
9207   (let ((count 1))
9208     (save-excursion
9209       (cond
9210        ((null unmark)                   ; Mark as read.
9211         (while (and
9212                 (progn
9213                   (gnus-summary-mark-article-as-read gnus-killed-mark)
9214                   (gnus-summary-show-thread) t)
9215                 (gnus-summary-find-subject subject))
9216           (setq count (1+ count))))
9217        ((> unmark 0)                    ; Tick.
9218         (while (and
9219                 (progn
9220                   (gnus-summary-mark-article-as-unread gnus-ticked-mark)
9221                   (gnus-summary-show-thread) t)
9222                 (gnus-summary-find-subject subject))
9223           (setq count (1+ count))))
9224        (t                               ; Mark as unread.
9225         (while (and
9226                 (progn
9227                   (gnus-summary-mark-article-as-unread gnus-unread-mark)
9228                   (gnus-summary-show-thread) t)
9229                 (gnus-summary-find-subject subject))
9230           (setq count (1+ count)))))
9231       (gnus-set-mode-line 'summary)
9232       ;; Return the number of marked articles.
9233       count)))
9234
9235 (defun gnus-summary-mark-as-processable (n &optional unmark)
9236   "Set the process mark on the next N articles.
9237 If N is negative, mark backward instead.  If UNMARK is non-nil, remove
9238 the process mark instead.  The difference between N and the actual
9239 number of articles marked is returned."
9240   (interactive "P")
9241   (if (and (null n) (gnus-region-active-p))
9242       (gnus-uu-mark-region (region-beginning) (region-end) unmark)
9243     (setq n (prefix-numeric-value n))
9244     (let ((backward (< n 0))
9245           (n (abs n)))
9246       (while (and
9247               (> n 0)
9248               (if unmark
9249                   (gnus-summary-remove-process-mark
9250                    (gnus-summary-article-number))
9251                 (gnus-summary-set-process-mark (gnus-summary-article-number)))
9252               (zerop (gnus-summary-next-subject (if backward -1 1) nil t)))
9253         (setq n (1- n)))
9254       (when (/= 0 n)
9255         (gnus-message 7 "No more articles"))
9256       (gnus-summary-recenter)
9257       (gnus-summary-position-point)
9258       n)))
9259
9260 (defun gnus-summary-unmark-as-processable (n)
9261   "Remove the process mark from the next N articles.
9262 If N is negative, unmark backward instead.  The difference between N and
9263 the actual number of articles unmarked is returned."
9264   (interactive "P")
9265   (gnus-summary-mark-as-processable n t))
9266
9267 (defun gnus-summary-unmark-all-processable ()
9268   "Remove the process mark from all articles."
9269   (interactive)
9270   (save-excursion
9271     (while gnus-newsgroup-processable
9272       (gnus-summary-remove-process-mark (car gnus-newsgroup-processable))))
9273   (gnus-summary-position-point))
9274
9275 (defun gnus-summary-add-mark (article type)
9276   "Mark ARTICLE with a mark of TYPE."
9277   (let ((vtype (car (assq type gnus-article-mark-lists)))
9278         var)
9279     (if (not vtype)
9280         (error "No such mark type: %s" type)
9281       (setq var (intern (format "gnus-newsgroup-%s" type)))
9282       (set var (cons article (symbol-value var)))
9283       (if (memq type '(processable cached replied forwarded recent saved))
9284           (gnus-summary-update-secondary-mark article)
9285         ;;; !!! This is bogus.  We should find out what primary
9286         ;;; !!! mark we want to set.
9287         (gnus-summary-update-mark gnus-del-mark 'unread)))))
9288
9289 (defun gnus-summary-mark-as-expirable (n)
9290   "Mark N articles forward as expirable.
9291 If N is negative, mark backward instead.  The difference between N and
9292 the actual number of articles marked is returned."
9293   (interactive "p")
9294   (gnus-summary-mark-forward n gnus-expirable-mark))
9295
9296 (defun gnus-summary-mark-as-spam (n)
9297   "Mark N articles forward as spam.
9298 If N is negative, mark backward instead.  The difference between N and
9299 the actual number of articles marked is returned."
9300   (interactive "p")
9301   (gnus-summary-mark-forward n gnus-spam-mark))
9302
9303 (defun gnus-summary-mark-article-as-replied (article)
9304   "Mark ARTICLE as replied to and update the summary line.
9305 ARTICLE can also be a list of articles."
9306   (interactive (list (gnus-summary-article-number)))
9307   (let ((articles (if (listp article) article (list article))))
9308     (dolist (article articles)
9309       (push article gnus-newsgroup-replied)
9310       (let ((buffer-read-only nil))
9311         (when (gnus-summary-goto-subject article nil t)
9312           (gnus-summary-update-secondary-mark article))))))
9313
9314 (defun gnus-summary-mark-article-as-forwarded (article)
9315   "Mark ARTICLE as forwarded and update the summary line.
9316 ARTICLE can also be a list of articles."
9317   (let ((articles (if (listp article) article (list article))))
9318     (dolist (article articles)
9319       (push article gnus-newsgroup-forwarded)
9320       (let ((buffer-read-only nil))
9321         (when (gnus-summary-goto-subject article nil t)
9322           (gnus-summary-update-secondary-mark article))))))
9323
9324 (defun gnus-summary-set-bookmark (article)
9325   "Set a bookmark in current article."
9326   (interactive (list (gnus-summary-article-number)))
9327   (when (or (not (get-buffer gnus-article-buffer))
9328             (not gnus-current-article)
9329             (not gnus-article-current)
9330             (not (equal gnus-newsgroup-name (car gnus-article-current))))
9331     (error "No current article selected"))
9332   ;; Remove old bookmark, if one exists.
9333   (let ((old (assq article gnus-newsgroup-bookmarks)))
9334     (when old
9335       (setq gnus-newsgroup-bookmarks
9336             (delq old gnus-newsgroup-bookmarks))))
9337   ;; Set the new bookmark, which is on the form
9338   ;; (article-number . line-number-in-body).
9339   (push
9340    (cons article
9341          (save-excursion
9342            (set-buffer gnus-article-buffer)
9343            (count-lines
9344             (min (point)
9345                  (save-excursion
9346                    (goto-char (point-min))
9347                    (search-forward "\n\n" nil t)
9348                    (point)))
9349             (point))))
9350    gnus-newsgroup-bookmarks)
9351   (gnus-message 6 "A bookmark has been added to the current article."))
9352
9353 (defun gnus-summary-remove-bookmark (article)
9354   "Remove the bookmark from the current article."
9355   (interactive (list (gnus-summary-article-number)))
9356   ;; Remove old bookmark, if one exists.
9357   (let ((old (assq article gnus-newsgroup-bookmarks)))
9358     (if old
9359         (progn
9360           (setq gnus-newsgroup-bookmarks
9361                 (delq old gnus-newsgroup-bookmarks))
9362           (gnus-message 6 "Removed bookmark."))
9363       (gnus-message 6 "No bookmark in current article."))))
9364
9365 ;; Suggested by Daniel Quinlan <quinlan@best.com>.
9366 (defun gnus-summary-mark-as-dormant (n)
9367   "Mark N articles forward as dormant.
9368 If N is negative, mark backward instead.  The difference between N and
9369 the actual number of articles marked is returned."
9370   (interactive "p")
9371   (gnus-summary-mark-forward n gnus-dormant-mark))
9372
9373 (defun gnus-summary-set-process-mark (article)
9374   "Set the process mark on ARTICLE and update the summary line."
9375   (setq gnus-newsgroup-processable
9376         (cons article
9377               (delq article gnus-newsgroup-processable)))
9378   (when (gnus-summary-goto-subject article)
9379     (gnus-summary-show-thread)
9380     (gnus-summary-goto-subject article)
9381     (gnus-summary-update-secondary-mark article)))
9382
9383 (defun gnus-summary-remove-process-mark (article)
9384   "Remove the process mark from ARTICLE and update the summary line."
9385   (setq gnus-newsgroup-processable (delq article gnus-newsgroup-processable))
9386   (when (gnus-summary-goto-subject article)
9387     (gnus-summary-show-thread)
9388     (gnus-summary-goto-subject article)
9389     (gnus-summary-update-secondary-mark article)))
9390
9391 (defun gnus-summary-set-saved-mark (article)
9392   "Set the process mark on ARTICLE and update the summary line."
9393   (push article gnus-newsgroup-saved)
9394   (when (gnus-summary-goto-subject article)
9395     (gnus-summary-update-secondary-mark article)))
9396
9397 (defun gnus-summary-mark-forward (n &optional mark no-expire)
9398   "Mark N articles as read forwards.
9399 If N is negative, mark backwards instead.  Mark with MARK, ?r by default.
9400 The difference between N and the actual number of articles marked is
9401 returned.
9402 If NO-EXPIRE, auto-expiry will be inhibited."
9403   (interactive "p")
9404   (gnus-summary-show-thread)
9405   (let ((backward (< n 0))
9406         (gnus-summary-goto-unread
9407          (and gnus-summary-goto-unread
9408               (not (eq gnus-summary-goto-unread 'never))
9409               (not (memq mark (list gnus-unread-mark
9410                                     gnus-ticked-mark gnus-dormant-mark)))))
9411         (n (abs n))
9412         (mark (or mark gnus-del-mark)))
9413     (while (and (> n 0)
9414                 (gnus-summary-mark-article nil mark no-expire)
9415                 (zerop (gnus-summary-next-subject
9416                         (if backward -1 1)
9417                         (and gnus-summary-goto-unread
9418                              (not (eq gnus-summary-goto-unread 'never)))
9419                         t)))
9420       (setq n (1- n)))
9421     (when (/= 0 n)
9422       (gnus-message 7 "No more %sarticles" (if mark "" "unread ")))
9423     (gnus-summary-recenter)
9424     (gnus-summary-position-point)
9425     (gnus-set-mode-line 'summary)
9426     n))
9427
9428 (defun gnus-summary-mark-article-as-read (mark)
9429   "Mark the current article quickly as read with MARK."
9430   (let ((article (gnus-summary-article-number)))
9431     (setq gnus-newsgroup-unreads (delq article gnus-newsgroup-unreads))
9432     (setq gnus-newsgroup-marked (delq article gnus-newsgroup-marked))
9433     (setq gnus-newsgroup-spam-marked (delq article gnus-newsgroup-spam-marked))
9434     (setq gnus-newsgroup-dormant (delq article gnus-newsgroup-dormant))
9435     (push (cons article mark) gnus-newsgroup-reads)
9436     ;; Possibly remove from cache, if that is used.
9437     (when gnus-use-cache
9438       (gnus-cache-enter-remove-article article))
9439     ;; Allow the backend to change the mark.
9440     (setq mark (gnus-request-update-mark gnus-newsgroup-name article mark))
9441     ;; Check for auto-expiry.
9442     (when (and gnus-newsgroup-auto-expire
9443                (memq mark gnus-auto-expirable-marks))
9444       (setq mark gnus-expirable-mark)
9445       ;; Let the backend know about the mark change.
9446       (setq mark (gnus-request-update-mark gnus-newsgroup-name article mark))
9447       (push article gnus-newsgroup-expirable))
9448     ;; Set the mark in the buffer.
9449     (gnus-summary-update-mark mark 'unread)
9450     t))
9451
9452 (defun gnus-summary-mark-article-as-unread (mark)
9453   "Mark the current article quickly as unread with MARK."
9454   (let* ((article (gnus-summary-article-number))
9455          (old-mark (gnus-summary-article-mark article)))
9456     ;; Allow the backend to change the mark.
9457     (setq mark (gnus-request-update-mark gnus-newsgroup-name article mark))
9458     (if (eq mark old-mark)
9459         t
9460       (if (<= article 0)
9461           (progn
9462             (gnus-error 1 "Can't mark negative article numbers")
9463             nil)
9464         (setq gnus-newsgroup-marked (delq article gnus-newsgroup-marked))
9465         (setq gnus-newsgroup-spam-marked
9466               (delq article gnus-newsgroup-spam-marked))
9467         (setq gnus-newsgroup-dormant (delq article gnus-newsgroup-dormant))
9468         (setq gnus-newsgroup-expirable (delq article gnus-newsgroup-expirable))
9469         (setq gnus-newsgroup-reads (delq article gnus-newsgroup-reads))
9470         (cond ((= mark gnus-ticked-mark)
9471                (setq gnus-newsgroup-marked
9472                      (gnus-add-to-sorted-list gnus-newsgroup-marked
9473                                               article)))
9474               ((= mark gnus-spam-mark)
9475                (setq gnus-newsgroup-spam-marked
9476                      (gnus-add-to-sorted-list gnus-newsgroup-spam-marked
9477                                               article)))
9478               ((= mark gnus-dormant-mark)
9479                (setq gnus-newsgroup-dormant
9480                      (gnus-add-to-sorted-list gnus-newsgroup-dormant
9481                                               article)))
9482               (t
9483                (setq gnus-newsgroup-unreads
9484                      (gnus-add-to-sorted-list gnus-newsgroup-unreads
9485                                               article))))
9486         (gnus-pull article gnus-newsgroup-reads)
9487
9488         ;; See whether the article is to be put in the cache.
9489         (and gnus-use-cache
9490              (vectorp (gnus-summary-article-header article))
9491              (save-excursion
9492                (gnus-cache-possibly-enter-article
9493                 gnus-newsgroup-name article
9494                 (= mark gnus-ticked-mark)
9495                 (= mark gnus-dormant-mark) (= mark gnus-unread-mark))))
9496
9497         ;; Fix the mark.
9498         (gnus-summary-update-mark mark 'unread)
9499         t))))
9500
9501 (defun gnus-summary-mark-article (&optional article mark no-expire)
9502   "Mark ARTICLE with MARK.  MARK can be any character.
9503 Four MARK strings are reserved: `? ' (unread), `?!' (ticked),
9504 `??' (dormant) and `?E' (expirable).
9505 If MARK is nil, then the default character `?r' is used.
9506 If ARTICLE is nil, then the article on the current line will be
9507 marked.
9508 If NO-EXPIRE, auto-expiry will be inhibited."
9509   ;; The mark might be a string.
9510   (when (stringp mark)
9511     (setq mark (aref mark 0)))
9512   ;; If no mark is given, then we check auto-expiring.
9513   (when (null mark)
9514     (setq mark gnus-del-mark))
9515   (when (and (not no-expire)
9516              gnus-newsgroup-auto-expire
9517              (memq mark gnus-auto-expirable-marks))
9518     (setq mark gnus-expirable-mark))
9519   (let ((article (or article (gnus-summary-article-number)))
9520         (old-mark (gnus-summary-article-mark article)))
9521     ;; Allow the backend to change the mark.
9522     (setq mark (gnus-request-update-mark gnus-newsgroup-name article mark))
9523     (if (eq mark old-mark)
9524         t
9525       (unless article
9526         (error "No article on current line"))
9527       (if (not (if (or (= mark gnus-unread-mark)
9528                        (= mark gnus-ticked-mark)
9529                        (= mark gnus-spam-mark)
9530                        (= mark gnus-dormant-mark))
9531                    (gnus-mark-article-as-unread article mark)
9532                  (gnus-mark-article-as-read article mark)))
9533           t
9534         ;; See whether the article is to be put in the cache.
9535         (and gnus-use-cache
9536              (not (= mark gnus-canceled-mark))
9537              (vectorp (gnus-summary-article-header article))
9538              (save-excursion
9539                (gnus-cache-possibly-enter-article
9540                 gnus-newsgroup-name article
9541                 (= mark gnus-ticked-mark)
9542                 (= mark gnus-dormant-mark) (= mark gnus-unread-mark))))
9543
9544         (when (gnus-summary-goto-subject article nil t)
9545           (let ((buffer-read-only nil))
9546             (gnus-summary-show-thread)
9547             ;; Fix the mark.
9548             (gnus-summary-update-mark mark 'unread)
9549             t))))))
9550
9551 (defun gnus-summary-update-secondary-mark (article)
9552   "Update the secondary (read, process, cache) mark."
9553   (gnus-summary-update-mark
9554    (cond ((memq article gnus-newsgroup-processable)
9555           gnus-process-mark)
9556          ((memq article gnus-newsgroup-cached)
9557           gnus-cached-mark)
9558          ((memq article gnus-newsgroup-replied)
9559           gnus-replied-mark)
9560          ((memq article gnus-newsgroup-forwarded)
9561           gnus-forwarded-mark)
9562          ((memq article gnus-newsgroup-saved)
9563           gnus-saved-mark)
9564          ((memq article gnus-newsgroup-recent)
9565           gnus-recent-mark)
9566          ((memq article gnus-newsgroup-unseen)
9567           gnus-unseen-mark)
9568          (t gnus-no-mark))
9569    'replied)
9570   (when (gnus-visual-p 'summary-highlight 'highlight)
9571     (gnus-run-hooks 'gnus-summary-update-hook))
9572   t)
9573
9574 (defun gnus-summary-update-mark (mark type)
9575   (let ((forward (cdr (assq type gnus-summary-mark-positions)))
9576         (buffer-read-only nil))
9577     (re-search-backward "[\n\r]" (gnus-point-at-bol) 'move-to-limit)
9578     (when forward
9579       (when (looking-at "\r")
9580         (incf forward))
9581       (when (<= (+ forward (point)) (point-max))
9582         ;; Go to the right position on the line.
9583         (goto-char (+ forward (point)))
9584         ;; Replace the old mark with the new mark.
9585         (subst-char-in-region (point) (1+ (point)) (char-after) mark)
9586         ;; Optionally update the marks by some user rule.
9587         (when (eq type 'unread)
9588           (gnus-data-set-mark
9589            (gnus-data-find (gnus-summary-article-number)) mark)
9590           (gnus-summary-update-line (eq mark gnus-unread-mark)))))))
9591
9592 (defun gnus-mark-article-as-read (article &optional mark)
9593   "Enter ARTICLE in the pertinent lists and remove it from others."
9594   ;; Make the article expirable.
9595   (let ((mark (or mark gnus-del-mark)))
9596     (setq gnus-newsgroup-expirable
9597           (if (= mark gnus-expirable-mark)
9598               (gnus-add-to-sorted-list gnus-newsgroup-expirable article)
9599             (delq article gnus-newsgroup-expirable)))
9600     ;; Remove from unread and marked lists.
9601     (setq gnus-newsgroup-unreads (delq article gnus-newsgroup-unreads))
9602     (setq gnus-newsgroup-marked (delq article gnus-newsgroup-marked))
9603     (setq gnus-newsgroup-spam-marked (delq article gnus-newsgroup-spam-marked))
9604     (setq gnus-newsgroup-dormant (delq article gnus-newsgroup-dormant))
9605     (push (cons article mark) gnus-newsgroup-reads)
9606     ;; Possibly remove from cache, if that is used.
9607     (when gnus-use-cache
9608       (gnus-cache-enter-remove-article article))
9609     t))
9610
9611 (defun gnus-mark-article-as-unread (article &optional mark)
9612   "Enter ARTICLE in the pertinent lists and remove it from others."
9613   (let ((mark (or mark gnus-ticked-mark)))
9614     (if (<= article 0)
9615         (progn
9616           (gnus-error 1 "Can't mark negative article numbers")
9617           nil)
9618       (setq gnus-newsgroup-marked (delq article gnus-newsgroup-marked)
9619             gnus-newsgroup-spam-marked (delq article gnus-newsgroup-spam-marked)
9620             gnus-newsgroup-dormant (delq article gnus-newsgroup-dormant)
9621             gnus-newsgroup-expirable (delq article gnus-newsgroup-expirable)
9622             gnus-newsgroup-unreads (delq article gnus-newsgroup-unreads))
9623
9624       ;; Unsuppress duplicates?
9625       (when gnus-suppress-duplicates
9626         (gnus-dup-unsuppress-article article))
9627
9628       (cond ((= mark gnus-ticked-mark)
9629              (setq gnus-newsgroup-marked
9630                    (gnus-add-to-sorted-list gnus-newsgroup-marked article)))
9631             ((= mark gnus-spam-mark)
9632              (setq gnus-newsgroup-spam-marked
9633                    (gnus-add-to-sorted-list gnus-newsgroup-spam-marked
9634                                             article)))
9635             ((= mark gnus-dormant-mark)
9636              (setq gnus-newsgroup-dormant
9637                    (gnus-add-to-sorted-list gnus-newsgroup-dormant article)))
9638             (t
9639              (setq gnus-newsgroup-unreads
9640                    (gnus-add-to-sorted-list gnus-newsgroup-unreads article))))
9641       (gnus-pull article gnus-newsgroup-reads)
9642       t)))
9643
9644 (defalias 'gnus-summary-mark-as-unread-forward
9645   'gnus-summary-tick-article-forward)
9646 (make-obsolete 'gnus-summary-mark-as-unread-forward
9647                'gnus-summary-tick-article-forward)
9648 (defun gnus-summary-tick-article-forward (n)
9649   "Tick N articles forwards.
9650 If N is negative, tick backwards instead.
9651 The difference between N and the number of articles ticked is returned."
9652   (interactive "p")
9653   (gnus-summary-mark-forward n gnus-ticked-mark))
9654
9655 (defalias 'gnus-summary-mark-as-unread-backward
9656   'gnus-summary-tick-article-backward)
9657 (make-obsolete 'gnus-summary-mark-as-unread-backward
9658                'gnus-summary-tick-article-backward)
9659 (defun gnus-summary-tick-article-backward (n)
9660   "Tick N articles backwards.
9661 The difference between N and the number of articles ticked is returned."
9662   (interactive "p")
9663   (gnus-summary-mark-forward (- n) gnus-ticked-mark))
9664
9665 (defalias 'gnus-summary-mark-as-unread 'gnus-summary-tick-article)
9666 (make-obsolete 'gnus-summary-mark-as-unread 'gnus-summary-tick-article)
9667 (defun gnus-summary-tick-article (&optional article clear-mark)
9668   "Mark current article as unread.
9669 Optional 1st argument ARTICLE specifies article number to be marked as unread.
9670 Optional 2nd argument CLEAR-MARK remove any kinds of mark."
9671   (interactive)
9672   (gnus-summary-mark-article article (if clear-mark gnus-unread-mark
9673                                        gnus-ticked-mark)))
9674
9675 (defun gnus-summary-mark-as-read-forward (n)
9676   "Mark N articles as read forwards.
9677 If N is negative, mark backwards instead.
9678 The difference between N and the actual number of articles marked is
9679 returned."
9680   (interactive "p")
9681   (gnus-summary-mark-forward n gnus-del-mark gnus-inhibit-user-auto-expire))
9682
9683 (defun gnus-summary-mark-as-read-backward (n)
9684   "Mark the N articles as read backwards.
9685 The difference between N and the actual number of articles marked is
9686 returned."
9687   (interactive "p")
9688   (gnus-summary-mark-forward
9689    (- n) gnus-del-mark gnus-inhibit-user-auto-expire))
9690
9691 (defun gnus-summary-mark-as-read (&optional article mark)
9692   "Mark current article as read.
9693 ARTICLE specifies the article to be marked as read.
9694 MARK specifies a string to be inserted at the beginning of the line."
9695   (gnus-summary-mark-article article mark))
9696
9697 (defun gnus-summary-clear-mark-forward (n)
9698   "Clear marks from N articles forward.
9699 If N is negative, clear backward instead.
9700 The difference between N and the number of marks cleared is returned."
9701   (interactive "p")
9702   (gnus-summary-mark-forward n gnus-unread-mark))
9703
9704 (defun gnus-summary-clear-mark-backward (n)
9705   "Clear marks from N articles backward.
9706 The difference between N and the number of marks cleared is returned."
9707   (interactive "p")
9708   (gnus-summary-mark-forward (- n) gnus-unread-mark))
9709
9710 (defun gnus-summary-mark-unread-as-read ()
9711   "Intended to be used by `gnus-summary-mark-article-hook'."
9712   (when (memq gnus-current-article gnus-newsgroup-unreads)
9713     (gnus-summary-mark-article gnus-current-article gnus-read-mark)))
9714
9715 (defun gnus-summary-mark-read-and-unread-as-read ()
9716   "Intended to be used by `gnus-summary-mark-article-hook'."
9717   (let ((mark (gnus-summary-article-mark)))
9718     (when (or (gnus-unread-mark-p mark)
9719               (gnus-read-mark-p mark))
9720       (gnus-summary-mark-article gnus-current-article gnus-read-mark))))
9721
9722 (defun gnus-summary-mark-unread-as-ticked ()
9723   "Intended to be used by `gnus-summary-mark-article-hook'."
9724   (when (memq gnus-current-article gnus-newsgroup-unreads)
9725     (gnus-summary-mark-article gnus-current-article gnus-ticked-mark)))
9726
9727 (defun gnus-summary-mark-region-as-read (point mark all)
9728   "Mark all unread articles between point and mark as read.
9729 If given a prefix, mark all articles between point and mark as read,
9730 even ticked and dormant ones."
9731   (interactive "r\nP")
9732   (save-excursion
9733     (let (article)
9734       (goto-char point)
9735       (beginning-of-line)
9736       (while (and
9737               (< (point) mark)
9738               (progn
9739                 (when (or all
9740                           (memq (setq article (gnus-summary-article-number))
9741                                 gnus-newsgroup-unreads))
9742                   (gnus-summary-mark-article article gnus-del-mark))
9743                 t)
9744               (gnus-summary-find-next))))))
9745
9746 (defun gnus-summary-mark-below (score mark)
9747   "Mark articles with score less than SCORE with MARK."
9748   (interactive "P\ncMark: ")
9749   (setq score (if score
9750                   (prefix-numeric-value score)
9751                 (or gnus-summary-default-score 0)))
9752   (save-excursion
9753     (set-buffer gnus-summary-buffer)
9754     (goto-char (point-min))
9755     (while
9756         (progn
9757           (and (< (gnus-summary-article-score) score)
9758                (gnus-summary-mark-article nil mark))
9759           (gnus-summary-find-next)))))
9760
9761 (defun gnus-summary-kill-below (&optional score)
9762   "Mark articles with score below SCORE as read."
9763   (interactive "P")
9764   (gnus-summary-mark-below score gnus-killed-mark))
9765
9766 (defun gnus-summary-clear-above (&optional score)
9767   "Clear all marks from articles with score above SCORE."
9768   (interactive "P")
9769   (gnus-summary-mark-above score gnus-unread-mark))
9770
9771 (defun gnus-summary-tick-above (&optional score)
9772   "Tick all articles with score above SCORE."
9773   (interactive "P")
9774   (gnus-summary-mark-above score gnus-ticked-mark))
9775
9776 (defun gnus-summary-mark-above (score mark)
9777   "Mark articles with score over SCORE with MARK."
9778   (interactive "P\ncMark: ")
9779   (setq score (if score
9780                   (prefix-numeric-value score)
9781                 (or gnus-summary-default-score 0)))
9782   (save-excursion
9783     (set-buffer gnus-summary-buffer)
9784     (goto-char (point-min))
9785     (while (and (progn
9786                   (when (> (gnus-summary-article-score) score)
9787                     (gnus-summary-mark-article nil mark))
9788                   t)
9789                 (gnus-summary-find-next)))))
9790
9791 ;; Suggested by Daniel Quinlan <quinlan@best.com>.
9792 (defalias 'gnus-summary-show-all-expunged 'gnus-summary-limit-include-expunged)
9793 (defun gnus-summary-limit-include-expunged (&optional no-error)
9794   "Display all the hidden articles that were expunged for low scores."
9795   (interactive)
9796   (let ((buffer-read-only nil))
9797     (let ((scored gnus-newsgroup-scored)
9798           headers h)
9799       (while scored
9800         (unless (gnus-summary-article-header (caar scored))
9801           (and (setq h (gnus-number-to-header (caar scored)))
9802                (< (cdar scored) gnus-summary-expunge-below)
9803                (push h headers)))
9804         (setq scored (cdr scored)))
9805       (if (not headers)
9806           (when (not no-error)
9807             (error "No expunged articles hidden"))
9808         (goto-char (point-min))
9809         (push gnus-newsgroup-limit gnus-newsgroup-limits)
9810         (setq gnus-newsgroup-limit (copy-sequence gnus-newsgroup-limit))
9811         (mapcar (lambda (x) (push (mail-header-number x)
9812                                   gnus-newsgroup-limit))
9813                 headers)
9814         (gnus-summary-prepare-unthreaded (nreverse headers))
9815         (goto-char (point-min))
9816         (gnus-summary-position-point)
9817         t))))
9818
9819 (defun gnus-summary-catchup (&optional all quietly to-here not-mark reverse)
9820   "Mark all unread articles in this newsgroup as read.
9821 If prefix argument ALL is non-nil, ticked and dormant articles will
9822 also be marked as read.
9823 If QUIETLY is non-nil, no questions will be asked.
9824 If TO-HERE is non-nil, it should be a point in the buffer.  All
9825 articles before (after, if REVERSE is set) this point will be marked as read.
9826 Note that this function will only catch up the unread article
9827 in the current summary buffer limitation.
9828 The number of articles marked as read is returned."
9829   (interactive "P")
9830   (prog1
9831       (save-excursion
9832         (when (or quietly
9833                   (not gnus-interactive-catchup) ;Without confirmation?
9834                   gnus-expert-user
9835                   (gnus-y-or-n-p
9836                    (if all
9837                        "Mark absolutely all articles as read? "
9838                      "Mark all unread articles as read? ")))
9839           (if (and not-mark
9840                    (not gnus-newsgroup-adaptive)
9841                    (not gnus-newsgroup-auto-expire)
9842                    (not gnus-suppress-duplicates)
9843                    (or (not gnus-use-cache)
9844                        (eq gnus-use-cache 'passive)))
9845               (progn
9846                 (when all
9847                   (setq gnus-newsgroup-marked nil
9848                         gnus-newsgroup-spam-marked nil
9849                         gnus-newsgroup-dormant nil))
9850                 (setq gnus-newsgroup-unreads gnus-newsgroup-downloadable))
9851             ;; We actually mark all articles as canceled, which we
9852             ;; have to do when using auto-expiry or adaptive scoring.
9853             (gnus-summary-show-all-threads)
9854             (if (and to-here reverse)
9855                 (progn
9856                   (goto-char to-here)
9857                   (while (and
9858                           (gnus-summary-mark-article-as-read gnus-catchup-mark)
9859                           (gnus-summary-find-next (not all) nil nil t))))
9860               (when (gnus-summary-first-subject (not all) t)
9861                 (while (and
9862                         (if to-here (< (point) to-here) t)
9863                         (gnus-summary-mark-article-as-read gnus-catchup-mark)
9864                         (gnus-summary-find-next (not all) nil nil t)))))
9865             (gnus-set-mode-line 'summary))
9866           t))
9867     (gnus-summary-position-point)))
9868
9869 (defun gnus-summary-catchup-to-here (&optional all)
9870   "Mark all unticked articles before the current one as read.
9871 If ALL is non-nil, also mark ticked and dormant articles as read."
9872   (interactive "P")
9873   (save-excursion
9874     (gnus-save-hidden-threads
9875       (let ((beg (point)))
9876         ;; We check that there are unread articles.
9877         (when (or all (gnus-summary-find-prev))
9878           (gnus-summary-catchup all t beg)))))
9879   (gnus-summary-position-point))
9880
9881 (defun gnus-summary-catchup-from-here (&optional all)
9882   "Mark all unticked articles after the current one as read.
9883 If ALL is non-nil, also mark ticked and dormant articles as read."
9884   (interactive "P")
9885   (save-excursion
9886     (gnus-save-hidden-threads
9887       (let ((beg (point)))
9888         ;; We check that there are unread articles.
9889         (when (or all (gnus-summary-find-next))
9890           (gnus-summary-catchup all t beg nil t)))))
9891
9892   (gnus-summary-position-point))
9893 (defun gnus-summary-catchup-all (&optional quietly)
9894   "Mark all articles in this newsgroup as read.
9895 This command is dangerous.  Normally, you want \\[gnus-summary-catchup]
9896 instead, which marks only unread articles as read."
9897   (interactive "P")
9898   (gnus-summary-catchup t quietly))
9899
9900 (defun gnus-summary-catchup-and-exit (&optional all quietly)
9901   "Mark all unread articles in this group as read, then exit.
9902 If prefix argument ALL is non-nil, all articles are marked as read.
9903 If QUIETLY is non-nil, no questions will be asked."
9904   (interactive "P")
9905   (when (gnus-summary-catchup all quietly nil 'fast)
9906     ;; Select next newsgroup or exit.
9907     (if (and (not (gnus-group-quit-config gnus-newsgroup-name))
9908              (eq gnus-auto-select-next 'quietly))
9909         (gnus-summary-next-group nil)
9910       (gnus-summary-exit))))
9911
9912 (defun gnus-summary-catchup-all-and-exit (&optional quietly)
9913   "Mark all articles in this newsgroup as read, and then exit.
9914 This command is dangerous.  Normally, you want \\[gnus-summary-catchup-and-exit]
9915 instead, which marks only unread articles as read."
9916   (interactive "P")
9917   (gnus-summary-catchup-and-exit t quietly))
9918
9919 (defun gnus-summary-catchup-and-goto-next-group (&optional all)
9920   "Mark all articles in this group as read and select the next group.
9921 If given a prefix, mark all articles, unread as well as ticked, as
9922 read."
9923   (interactive "P")
9924   (save-excursion
9925     (gnus-summary-catchup all))
9926   (gnus-summary-next-group))
9927
9928 ;;;
9929 ;;; with article
9930 ;;;
9931
9932 (defmacro gnus-with-article (article &rest forms)
9933   "Select ARTICLE and perform FORMS in the original article buffer.
9934 Then replace the article with the result."
9935   `(progn
9936      ;; We don't want the article to be marked as read.
9937      (let (gnus-mark-article-hook)
9938        (gnus-summary-select-article t t nil ,article))
9939      (set-buffer gnus-original-article-buffer)
9940      ,@forms
9941      (if (not (gnus-check-backend-function
9942                'request-replace-article (car gnus-article-current)))
9943          (gnus-message 5 "Read-only group; not replacing")
9944        (unless (gnus-request-replace-article
9945                 ,article (car gnus-article-current)
9946                 (current-buffer) t)
9947          (error "Couldn't replace article")))
9948      ;; The cache and backlog have to be flushed somewhat.
9949      (when gnus-keep-backlog
9950        (gnus-backlog-remove-article
9951         (car gnus-article-current) (cdr gnus-article-current)))
9952      (when gnus-use-cache
9953        (gnus-cache-update-article
9954         (car gnus-article-current) (cdr gnus-article-current)))))
9955
9956 (put 'gnus-with-article 'lisp-indent-function 1)
9957 (put 'gnus-with-article 'edebug-form-spec '(form body))
9958
9959 ;; Thread-based commands.
9960
9961 (defun gnus-summary-articles-in-thread (&optional article)
9962   "Return a list of all articles in the current thread.
9963 If ARTICLE is non-nil, return all articles in the thread that starts
9964 with that article."
9965   (let* ((article (or article (gnus-summary-article-number)))
9966          (data (gnus-data-find-list article))
9967          (top-level (gnus-data-level (car data)))
9968          (top-subject
9969           (cond ((null gnus-thread-operation-ignore-subject)
9970                  (gnus-simplify-subject-re
9971                   (mail-header-subject (gnus-data-header (car data)))))
9972                 ((eq gnus-thread-operation-ignore-subject 'fuzzy)
9973                  (gnus-simplify-subject-fuzzy
9974                   (mail-header-subject (gnus-data-header (car data)))))
9975                 (t nil)))
9976          (end-point (save-excursion
9977                       (if (gnus-summary-go-to-next-thread)
9978                           (point) (point-max))))
9979          articles)
9980     (while (and data
9981                 (< (gnus-data-pos (car data)) end-point))
9982       (when (or (not top-subject)
9983                 (string= top-subject
9984                          (if (eq gnus-thread-operation-ignore-subject 'fuzzy)
9985                              (gnus-simplify-subject-fuzzy
9986                               (mail-header-subject
9987                                (gnus-data-header (car data))))
9988                            (gnus-simplify-subject-re
9989                             (mail-header-subject
9990                              (gnus-data-header (car data)))))))
9991         (push (gnus-data-number (car data)) articles))
9992       (unless (and (setq data (cdr data))
9993                    (> (gnus-data-level (car data)) top-level))
9994         (setq data nil)))
9995     ;; Return the list of articles.
9996     (nreverse articles)))
9997
9998 (defun gnus-summary-rethread-current ()
9999   "Rethread the thread the current article is part of."
10000   (interactive)
10001   (let* ((gnus-show-threads t)
10002          (article (gnus-summary-article-number))
10003          (id (mail-header-id (gnus-summary-article-header)))
10004          (gnus-newsgroup-threads (list (gnus-id-to-thread (gnus-root-id id)))))
10005     (unless id
10006       (error "No article on the current line"))
10007     (gnus-rebuild-thread id)
10008     (gnus-summary-goto-subject article)))
10009
10010 (defun gnus-summary-reparent-thread ()
10011   "Make the current article child of the marked (or previous) article.
10012
10013 Note that the re-threading will only work if `gnus-thread-ignore-subject'
10014 is non-nil or the Subject: of both articles are the same."
10015   (interactive)
10016   (unless (not (gnus-group-read-only-p))
10017     (error "The current newsgroup does not support article editing"))
10018   (unless (<= (length gnus-newsgroup-processable) 1)
10019     (error "No more than one article may be marked"))
10020   (save-window-excursion
10021     (let ((gnus-article-buffer " *reparent*")
10022           (current-article (gnus-summary-article-number))
10023           ;; First grab the marked article, otherwise one line up.
10024           (parent-article (if (not (null gnus-newsgroup-processable))
10025                               (car gnus-newsgroup-processable)
10026                             (save-excursion
10027                               (if (eq (forward-line -1) 0)
10028                                   (gnus-summary-article-number)
10029                                 (error "Beginning of summary buffer"))))))
10030       (unless (not (eq current-article parent-article))
10031         (error "An article may not be self-referential"))
10032       (let ((message-id (mail-header-id
10033                          (gnus-summary-article-header parent-article))))
10034         (unless (and message-id (not (equal message-id "")))
10035           (error "No message-id in desired parent"))
10036         (gnus-with-article current-article
10037           (save-restriction
10038             (goto-char (point-min))
10039             (message-narrow-to-head)
10040             (if (re-search-forward "^References: " nil t)
10041                 (progn
10042                   (re-search-forward "^[^ \t]" nil t)
10043                   (forward-line -1)
10044                   (end-of-line)
10045                   (insert " " message-id))
10046               (insert "References: " message-id "\n"))))
10047         (set-buffer gnus-summary-buffer)
10048         (gnus-summary-unmark-all-processable)
10049         (gnus-summary-update-article current-article)
10050         (if (gnus-summary-goto-subject (cdr gnus-article-current) nil t)
10051             (gnus-summary-update-secondary-mark (cdr gnus-article-current)))
10052         (gnus-summary-rethread-current)
10053         (gnus-message 3 "Article %d is now the child of article %d"
10054                       current-article parent-article)))))
10055
10056 (defun gnus-summary-toggle-threads (&optional arg)
10057   "Toggle showing conversation threads.
10058 If ARG is positive number, turn showing conversation threads on."
10059   (interactive "P")
10060   (let ((current (or (gnus-summary-article-number) gnus-newsgroup-end)))
10061     (setq gnus-show-threads
10062           (if (null arg) (not gnus-show-threads)
10063             (> (prefix-numeric-value arg) 0)))
10064     (gnus-summary-prepare)
10065     (gnus-summary-goto-subject current)
10066     (gnus-message 6 "Threading is now %s" (if gnus-show-threads "on" "off"))
10067     (gnus-summary-position-point)))
10068
10069 (defun gnus-summary-show-all-threads ()
10070   "Show all threads."
10071   (interactive)
10072   (save-excursion
10073     (let ((buffer-read-only nil))
10074       (subst-char-in-region (point-min) (point-max) ?\^M ?\n t)))
10075   (gnus-summary-position-point))
10076
10077 (defun gnus-summary-show-thread ()
10078   "Show thread subtrees.
10079 Returns nil if no thread was there to be shown."
10080   (interactive)
10081   (let ((buffer-read-only nil)
10082         (orig (point))
10083         ;; first goto end then to beg, to have point at beg after let
10084         (end (progn (end-of-line) (point)))
10085         (beg (progn (beginning-of-line) (point))))
10086     (prog1
10087         ;; Any hidden lines here?
10088         (search-forward "\r" end t)
10089       (subst-char-in-region beg end ?\^M ?\n t)
10090       (goto-char orig)
10091       (gnus-summary-position-point))))
10092
10093 (defun gnus-summary-maybe-hide-threads ()
10094   "If requested, hide the threads that should be hidden."
10095   (when (and gnus-show-threads
10096              gnus-thread-hide-subtree)
10097     (gnus-summary-hide-all-threads
10098      (if (or (consp gnus-thread-hide-subtree)
10099              (gnus-functionp gnus-thread-hide-subtree))
10100          (gnus-make-predicate gnus-thread-hide-subtree)
10101        nil))))
10102
10103 ;;; Hiding predicates.
10104
10105 (defun gnus-article-unread-p (header)
10106   (memq (mail-header-number header) gnus-newsgroup-unreads))
10107
10108 (defun gnus-article-unseen-p (header)
10109   (memq (mail-header-number header) gnus-newsgroup-unseen))
10110
10111 (defun gnus-map-articles (predicate articles)
10112   "Map PREDICATE over ARTICLES and return non-nil if any predicate is non-nil."
10113   (apply 'gnus-or (mapcar predicate
10114                           (mapcar 'gnus-summary-article-header articles))))
10115
10116 (defun gnus-summary-hide-all-threads (&optional predicate)
10117   "Hide all thread subtrees.
10118 If PREDICATE is supplied, threads that satisfy this predicate
10119 will not be hidden."
10120   (interactive)
10121   (save-excursion
10122     (goto-char (point-min))
10123     (let ((end nil))
10124       (while (not end)
10125         (when (or (not predicate)
10126                   (gnus-map-articles
10127                    predicate (gnus-summary-article-children)))
10128             (gnus-summary-hide-thread))
10129         (setq end (not (zerop (gnus-summary-next-thread 1 t)))))))
10130   (gnus-summary-position-point))
10131
10132 (defun gnus-summary-hide-thread ()
10133   "Hide thread subtrees.
10134 If PREDICATE is supplied, threads that satisfy this predicate
10135 will not be hidden.
10136 Returns nil if no threads were there to be hidden."
10137   (interactive)
10138   (let ((buffer-read-only nil)
10139         (start (point))
10140         (article (gnus-summary-article-number)))
10141     (goto-char start)
10142     ;; Go forward until either the buffer ends or the subthread
10143     ;; ends.
10144     (when (and (not (eobp))
10145                (or (zerop (gnus-summary-next-thread 1 t))
10146                    (goto-char (point-max))))
10147       (prog1
10148           (if (and (> (point) start)
10149                    (search-backward "\n" start t))
10150               (progn
10151                 (subst-char-in-region start (point) ?\n ?\^M)
10152                 (gnus-summary-goto-subject article))
10153             (goto-char start)
10154             nil)))))
10155
10156 (defun gnus-summary-go-to-next-thread (&optional previous)
10157   "Go to the same level (or less) next thread.
10158 If PREVIOUS is non-nil, go to previous thread instead.
10159 Return the article number moved to, or nil if moving was impossible."
10160   (let ((level (gnus-summary-thread-level))
10161         (way (if previous -1 1))
10162         (beg (point)))
10163     (forward-line way)
10164     (while (and (not (eobp))
10165                 (< level (gnus-summary-thread-level)))
10166       (forward-line way))
10167     (if (eobp)
10168         (progn
10169           (goto-char beg)
10170           nil)
10171       (setq beg (point))
10172       (prog1
10173           (gnus-summary-article-number)
10174         (goto-char beg)))))
10175
10176 (defun gnus-summary-next-thread (n &optional silent)
10177   "Go to the same level next N'th thread.
10178 If N is negative, search backward instead.
10179 Returns the difference between N and the number of skips actually
10180 done.
10181
10182 If SILENT, don't output messages."
10183   (interactive "p")
10184   (let ((backward (< n 0))
10185         (n (abs n)))
10186     (while (and (> n 0)
10187                 (gnus-summary-go-to-next-thread backward))
10188       (decf n))
10189     (unless silent
10190       (gnus-summary-position-point))
10191     (when (and (not silent) (/= 0 n))
10192       (gnus-message 7 "No more threads"))
10193     n))
10194
10195 (defun gnus-summary-prev-thread (n)
10196   "Go to the same level previous N'th thread.
10197 Returns the difference between N and the number of skips actually
10198 done."
10199   (interactive "p")
10200   (gnus-summary-next-thread (- n)))
10201
10202 (defun gnus-summary-go-down-thread ()
10203   "Go down one level in the current thread."
10204   (let ((children (gnus-summary-article-children)))
10205     (when children
10206       (gnus-summary-goto-subject (car children)))))
10207
10208 (defun gnus-summary-go-up-thread ()
10209   "Go up one level in the current thread."
10210   (let ((parent (gnus-summary-article-parent)))
10211     (when parent
10212       (gnus-summary-goto-subject parent))))
10213
10214 (defun gnus-summary-down-thread (n)
10215   "Go down thread N steps.
10216 If N is negative, go up instead.
10217 Returns the difference between N and how many steps down that were
10218 taken."
10219   (interactive "p")
10220   (let ((up (< n 0))
10221         (n (abs n)))
10222     (while (and (> n 0)
10223                 (if up (gnus-summary-go-up-thread)
10224                   (gnus-summary-go-down-thread)))
10225       (setq n (1- n)))
10226     (gnus-summary-position-point)
10227     (when (/= 0 n)
10228       (gnus-message 7 "Can't go further"))
10229     n))
10230
10231 (defun gnus-summary-up-thread (n)
10232   "Go up thread N steps.
10233 If N is negative, go down instead.
10234 Returns the difference between N and how many steps down that were
10235 taken."
10236   (interactive "p")
10237   (gnus-summary-down-thread (- n)))
10238
10239 (defun gnus-summary-top-thread ()
10240   "Go to the top of the thread."
10241   (interactive)
10242   (while (gnus-summary-go-up-thread))
10243   (gnus-summary-article-number))
10244
10245 (defun gnus-summary-kill-thread (&optional unmark)
10246   "Mark articles under current thread as read.
10247 If the prefix argument is positive, remove any kinds of marks.
10248 If the prefix argument is negative, tick articles instead."
10249   (interactive "P")
10250   (when unmark
10251     (setq unmark (prefix-numeric-value unmark)))
10252   (let ((articles (gnus-summary-articles-in-thread)))
10253     (save-excursion
10254       ;; Expand the thread.
10255       (gnus-summary-show-thread)
10256       ;; Mark all the articles.
10257       (while articles
10258         (gnus-summary-goto-subject (car articles))
10259         (cond ((null unmark)
10260                (gnus-summary-mark-article-as-read gnus-killed-mark))
10261               ((> unmark 0)
10262                (gnus-summary-mark-article-as-unread gnus-unread-mark))
10263               (t
10264                (gnus-summary-mark-article-as-unread gnus-ticked-mark)))
10265         (setq articles (cdr articles))))
10266     ;; Hide killed subtrees.
10267     (and (null unmark)
10268          gnus-thread-hide-killed
10269          (gnus-summary-hide-thread))
10270     ;; If marked as read, go to next unread subject.
10271     (when (null unmark)
10272       ;; Go to next unread subject.
10273       (gnus-summary-next-subject 1 t)))
10274   (gnus-set-mode-line 'summary))
10275
10276 ;; Summary sorting commands
10277
10278 (defun gnus-summary-sort-by-number (&optional reverse)
10279   "Sort the summary buffer by article number.
10280 Argument REVERSE means reverse order."
10281   (interactive "P")
10282   (gnus-summary-sort 'number reverse))
10283
10284 (defun gnus-summary-sort-by-random (&optional reverse)
10285   "Randomize the order in the summary buffer.
10286 Argument REVERSE means to randomize in reverse order."
10287   (interactive "P")
10288   (gnus-summary-sort 'random reverse))
10289
10290 (defun gnus-summary-sort-by-author (&optional reverse)
10291   "Sort the summary buffer by author name alphabetically.
10292 If `case-fold-search' is non-nil, case of letters is ignored.
10293 Argument REVERSE means reverse order."
10294   (interactive "P")
10295   (gnus-summary-sort 'author reverse))
10296
10297 (defun gnus-summary-sort-by-subject (&optional reverse)
10298   "Sort the summary buffer by subject alphabetically.  `Re:'s are ignored.
10299 If `case-fold-search' is non-nil, case of letters is ignored.
10300 Argument REVERSE means reverse order."
10301   (interactive "P")
10302   (gnus-summary-sort 'subject reverse))
10303
10304 (defun gnus-summary-sort-by-date (&optional reverse)
10305   "Sort the summary buffer by date.
10306 Argument REVERSE means reverse order."
10307   (interactive "P")
10308   (gnus-summary-sort 'date reverse))
10309
10310 (defun gnus-summary-sort-by-score (&optional reverse)
10311   "Sort the summary buffer by score.
10312 Argument REVERSE means reverse order."
10313   (interactive "P")
10314   (gnus-summary-sort 'score reverse))
10315
10316 (defun gnus-summary-sort-by-lines (&optional reverse)
10317   "Sort the summary buffer by the number of lines.
10318 Argument REVERSE means reverse order."
10319   (interactive "P")
10320   (gnus-summary-sort 'lines reverse))
10321
10322 (defun gnus-summary-sort-by-chars (&optional reverse)
10323   "Sort the summary buffer by article length.
10324 Argument REVERSE means reverse order."
10325   (interactive "P")
10326   (gnus-summary-sort 'chars reverse))
10327
10328 (defun gnus-summary-sort-by-original (&optional reverse)
10329   "Sort the summary buffer using the default sorting method.
10330 Argument REVERSE means reverse order."
10331   (interactive "P")
10332   (let* ((buffer-read-only)
10333          (gnus-summary-prepare-hook nil))
10334     ;; We do the sorting by regenerating the threads.
10335     (gnus-summary-prepare)
10336     ;; Hide subthreads if needed.
10337     (gnus-summary-maybe-hide-threads)))
10338
10339 (defun gnus-summary-sort (predicate reverse)
10340   "Sort summary buffer by PREDICATE.  REVERSE means reverse order."
10341   (let* ((thread (intern (format "gnus-thread-sort-by-%s" predicate)))
10342          (article (intern (format "gnus-article-sort-by-%s" predicate)))
10343          (gnus-thread-sort-functions
10344           (if (not reverse)
10345               thread
10346             `(lambda (t1 t2)
10347                (,thread t2 t1))))
10348          (gnus-sort-gathered-threads-function
10349           gnus-thread-sort-functions)
10350          (gnus-article-sort-functions
10351           (if (not reverse)
10352               article
10353             `(lambda (t1 t2)
10354                (,article t2 t1))))
10355          (buffer-read-only)
10356          (gnus-summary-prepare-hook nil))
10357     ;; We do the sorting by regenerating the threads.
10358     (gnus-summary-prepare)
10359     ;; Hide subthreads if needed.
10360     (gnus-summary-maybe-hide-threads)))
10361
10362 ;; Summary saving commands.
10363
10364 (defun gnus-summary-save-article (&optional n not-saved)
10365   "Save the current article using the default saver function.
10366 If N is a positive number, save the N next articles.
10367 If N is a negative number, save the N previous articles.
10368 If N is nil and any articles have been marked with the process mark,
10369 save those articles instead.
10370 The variable `gnus-default-article-saver' specifies the saver function."
10371   (interactive "P")
10372   (let* ((articles (gnus-summary-work-articles n))
10373          (save-buffer (save-excursion
10374                         (nnheader-set-temp-buffer " *Gnus Save*")))
10375          (num (length articles))
10376          header file)
10377     (dolist (article articles)
10378       (setq header (gnus-summary-article-header article))
10379       (if (not (vectorp header))
10380           ;; This is a pseudo-article.
10381           (if (assq 'name header)
10382               (gnus-copy-file (cdr (assq 'name header)))
10383             (gnus-message 1 "Article %d is unsaveable" article))
10384         ;; This is a real article.
10385         (save-window-excursion
10386           (let ((gnus-display-mime-function nil)
10387                 (gnus-article-prepare-hook nil))
10388             (gnus-summary-select-article t nil nil article)))
10389         (save-excursion
10390           (set-buffer save-buffer)
10391           (erase-buffer)
10392           (insert-buffer-substring gnus-original-article-buffer))
10393         (setq file (gnus-article-save save-buffer file num))
10394         (gnus-summary-remove-process-mark article)
10395         (unless not-saved
10396           (gnus-summary-set-saved-mark article))))
10397     (gnus-kill-buffer save-buffer)
10398     (gnus-summary-position-point)
10399     (gnus-set-mode-line 'summary)
10400     n))
10401
10402 (defun gnus-summary-pipe-output (&optional arg)
10403   "Pipe the current article to a subprocess.
10404 If N is a positive number, pipe the N next articles.
10405 If N is a negative number, pipe the N previous articles.
10406 If N is nil and any articles have been marked with the process mark,
10407 pipe those articles instead."
10408   (interactive "P")
10409   (require 'gnus-art)
10410   (let ((gnus-default-article-saver 'gnus-summary-save-in-pipe))
10411     (gnus-summary-save-article arg t))
10412   (let ((buffer (get-buffer "*Shell Command Output*")))
10413     (if (and buffer
10414              (with-current-buffer buffer (> (point-max) (point-min))))
10415         (gnus-configure-windows 'pipe))))
10416
10417 (defun gnus-summary-save-article-mail (&optional arg)
10418   "Append the current article to an mail file.
10419 If N is a positive number, save the N next articles.
10420 If N is a negative number, save the N previous articles.
10421 If N is nil and any articles have been marked with the process mark,
10422 save those articles instead."
10423   (interactive "P")
10424   (require 'gnus-art)
10425   (let ((gnus-default-article-saver 'gnus-summary-save-in-mail))
10426     (gnus-summary-save-article arg)))
10427
10428 (defun gnus-summary-save-article-rmail (&optional arg)
10429   "Append the current article to an rmail file.
10430 If N is a positive number, save the N next articles.
10431 If N is a negative number, save the N previous articles.
10432 If N is nil and any articles have been marked with the process mark,
10433 save those articles instead."
10434   (interactive "P")
10435   (require 'gnus-art)
10436   (let ((gnus-default-article-saver 'gnus-summary-save-in-rmail))
10437     (gnus-summary-save-article arg)))
10438
10439 (defun gnus-summary-save-article-file (&optional arg)
10440   "Append the current article to a file.
10441 If N is a positive number, save the N next articles.
10442 If N is a negative number, save the N previous articles.
10443 If N is nil and any articles have been marked with the process mark,
10444 save those articles instead."
10445   (interactive "P")
10446   (require 'gnus-art)
10447   (let ((gnus-default-article-saver 'gnus-summary-save-in-file))
10448     (gnus-summary-save-article arg)))
10449
10450 (defun gnus-summary-write-article-file (&optional arg)
10451   "Write the current article to a file, deleting the previous file.
10452 If N is a positive number, save the N next articles.
10453 If N is a negative number, save the N previous articles.
10454 If N is nil and any articles have been marked with the process mark,
10455 save those articles instead."
10456   (interactive "P")
10457   (require 'gnus-art)
10458   (let ((gnus-default-article-saver 'gnus-summary-write-to-file))
10459     (gnus-summary-save-article arg)))
10460
10461 (defun gnus-summary-save-article-body-file (&optional arg)
10462   "Append the current article body to a file.
10463 If N is a positive number, save the N next articles.
10464 If N is a negative number, save the N previous articles.
10465 If N is nil and any articles have been marked with the process mark,
10466 save those articles instead."
10467   (interactive "P")
10468   (require 'gnus-art)
10469   (let ((gnus-default-article-saver 'gnus-summary-save-body-in-file))
10470     (gnus-summary-save-article arg)))
10471
10472 (defun gnus-summary-muttprint (&optional arg)
10473   "Print the current article using Muttprint.
10474 If N is a positive number, save the N next articles.
10475 If N is a negative number, save the N previous articles.
10476 If N is nil and any articles have been marked with the process mark,
10477 save those articles instead."
10478   (interactive "P")
10479   (require 'gnus-art)
10480   (let ((gnus-default-article-saver 'gnus-summary-pipe-to-muttprint))
10481     (gnus-summary-save-article arg t)))
10482
10483 (defun gnus-summary-pipe-message (program)
10484   "Pipe the current article through PROGRAM."
10485   (interactive "sProgram: ")
10486   (gnus-summary-select-article)
10487   (let ((mail-header-separator ""))
10488     (gnus-eval-in-buffer-window gnus-article-buffer
10489       (save-restriction
10490         (widen)
10491         (let ((start (window-start))
10492               buffer-read-only)
10493           (message-pipe-buffer-body program)
10494           (set-window-start (get-buffer-window (current-buffer)) start))))))
10495
10496 (defun gnus-get-split-value (methods)
10497   "Return a value based on the split METHODS."
10498   (let (split-name method result match)
10499     (when methods
10500       (save-excursion
10501         (set-buffer gnus-original-article-buffer)
10502         (save-restriction
10503           (nnheader-narrow-to-headers)
10504           (while (and methods (not split-name))
10505             (goto-char (point-min))
10506             (setq method (pop methods))
10507             (setq match (car method))
10508             (when (cond
10509                    ((stringp match)
10510                     ;; Regular expression.
10511                     (ignore-errors
10512                       (re-search-forward match nil t)))
10513                    ((gnus-functionp match)
10514                     ;; Function.
10515                     (save-restriction
10516                       (widen)
10517                       (setq result (funcall match gnus-newsgroup-name))))
10518                    ((consp match)
10519                     ;; Form.
10520                     (save-restriction
10521                       (widen)
10522                       (setq result (eval match)))))
10523               (setq split-name (cdr method))
10524               (cond ((stringp result)
10525                      (push (expand-file-name
10526                             result gnus-article-save-directory)
10527                            split-name))
10528                     ((consp result)
10529                      (setq split-name (append result split-name)))))))))
10530     (nreverse split-name)))
10531
10532 (defun gnus-valid-move-group-p (group)
10533   (and (boundp group)
10534        (symbol-name group)
10535        (symbol-value group)
10536        (gnus-get-function (gnus-find-method-for-group
10537                            (symbol-name group)) 'request-accept-article t)))
10538
10539 (defun gnus-read-move-group-name (prompt default articles prefix)
10540   "Read a group name."
10541   (let* ((split-name (gnus-get-split-value gnus-move-split-methods))
10542          (minibuffer-confirm-incomplete nil) ; XEmacs
10543          (prom
10544           (format "%s %s to:"
10545                   prompt
10546                   (if (> (length articles) 1)
10547                       (format "these %d articles" (length articles))
10548                     "this article")))
10549          (to-newsgroup
10550           (cond
10551            ((null split-name)
10552             (gnus-completing-read-with-default
10553              default prom
10554              gnus-active-hashtb
10555              'gnus-valid-move-group-p
10556              nil prefix
10557              'gnus-group-history))
10558            ((= 1 (length split-name))
10559             (gnus-completing-read-with-default
10560              (car split-name) prom
10561              gnus-active-hashtb
10562              'gnus-valid-move-group-p
10563              nil nil
10564              'gnus-group-history))
10565            (t
10566             (gnus-completing-read-with-default
10567              nil prom
10568              (mapcar (lambda (el) (list el))
10569                      (nreverse split-name))
10570              nil nil nil
10571              'gnus-group-history))))
10572          (to-method (gnus-server-to-method (gnus-group-method to-newsgroup))))
10573     (when to-newsgroup
10574       (if (or (string= to-newsgroup "")
10575               (string= to-newsgroup prefix))
10576           (setq to-newsgroup default))
10577       (unless to-newsgroup
10578         (error "No group name entered"))
10579       (or (gnus-active to-newsgroup)
10580           (gnus-activate-group to-newsgroup nil nil to-method)
10581           (if (gnus-y-or-n-p (format "No such group: %s.  Create it? "
10582                                      to-newsgroup))
10583               (or (and (gnus-request-create-group to-newsgroup to-method)
10584                        (gnus-activate-group
10585                         to-newsgroup nil nil to-method)
10586                        (gnus-subscribe-group to-newsgroup))
10587                   (error "Couldn't create group %s" to-newsgroup)))
10588           (error "No such group: %s" to-newsgroup)))
10589     to-newsgroup))
10590
10591 (defun gnus-summary-save-parts (type dir n &optional reverse)
10592   "Save parts matching TYPE to DIR.
10593 If REVERSE, save parts that do not match TYPE."
10594   (interactive
10595    (list (read-string "Save parts of type: "
10596                       (or (car gnus-summary-save-parts-type-history)
10597                           gnus-summary-save-parts-default-mime)
10598                       'gnus-summary-save-parts-type-history)
10599          (setq gnus-summary-save-parts-last-directory
10600                (read-file-name "Save to directory: "
10601                                gnus-summary-save-parts-last-directory
10602                                nil t))
10603          current-prefix-arg))
10604   (gnus-summary-iterate n
10605     (let ((gnus-display-mime-function nil)
10606           (gnus-inhibit-treatment t))
10607       (gnus-summary-select-article))
10608     (save-excursion
10609       (set-buffer gnus-article-buffer)
10610       (let ((handles (or gnus-article-mime-handles
10611                          (mm-dissect-buffer nil gnus-article-loose-mime)
10612                          (mm-uu-dissect))))
10613         (when handles
10614           (gnus-summary-save-parts-1 type dir handles reverse)
10615           (unless gnus-article-mime-handles ;; Don't destroy this case.
10616             (mm-destroy-parts handles)))))))
10617
10618 (defun gnus-summary-save-parts-1 (type dir handle reverse)
10619   (if (stringp (car handle))
10620       (mapcar (lambda (h) (gnus-summary-save-parts-1 type dir h reverse))
10621               (cdr handle))
10622     (when (if reverse
10623               (not (string-match type (mm-handle-media-type handle)))
10624             (string-match type (mm-handle-media-type handle)))
10625       (let ((file (expand-file-name
10626                    (file-name-nondirectory
10627                     (or
10628                      (mail-content-type-get
10629                       (mm-handle-disposition handle) 'filename)
10630                      (concat gnus-newsgroup-name
10631                              "." (number-to-string
10632                                   (cdr gnus-article-current)))))
10633                    dir)))
10634         (unless (file-exists-p file)
10635           (mm-save-part-to-file handle file))))))
10636
10637 ;; Summary extract commands
10638
10639 (defun gnus-summary-insert-pseudos (pslist &optional not-view)
10640   (let ((buffer-read-only nil)
10641         (article (gnus-summary-article-number))
10642         after-article b e)
10643     (unless (gnus-summary-goto-subject article)
10644       (error "No such article: %d" article))
10645     (gnus-summary-position-point)
10646     ;; If all commands are to be bunched up on one line, we collect
10647     ;; them here.
10648     (unless gnus-view-pseudos-separately
10649       (let ((ps (setq pslist (sort pslist 'gnus-pseudos<)))
10650             files action)
10651         (while ps
10652           (setq action (cdr (assq 'action (car ps))))
10653           (setq files (list (cdr (assq 'name (car ps)))))
10654           (while (and ps (cdr ps)
10655                       (string= (or action "1")
10656                                (or (cdr (assq 'action (cadr ps))) "2")))
10657             (push (cdr (assq 'name (cadr ps))) files)
10658             (setcdr ps (cddr ps)))
10659           (when files
10660             (when (not (string-match "%s" action))
10661               (push " " files))
10662             (push " " files)
10663             (when (assq 'execute (car ps))
10664               (setcdr (assq 'execute (car ps))
10665                       (funcall (if (string-match "%s" action)
10666                                    'format 'concat)
10667                                action
10668                                (mapconcat
10669                                 (lambda (f)
10670                                   (if (equal f " ")
10671                                       f
10672                                     (mm-quote-arg f)))
10673                                 files " ")))))
10674           (setq ps (cdr ps)))))
10675     (if (and gnus-view-pseudos (not not-view))
10676         (while pslist
10677           (when (assq 'execute (car pslist))
10678             (gnus-execute-command (cdr (assq 'execute (car pslist)))
10679                                   (eq gnus-view-pseudos 'not-confirm)))
10680           (setq pslist (cdr pslist)))
10681       (save-excursion
10682         (while pslist
10683           (setq after-article (or (cdr (assq 'article (car pslist)))
10684                                   (gnus-summary-article-number)))
10685           (gnus-summary-goto-subject after-article)
10686           (forward-line 1)
10687           (setq b (point))
10688           (insert "    " (file-name-nondirectory
10689                           (cdr (assq 'name (car pslist))))
10690                   ": " (or (cdr (assq 'execute (car pslist))) "") "\n")
10691           (setq e (point))
10692           (forward-line -1)             ; back to `b'
10693           (gnus-add-text-properties
10694            b (1- e) (list 'gnus-number gnus-reffed-article-number
10695                           gnus-mouse-face-prop gnus-mouse-face))
10696           (gnus-data-enter
10697            after-article gnus-reffed-article-number
10698            gnus-unread-mark b (car pslist) 0 (- e b))
10699           (setq gnus-newsgroup-unreads
10700                 (gnus-add-to-sorted-list gnus-newsgroup-unreads
10701                                          gnus-reffed-article-number))
10702           (setq gnus-reffed-article-number (1- gnus-reffed-article-number))
10703           (setq pslist (cdr pslist)))))))
10704
10705 (defun gnus-pseudos< (p1 p2)
10706   (let ((c1 (cdr (assq 'action p1)))
10707         (c2 (cdr (assq 'action p2))))
10708     (and c1 c2 (string< c1 c2))))
10709
10710 (defun gnus-request-pseudo-article (props)
10711   (cond ((assq 'execute props)
10712          (gnus-execute-command (cdr (assq 'execute props)))))
10713   (let ((gnus-current-article (gnus-summary-article-number)))
10714     (gnus-run-hooks 'gnus-mark-article-hook)))
10715
10716 (defun gnus-execute-command (command &optional automatic)
10717   (save-excursion
10718     (gnus-article-setup-buffer)
10719     (set-buffer gnus-article-buffer)
10720     (setq buffer-read-only nil)
10721     (let ((command (if automatic command
10722                      (read-string "Command: " (cons command 0)))))
10723       (erase-buffer)
10724       (insert "$ " command "\n\n")
10725       (if gnus-view-pseudo-asynchronously
10726           (start-process "gnus-execute" (current-buffer) shell-file-name
10727                          shell-command-switch command)
10728         (call-process shell-file-name nil t nil
10729                       shell-command-switch command)))))
10730
10731 ;; Summary kill commands.
10732
10733 (defun gnus-summary-edit-global-kill (article)
10734   "Edit the \"global\" kill file."
10735   (interactive (list (gnus-summary-article-number)))
10736   (gnus-group-edit-global-kill article))
10737
10738 (defun gnus-summary-edit-local-kill ()
10739   "Edit a local kill file applied to the current newsgroup."
10740   (interactive)
10741   (setq gnus-current-headers (gnus-summary-article-header))
10742   (gnus-group-edit-local-kill
10743    (gnus-summary-article-number) gnus-newsgroup-name))
10744
10745 ;;; Header reading.
10746
10747 (defun gnus-read-header (id &optional header)
10748   "Read the headers of article ID and enter them into the Gnus system."
10749   (let ((group gnus-newsgroup-name)
10750         (gnus-override-method
10751          (or
10752           gnus-override-method
10753           (and (gnus-news-group-p gnus-newsgroup-name)
10754                (car (gnus-refer-article-methods)))))
10755         where)
10756     ;; First we check to see whether the header in question is already
10757     ;; fetched.
10758     (if (stringp id)
10759         ;; This is a Message-ID.
10760         (setq header (or header (gnus-id-to-header id)))
10761       ;; This is an article number.
10762       (setq header (or header (gnus-summary-article-header id))))
10763     (if (and header
10764              (not (gnus-summary-article-sparse-p (mail-header-number header))))
10765         ;; We have found the header.
10766         header
10767       ;; If this is a sparse article, we have to nix out its
10768       ;; previous entry in the thread hashtb.
10769       (when (and header
10770                  (gnus-summary-article-sparse-p (mail-header-number header)))
10771         (let* ((parent (gnus-parent-id (mail-header-references header)))
10772                (thread (and parent (gnus-id-to-thread parent))))
10773           (when thread
10774             (delq (assq header thread) thread))))
10775       ;; We have to really fetch the header to this article.
10776       (save-excursion
10777         (set-buffer nntp-server-buffer)
10778         (when (setq where (gnus-request-head id group))
10779           (nnheader-fold-continuation-lines)
10780           (goto-char (point-max))
10781           (insert ".\n")
10782           (goto-char (point-min))
10783           (insert "211 ")
10784           (princ (cond
10785                   ((numberp id) id)
10786                   ((cdr where) (cdr where))
10787                   (header (mail-header-number header))
10788                   (t gnus-reffed-article-number))
10789                  (current-buffer))
10790           (insert " Article retrieved.\n"))
10791         (if (or (not where)
10792                 (not (setq header (car (gnus-get-newsgroup-headers nil t)))))
10793             ()                          ; Malformed head.
10794           (unless (gnus-summary-article-sparse-p (mail-header-number header))
10795             (when (and (stringp id)
10796                        (not (string= (gnus-group-real-name group)
10797                                      (car where))))
10798               ;; If we fetched by Message-ID and the article came
10799               ;; from a different group, we fudge some bogus article
10800               ;; numbers for this article.
10801               (mail-header-set-number header gnus-reffed-article-number))
10802             (save-excursion
10803               (set-buffer gnus-summary-buffer)
10804               (decf gnus-reffed-article-number)
10805               (gnus-remove-header (mail-header-number header))
10806               (push header gnus-newsgroup-headers)
10807               (setq gnus-current-headers header)
10808               (push (mail-header-number header) gnus-newsgroup-limit)))
10809           header)))))
10810
10811 (defun gnus-remove-header (number)
10812   "Remove header NUMBER from `gnus-newsgroup-headers'."
10813   (if (and gnus-newsgroup-headers
10814            (= number (mail-header-number (car gnus-newsgroup-headers))))
10815       (pop gnus-newsgroup-headers)
10816     (let ((headers gnus-newsgroup-headers))
10817       (while (and (cdr headers)
10818                   (not (= number (mail-header-number (cadr headers)))))
10819         (pop headers))
10820       (when (cdr headers)
10821         (setcdr headers (cddr headers))))))
10822
10823 ;;;
10824 ;;; summary highlights
10825 ;;;
10826
10827 (defun gnus-highlight-selected-summary ()
10828   "Highlight selected article in summary buffer."
10829   ;; Added by Per Abrahamsen <amanda@iesd.auc.dk>.
10830   (when gnus-summary-selected-face
10831     (save-excursion
10832       (let* ((beg (progn (beginning-of-line) (point)))
10833              (end (progn (end-of-line) (point)))
10834              ;; Fix by Mike Dugan <dugan@bucrf16.bu.edu>.
10835              (from (if (get-text-property beg gnus-mouse-face-prop)
10836                        beg
10837                      (or (next-single-property-change
10838                           beg gnus-mouse-face-prop nil end)
10839                          beg)))
10840              (to
10841               (if (= from end)
10842                   (- from 2)
10843                 (or (next-single-property-change
10844                      from gnus-mouse-face-prop nil end)
10845                     end))))
10846         ;; If no mouse-face prop on line we will have to = from = end,
10847         ;; so we highlight the entire line instead.
10848         (when (= (+ to 2) from)
10849           (setq from beg)
10850           (setq to end))
10851         (if gnus-newsgroup-selected-overlay
10852             ;; Move old overlay.
10853             (gnus-move-overlay
10854              gnus-newsgroup-selected-overlay from to (current-buffer))
10855           ;; Create new overlay.
10856           (gnus-overlay-put
10857            (setq gnus-newsgroup-selected-overlay (gnus-make-overlay from to))
10858            'face gnus-summary-selected-face))))))
10859
10860 (defvar gnus-summary-highlight-line-cached nil)
10861 (defvar gnus-summary-highlight-line-trigger nil)
10862 (defun gnus-summary-highlight-line-0 ()
10863   (if (and (eq gnus-summary-highlight-line-trigger 
10864                gnus-summary-highlight)
10865            gnus-summary-highlight-line-cached)
10866       gnus-summary-highlight-line-cached
10867     (setq gnus-summary-highlight-line-trigger gnus-summary-highlight
10868           gnus-summary-highlight-line-cached
10869           (let* ((cond (list 'cond))
10870                  (c cond)
10871                  (list gnus-summary-highlight))
10872             (while list
10873               (setcdr c (cons (list (caar list) (list 'quote (cdar list))) nil))
10874               (setq c (cdr c)
10875                     list (cdr list)))
10876             (gnus-byte-compile (list 'lambda nil cond))))))
10877
10878 (defvar gnus-summary-highlight-line-downloaded-alist nil)
10879 (defvar gnus-summary-highlight-line-downloaded-cached nil)
10880
10881 ;; New implementation by Christian Limpach <Christian.Limpach@nice.ch>.
10882 (defun gnus-summary-highlight-line ()
10883   "Highlight current line according to `gnus-summary-highlight'."
10884   (let*
10885       ((list gnus-summary-highlight)
10886        (beg (gnus-point-at-bol))
10887        (article (gnus-summary-article-number))
10888        (score (or (cdr (assq (or article gnus-current-article)
10889                              gnus-newsgroup-scored))
10890                   gnus-summary-default-score 0))
10891        (mark (or (gnus-summary-article-mark) gnus-unread-mark))
10892        (inhibit-read-only t)
10893        (default gnus-summary-default-score)
10894        (default-high gnus-summary-default-high-score)
10895        (default-low gnus-summary-default-low-score)
10896        (downloaded
10897         (and
10898          (boundp 'gnus-agent-article-alist)
10899          gnus-agent-article-alist
10900          (gnus-agent-group-covered-p gnus-newsgroup-name)
10901          ;; Optimized for when gnus-summary-highlight-line is
10902          ;; called multiple times for articles in ascending
10903          ;; order (i.e. initial generation of summary buffer).
10904          (progn 
10905            (unless (and
10906                     (eq gnus-summary-highlight-line-downloaded-alist
10907                         gnus-agent-article-alist)
10908                     (<= (caar gnus-summary-highlight-line-downloaded-cached)
10909                         article))
10910              (setq gnus-summary-highlight-line-downloaded-alist
10911                    gnus-agent-article-alist)
10912              (setq gnus-summary-highlight-line-downloaded-cached
10913                    gnus-agent-article-alist))
10914            (let (n)
10915              (while (and (< (caar gnus-summary-highlight-line-downloaded-cached)
10916                             article)
10917                          (setq n (cdr gnus-summary-highlight-line-downloaded-cached)))
10918                (setq gnus-summary-highlight-line-downloaded-cached n)))
10919            (and (eq (caar gnus-summary-highlight-line-downloaded-cached)
10920                     article)
10921                 (cdar gnus-summary-highlight-line-downloaded-cached))))))
10922     (let ((face (funcall (gnus-summary-highlight-line-0))))
10923       (unless (eq face (get-text-property beg 'face))
10924         (gnus-put-text-property-excluding-characters-with-faces
10925          beg (gnus-point-at-eol) 'face
10926          (setq face (if (boundp face) (symbol-value face) face)))
10927         (when gnus-summary-highlight-line-function
10928           (funcall gnus-summary-highlight-line-function article face))))))
10929
10930 (defun gnus-update-read-articles (group unread &optional compute)
10931   "Update the list of read articles in GROUP.
10932 UNREAD is a sorted list."
10933   (let* ((active (or gnus-newsgroup-active (gnus-active group)))
10934          (entry (gnus-gethash group gnus-newsrc-hashtb))
10935          (info (nth 2 entry))
10936          (prev 1)
10937          read)
10938     (if (or (not info) (not active))
10939         ;; There is no info on this group if it was, in fact,
10940         ;; killed.  Gnus stores no information on killed groups, so
10941         ;; there's nothing to be done.
10942         ;; One could store the information somewhere temporarily,
10943         ;; perhaps...  Hmmm...
10944         ()
10945       ;; Remove any negative articles numbers.
10946       (while (and unread (< (car unread) 0))
10947         (setq unread (cdr unread)))
10948       ;; Remove any expired article numbers
10949       (while (and unread (< (car unread) (car active)))
10950         (setq unread (cdr unread)))
10951       ;; Compute the ranges of read articles by looking at the list of
10952       ;; unread articles.
10953       (while unread
10954         (when (/= (car unread) prev)
10955           (push (if (= prev (1- (car unread))) prev
10956                   (cons prev (1- (car unread))))
10957                 read))
10958         (setq prev (1+ (car unread)))
10959         (setq unread (cdr unread)))
10960       (when (<= prev (cdr active))
10961         (push (cons prev (cdr active)) read))
10962       (setq read (if (> (length read) 1) (nreverse read) read))
10963       (if compute
10964           read
10965         (save-excursion
10966           (let (setmarkundo)
10967             ;; Propagate the read marks to the backend.
10968             (when (gnus-check-backend-function 'request-set-mark group)
10969               (let ((del (gnus-remove-from-range (gnus-info-read info) read))
10970                     (add (gnus-remove-from-range read (gnus-info-read info))))
10971                 (when (or add del)
10972                   (unless (gnus-check-group group)
10973                     (error "Can't open server for %s" group))
10974                   (gnus-request-set-mark
10975                    group (delq nil (list (if add (list add 'add '(read)))
10976                                          (if del (list del 'del '(read))))))
10977                   (setq setmarkundo
10978                         `(gnus-request-set-mark
10979                           ,group
10980                           ',(delq nil (list
10981                                        (if del (list del 'add '(read)))
10982                                        (if add (list add 'del '(read))))))))))
10983             (set-buffer gnus-group-buffer)
10984             (gnus-undo-register
10985               `(progn
10986                  (gnus-info-set-marks ',info ',(gnus-info-marks info) t)
10987                  (gnus-info-set-read ',info ',(gnus-info-read info))
10988                  (gnus-get-unread-articles-in-group ',info
10989                                                     (gnus-active ,group))
10990                  (gnus-group-update-group ,group t)
10991                  ,setmarkundo))))
10992         ;; Enter this list into the group info.
10993         (gnus-info-set-read info read)
10994         ;; Set the number of unread articles in gnus-newsrc-hashtb.
10995         (gnus-get-unread-articles-in-group info (gnus-active group))
10996         t))))
10997
10998 (defun gnus-offer-save-summaries ()
10999   "Offer to save all active summary buffers."
11000   (let (buffers)
11001     ;; Go through all buffers and find all summaries.
11002     (dolist (buffer (buffer-list))
11003       (when (and (setq buffer (buffer-name buffer))
11004                  (string-match "Summary" buffer)
11005                  (save-excursion
11006                    (set-buffer buffer)
11007                    ;; We check that this is, indeed, a summary buffer.
11008                    (and (eq major-mode 'gnus-summary-mode)
11009                         ;; Also make sure this isn't bogus.
11010                         gnus-newsgroup-prepared
11011                         ;; Also make sure that this isn't a
11012                         ;; dead summary buffer.
11013                         (not gnus-dead-summary-mode))))
11014         (push buffer buffers)))
11015     ;; Go through all these summary buffers and offer to save them.
11016     (when buffers
11017       (save-excursion
11018         (map-y-or-n-p
11019          "Update summary buffer %s? "
11020          (lambda (buf)
11021            (switch-to-buffer buf)
11022            (gnus-summary-exit))
11023          buffers)))))
11024
11025 (defun gnus-summary-setup-default-charset ()
11026   "Setup newsgroup default charset."
11027   (if (member gnus-newsgroup-name '("nndraft:delayed" "nndraft:drafts"))
11028       (setq gnus-newsgroup-charset nil)
11029     (let* ((ignored-charsets
11030             (or gnus-newsgroup-ephemeral-ignored-charsets
11031                 (append
11032                  (and gnus-newsgroup-name
11033                       (gnus-parameter-ignored-charsets gnus-newsgroup-name))
11034                  gnus-newsgroup-ignored-charsets))))
11035       (setq gnus-newsgroup-charset
11036             (or gnus-newsgroup-ephemeral-charset
11037                 (and gnus-newsgroup-name
11038                      (gnus-parameter-charset gnus-newsgroup-name))
11039                 gnus-default-charset))
11040       (set (make-local-variable 'gnus-newsgroup-ignored-charsets)
11041            ignored-charsets))))
11042
11043 ;;;
11044 ;;; Mime Commands
11045 ;;;
11046
11047 (defun gnus-summary-display-buttonized (&optional show-all-parts)
11048   "Display the current article buffer fully MIME-buttonized.
11049 If SHOW-ALL-PARTS (the prefix) is non-nil, all multipart/* parts are
11050 treated as multipart/mixed."
11051   (interactive "P")
11052   (require 'gnus-art)
11053   (let ((gnus-unbuttonized-mime-types nil)
11054         (gnus-mime-display-multipart-as-mixed show-all-parts))
11055     (gnus-summary-show-article)))
11056
11057 (defun gnus-summary-repair-multipart (article)
11058   "Add a Content-Type header to a multipart article without one."
11059   (interactive (list (gnus-summary-article-number)))
11060   (gnus-with-article article
11061     (message-narrow-to-head)
11062     (message-remove-header "Mime-Version")
11063     (goto-char (point-max))
11064     (insert "Mime-Version: 1.0\n")
11065     (widen)
11066     (when (search-forward "\n--" nil t)
11067       (let ((separator (buffer-substring (point) (gnus-point-at-eol))))
11068         (message-narrow-to-head)
11069         (message-remove-header "Content-Type")
11070         (goto-char (point-max))
11071         (insert (format "Content-Type: multipart/mixed; boundary=\"%s\"\n"
11072                         separator))
11073         (widen))))
11074   (let (gnus-mark-article-hook)
11075     (gnus-summary-select-article t t nil article)))
11076
11077 (defun gnus-summary-toggle-display-buttonized ()
11078   "Toggle the buttonizing of the article buffer."
11079   (interactive)
11080   (require 'gnus-art)
11081   (if (setq gnus-inhibit-mime-unbuttonizing
11082             (not gnus-inhibit-mime-unbuttonizing))
11083       (let ((gnus-unbuttonized-mime-types nil))
11084         (gnus-summary-show-article))
11085     (gnus-summary-show-article)))
11086
11087 ;;;
11088 ;;; Generic summary marking commands
11089 ;;;
11090
11091 (defvar gnus-summary-marking-alist
11092   '((read gnus-del-mark "d")
11093     (unread gnus-unread-mark "u")
11094     (ticked gnus-ticked-mark "!")
11095     (dormant gnus-dormant-mark "?")
11096     (expirable gnus-expirable-mark "e"))
11097   "An alist of names/marks/keystrokes.")
11098
11099 (defvar gnus-summary-generic-mark-map (make-sparse-keymap))
11100 (defvar gnus-summary-mark-map)
11101
11102 (defun gnus-summary-make-all-marking-commands ()
11103   (define-key gnus-summary-mark-map "M" gnus-summary-generic-mark-map)
11104   (dolist (elem gnus-summary-marking-alist)
11105     (apply 'gnus-summary-make-marking-command elem)))
11106
11107 (defun gnus-summary-make-marking-command (name mark keystroke)
11108   (let ((map (make-sparse-keymap)))
11109     (define-key gnus-summary-generic-mark-map keystroke map)
11110     (dolist (lway `((next "next" next nil "n")
11111                     (next-unread "next unread" next t "N")
11112                     (prev "previous" prev nil "p")
11113                     (prev-unread "previous unread" prev t "P")
11114                     (nomove "" nil nil ,keystroke)))
11115       (let ((func (gnus-summary-make-marking-command-1
11116                    mark (car lway) lway name)))
11117         (setq func (eval func))
11118         (define-key map (nth 4 lway) func)))))
11119
11120 (defun gnus-summary-make-marking-command-1 (mark way lway name)
11121   `(defun ,(intern
11122             (format "gnus-summary-put-mark-as-%s%s"
11123                     name (if (eq way 'nomove)
11124                              ""
11125                            (concat "-" (symbol-name way)))))
11126      (n)
11127      ,(format
11128        "Mark the current article as %s%s.
11129 If N, the prefix, then repeat N times.
11130 If N is negative, move in reverse order.
11131 The difference between N and the actual number of articles marked is
11132 returned."
11133        name (cadr lway))
11134      (interactive "p")
11135      (gnus-summary-generic-mark n ,mark ',(nth 2 lway) ,(nth 3 lway))))
11136
11137 (defun gnus-summary-generic-mark (n mark move unread)
11138   "Mark N articles with MARK."
11139   (unless (eq major-mode 'gnus-summary-mode)
11140     (error "This command can only be used in the summary buffer"))
11141   (gnus-summary-show-thread)
11142   (let ((nummove
11143          (cond
11144           ((eq move 'next) 1)
11145           ((eq move 'prev) -1)
11146           (t 0))))
11147     (if (zerop nummove)
11148         (setq n 1)
11149       (when (< n 0)
11150         (setq n (abs n)
11151               nummove (* -1 nummove))))
11152     (while (and (> n 0)
11153                 (gnus-summary-mark-article nil mark)
11154                 (zerop (gnus-summary-next-subject nummove unread t)))
11155       (setq n (1- n)))
11156     (when (/= 0 n)
11157       (gnus-message 7 "No more %sarticles" (if mark "" "unread ")))
11158     (gnus-summary-recenter)
11159     (gnus-summary-position-point)
11160     (gnus-set-mode-line 'summary)
11161     n))
11162
11163 (defun gnus-summary-insert-articles (articles)
11164   (when (setq articles
11165               (gnus-sorted-difference articles
11166                                       (mapcar (lambda (h)
11167                                                 (mail-header-number h))
11168                                               gnus-newsgroup-headers)))
11169     (setq gnus-newsgroup-headers
11170           (merge 'list
11171                  gnus-newsgroup-headers
11172                  (gnus-fetch-headers articles)
11173                  'gnus-article-sort-by-number))
11174     ;; Suppress duplicates?
11175     (when gnus-suppress-duplicates
11176       (gnus-dup-suppress-articles))
11177
11178     ;; We might want to build some more threads first.
11179     (when (and gnus-fetch-old-headers
11180                (eq gnus-headers-retrieved-by 'nov))
11181       (if (eq gnus-fetch-old-headers 'invisible)
11182           (gnus-build-all-threads)
11183         (gnus-build-old-threads)))
11184     ;; Let the Gnus agent mark articles as read.
11185     (when gnus-agent
11186       (gnus-agent-get-undownloaded-list))
11187     ;; Remove list identifiers from subject
11188     (when gnus-list-identifiers
11189       (gnus-summary-remove-list-identifiers))
11190     ;; First and last article in this newsgroup.
11191     (when gnus-newsgroup-headers
11192       (setq gnus-newsgroup-begin
11193             (mail-header-number (car gnus-newsgroup-headers))
11194             gnus-newsgroup-end
11195             (mail-header-number
11196              (gnus-last-element gnus-newsgroup-headers))))
11197     (when gnus-use-scoring
11198       (gnus-possibly-score-headers))))
11199
11200 (defun gnus-summary-insert-old-articles (&optional all)
11201   "Insert all old articles in this group.
11202 If ALL is non-nil, already read articles become readable.
11203 If ALL is a number, fetch this number of articles."
11204   (interactive "P")
11205   (prog1
11206       (let ((old (sort (mapcar 'car gnus-newsgroup-data) '<))
11207             older len)
11208         (setq older
11209               ;; Some nntp servers lie about their active range.  When this happens, the active
11210               ;; range can be in the millions.
11211               ;; Use a compressed range to avoid creating a huge list.
11212               (gnus-range-difference (list gnus-newsgroup-active) old))
11213         (setq len (gnus-range-length older))
11214         (cond
11215          ((null older) nil)
11216          ((numberp all)
11217           (if (< all len)
11218               (let ((older-range (nreverse older)))
11219                 (setq older nil)
11220
11221                 (while (> all 0)
11222                   (let* ((r (pop older-range))
11223                          (min (if (numberp r) r (car r)))
11224                          (max (if (numberp r) r (cdr r))))
11225                     (while (and (<= min max)
11226                                 (> all 0))
11227                       (push max older)
11228                       (setq all (1- all)
11229                             max (1- max))))))
11230             (setq older (gnus-uncompress-range older))))
11231          (all
11232           (setq older (gnus-uncompress-range older)))
11233          (t
11234           (when (and (numberp gnus-large-newsgroup)
11235                    (> len gnus-large-newsgroup))
11236               (let* ((cursor-in-echo-area nil)
11237                      (initial (gnus-parameter-large-newsgroup-initial
11238                                gnus-newsgroup-name))
11239                      (input
11240                       (read-string
11241                        (format
11242                         "How many articles from %s (%s %d): "
11243                         (gnus-limit-string
11244                          (gnus-group-decoded-name gnus-newsgroup-name) 35)
11245                         (if initial "max" "default")
11246                         len)
11247                        (if initial
11248                            (cons (number-to-string initial)
11249                                  0)))))
11250                 (unless (string-match "^[ \t]*$" input)
11251                   (setq all (string-to-number input))
11252                   (if (< all len)
11253                       (let ((older-range (nreverse older)))
11254                         (setq older nil)
11255
11256                         (while (> all 0)
11257                           (let* ((r (pop older-range))
11258                                  (min (if (numberp r) r (car r)))
11259                                  (max (if (numberp r) r (cdr r))))
11260                             (while (and (<= min max)
11261                                         (> all 0))
11262                               (push max older)
11263                               (setq all (1- all)
11264                                     max (1- max))))))))))
11265           (setq older (gnus-uncompress-range older))))
11266         (if (not older)
11267             (message "No old news.")
11268           (gnus-summary-insert-articles older)
11269           (gnus-summary-limit (gnus-sorted-nunion old older))))
11270     (gnus-summary-position-point)))
11271
11272 (defun gnus-summary-insert-new-articles ()
11273   "Insert all new articles in this group."
11274   (interactive)
11275   (prog1
11276       (let ((old (sort (mapcar 'car gnus-newsgroup-data) '<))
11277             (old-active gnus-newsgroup-active)
11278             (nnmail-fetched-sources (list t))
11279             i new)
11280         (setq gnus-newsgroup-active
11281               (gnus-activate-group gnus-newsgroup-name 'scan))
11282         (setq i (cdr gnus-newsgroup-active))
11283         (while (> i (cdr old-active))
11284           (push i new)
11285           (decf i))
11286         (if (not new)
11287             (message "No gnus is bad news.")
11288           (gnus-summary-insert-articles new)
11289           (setq gnus-newsgroup-unreads
11290                 (gnus-sorted-nunion gnus-newsgroup-unreads new))
11291           (gnus-summary-limit (gnus-sorted-nunion old new))))
11292     (gnus-summary-position-point)))
11293
11294 (gnus-summary-make-all-marking-commands)
11295
11296 (gnus-ems-redefine)
11297
11298 (provide 'gnus-sum)
11299
11300 (run-hooks 'gnus-sum-load-hook)
11301
11302 ;;; gnus-sum.el ends here