2000-12-19 17:00:00 ShengHuo ZHU <zsh@cs.rochester.edu>
[gnus] / lisp / gnus-sum.el
1 ;;; gnus-sum.el --- summary mode commands for Gnus
2 ;; Copyright (C) 1996, 1997, 1998, 1999, 2000
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 ;; Recursive :-(.
40 ;; (require 'gnus-art)
41 (require 'nnoo)
42 (autoload 'gnus-summary-limit-include-cached "gnus-cache" nil t)
43 (autoload 'gnus-cache-write-active "gnus-cache")
44 (autoload 'gnus-mailing-list-insinuate "gnus-ml" nil t)
45 (autoload 'mm-uu-dissect "mm-uu")
46
47 (defcustom gnus-kill-summary-on-exit t
48   "*If non-nil, kill the summary buffer when you exit from it.
49 If nil, the summary will become a \"*Dead Summary*\" buffer, and
50 it will be killed sometime later."
51   :group 'gnus-summary-exit
52   :type 'boolean)
53
54 (defcustom gnus-fetch-old-headers nil
55   "*Non-nil means that Gnus will try to build threads by grabbing old headers.
56 If an unread article in the group refers to an older, already read (or
57 just marked as read) article, the old article will not normally be
58 displayed in the Summary buffer.  If this variable is non-nil, Gnus
59 will attempt to grab the headers to the old articles, and thereby
60 build complete threads.  If it has the value `some', only enough
61 headers to connect otherwise loose threads will be displayed.  This
62 variable can also be a number.  In that case, no more than that number
63 of old headers will be fetched.  If it has the value `invisible', all
64 old headers will be fetched, but none will be displayed.
65
66 The server has to support NOV for any of this to work."
67   :group 'gnus-thread
68   :type '(choice (const :tag "off" nil)
69                  (const some)
70                  number
71                  (sexp :menu-tag "other" t)))
72
73 (defcustom gnus-refer-thread-limit 200
74   "*The number of old headers to fetch when doing \\<gnus-summary-mode-map>\\[gnus-summary-refer-thread].
75 If t, fetch all the available old headers."
76   :group 'gnus-thread
77   :type '(choice number
78                  (sexp :menu-tag "other" t)))
79
80 (defcustom gnus-summary-make-false-root 'adopt
81   "*nil means that Gnus won't gather loose threads.
82 If the root of a thread has expired or been read in a previous
83 session, the information necessary to build a complete thread has been
84 lost.  Instead of having many small sub-threads from this original thread
85 scattered all over the summary buffer, Gnus can gather them.
86
87 If non-nil, Gnus will try to gather all loose sub-threads from an
88 original thread into one large thread.
89
90 If this variable is non-nil, it should be one of `none', `adopt',
91 `dummy' or `empty'.
92
93 If this variable is `none', Gnus will not make a false root, but just
94 present the sub-threads after another.
95 If this variable is `dummy', Gnus will create a dummy root that will
96 have all the sub-threads as children.
97 If this variable is `adopt', Gnus will make one of the \"children\"
98 the parent and mark all the step-children as such.
99 If this variable is `empty', the \"children\" are printed with empty
100 subject fields.  (Or rather, they will be printed with a string
101 given by the `gnus-summary-same-subject' variable.)"
102   :group 'gnus-thread
103   :type '(choice (const :tag "off" nil)
104                  (const none)
105                  (const dummy)
106                  (const adopt)
107                  (const empty)))
108
109 (defcustom gnus-summary-gather-exclude-subject "^ *$\\|^(none)$"
110   "*A regexp to match subjects to be excluded from loose thread gathering.
111 As loose thread gathering is done on subjects only, that means that
112 there can be many false gatherings performed.  By rooting out certain
113 common subjects, gathering might become saner."
114   :group 'gnus-thread
115   :type 'regexp)
116
117 (defcustom gnus-summary-gather-subject-limit nil
118   "*Maximum length of subject comparisons when gathering loose threads.
119 Use nil to compare full subjects.  Setting this variable to a low
120 number will help gather threads that have been corrupted by
121 newsreaders chopping off subject lines, but it might also mean that
122 unrelated articles that have subject that happen to begin with the
123 same few characters will be incorrectly gathered.
124
125 If this variable is `fuzzy', Gnus will use a fuzzy algorithm when
126 comparing subjects."
127   :group 'gnus-thread
128   :type '(choice (const :tag "off" nil)
129                  (const fuzzy)
130                  (sexp :menu-tag "on" t)))
131
132 (defcustom gnus-simplify-subject-functions nil
133   "List of functions taking a string argument that simplify subjects.
134 The functions are applied recursively.
135
136 Useful functions to put in this list include: `gnus-simplify-subject-re',
137 `gnus-simplify-subject-fuzzy' and `gnus-simplify-whitespace'."
138   :group 'gnus-thread
139   :type '(repeat function))
140
141 (defcustom gnus-simplify-ignored-prefixes nil
142   "*Regexp, matches for which are removed from subject lines when simplifying fuzzily."
143   :group 'gnus-thread
144   :type '(choice (const :tag "off" nil)
145                  regexp))
146
147 (defcustom gnus-build-sparse-threads nil
148   "*If non-nil, fill in the gaps in threads.
149 If `some', only fill in the gaps that are needed to tie loose threads
150 together.  If `more', fill in all leaf nodes that Gnus can find.  If
151 non-nil and non-`some', fill in all gaps that Gnus manages to guess."
152   :group 'gnus-thread
153   :type '(choice (const :tag "off" nil)
154                  (const some)
155                  (const more)
156                  (sexp :menu-tag "all" t)))
157
158 (defcustom gnus-summary-thread-gathering-function
159   'gnus-gather-threads-by-subject
160   "*Function used for gathering loose threads.
161 There are two pre-defined functions: `gnus-gather-threads-by-subject',
162 which only takes Subjects into consideration; and
163 `gnus-gather-threads-by-references', which compared the References
164 headers of the articles to find matches."
165   :group 'gnus-thread
166   :type '(radio (function-item gnus-gather-threads-by-subject)
167                 (function-item gnus-gather-threads-by-references)
168                 (function :tag "other")))
169
170 (defcustom gnus-summary-same-subject ""
171   "*String indicating that the current article has the same subject as the previous.
172 This variable will only be used if the value of
173 `gnus-summary-make-false-root' is `empty'."
174   :group 'gnus-summary-format
175   :type 'string)
176
177 (defcustom gnus-summary-goto-unread t
178   "*If t, many commands will go to the next unread article.
179 This applies to marking commands as well as other commands that
180 \"naturally\" select the next article, like, for instance, `SPC' at
181 the end of an article.
182
183 If nil, the marking commands do NOT go to the next unread article
184 (they go to the next article instead).  If `never', commands that
185 usually go to the next unread article, will go to the next article,
186 whether it is read or not."
187   :group 'gnus-summary-marks
188   :link '(custom-manual "(gnus)Setting Marks")
189   :type '(choice (const :tag "off" nil)
190                  (const never)
191                  (sexp :menu-tag "on" t)))
192
193 (defcustom gnus-summary-default-score 0
194   "*Default article score level.
195 All scores generated by the score files will be added to this score.
196 If this variable is nil, scoring will be disabled."
197   :group 'gnus-score-default
198   :type '(choice (const :tag "disable")
199                  integer))
200
201 (defcustom gnus-summary-zcore-fuzz 0
202   "*Fuzziness factor for the zcore in the summary buffer.
203 Articles with scores closer than this to `gnus-summary-default-score'
204 will not be marked."
205   :group 'gnus-summary-format
206   :type 'integer)
207
208 (defcustom gnus-simplify-subject-fuzzy-regexp nil
209   "*Strings to be removed when doing fuzzy matches.
210 This can either be a regular expression or list of regular expressions
211 that will be removed from subject strings if fuzzy subject
212 simplification is selected."
213   :group 'gnus-thread
214   :type '(repeat regexp))
215
216 (defcustom gnus-show-threads t
217   "*If non-nil, display threads in summary mode."
218   :group 'gnus-thread
219   :type 'boolean)
220
221 (defcustom gnus-thread-hide-subtree nil
222   "*If non-nil, hide all threads initially.
223 If threads are hidden, you have to run the command
224 `gnus-summary-show-thread' by hand or use `gnus-select-article-hook'
225 to expose hidden threads."
226   :group 'gnus-thread
227   :type 'boolean)
228
229 (defcustom gnus-thread-hide-killed t
230   "*If non-nil, hide killed threads automatically."
231   :group 'gnus-thread
232   :type 'boolean)
233
234 (defcustom gnus-thread-ignore-subject t
235   "*If non-nil, which is the default, ignore subjects and do all threading based on the Reference header.
236 If nil, articles that have different subjects from their parents will
237 start separate threads."
238   :group 'gnus-thread
239   :type 'boolean)
240
241 (defcustom gnus-thread-operation-ignore-subject t
242   "*If non-nil, subjects will be ignored when doing thread commands.
243 This affects commands like `gnus-summary-kill-thread' and
244 `gnus-summary-lower-thread'.
245
246 If this variable is nil, articles in the same thread with different
247 subjects will not be included in the operation in question.  If this
248 variable is `fuzzy', only articles that have subjects that are fuzzily
249 equal will be included."
250   :group 'gnus-thread
251   :type '(choice (const :tag "off" nil)
252                  (const fuzzy)
253                  (sexp :tag "on" t)))
254
255 (defcustom gnus-thread-indent-level 4
256   "*Number that says how much each sub-thread should be indented."
257   :group 'gnus-thread
258   :type 'integer)
259
260 (defcustom gnus-auto-extend-newsgroup t
261   "*If non-nil, extend newsgroup forward and backward when requested."
262   :group 'gnus-summary-choose
263   :type 'boolean)
264
265 (defcustom gnus-auto-select-first t
266   "*If nil, don't select the first unread article when entering a group.
267 If this variable is `best', select the highest-scored unread article
268 in the group.  If t, select the first unread article.
269
270 This variable can also be a function to place point on a likely
271 subject line.  Useful values include `gnus-summary-first-unread-subject',
272 `gnus-summary-first-unread-article' and
273 `gnus-summary-best-unread-article'.
274
275 If you want to prevent automatic selection of the first unread article
276 in some newsgroups, set the variable to nil in
277 `gnus-select-group-hook'."
278   :group 'gnus-group-select
279   :type '(choice (const :tag "none" nil)
280                  (const best)
281                  (sexp :menu-tag "first" t)
282                  (function-item gnus-summary-first-unread-subject)
283                  (function-item gnus-summary-first-unread-article)
284                  (function-item gnus-summary-best-unread-article)))
285
286 (defcustom gnus-auto-select-next t
287   "*If non-nil, offer to go to the next group from the end of the previous.
288 If the value is t and the next newsgroup is empty, Gnus will exit
289 summary mode and go back to group mode.  If the value is neither nil
290 nor t, Gnus will select the following unread newsgroup.  In
291 particular, if the value is the symbol `quietly', the next unread
292 newsgroup will be selected without any confirmation, and if it is
293 `almost-quietly', the next group will be selected without any
294 confirmation if you are located on the last article in the group.
295 Finally, if this variable is `slightly-quietly', the `Z n' command
296 will go to the next group without confirmation."
297   :group 'gnus-summary-maneuvering
298   :type '(choice (const :tag "off" nil)
299                  (const quietly)
300                  (const almost-quietly)
301                  (const slightly-quietly)
302                  (sexp :menu-tag "on" t)))
303
304 (defcustom gnus-auto-select-same nil
305   "*If non-nil, select the next article with the same subject.
306 If there are no more articles with the same subject, go to
307 the first unread article."
308   :group 'gnus-summary-maneuvering
309   :type 'boolean)
310
311 (defcustom gnus-summary-check-current nil
312   "*If non-nil, consider the current article when moving.
313 The \"unread\" movement commands will stay on the same line if the
314 current article is unread."
315   :group 'gnus-summary-maneuvering
316   :type 'boolean)
317
318 (defcustom gnus-auto-center-summary t
319   "*If non-nil, always center the current summary buffer.
320 In particular, if `vertical' do only vertical recentering.  If non-nil
321 and non-`vertical', do both horizontal and vertical recentering."
322   :group 'gnus-summary-maneuvering
323   :type '(choice (const :tag "none" nil)
324                  (const vertical)
325                  (integer :tag "height")
326                  (sexp :menu-tag "both" t)))
327
328 (defcustom gnus-show-all-headers nil
329   "*If non-nil, don't hide any headers."
330   :group 'gnus-article-hiding
331   :group 'gnus-article-headers
332   :type 'boolean)
333
334 (defcustom gnus-summary-ignore-duplicates nil
335   "*If non-nil, ignore articles with identical Message-ID headers."
336   :group 'gnus-summary
337   :type 'boolean)
338
339 (defcustom gnus-single-article-buffer t
340   "*If non-nil, display all articles in the same buffer.
341 If nil, each group will get its own article buffer."
342   :group 'gnus-article-various
343   :type 'boolean)
344
345 (defcustom gnus-break-pages t
346   "*If non-nil, do page breaking on articles.
347 The page delimiter is specified by the `gnus-page-delimiter'
348 variable."
349   :group 'gnus-article-various
350   :type 'boolean)
351
352 (defcustom gnus-move-split-methods nil
353   "*Variable used to suggest where articles are to be moved to.
354 It uses the same syntax as the `gnus-split-methods' variable.
355 However, whereas `gnus-split-methods' specifies file names as targets,
356 this variable specifies group names."
357   :group 'gnus-summary-mail
358   :type '(repeat (choice (list :value (fun) function)
359                          (cons :value ("" "") regexp (repeat string))
360                          (sexp :value nil))))
361
362 (defcustom gnus-unread-mark ?  ;Whitespace
363   "*Mark used for unread articles."
364   :group 'gnus-summary-marks
365   :type 'character)
366
367 (defcustom gnus-ticked-mark ?!
368   "*Mark used for ticked articles."
369   :group 'gnus-summary-marks
370   :type 'character)
371
372 (defcustom gnus-dormant-mark ??
373   "*Mark used for dormant articles."
374   :group 'gnus-summary-marks
375   :type 'character)
376
377 (defcustom gnus-del-mark ?r
378   "*Mark used for del'd articles."
379   :group 'gnus-summary-marks
380   :type 'character)
381
382 (defcustom gnus-read-mark ?R
383   "*Mark used for read articles."
384   :group 'gnus-summary-marks
385   :type 'character)
386
387 (defcustom gnus-expirable-mark ?E
388   "*Mark used for expirable articles."
389   :group 'gnus-summary-marks
390   :type 'character)
391
392 (defcustom gnus-killed-mark ?K
393   "*Mark used for killed articles."
394   :group 'gnus-summary-marks
395   :type 'character)
396
397 (defcustom gnus-souped-mark ?F
398   "*Mark used for killed articles."
399   :group 'gnus-summary-marks
400   :type 'character)
401
402 (defcustom gnus-kill-file-mark ?X
403   "*Mark used for articles killed by kill files."
404   :group 'gnus-summary-marks
405   :type 'character)
406
407 (defcustom gnus-low-score-mark ?Y
408   "*Mark used for articles with a low score."
409   :group 'gnus-summary-marks
410   :type 'character)
411
412 (defcustom gnus-catchup-mark ?C
413   "*Mark used for articles that are caught up."
414   :group 'gnus-summary-marks
415   :type 'character)
416
417 (defcustom gnus-replied-mark ?A
418   "*Mark used for articles that have been replied to."
419   :group 'gnus-summary-marks
420   :type 'character)
421
422 (defcustom gnus-cached-mark ?*
423   "*Mark used for articles that are in the cache."
424   :group 'gnus-summary-marks
425   :type 'character)
426
427 (defcustom gnus-saved-mark ?S
428   "*Mark used for articles that have been saved to."
429   :group 'gnus-summary-marks
430   :type 'character)
431
432 (defcustom gnus-ancient-mark ?O
433   "*Mark used for ancient articles."
434   :group 'gnus-summary-marks
435   :type 'character)
436
437 (defcustom gnus-sparse-mark ?Q
438   "*Mark used for sparsely reffed articles."
439   :group 'gnus-summary-marks
440   :type 'character)
441
442 (defcustom gnus-canceled-mark ?G
443   "*Mark used for canceled articles."
444   :group 'gnus-summary-marks
445   :type 'character)
446
447 (defcustom gnus-duplicate-mark ?M
448   "*Mark used for duplicate articles."
449   :group 'gnus-summary-marks
450   :type 'character)
451
452 (defcustom gnus-undownloaded-mark ?@
453   "*Mark used for articles that weren't downloaded."
454   :group 'gnus-summary-marks
455   :type 'character)
456
457 (defcustom gnus-downloadable-mark ?%
458   "*Mark used for articles that are to be downloaded."
459   :group 'gnus-summary-marks
460   :type 'character)
461
462 (defcustom gnus-unsendable-mark ?=
463   "*Mark used for articles that won't be sent."
464   :group 'gnus-summary-marks
465   :type 'character)
466
467 (defcustom gnus-score-over-mark ?+
468   "*Score mark used for articles with high scores."
469   :group 'gnus-summary-marks
470   :type 'character)
471
472 (defcustom gnus-score-below-mark ?-
473   "*Score mark used for articles with low scores."
474   :group 'gnus-summary-marks
475   :type 'character)
476
477 (defcustom gnus-empty-thread-mark ?  ;Whitespace
478   "*There is no thread under the article."
479   :group 'gnus-summary-marks
480   :type 'character)
481
482 (defcustom gnus-not-empty-thread-mark ?=
483   "*There is a thread under the article."
484   :group 'gnus-summary-marks
485   :type 'character)
486
487 (defcustom gnus-view-pseudo-asynchronously nil
488   "*If non-nil, Gnus will view pseudo-articles asynchronously."
489   :group 'gnus-extract-view
490   :type 'boolean)
491
492 (defcustom gnus-auto-expirable-marks
493   (list gnus-killed-mark gnus-del-mark gnus-catchup-mark
494         gnus-low-score-mark gnus-ancient-mark gnus-read-mark
495         gnus-souped-mark gnus-duplicate-mark)
496   "*The list of marks converted into expiration if a group is auto-expirable."
497   :version "21.1"
498   :group 'gnus-summary
499   :type '(repeat character))
500
501 (defcustom gnus-inhibit-user-auto-expire t
502   "*If non-nil, user marking commands will not mark an article as expirable, even if the group has auto-expire turned on."
503   :version "21.1"
504   :group 'gnus-summary
505   :type 'boolean)
506
507 (defcustom gnus-view-pseudos nil
508   "*If `automatic', pseudo-articles will be viewed automatically.
509 If `not-confirm', pseudos will be viewed automatically, and the user
510 will not be asked to confirm the command."
511   :group 'gnus-extract-view
512   :type '(choice (const :tag "off" nil)
513                  (const automatic)
514                  (const not-confirm)))
515
516 (defcustom gnus-view-pseudos-separately t
517   "*If non-nil, one pseudo-article will be created for each file to be viewed.
518 If nil, all files that use the same viewing command will be given as a
519 list of parameters to that command."
520   :group 'gnus-extract-view
521   :type 'boolean)
522
523 (defcustom gnus-insert-pseudo-articles t
524   "*If non-nil, insert pseudo-articles when decoding articles."
525   :group 'gnus-extract-view
526   :type 'boolean)
527
528 (defcustom gnus-summary-dummy-line-format
529   "  %(:                          :%) %S\n"
530   "*The format specification for the dummy roots in the summary buffer.
531 It works along the same lines as a normal formatting string,
532 with some simple extensions.
533
534 %S  The subject"
535   :group 'gnus-threading
536   :type 'string)
537
538 (defcustom gnus-summary-mode-line-format "Gnus: %g [%A] %Z"
539   "*The format specification for the summary mode line.
540 It works along the same lines as a normal formatting string,
541 with some simple extensions:
542
543 %G  Group name
544 %p  Unprefixed group name
545 %A  Current article number
546 %z  Current article score
547 %V  Gnus version
548 %U  Number of unread articles in the group
549 %e  Number of unselected articles in the group
550 %Z  A string with unread/unselected article counts
551 %g  Shortish group name
552 %S  Subject of the current article
553 %u  User-defined spec
554 %s  Current score file name
555 %d  Number of dormant articles
556 %r  Number of articles that have been marked as read in this session
557 %E  Number of articles expunged by the score files"
558   :group 'gnus-summary-format
559   :type 'string)
560
561 (defcustom gnus-list-identifiers nil
562   "Regexp that matches list identifiers to be removed from subject.
563 This can also be a list of regexps."
564   :version "21.1"
565   :group 'gnus-summary-format
566   :group 'gnus-article-hiding
567   :type '(choice (const :tag "none" nil)
568                  (regexp :value ".*")
569                  (repeat :value (".*") regexp)))
570
571 (defcustom gnus-summary-mark-below 0
572   "*Mark all articles with a score below this variable as read.
573 This variable is local to each summary buffer and usually set by the
574 score file."
575   :group 'gnus-score-default
576   :type 'integer)
577
578 (defcustom gnus-article-sort-functions '(gnus-article-sort-by-number)
579   "*List of functions used for sorting articles in the summary buffer.
580 This variable is only used when not using a threaded display."
581   :group 'gnus-summary-sort
582   :type '(repeat (choice (function-item gnus-article-sort-by-number)
583                          (function-item gnus-article-sort-by-author)
584                          (function-item gnus-article-sort-by-subject)
585                          (function-item gnus-article-sort-by-date)
586                          (function-item gnus-article-sort-by-score)
587                          (function :tag "other"))))
588
589 (defcustom gnus-thread-sort-functions '(gnus-thread-sort-by-number)
590   "*List of functions used for sorting threads in the summary buffer.
591 By default, threads are sorted by article number.
592
593 Each function takes two threads and return non-nil if the first thread
594 should be sorted before the other.  If you use more than one function,
595 the primary sort function should be the last.  You should probably
596 always include `gnus-thread-sort-by-number' in the list of sorting
597 functions -- preferably first.
598
599 Ready-made functions include `gnus-thread-sort-by-number',
600 `gnus-thread-sort-by-author', `gnus-thread-sort-by-subject',
601 `gnus-thread-sort-by-date', `gnus-thread-sort-by-score' and
602 `gnus-thread-sort-by-total-score' (see `gnus-thread-score-function')."
603   :group 'gnus-summary-sort
604   :type '(repeat (choice (function-item gnus-thread-sort-by-number)
605                          (function-item gnus-thread-sort-by-author)
606                          (function-item gnus-thread-sort-by-subject)
607                          (function-item gnus-thread-sort-by-date)
608                          (function-item gnus-thread-sort-by-score)
609                          (function-item gnus-thread-sort-by-total-score)
610                          (function :tag "other"))))
611
612 (defcustom gnus-thread-score-function '+
613   "*Function used for calculating the total score of a thread.
614
615 The function is called with the scores of the article and each
616 subthread and should then return the score of the thread.
617
618 Some functions you can use are `+', `max', or `min'."
619   :group 'gnus-summary-sort
620   :type 'function)
621
622 (defcustom gnus-summary-expunge-below nil
623   "All articles that have a score less than this variable will be expunged.
624 This variable is local to the summary buffers."
625   :group 'gnus-score-default
626   :type '(choice (const :tag "off" nil)
627                  integer))
628
629 (defcustom gnus-thread-expunge-below nil
630   "All threads that have a total score less than this variable will be expunged.
631 See `gnus-thread-score-function' for en explanation of what a
632 \"thread score\" is.
633
634 This variable is local to the summary buffers."
635   :group 'gnus-threading
636   :group 'gnus-score-default
637   :type '(choice (const :tag "off" nil)
638                  integer))
639
640 (defcustom gnus-summary-mode-hook nil
641   "*A hook for Gnus summary mode.
642 This hook is run before any variables are set in the summary buffer."
643   :options '(turn-on-gnus-mailing-list-mode)
644   :group 'gnus-summary-various
645   :type 'hook)
646
647 (defcustom gnus-summary-menu-hook nil
648   "*Hook run after the creation of the summary mode menu."
649   :group 'gnus-summary-visual
650   :type 'hook)
651
652 (defcustom gnus-summary-exit-hook nil
653   "*A hook called on exit from the summary buffer.
654 It will be called with point in the group buffer."
655   :group 'gnus-summary-exit
656   :type 'hook)
657
658 (defcustom gnus-summary-prepare-hook nil
659   "*A hook called after the summary buffer has been generated.
660 If you want to modify the summary buffer, you can use this hook."
661   :group 'gnus-summary-various
662   :type 'hook)
663
664 (defcustom gnus-summary-prepared-hook nil
665   "*A hook called as the last thing after the summary buffer has been generated."
666   :group 'gnus-summary-various
667   :type 'hook)
668
669 (defcustom gnus-summary-generate-hook nil
670   "*A hook run just before generating the summary buffer.
671 This hook is commonly used to customize threading variables and the
672 like."
673   :group 'gnus-summary-various
674   :type 'hook)
675
676 (defcustom gnus-select-group-hook nil
677   "*A hook called when a newsgroup is selected.
678
679 If you'd like to simplify subjects like the
680 `gnus-summary-next-same-subject' command does, you can use the
681 following hook:
682
683  (setq gnus-select-group-hook
684       (list
685         (lambda ()
686           (mapcar (lambda (header)
687                      (mail-header-set-subject
688                       header
689                       (gnus-simplify-subject
690                        (mail-header-subject header) 're-only)))
691                   gnus-newsgroup-headers))))"
692   :group 'gnus-group-select
693   :type 'hook)
694
695 (defcustom gnus-select-article-hook nil
696   "*A hook called when an article is selected."
697   :group 'gnus-summary-choose
698   :type 'hook)
699
700 (defcustom gnus-visual-mark-article-hook
701   (list 'gnus-highlight-selected-summary)
702   "*Hook run after selecting an article in the summary buffer.
703 It is meant to be used for highlighting the article in some way.  It
704 is not run if `gnus-visual' is nil."
705   :group 'gnus-summary-visual
706   :type 'hook)
707
708 (defcustom gnus-parse-headers-hook nil
709   "*A hook called before parsing the headers."
710   :group 'gnus-various
711   :type 'hook)
712
713 (defcustom gnus-exit-group-hook nil
714   "*A hook called when exiting summary mode.
715 This hook is not called from the non-updating exit commands like `Q'."
716   :group 'gnus-various
717   :type 'hook)
718
719 (defcustom gnus-summary-update-hook
720   (list 'gnus-summary-highlight-line)
721   "*A hook called when a summary line is changed.
722 The hook will not be called if `gnus-visual' is nil.
723
724 The default function `gnus-summary-highlight-line' will
725 highlight the line according to the `gnus-summary-highlight'
726 variable."
727   :group 'gnus-summary-visual
728   :type 'hook)
729
730 (defcustom gnus-mark-article-hook '(gnus-summary-mark-read-and-unread-as-read)
731   "*A hook called when an article is selected for the first time.
732 The hook is intended to mark an article as read (or unread)
733 automatically when it is selected."
734   :group 'gnus-summary-choose
735   :type 'hook)
736
737 (defcustom gnus-group-no-more-groups-hook nil
738   "*A hook run when returning to group mode having no more (unread) groups."
739   :group 'gnus-group-select
740   :type 'hook)
741
742 (defcustom gnus-ps-print-hook nil
743   "*A hook run before ps-printing something from Gnus."
744   :group 'gnus-summary
745   :type 'hook)
746
747 (defcustom gnus-summary-selected-face 'gnus-summary-selected-face
748   "Face used for highlighting the current article in the summary buffer."
749   :group 'gnus-summary-visual
750   :type 'face)
751
752 (defcustom gnus-summary-highlight
753   '(((= mark gnus-canceled-mark)
754      . gnus-summary-cancelled-face)
755     ((and (> score default)
756           (or (= mark gnus-dormant-mark)
757               (= mark gnus-ticked-mark)))
758      . gnus-summary-high-ticked-face)
759     ((and (< score default)
760           (or (= mark gnus-dormant-mark)
761               (= mark gnus-ticked-mark)))
762      . gnus-summary-low-ticked-face)
763     ((or (= mark gnus-dormant-mark)
764          (= mark gnus-ticked-mark))
765      . gnus-summary-normal-ticked-face)
766     ((and (> score default) (= mark gnus-ancient-mark))
767      . gnus-summary-high-ancient-face)
768     ((and (< score default) (= mark gnus-ancient-mark))
769      . gnus-summary-low-ancient-face)
770     ((= mark gnus-ancient-mark)
771      . gnus-summary-normal-ancient-face)
772     ((and (> score default) (= mark gnus-unread-mark))
773      . gnus-summary-high-unread-face)
774     ((and (< score default) (= mark gnus-unread-mark))
775      . gnus-summary-low-unread-face)
776     ((= mark gnus-unread-mark)
777      . gnus-summary-normal-unread-face)
778     ((and (> score default) (memq mark (list gnus-downloadable-mark
779                                              gnus-undownloaded-mark)))
780      . gnus-summary-high-unread-face)
781     ((and (< score default) (memq mark (list gnus-downloadable-mark
782                                              gnus-undownloaded-mark)))
783      . gnus-summary-low-unread-face)
784     ((memq mark (list gnus-downloadable-mark gnus-undownloaded-mark))
785      . gnus-summary-normal-unread-face)
786     ((> score default)
787      . gnus-summary-high-read-face)
788     ((< score default)
789      . gnus-summary-low-read-face)
790     (t
791      . gnus-summary-normal-read-face))
792   "*Controls the highlighting of summary buffer lines.
793
794 A list of (FORM . FACE) pairs.  When deciding how a a particular
795 summary line should be displayed, each form is evaluated.  The content
796 of the face field after the first true form is used.  You can change
797 how those summary lines are displayed, by editing the face field.
798
799 You can use the following variables in the FORM field.
800
801 score:   The articles score
802 default: The default article score.
803 below:   The score below which articles are automatically marked as read.
804 mark:    The articles mark."
805   :group 'gnus-summary-visual
806   :type '(repeat (cons (sexp :tag "Form" nil)
807                        face)))
808
809 (defcustom gnus-alter-header-function nil
810   "Function called to allow alteration of article header structures.
811 The function is called with one parameter, the article header vector,
812 which it may alter in any way.")
813
814 (defvar gnus-decode-encoded-word-function 'mail-decode-encoded-word-string
815   "Variable that says which function should be used to decode a string with encoded words.")
816
817 (defcustom gnus-extra-headers nil
818   "*Extra headers to parse."
819   :version "21.1"
820   :group 'gnus-summary
821   :type '(repeat symbol))
822
823 (defcustom gnus-ignored-from-addresses
824   (and user-mail-address (regexp-quote user-mail-address))
825   "*Regexp of From headers that may be suppressed in favor of To headers."
826   :version "21.1"
827   :group 'gnus-summary
828   :type 'regexp)
829
830 (defcustom gnus-group-charset-alist
831   '(("^hk\\>\\|^tw\\>\\|\\<big5\\>" cn-big5)
832     ("^cn\\>\\|\\<chinese\\>" cn-gb-2312)
833     ("^fj\\>\\|^japan\\>" iso-2022-jp-2)
834     ("^tnn\\>\\|^pin\\>\\|^sci.lang.japan" iso-2022-7bit)
835     ("^relcom\\>" koi8-r)
836     ("^fido7\\>" koi8-r)
837     ("^\\(cz\\|hun\\|pl\\|sk\\|hr\\)\\>" iso-8859-2)
838     ("^israel\\>" iso-8859-1)
839     ("^han\\>" euc-kr)
840     ("^alt.chinese.text.big5\\>" chinese-big5)
841     ("^soc.culture.vietnamese\\>" vietnamese-viqr)
842     ("^\\(comp\\|rec\\|alt\\|sci\\|soc\\|news\\|gnu\\|bofh\\)\\>" iso-8859-1)
843     (".*" iso-8859-1))
844   "Alist of regexps (to match group names) and default charsets to be used when reading."
845   :type '(repeat (list (regexp :tag "Group")
846                        (symbol :tag "Charset")))
847   :group 'gnus-charset)
848
849 (defcustom gnus-newsgroup-ignored-charsets '(unknown-8bit x-unknown)
850   "List of charsets that should be ignored.
851 When these charsets are used in the \"charset\" parameter, the
852 default charset will be used instead."
853   :version "21.1"
854   :type '(repeat symbol)
855   :group 'gnus-charset)
856
857 (defcustom gnus-group-ignored-charsets-alist
858   '(("alt\\.chinese\\.text" iso-8859-1))
859   "Alist of regexps (to match group names) and charsets that should be ignored.
860 When these charsets are used in the \"charset\" parameter, the
861 default charset will be used instead."
862   :type '(repeat (cons (regexp :tag "Group")
863                        (repeat symbol)))
864   :group 'gnus-charset)
865
866 (defcustom gnus-group-highlight-words-alist nil
867   "Alist of group regexps and highlight regexps.
868 This variable uses the same syntax as `gnus-emphasis-alist'."
869   :version "21.1"
870   :type '(repeat (cons (regexp :tag "Group")
871                        (repeat (list (regexp :tag "Highlight regexp")
872                                      (number :tag "Group for entire word" 0)
873                                      (number :tag "Group for displayed part" 0)
874                                      (symbol :tag "Face"
875                                              gnus-emphasis-highlight-words)))))
876   :group 'gnus-summary-visual)
877
878 (defcustom gnus-summary-show-article-charset-alist
879   nil
880   "Alist of number and charset.
881 The article will be shown with the charset corresponding to the
882 numbered argument.
883 For example: ((1 . cn-gb-2312) (2 . big5))."
884   :version "21.1"
885   :type '(repeat (cons (number :tag "Argument" 1)
886                        (symbol :tag "Charset")))
887   :group 'gnus-charset)
888
889 (defcustom gnus-preserve-marks t
890   "Whether marks are preserved when moving, copying and respooling messages."
891   :version "21.1"
892   :type 'boolean
893   :group 'gnus-summary-marks)
894
895 (defcustom gnus-alter-articles-to-read-function nil
896   "Function to be called to alter the list of articles to be selected."
897   :type 'function
898   :group 'gnus-summary)
899
900 (defcustom gnus-orphan-score nil
901   "*All orphans get this score added.  Set in the score file."
902   :group 'gnus-score-default
903   :type '(choice (const nil)
904                  integer))
905
906 (defcustom gnus-summary-save-parts-default-mime "image/.*"
907   "*A regexp to match MIME parts when saving multiple parts of a message
908 with gnus-summary-save-parts (X m). This regexp will be used by default
909 when prompting the user for which type of files to save."
910   :group 'gnus-summary
911   :type 'regexp)
912
913
914 (defcustom gnus-summary-save-parts-default-mime "image/.*"
915   "*A regexp to match MIME parts when saving multiple parts of a message
916 with gnus-summary-save-parts (X m). This regexp will be used by default
917 when prompting the user for which type of files to save."
918   :group 'gnus-summary
919   :type 'regexp)
920
921
922 ;;; Internal variables
923
924 (defvar gnus-article-mime-handles nil)
925 (defvar gnus-article-decoded-p nil)
926 (defvar gnus-article-charset nil)
927 (defvar gnus-article-ignored-charsets nil)
928 (defvar gnus-scores-exclude-files nil)
929 (defvar gnus-page-broken nil)
930 (defvar gnus-inhibit-mime-unbuttonizing nil)
931
932 (defvar gnus-original-article nil)
933 (defvar gnus-article-internal-prepare-hook nil)
934 (defvar gnus-newsgroup-process-stack nil)
935
936 (defvar gnus-thread-indent-array nil)
937 (defvar gnus-thread-indent-array-level gnus-thread-indent-level)
938 (defvar gnus-sort-gathered-threads-function 'gnus-thread-sort-by-number
939   "Function called to sort the articles within a thread after it has been gathered together.")
940
941 (defvar gnus-summary-save-parts-type-history nil)
942 (defvar gnus-summary-save-parts-last-directory nil)
943
944 (defvar gnus-summary-save-parts-type-history nil)
945 (defvar gnus-summary-save-parts-last-directory nil)
946
947 ;; Avoid highlighting in kill files.
948 (defvar gnus-summary-inhibit-highlight nil)
949 (defvar gnus-newsgroup-selected-overlay nil)
950 (defvar gnus-inhibit-limiting nil)
951 (defvar gnus-newsgroup-adaptive-score-file nil)
952 (defvar gnus-current-score-file nil)
953 (defvar gnus-current-move-group nil)
954 (defvar gnus-current-copy-group nil)
955 (defvar gnus-current-crosspost-group nil)
956
957 (defvar gnus-newsgroup-dependencies nil)
958 (defvar gnus-newsgroup-adaptive nil)
959 (defvar gnus-summary-display-article-function nil)
960 (defvar gnus-summary-highlight-line-function nil
961   "Function called after highlighting a summary line.")
962
963 (defvar gnus-summary-line-format-alist
964   `((?N ,(macroexpand '(mail-header-number gnus-tmp-header)) ?d)
965     (?S ,(macroexpand '(mail-header-subject gnus-tmp-header)) ?s)
966     (?s gnus-tmp-subject-or-nil ?s)
967     (?n gnus-tmp-name ?s)
968     (?A (car (cdr (funcall gnus-extract-address-components gnus-tmp-from)))
969         ?s)
970     (?a (or (car (funcall gnus-extract-address-components gnus-tmp-from))
971             gnus-tmp-from) ?s)
972     (?F gnus-tmp-from ?s)
973     (?x ,(macroexpand '(mail-header-xref gnus-tmp-header)) ?s)
974     (?D ,(macroexpand '(mail-header-date gnus-tmp-header)) ?s)
975     (?d (gnus-dd-mmm (mail-header-date gnus-tmp-header)) ?s)
976     (?o (gnus-date-iso8601 (mail-header-date gnus-tmp-header)) ?s)
977     (?M ,(macroexpand '(mail-header-id gnus-tmp-header)) ?s)
978     (?r ,(macroexpand '(mail-header-references gnus-tmp-header)) ?s)
979     (?c (or (mail-header-chars gnus-tmp-header) 0) ?d)
980     (?L gnus-tmp-lines ?d)
981     (?I gnus-tmp-indentation ?s)
982     (?T (if (= gnus-tmp-level 0) "" (make-string (frame-width) ? )) ?s)
983     (?R gnus-tmp-replied ?c)
984     (?\[ gnus-tmp-opening-bracket ?c)
985     (?\] gnus-tmp-closing-bracket ?c)
986     (?\> (make-string gnus-tmp-level ? ) ?s)
987     (?\< (make-string (max 0 (- 20 gnus-tmp-level)) ? ) ?s)
988     (?i gnus-tmp-score ?d)
989     (?z gnus-tmp-score-char ?c)
990     (?l (bbb-grouplens-score gnus-tmp-header) ?s)
991     (?V (gnus-thread-total-score (and (boundp 'thread) (car thread))) ?d)
992     (?U gnus-tmp-unread ?c)
993     (?f (gnus-summary-from-or-to-or-newsgroups gnus-tmp-header) ?s)
994     (?t (gnus-summary-number-of-articles-in-thread
995          (and (boundp 'thread) (car thread)) gnus-tmp-level)
996         ?d)
997     (?e (gnus-summary-number-of-articles-in-thread
998          (and (boundp 'thread) (car thread)) gnus-tmp-level t)
999         ?c)
1000     (?u gnus-tmp-user-defined ?s)
1001     (?P (gnus-pick-line-number) ?d))
1002   "An alist of format specifications that can appear in summary lines.
1003 These are paired with what variables they correspond with, along with
1004 the type of the variable (string, integer, character, etc).")
1005
1006 (defvar gnus-summary-dummy-line-format-alist
1007   `((?S gnus-tmp-subject ?s)
1008     (?N gnus-tmp-number ?d)
1009     (?u gnus-tmp-user-defined ?s)))
1010
1011 (defvar gnus-summary-mode-line-format-alist
1012   `((?G gnus-tmp-group-name ?s)
1013     (?g (gnus-short-group-name gnus-tmp-group-name) ?s)
1014     (?p (gnus-group-real-name gnus-tmp-group-name) ?s)
1015     (?A gnus-tmp-article-number ?d)
1016     (?Z gnus-tmp-unread-and-unselected ?s)
1017     (?V gnus-version ?s)
1018     (?U gnus-tmp-unread-and-unticked ?d)
1019     (?S gnus-tmp-subject ?s)
1020     (?e gnus-tmp-unselected ?d)
1021     (?u gnus-tmp-user-defined ?s)
1022     (?d (length gnus-newsgroup-dormant) ?d)
1023     (?t (length gnus-newsgroup-marked) ?d)
1024     (?r (length gnus-newsgroup-reads) ?d)
1025     (?z (gnus-summary-article-score gnus-tmp-article-number) ?d)
1026     (?E gnus-newsgroup-expunged-tally ?d)
1027     (?s (gnus-current-score-file-nondirectory) ?s)))
1028
1029 (defvar gnus-last-search-regexp nil
1030   "Default regexp for article search command.")
1031
1032 (defvar gnus-last-shell-command nil
1033   "Default shell command on article.")
1034
1035 (defvar gnus-newsgroup-begin nil)
1036 (defvar gnus-newsgroup-end nil)
1037 (defvar gnus-newsgroup-last-rmail nil)
1038 (defvar gnus-newsgroup-last-mail nil)
1039 (defvar gnus-newsgroup-last-folder nil)
1040 (defvar gnus-newsgroup-last-file nil)
1041 (defvar gnus-newsgroup-auto-expire nil)
1042 (defvar gnus-newsgroup-active nil)
1043
1044 (defvar gnus-newsgroup-data nil)
1045 (defvar gnus-newsgroup-data-reverse nil)
1046 (defvar gnus-newsgroup-limit nil)
1047 (defvar gnus-newsgroup-limits nil)
1048
1049 (defvar gnus-newsgroup-unreads nil
1050   "List of unread articles in the current newsgroup.")
1051
1052 (defvar gnus-newsgroup-unselected nil
1053   "List of unselected unread articles in the current newsgroup.")
1054
1055 (defvar gnus-newsgroup-reads nil
1056   "Alist of read articles and article marks in the current newsgroup.")
1057
1058 (defvar gnus-newsgroup-expunged-tally nil)
1059
1060 (defvar gnus-newsgroup-marked nil
1061   "List of ticked articles in the current newsgroup (a subset of unread art).")
1062
1063 (defvar gnus-newsgroup-killed nil
1064   "List of ranges of articles that have been through the scoring process.")
1065
1066 (defvar gnus-newsgroup-cached nil
1067   "List of articles that come from the article cache.")
1068
1069 (defvar gnus-newsgroup-saved nil
1070   "List of articles that have been saved.")
1071
1072 (defvar gnus-newsgroup-kill-headers nil)
1073
1074 (defvar gnus-newsgroup-replied nil
1075   "List of articles that have been replied to in the current newsgroup.")
1076
1077 (defvar gnus-newsgroup-expirable nil
1078   "List of articles in the current newsgroup that can be expired.")
1079
1080 (defvar gnus-newsgroup-processable nil
1081   "List of articles in the current newsgroup that can be processed.")
1082
1083 (defvar gnus-newsgroup-downloadable nil
1084   "List of articles in the current newsgroup that can be processed.")
1085
1086 (defvar gnus-newsgroup-undownloaded nil
1087   "List of articles in the current newsgroup that haven't been downloaded..")
1088
1089 (defvar gnus-newsgroup-unsendable nil
1090   "List of articles in the current newsgroup that won't be sent.")
1091
1092 (defvar gnus-newsgroup-bookmarks nil
1093   "List of articles in the current newsgroup that have bookmarks.")
1094
1095 (defvar gnus-newsgroup-dormant nil
1096   "List of dormant articles in the current newsgroup.")
1097
1098 (defvar gnus-newsgroup-scored nil
1099   "List of scored articles in the current newsgroup.")
1100
1101 (defvar gnus-newsgroup-headers nil
1102   "List of article headers in the current newsgroup.")
1103
1104 (defvar gnus-newsgroup-threads nil)
1105
1106 (defvar gnus-newsgroup-prepared nil
1107   "Whether the current group has been prepared properly.")
1108
1109 (defvar gnus-newsgroup-ancient nil
1110   "List of `gnus-fetch-old-headers' articles in the current newsgroup.")
1111
1112 (defvar gnus-newsgroup-sparse nil)
1113
1114 (defvar gnus-current-article nil)
1115 (defvar gnus-article-current nil)
1116 (defvar gnus-current-headers nil)
1117 (defvar gnus-have-all-headers nil)
1118 (defvar gnus-last-article nil)
1119 (defvar gnus-newsgroup-history nil)
1120 (defvar gnus-newsgroup-charset nil)
1121 (defvar gnus-newsgroup-ephemeral-charset nil)
1122 (defvar gnus-newsgroup-ephemeral-ignored-charsets nil)
1123
1124 (defvar gnus-article-before-search nil)
1125
1126 (defconst gnus-summary-local-variables
1127   '(gnus-newsgroup-name
1128     gnus-newsgroup-begin gnus-newsgroup-end
1129     gnus-newsgroup-last-rmail gnus-newsgroup-last-mail
1130     gnus-newsgroup-last-folder gnus-newsgroup-last-file
1131     gnus-newsgroup-auto-expire gnus-newsgroup-unreads
1132     gnus-newsgroup-unselected gnus-newsgroup-marked
1133     gnus-newsgroup-reads gnus-newsgroup-saved
1134     gnus-newsgroup-replied gnus-newsgroup-expirable
1135     gnus-newsgroup-processable gnus-newsgroup-killed
1136     gnus-newsgroup-downloadable gnus-newsgroup-undownloaded
1137     gnus-newsgroup-unsendable
1138     gnus-newsgroup-bookmarks gnus-newsgroup-dormant
1139     gnus-newsgroup-headers gnus-newsgroup-threads
1140     gnus-newsgroup-prepared gnus-summary-highlight-line-function
1141     gnus-current-article gnus-current-headers gnus-have-all-headers
1142     gnus-last-article gnus-article-internal-prepare-hook
1143     gnus-newsgroup-dependencies gnus-newsgroup-selected-overlay
1144     gnus-newsgroup-scored gnus-newsgroup-kill-headers
1145     gnus-thread-expunge-below
1146     gnus-score-alist gnus-current-score-file
1147     (gnus-summary-expunge-below . global)
1148     (gnus-summary-mark-below . global)
1149     (gnus-orphan-score . global)
1150     gnus-newsgroup-active gnus-scores-exclude-files
1151     gnus-newsgroup-history gnus-newsgroup-ancient
1152     gnus-newsgroup-sparse gnus-newsgroup-process-stack
1153     (gnus-newsgroup-adaptive . gnus-use-adaptive-scoring)
1154     gnus-newsgroup-adaptive-score-file (gnus-reffed-article-number . -1)
1155     (gnus-newsgroup-expunged-tally . 0)
1156     gnus-cache-removable-articles gnus-newsgroup-cached
1157     gnus-newsgroup-data gnus-newsgroup-data-reverse
1158     gnus-newsgroup-limit gnus-newsgroup-limits
1159     gnus-newsgroup-charset)
1160   "Variables that are buffer-local to the summary buffers.")
1161
1162 (defvar gnus-newsgroup-variables nil
1163   "Variables that have separate values in the newsgroups.")
1164
1165 ;; Byte-compiler warning.
1166 (eval-when-compile (defvar gnus-article-mode-map))
1167
1168 ;; MIME stuff.
1169
1170 (defvar gnus-decode-encoded-word-methods
1171   '(mail-decode-encoded-word-string)
1172   "List of methods used to decode encoded words.
1173
1174 This variable is a list of FUNCTION or (REGEXP . FUNCTION).  If item is
1175 FUNCTION, FUNCTION will be apply to all newsgroups.  If item is a
1176 (REGEXP . FUNCTION), FUNCTION will be only apply to thes newsgroups
1177 whose names match REGEXP.
1178
1179 For example:
1180 ((\"chinese\" . gnus-decode-encoded-word-string-by-guess)
1181  mail-decode-encoded-word-string
1182  (\"chinese\" . rfc1843-decode-string))")
1183
1184 (defvar gnus-decode-encoded-word-methods-cache nil)
1185
1186 (defun gnus-multi-decode-encoded-word-string (string)
1187   "Apply the functions from `gnus-encoded-word-methods' that match."
1188   (unless (and gnus-decode-encoded-word-methods-cache
1189                (eq gnus-newsgroup-name
1190                    (car gnus-decode-encoded-word-methods-cache)))
1191     (setq gnus-decode-encoded-word-methods-cache (list gnus-newsgroup-name))
1192     (mapcar (lambda (x)
1193               (if (symbolp x)
1194                   (nconc gnus-decode-encoded-word-methods-cache (list x))
1195                 (if (and gnus-newsgroup-name
1196                          (string-match (car x) gnus-newsgroup-name))
1197                     (nconc gnus-decode-encoded-word-methods-cache
1198                            (list (cdr x))))))
1199           gnus-decode-encoded-word-methods))
1200   (let ((xlist gnus-decode-encoded-word-methods-cache))
1201     (pop xlist)
1202     (while xlist
1203       (setq string (funcall (pop xlist) string))))
1204   string)
1205
1206 ;; Subject simplification.
1207
1208 (defun gnus-simplify-whitespace (str)
1209   "Remove excessive whitespace from STR."
1210   (let ((mystr str))
1211     ;; Multiple spaces.
1212     (while (string-match "[ \t][ \t]+" mystr)
1213       (setq mystr (concat (substring mystr 0 (match-beginning 0))
1214                           " "
1215                           (substring mystr (match-end 0)))))
1216     ;; Leading spaces.
1217     (when (string-match "^[ \t]+" mystr)
1218       (setq mystr (substring mystr (match-end 0))))
1219     ;; Trailing spaces.
1220     (when (string-match "[ \t]+$" mystr)
1221       (setq mystr (substring mystr 0 (match-beginning 0))))
1222     mystr))
1223
1224 (defsubst gnus-simplify-subject-re (subject)
1225   "Remove \"Re:\" from subject lines."
1226   (if (string-match "^[Rr][Ee]: *" subject)
1227       (substring subject (match-end 0))
1228     subject))
1229
1230 (defun gnus-simplify-subject (subject &optional re-only)
1231   "Remove `Re:' and words in parentheses.
1232 If RE-ONLY is non-nil, strip leading `Re:'s only."
1233   (let ((case-fold-search t))           ;Ignore case.
1234     ;; Remove `Re:', `Re^N:', `Re(n)', and `Re[n]:'.
1235     (when (string-match "\\`\\(re\\([[(^][0-9]+[])]?\\)?:[ \t]*\\)+" subject)
1236       (setq subject (substring subject (match-end 0))))
1237     ;; Remove uninteresting prefixes.
1238     (when (and (not re-only)
1239                gnus-simplify-ignored-prefixes
1240                (string-match gnus-simplify-ignored-prefixes subject))
1241       (setq subject (substring subject (match-end 0))))
1242     ;; Remove words in parentheses from end.
1243     (unless re-only
1244       (while (string-match "[ \t\n]*([^()]*)[ \t\n]*\\'" subject)
1245         (setq subject (substring subject 0 (match-beginning 0)))))
1246     ;; Return subject string.
1247     subject))
1248
1249 ;; Remove any leading "re:"s, any trailing paren phrases, and simplify
1250 ;; all whitespace.
1251 (defsubst gnus-simplify-buffer-fuzzy-step (regexp &optional newtext)
1252   (goto-char (point-min))
1253   (while (re-search-forward regexp nil t)
1254     (replace-match (or newtext ""))))
1255
1256 (defun gnus-simplify-buffer-fuzzy ()
1257   "Simplify string in the buffer fuzzily.
1258 The string in the accessible portion of the current buffer is simplified.
1259 It is assumed to be a single-line subject.
1260 Whitespace is generally cleaned up, and miscellaneous leading/trailing
1261 matter is removed.  Additional things can be deleted by setting
1262 `gnus-simplify-subject-fuzzy-regexp'."
1263   (let ((case-fold-search t)
1264         (modified-tick))
1265     (gnus-simplify-buffer-fuzzy-step "\t" " ")
1266
1267     (while (not (eq modified-tick (buffer-modified-tick)))
1268       (setq modified-tick (buffer-modified-tick))
1269       (cond
1270        ((listp gnus-simplify-subject-fuzzy-regexp)
1271         (mapcar 'gnus-simplify-buffer-fuzzy-step
1272                 gnus-simplify-subject-fuzzy-regexp))
1273        (gnus-simplify-subject-fuzzy-regexp
1274         (gnus-simplify-buffer-fuzzy-step gnus-simplify-subject-fuzzy-regexp)))
1275       (gnus-simplify-buffer-fuzzy-step "^ *\\[[-+?*!][-+?*!]\\] *")
1276       (gnus-simplify-buffer-fuzzy-step
1277        "^ *\\(re\\|fw\\|fwd\\)[[{(^0-9]*[])}]?[:;] *")
1278       (gnus-simplify-buffer-fuzzy-step "^[[].*:\\( .*\\)[]]$" "\\1"))
1279
1280     (gnus-simplify-buffer-fuzzy-step " *[[{(][^()\n]*[]})] *$")
1281     (gnus-simplify-buffer-fuzzy-step "  +" " ")
1282     (gnus-simplify-buffer-fuzzy-step " $")
1283     (gnus-simplify-buffer-fuzzy-step "^ +")))
1284
1285 (defun gnus-simplify-subject-fuzzy (subject)
1286   "Simplify a subject string fuzzily.
1287 See `gnus-simplify-buffer-fuzzy' for details."
1288   (save-excursion
1289     (gnus-set-work-buffer)
1290     (let ((case-fold-search t))
1291       ;; Remove uninteresting prefixes.
1292       (when (and gnus-simplify-ignored-prefixes
1293                  (string-match gnus-simplify-ignored-prefixes subject))
1294         (setq subject (substring subject (match-end 0))))
1295       (insert subject)
1296       (inline (gnus-simplify-buffer-fuzzy))
1297       (buffer-string))))
1298
1299 (defsubst gnus-simplify-subject-fully (subject)
1300   "Simplify a subject string according to gnus-summary-gather-subject-limit."
1301   (cond
1302    (gnus-simplify-subject-functions
1303     (gnus-map-function gnus-simplify-subject-functions subject))
1304    ((null gnus-summary-gather-subject-limit)
1305     (gnus-simplify-subject-re subject))
1306    ((eq gnus-summary-gather-subject-limit 'fuzzy)
1307     (gnus-simplify-subject-fuzzy subject))
1308    ((numberp gnus-summary-gather-subject-limit)
1309     (gnus-limit-string (gnus-simplify-subject-re subject)
1310                        gnus-summary-gather-subject-limit))
1311    (t
1312     subject)))
1313
1314 (defsubst gnus-subject-equal (s1 s2 &optional simple-first)
1315   "Check whether two subjects are equal.
1316 If optional argument simple-first is t, first argument is already
1317 simplified."
1318   (cond
1319    ((null simple-first)
1320     (equal (gnus-simplify-subject-fully s1)
1321            (gnus-simplify-subject-fully s2)))
1322    (t
1323     (equal s1
1324            (gnus-simplify-subject-fully s2)))))
1325
1326 (defun gnus-summary-bubble-group ()
1327   "Increase the score of the current group.
1328 This is a handy function to add to `gnus-summary-exit-hook' to
1329 increase the score of each group you read."
1330   (gnus-group-add-score gnus-newsgroup-name))
1331
1332 \f
1333 ;;;
1334 ;;; Gnus summary mode
1335 ;;;
1336
1337 (put 'gnus-summary-mode 'mode-class 'special)
1338
1339 (when t
1340   ;; Non-orthogonal keys
1341
1342   (gnus-define-keys gnus-summary-mode-map
1343     " " gnus-summary-next-page
1344     "\177" gnus-summary-prev-page
1345     [delete] gnus-summary-prev-page
1346     [backspace] gnus-summary-prev-page
1347     "\r" gnus-summary-scroll-up
1348     "\M-\r" gnus-summary-scroll-down
1349     "n" gnus-summary-next-unread-article
1350     "p" gnus-summary-prev-unread-article
1351     "N" gnus-summary-next-article
1352     "P" gnus-summary-prev-article
1353     "\M-\C-n" gnus-summary-next-same-subject
1354     "\M-\C-p" gnus-summary-prev-same-subject
1355     "\M-n" gnus-summary-next-unread-subject
1356     "\M-p" gnus-summary-prev-unread-subject
1357     "." gnus-summary-first-unread-article
1358     "," gnus-summary-best-unread-article
1359     "\M-s" gnus-summary-search-article-forward
1360     "\M-r" gnus-summary-search-article-backward
1361     "<" gnus-summary-beginning-of-article
1362     ">" gnus-summary-end-of-article
1363     "j" gnus-summary-goto-article
1364     "^" gnus-summary-refer-parent-article
1365     "\M-^" gnus-summary-refer-article
1366     "u" gnus-summary-tick-article-forward
1367     "!" gnus-summary-tick-article-forward
1368     "U" gnus-summary-tick-article-backward
1369     "d" gnus-summary-mark-as-read-forward
1370     "D" gnus-summary-mark-as-read-backward
1371     "E" gnus-summary-mark-as-expirable
1372     "\M-u" gnus-summary-clear-mark-forward
1373     "\M-U" gnus-summary-clear-mark-backward
1374     "k" gnus-summary-kill-same-subject-and-select
1375     "\C-k" gnus-summary-kill-same-subject
1376     "\M-\C-k" gnus-summary-kill-thread
1377     "\M-\C-l" gnus-summary-lower-thread
1378     "e" gnus-summary-edit-article
1379     "#" gnus-summary-mark-as-processable
1380     "\M-#" gnus-summary-unmark-as-processable
1381     "\M-\C-t" gnus-summary-toggle-threads
1382     "\M-\C-s" gnus-summary-show-thread
1383     "\M-\C-h" gnus-summary-hide-thread
1384     "\M-\C-f" gnus-summary-next-thread
1385     "\M-\C-b" gnus-summary-prev-thread
1386     [(meta down)] gnus-summary-next-thread
1387     [(meta up)] gnus-summary-prev-thread
1388     "\M-\C-u" gnus-summary-up-thread
1389     "\M-\C-d" gnus-summary-down-thread
1390     "&" gnus-summary-execute-command
1391     "c" gnus-summary-catchup-and-exit
1392     "\C-w" gnus-summary-mark-region-as-read
1393     "\C-t" gnus-summary-toggle-truncation
1394     "?" gnus-summary-mark-as-dormant
1395     "\C-c\M-\C-s" gnus-summary-limit-include-expunged
1396     "\C-c\C-s\C-n" gnus-summary-sort-by-number
1397     "\C-c\C-s\C-l" gnus-summary-sort-by-lines
1398     "\C-c\C-s\C-c" gnus-summary-sort-by-chars
1399     "\C-c\C-s\C-a" gnus-summary-sort-by-author
1400     "\C-c\C-s\C-s" gnus-summary-sort-by-subject
1401     "\C-c\C-s\C-d" gnus-summary-sort-by-date
1402     "\C-c\C-s\C-i" gnus-summary-sort-by-score
1403     "=" gnus-summary-expand-window
1404     "\C-x\C-s" gnus-summary-reselect-current-group
1405     "\M-g" gnus-summary-rescan-group
1406     "w" gnus-summary-stop-page-breaking
1407     "\C-c\C-r" gnus-summary-caesar-message
1408     "f" gnus-summary-followup
1409     "F" gnus-summary-followup-with-original
1410     "C" gnus-summary-cancel-article
1411     "r" gnus-summary-reply
1412     "R" gnus-summary-reply-with-original
1413     "\C-c\C-f" gnus-summary-mail-forward
1414     "o" gnus-summary-save-article
1415     "\C-o" gnus-summary-save-article-mail
1416     "|" gnus-summary-pipe-output
1417     "\M-k" gnus-summary-edit-local-kill
1418     "\M-K" gnus-summary-edit-global-kill
1419     ;; "V" gnus-version
1420     "\C-c\C-d" gnus-summary-describe-group
1421     "q" gnus-summary-exit
1422     "Q" gnus-summary-exit-no-update
1423     "\C-c\C-i" gnus-info-find-node
1424     gnus-mouse-2 gnus-mouse-pick-article
1425     "m" gnus-summary-mail-other-window
1426     "a" gnus-summary-post-news
1427     "x" gnus-summary-limit-to-unread
1428     "s" gnus-summary-isearch-article
1429     "t" gnus-summary-toggle-header
1430     "g" gnus-summary-show-article
1431     "l" gnus-summary-goto-last-article
1432     "\C-c\C-v\C-v" gnus-uu-decode-uu-view
1433     "\C-d" gnus-summary-enter-digest-group
1434     "\M-\C-d" gnus-summary-read-document
1435     "\M-\C-e" gnus-summary-edit-parameters
1436     "\M-\C-a" gnus-summary-customize-parameters
1437     "\C-c\C-b" gnus-bug
1438     "*" gnus-cache-enter-article
1439     "\M-*" gnus-cache-remove-article
1440     "\M-&" gnus-summary-universal-argument
1441     "\C-l" gnus-recenter
1442     "I" gnus-summary-increase-score
1443     "L" gnus-summary-lower-score
1444     "\M-i" gnus-symbolic-argument
1445     "h" gnus-summary-select-article-buffer
1446
1447     "b" gnus-article-view-part
1448     "\M-t" gnus-summary-toggle-display-buttonized
1449
1450     "V" gnus-summary-score-map
1451     "X" gnus-uu-extract-map
1452     "S" gnus-summary-send-map)
1453
1454   ;; Sort of orthogonal keymap
1455   (gnus-define-keys (gnus-summary-mark-map "M" gnus-summary-mode-map)
1456     "t" gnus-summary-tick-article-forward
1457     "!" gnus-summary-tick-article-forward
1458     "d" gnus-summary-mark-as-read-forward
1459     "r" gnus-summary-mark-as-read-forward
1460     "c" gnus-summary-clear-mark-forward
1461     " " gnus-summary-clear-mark-forward
1462     "e" gnus-summary-mark-as-expirable
1463     "x" gnus-summary-mark-as-expirable
1464     "?" gnus-summary-mark-as-dormant
1465     "b" gnus-summary-set-bookmark
1466     "B" gnus-summary-remove-bookmark
1467     "#" gnus-summary-mark-as-processable
1468     "\M-#" gnus-summary-unmark-as-processable
1469     "S" gnus-summary-limit-include-expunged
1470     "C" gnus-summary-catchup
1471     "H" gnus-summary-catchup-to-here
1472     "\C-c" gnus-summary-catchup-all
1473     "k" gnus-summary-kill-same-subject-and-select
1474     "K" gnus-summary-kill-same-subject
1475     "P" gnus-uu-mark-map)
1476
1477   (gnus-define-keys (gnus-summary-mscore-map "V" gnus-summary-mark-map)
1478     "c" gnus-summary-clear-above
1479     "u" gnus-summary-tick-above
1480     "m" gnus-summary-mark-above
1481     "k" gnus-summary-kill-below)
1482
1483   (gnus-define-keys (gnus-summary-limit-map "/" gnus-summary-mode-map)
1484     "/" gnus-summary-limit-to-subject
1485     "n" gnus-summary-limit-to-articles
1486     "w" gnus-summary-pop-limit
1487     "s" gnus-summary-limit-to-subject
1488     "a" gnus-summary-limit-to-author
1489     "u" gnus-summary-limit-to-unread
1490     "m" gnus-summary-limit-to-marks
1491     "M" gnus-summary-limit-exclude-marks
1492     "v" gnus-summary-limit-to-score
1493     "*" gnus-summary-limit-include-cached
1494     "D" gnus-summary-limit-include-dormant
1495     "T" gnus-summary-limit-include-thread
1496     "d" gnus-summary-limit-exclude-dormant
1497     "t" gnus-summary-limit-to-age
1498     "x" gnus-summary-limit-to-extra
1499     "E" gnus-summary-limit-include-expunged
1500     "c" gnus-summary-limit-exclude-childless-dormant
1501     "C" gnus-summary-limit-mark-excluded-as-read)
1502
1503   (gnus-define-keys (gnus-summary-goto-map "G" gnus-summary-mode-map)
1504     "n" gnus-summary-next-unread-article
1505     "p" gnus-summary-prev-unread-article
1506     "N" gnus-summary-next-article
1507     "P" gnus-summary-prev-article
1508     "\C-n" gnus-summary-next-same-subject
1509     "\C-p" gnus-summary-prev-same-subject
1510     "\M-n" gnus-summary-next-unread-subject
1511     "\M-p" gnus-summary-prev-unread-subject
1512     "f" gnus-summary-first-unread-article
1513     "b" gnus-summary-best-unread-article
1514     "j" gnus-summary-goto-article
1515     "g" gnus-summary-goto-subject
1516     "l" gnus-summary-goto-last-article
1517     "o" gnus-summary-pop-article)
1518
1519   (gnus-define-keys (gnus-summary-thread-map "T" gnus-summary-mode-map)
1520     "k" gnus-summary-kill-thread
1521     "l" gnus-summary-lower-thread
1522     "i" gnus-summary-raise-thread
1523     "T" gnus-summary-toggle-threads
1524     "t" gnus-summary-rethread-current
1525     "^" gnus-summary-reparent-thread
1526     "s" gnus-summary-show-thread
1527     "S" gnus-summary-show-all-threads
1528     "h" gnus-summary-hide-thread
1529     "H" gnus-summary-hide-all-threads
1530     "n" gnus-summary-next-thread
1531     "p" gnus-summary-prev-thread
1532     "u" gnus-summary-up-thread
1533     "o" gnus-summary-top-thread
1534     "d" gnus-summary-down-thread
1535     "#" gnus-uu-mark-thread
1536     "\M-#" gnus-uu-unmark-thread)
1537
1538   (gnus-define-keys (gnus-summary-buffer-map "Y" gnus-summary-mode-map)
1539     "g" gnus-summary-prepare
1540     "c" gnus-summary-insert-cached-articles)
1541
1542   (gnus-define-keys (gnus-summary-exit-map "Z" gnus-summary-mode-map)
1543     "c" gnus-summary-catchup-and-exit
1544     "C" gnus-summary-catchup-all-and-exit
1545     "E" gnus-summary-exit-no-update
1546     "Q" gnus-summary-exit
1547     "Z" gnus-summary-exit
1548     "n" gnus-summary-catchup-and-goto-next-group
1549     "R" gnus-summary-reselect-current-group
1550     "G" gnus-summary-rescan-group
1551     "N" gnus-summary-next-group
1552     "s" gnus-summary-save-newsrc
1553     "P" gnus-summary-prev-group)
1554
1555   (gnus-define-keys (gnus-summary-article-map "A" gnus-summary-mode-map)
1556     " " gnus-summary-next-page
1557     "n" gnus-summary-next-page
1558     "\177" gnus-summary-prev-page
1559     [delete] gnus-summary-prev-page
1560     "p" gnus-summary-prev-page
1561     "\r" gnus-summary-scroll-up
1562     "\M-\r" gnus-summary-scroll-down
1563     "<" gnus-summary-beginning-of-article
1564     ">" gnus-summary-end-of-article
1565     "b" gnus-summary-beginning-of-article
1566     "e" gnus-summary-end-of-article
1567     "^" gnus-summary-refer-parent-article
1568     "r" gnus-summary-refer-parent-article
1569     "D" gnus-summary-enter-digest-group
1570     "R" gnus-summary-refer-references
1571     "T" gnus-summary-refer-thread
1572     "g" gnus-summary-show-article
1573     "s" gnus-summary-isearch-article
1574     "P" gnus-summary-print-article
1575     "M" gnus-mailing-list-insinuate
1576     "t" gnus-article-babel)
1577
1578   (gnus-define-keys (gnus-summary-wash-map "W" gnus-summary-mode-map)
1579     "b" gnus-article-add-buttons
1580     "B" gnus-article-add-buttons-to-head
1581     "o" gnus-article-treat-overstrike
1582     "e" gnus-article-emphasize
1583     "w" gnus-article-fill-cited-article
1584     "Q" gnus-article-fill-long-lines
1585     "C" gnus-article-capitalize-sentences
1586     "c" gnus-article-remove-cr
1587     "q" gnus-article-de-quoted-unreadable
1588     "6" gnus-article-de-base64-unreadable
1589     "Z" gnus-article-decode-HZ
1590     "h" gnus-article-wash-html
1591     "s" gnus-summary-force-verify-and-decrypt
1592     "f" gnus-article-display-x-face
1593     "l" gnus-summary-stop-page-breaking
1594     "r" gnus-summary-caesar-message
1595     "t" gnus-summary-toggle-header
1596     "v" gnus-summary-verbose-headers
1597     "H" gnus-article-strip-headers-in-body
1598     "p" gnus-article-verify-x-pgp-sig
1599     "d" gnus-article-treat-dumbquotes)
1600
1601   (gnus-define-keys (gnus-summary-wash-hide-map "W" gnus-summary-wash-map)
1602     "a" gnus-article-hide
1603     "h" gnus-article-hide-headers
1604     "b" gnus-article-hide-boring-headers
1605     "s" gnus-article-hide-signature
1606     "c" gnus-article-hide-citation
1607     "C" gnus-article-hide-citation-in-followups
1608     "l" gnus-article-hide-list-identifiers
1609     "p" gnus-article-hide-pgp
1610     "B" gnus-article-strip-banner
1611     "P" gnus-article-hide-pem
1612     "\C-c" gnus-article-hide-citation-maybe)
1613
1614   (gnus-define-keys (gnus-summary-wash-highlight-map "H" gnus-summary-wash-map)
1615     "a" gnus-article-highlight
1616     "h" gnus-article-highlight-headers
1617     "c" gnus-article-highlight-citation
1618     "s" gnus-article-highlight-signature)
1619
1620   (gnus-define-keys (gnus-summary-wash-mime-map "M" gnus-summary-wash-map)
1621     "w" gnus-article-decode-mime-words
1622     "c" gnus-article-decode-charset
1623     "v" gnus-mime-view-all-parts
1624     "b" gnus-article-view-part)
1625
1626   (gnus-define-keys (gnus-summary-wash-time-map "T" gnus-summary-wash-map)
1627     "z" gnus-article-date-ut
1628     "u" gnus-article-date-ut
1629     "l" gnus-article-date-local
1630     "e" gnus-article-date-lapsed
1631     "o" gnus-article-date-original
1632     "i" gnus-article-date-iso8601
1633     "s" gnus-article-date-user)
1634
1635   (gnus-define-keys (gnus-summary-wash-empty-map "E" gnus-summary-wash-map)
1636     "t" gnus-article-remove-trailing-blank-lines
1637     "l" gnus-article-strip-leading-blank-lines
1638     "m" gnus-article-strip-multiple-blank-lines
1639     "a" gnus-article-strip-blank-lines
1640     "A" gnus-article-strip-all-blank-lines
1641     "s" gnus-article-strip-leading-space
1642     "e" gnus-article-strip-trailing-space)
1643
1644   (gnus-define-keys (gnus-summary-help-map "H" gnus-summary-mode-map)
1645     "v" gnus-version
1646     "f" gnus-summary-fetch-faq
1647     "d" gnus-summary-describe-group
1648     "h" gnus-summary-describe-briefly
1649     "i" gnus-info-find-node)
1650
1651   (gnus-define-keys (gnus-summary-backend-map "B" gnus-summary-mode-map)
1652     "e" gnus-summary-expire-articles
1653     "\M-\C-e" gnus-summary-expire-articles-now
1654     "\177" gnus-summary-delete-article
1655     [delete] gnus-summary-delete-article
1656     [backspace] gnus-summary-delete-article
1657     "m" gnus-summary-move-article
1658     "r" gnus-summary-respool-article
1659     "w" gnus-summary-edit-article
1660     "c" gnus-summary-copy-article
1661     "B" gnus-summary-crosspost-article
1662     "q" gnus-summary-respool-query
1663     "t" gnus-summary-respool-trace
1664     "i" gnus-summary-import-article
1665     "p" gnus-summary-article-posted-p)
1666
1667   (gnus-define-keys (gnus-summary-save-map "O" gnus-summary-mode-map)
1668     "o" gnus-summary-save-article
1669     "m" gnus-summary-save-article-mail
1670     "F" gnus-summary-write-article-file
1671     "r" gnus-summary-save-article-rmail
1672     "f" gnus-summary-save-article-file
1673     "b" gnus-summary-save-article-body-file
1674     "h" gnus-summary-save-article-folder
1675     "v" gnus-summary-save-article-vm
1676     "p" gnus-summary-pipe-output
1677     "s" gnus-soup-add-article)
1678
1679   (gnus-define-keys (gnus-summary-mime-map "K" gnus-summary-mode-map)
1680     "b" gnus-summary-display-buttonized
1681     "m" gnus-summary-repair-multipart
1682     "v" gnus-article-view-part
1683     "o" gnus-article-save-part
1684     "c" gnus-article-copy-part
1685     "C" gnus-article-view-part-as-charset
1686     "e" gnus-article-externalize-part
1687     "E" gnus-article-encrypt-body
1688     "i" gnus-article-inline-part
1689     "|" gnus-article-pipe-part))
1690
1691 (defun gnus-summary-make-menu-bar ()
1692   (gnus-turn-off-edit-menu 'summary)
1693
1694   (unless (boundp 'gnus-summary-misc-menu)
1695
1696     (easy-menu-define
1697      gnus-summary-kill-menu gnus-summary-mode-map ""
1698      (cons
1699       "Score"
1700       (nconc
1701        (list
1702         ["Enter score..." gnus-summary-score-entry t]
1703         ["Customize" gnus-score-customize t])
1704        (gnus-make-score-map 'increase)
1705        (gnus-make-score-map 'lower)
1706        '(("Mark"
1707           ["Kill below" gnus-summary-kill-below t]
1708           ["Mark above" gnus-summary-mark-above t]
1709           ["Tick above" gnus-summary-tick-above t]
1710           ["Clear above" gnus-summary-clear-above t])
1711          ["Current score" gnus-summary-current-score t]
1712          ["Set score" gnus-summary-set-score t]
1713          ["Switch current score file..." gnus-score-change-score-file t]
1714          ["Set mark below..." gnus-score-set-mark-below t]
1715          ["Set expunge below..." gnus-score-set-expunge-below t]
1716          ["Edit current score file" gnus-score-edit-current-scores t]
1717          ["Edit score file" gnus-score-edit-file t]
1718          ["Trace score" gnus-score-find-trace t]
1719          ["Find words" gnus-score-find-favourite-words t]
1720          ["Rescore buffer" gnus-summary-rescore t]
1721          ["Increase score..." gnus-summary-increase-score t]
1722          ["Lower score..." gnus-summary-lower-score t]))))
1723
1724     ;; Define both the Article menu in the summary buffer and the equivalent
1725     ;; Commands menu in the article buffer here for consistency.
1726     (let ((innards
1727            '(("Hide"
1728               ["All" gnus-article-hide t]
1729               ["Headers" gnus-article-hide-headers t]
1730               ["Signature" gnus-article-hide-signature t]
1731               ["Citation" gnus-article-hide-citation t]
1732               ["List identifiers" gnus-article-hide-list-identifiers t]
1733               ["PGP" gnus-article-hide-pgp t]
1734               ["Banner" gnus-article-strip-banner t]
1735               ["Boring headers" gnus-article-hide-boring-headers t])
1736              ("Highlight"
1737               ["All" gnus-article-highlight t]
1738               ["Headers" gnus-article-highlight-headers t]
1739               ["Signature" gnus-article-highlight-signature t]
1740               ["Citation" gnus-article-highlight-citation t])
1741              ("MIME"
1742               ["Words" gnus-article-decode-mime-words t]
1743               ["Charset" gnus-article-decode-charset t]
1744               ["QP" gnus-article-de-quoted-unreadable t]
1745               ["Base64" gnus-article-de-base64-unreadable t]
1746               ["View all" gnus-mime-view-all-parts t]
1747               ["Verify and Decrypt" gnus-summary-force-verify-and-decrypt t]
1748               ["Encrypt body" gnus-article-encrypt-body t])
1749              ("Date"
1750               ["Local" gnus-article-date-local t]
1751               ["ISO8601" gnus-article-date-iso8601 t]
1752               ["UT" gnus-article-date-ut t]
1753               ["Original" gnus-article-date-original t]
1754               ["Lapsed" gnus-article-date-lapsed t]
1755               ["User-defined" gnus-article-date-user t])
1756              ("Washing"
1757               ("Remove Blanks"
1758                ["Leading" gnus-article-strip-leading-blank-lines t]
1759                ["Multiple" gnus-article-strip-multiple-blank-lines t]
1760                ["Trailing" gnus-article-remove-trailing-blank-lines t]
1761                ["All of the above" gnus-article-strip-blank-lines t]
1762                ["All" gnus-article-strip-all-blank-lines t]
1763                ["Leading space" gnus-article-strip-leading-space t]
1764                ["Trailing space" gnus-article-strip-trailing-space t])
1765               ["Overstrike" gnus-article-treat-overstrike t]
1766               ["Dumb quotes" gnus-article-treat-dumbquotes t]
1767               ["Emphasis" gnus-article-emphasize t]
1768               ["Word wrap" gnus-article-fill-cited-article t]
1769               ["Fill long lines" gnus-article-fill-long-lines t]
1770               ["Capitalize sentences" gnus-article-capitalize-sentences t]
1771               ["CR" gnus-article-remove-cr t]
1772               ["Show X-Face" gnus-article-display-x-face t]
1773               ["Quoted-Printable" gnus-article-de-quoted-unreadable t]
1774               ["Base64" gnus-article-de-base64-unreadable t]
1775               ["Rot 13" gnus-summary-caesar-message
1776                ;;:help "\"Caesar rotate\" article by 13"
1777                ]
1778               ["Unix pipe" gnus-summary-pipe-message t]
1779               ["Add buttons" gnus-article-add-buttons t]
1780               ["Add buttons to head" gnus-article-add-buttons-to-head t]
1781               ["Stop page breaking" gnus-summary-stop-page-breaking t]
1782               ["Verbose header" gnus-summary-verbose-headers t]
1783               ["Toggle header" gnus-summary-toggle-header t]
1784               ["Html" gnus-article-wash-html t]
1785               ["Verify X-PGP-Sig" gnus-article-verify-x-pgp-sig t]
1786               ["HZ" gnus-article-decode-HZ t])
1787              ("Output"
1788               ["Save in default format" gnus-summary-save-article
1789                ;;:help "Save article using default method"
1790                ]
1791               ["Save in file" gnus-summary-save-article-file
1792                ;;:help "Save article in file"
1793                ]
1794               ["Save in Unix mail format" gnus-summary-save-article-mail t]
1795               ["Save in MH folder" gnus-summary-save-article-folder t]
1796               ["Save in VM folder" gnus-summary-save-article-vm t]
1797               ["Save in RMAIL mbox" gnus-summary-save-article-rmail t]
1798               ["Save body in file" gnus-summary-save-article-body-file t]
1799               ["Pipe through a filter" gnus-summary-pipe-output t]
1800               ["Add to SOUP packet" gnus-soup-add-article t]
1801               ["Print" gnus-summary-print-article t])
1802              ("Backend"
1803               ["Respool article..." gnus-summary-respool-article t]
1804               ["Move article..." gnus-summary-move-article
1805                (gnus-check-backend-function
1806                 'request-move-article gnus-newsgroup-name)]
1807               ["Copy article..." gnus-summary-copy-article t]
1808               ["Crosspost article..." gnus-summary-crosspost-article
1809                (gnus-check-backend-function
1810                 'request-replace-article gnus-newsgroup-name)]
1811               ["Import file..." gnus-summary-import-article t]
1812               ["Check if posted" gnus-summary-article-posted-p t]
1813               ["Edit article" gnus-summary-edit-article
1814                (not (gnus-group-read-only-p))]
1815               ["Delete article" gnus-summary-delete-article
1816                (gnus-check-backend-function
1817                 'request-expire-articles gnus-newsgroup-name)]
1818               ["Query respool" gnus-summary-respool-query t]
1819               ["Trace respool" gnus-summary-respool-trace t]
1820               ["Delete expirable articles" gnus-summary-expire-articles-now
1821                (gnus-check-backend-function
1822                 'request-expire-articles gnus-newsgroup-name)])
1823              ("Extract"
1824               ["Uudecode" gnus-uu-decode-uu
1825                ;;:help "Decode uuencoded article(s)"
1826                ]
1827               ["Uudecode and save" gnus-uu-decode-uu-and-save t]
1828               ["Unshar" gnus-uu-decode-unshar t]
1829               ["Unshar and save" gnus-uu-decode-unshar-and-save t]
1830               ["Save" gnus-uu-decode-save t]
1831               ["Binhex" gnus-uu-decode-binhex t]
1832               ["Postscript" gnus-uu-decode-postscript t])
1833              ("Cache"
1834               ["Enter article" gnus-cache-enter-article t]
1835               ["Remove article" gnus-cache-remove-article t])
1836              ["Translate" gnus-article-babel t]
1837              ["Select article buffer" gnus-summary-select-article-buffer t]
1838              ["Enter digest buffer" gnus-summary-enter-digest-group t]
1839              ["Isearch article..." gnus-summary-isearch-article t]
1840              ["Beginning of the article" gnus-summary-beginning-of-article t]
1841              ["End of the article" gnus-summary-end-of-article t]
1842              ["Fetch parent of article" gnus-summary-refer-parent-article t]
1843              ["Fetch referenced articles" gnus-summary-refer-references t]
1844              ["Fetch current thread" gnus-summary-refer-thread t]
1845              ["Fetch article with id..." gnus-summary-refer-article t]
1846              ["Setup Mailing List Params" gnus-mailing-list-insinuate t]
1847              ["Redisplay" gnus-summary-show-article t])))
1848       (easy-menu-define
1849        gnus-summary-article-menu gnus-summary-mode-map ""
1850        (cons "Article" innards))
1851
1852       (easy-menu-define
1853        gnus-article-commands-menu gnus-article-mode-map ""
1854        (cons "Commands" innards)))
1855
1856     (easy-menu-define
1857      gnus-summary-thread-menu gnus-summary-mode-map ""
1858      '("Threads"
1859        ["Toggle threading" gnus-summary-toggle-threads t]
1860        ["Hide threads" gnus-summary-hide-all-threads t]
1861        ["Show threads" gnus-summary-show-all-threads t]
1862        ["Hide thread" gnus-summary-hide-thread t]
1863        ["Show thread" gnus-summary-show-thread t]
1864        ["Go to next thread" gnus-summary-next-thread t]
1865        ["Go to previous thread" gnus-summary-prev-thread t]
1866        ["Go down thread" gnus-summary-down-thread t]
1867        ["Go up thread" gnus-summary-up-thread t]
1868        ["Top of thread" gnus-summary-top-thread t]
1869        ["Mark thread as read" gnus-summary-kill-thread t]
1870        ["Lower thread score" gnus-summary-lower-thread t]
1871        ["Raise thread score" gnus-summary-raise-thread t]
1872        ["Rethread current" gnus-summary-rethread-current t]))
1873
1874     (easy-menu-define
1875      gnus-summary-post-menu gnus-summary-mode-map ""
1876      '("Post"
1877        ["Post an article" gnus-summary-post-news
1878         ;;:help "Post an article"
1879         ]
1880        ["Followup" gnus-summary-followup
1881         ;;:help "Post followup to this article"
1882         ]
1883        ["Followup and yank" gnus-summary-followup-with-original
1884         ;;:help "Post followup to this article, quoting its contents"
1885         ]
1886        ["Supersede article" gnus-summary-supersede-article t]
1887        ["Cancel article" gnus-summary-cancel-article
1888         ;;:help "Cancel an article you posted"
1889         ]
1890        ["Reply" gnus-summary-reply t]
1891        ["Reply and yank" gnus-summary-reply-with-original t]
1892        ["Wide reply" gnus-summary-wide-reply t]
1893        ["Wide reply and yank" gnus-summary-wide-reply-with-original
1894         ;;:help "Mail a reply, quoting this article"
1895         ]
1896        ["Mail forward" gnus-summary-mail-forward t]
1897        ["Post forward" gnus-summary-post-forward t]
1898        ["Digest and mail" gnus-uu-digest-mail-forward t]
1899        ["Digest and post" gnus-uu-digest-post-forward t]
1900        ["Resend message" gnus-summary-resend-message t]
1901        ["Send bounced mail" gnus-summary-resend-bounced-mail t]
1902        ["Send a mail" gnus-summary-mail-other-window t]
1903        ["Uuencode and post" gnus-uu-post-news
1904         ;;:help "Post a uuencoded article"
1905         ]
1906        ["Followup via news" gnus-summary-followup-to-mail t]
1907        ["Followup via news and yank"
1908         gnus-summary-followup-to-mail-with-original t]
1909        ;;("Draft"
1910        ;;["Send" gnus-summary-send-draft t]
1911        ;;["Send bounced" gnus-resend-bounced-mail t])
1912        ))
1913
1914     (easy-menu-define
1915      gnus-summary-misc-menu gnus-summary-mode-map ""
1916      '("Misc"
1917        ("Mark Read"
1918         ["Mark as read" gnus-summary-mark-as-read-forward t]
1919         ["Mark same subject and select"
1920          gnus-summary-kill-same-subject-and-select t]
1921         ["Mark same subject" gnus-summary-kill-same-subject t]
1922         ["Catchup" gnus-summary-catchup
1923          ;;:help "Mark unread articles in this group as read"
1924          ]
1925         ["Catchup all" gnus-summary-catchup-all t]
1926         ["Catchup to here" gnus-summary-catchup-to-here t]
1927         ["Catchup region" gnus-summary-mark-region-as-read t]
1928         ["Mark excluded" gnus-summary-limit-mark-excluded-as-read t])
1929        ("Mark Various"
1930         ["Tick" gnus-summary-tick-article-forward t]
1931         ["Mark as dormant" gnus-summary-mark-as-dormant t]
1932         ["Remove marks" gnus-summary-clear-mark-forward t]
1933         ["Set expirable mark" gnus-summary-mark-as-expirable t]
1934         ["Set bookmark" gnus-summary-set-bookmark t]
1935         ["Remove bookmark" gnus-summary-remove-bookmark t])
1936        ("Mark Limit"
1937         ["Marks..." gnus-summary-limit-to-marks t]
1938         ["Subject..." gnus-summary-limit-to-subject t]
1939         ["Author..." gnus-summary-limit-to-author t]
1940         ["Age..." gnus-summary-limit-to-age t]
1941         ["Extra..." gnus-summary-limit-to-extra t]
1942         ["Score" gnus-summary-limit-to-score t]
1943         ["Unread" gnus-summary-limit-to-unread t]
1944         ["Non-dormant" gnus-summary-limit-exclude-dormant t]
1945         ["Articles" gnus-summary-limit-to-articles t]
1946         ["Pop limit" gnus-summary-pop-limit t]
1947         ["Show dormant" gnus-summary-limit-include-dormant t]
1948         ["Hide childless dormant"
1949          gnus-summary-limit-exclude-childless-dormant t]
1950         ;;["Hide thread" gnus-summary-limit-exclude-thread t]
1951         ["Hide marked" gnus-summary-limit-exclude-marks t]
1952         ["Show expunged" gnus-summary-show-all-expunged t])
1953        ("Process Mark"
1954         ["Set mark" gnus-summary-mark-as-processable t]
1955         ["Remove mark" gnus-summary-unmark-as-processable t]
1956         ["Remove all marks" gnus-summary-unmark-all-processable t]
1957         ["Mark above" gnus-uu-mark-over t]
1958         ["Mark series" gnus-uu-mark-series t]
1959         ["Mark region" gnus-uu-mark-region t]
1960         ["Unmark region" gnus-uu-unmark-region t]
1961         ["Mark by regexp..." gnus-uu-mark-by-regexp t]
1962         ["Unmark by regexp..." gnus-uu-unmark-by-regexp t]
1963         ["Mark all" gnus-uu-mark-all t]
1964         ["Mark buffer" gnus-uu-mark-buffer t]
1965         ["Mark sparse" gnus-uu-mark-sparse t]
1966         ["Mark thread" gnus-uu-mark-thread t]
1967         ["Unmark thread" gnus-uu-unmark-thread t]
1968         ("Process Mark Sets"
1969          ["Kill" gnus-summary-kill-process-mark t]
1970          ["Yank" gnus-summary-yank-process-mark
1971           gnus-newsgroup-process-stack]
1972          ["Save" gnus-summary-save-process-mark t]))
1973        ("Scroll article"
1974         ["Page forward" gnus-summary-next-page
1975          ;;:help "Show next page of article"
1976          ]
1977         ["Page backward" gnus-summary-prev-page
1978          ;;:help "Show previous page of article"
1979          ]
1980         ["Line forward" gnus-summary-scroll-up t])
1981        ("Move"
1982         ["Next unread article" gnus-summary-next-unread-article t]
1983         ["Previous unread article" gnus-summary-prev-unread-article t]
1984         ["Next article" gnus-summary-next-article t]
1985         ["Previous article" gnus-summary-prev-article t]
1986         ["Next unread subject" gnus-summary-next-unread-subject t]
1987         ["Previous unread subject" gnus-summary-prev-unread-subject t]
1988         ["Next article same subject" gnus-summary-next-same-subject t]
1989         ["Previous article same subject" gnus-summary-prev-same-subject t]
1990         ["First unread article" gnus-summary-first-unread-article t]
1991         ["Best unread article" gnus-summary-best-unread-article t]
1992         ["Go to subject number..." gnus-summary-goto-subject t]
1993         ["Go to article number..." gnus-summary-goto-article t]
1994         ["Go to the last article" gnus-summary-goto-last-article t]
1995         ["Pop article off history" gnus-summary-pop-article t])
1996        ("Sort"
1997         ["Sort by number" gnus-summary-sort-by-number t]
1998         ["Sort by author" gnus-summary-sort-by-author t]
1999         ["Sort by subject" gnus-summary-sort-by-subject t]
2000         ["Sort by date" gnus-summary-sort-by-date t]
2001         ["Sort by score" gnus-summary-sort-by-score t]
2002         ["Sort by lines" gnus-summary-sort-by-lines t]
2003         ["Sort by characters" gnus-summary-sort-by-chars t])
2004        ("Help"
2005         ["Fetch group FAQ" gnus-summary-fetch-faq t]
2006         ["Describe group" gnus-summary-describe-group t]
2007         ["Read manual" gnus-info-find-node t])
2008        ("Modes"
2009         ["Pick and read" gnus-pick-mode t]
2010         ["Binary" gnus-binary-mode t])
2011        ("Regeneration"
2012         ["Regenerate" gnus-summary-prepare t]
2013         ["Insert cached articles" gnus-summary-insert-cached-articles t]
2014         ["Toggle threading" gnus-summary-toggle-threads t])
2015        ["Filter articles..." gnus-summary-execute-command t]
2016        ["Run command on subjects..." gnus-summary-universal-argument t]
2017        ["Search articles forward..." gnus-summary-search-article-forward t]
2018        ["Search articles backward..." gnus-summary-search-article-backward t]
2019        ["Toggle line truncation" gnus-summary-toggle-truncation t]
2020        ["Expand window" gnus-summary-expand-window t]
2021        ["Expire expirable articles" gnus-summary-expire-articles
2022         (gnus-check-backend-function
2023          'request-expire-articles gnus-newsgroup-name)]
2024        ["Edit local kill file" gnus-summary-edit-local-kill t]
2025        ["Edit main kill file" gnus-summary-edit-global-kill t]
2026        ["Edit group parameters" gnus-summary-edit-parameters t]
2027        ["Customize group parameters" gnus-summary-customize-parameters t]
2028        ["Send a bug report" gnus-bug t]
2029        ("Exit"
2030         ["Catchup and exit" gnus-summary-catchup-and-exit
2031          ;;:help "Mark unread articles in this group as read, then exit"
2032          ]
2033         ["Catchup all and exit" gnus-summary-catchup-all-and-exit t]
2034         ["Catchup and goto next" gnus-summary-catchup-and-goto-next-group t]
2035         ["Exit group" gnus-summary-exit
2036          ;;:help "Exit current group, return to group selection mode"
2037          ]
2038         ["Exit group without updating" gnus-summary-exit-no-update t]
2039         ["Exit and goto next group" gnus-summary-next-group t]
2040         ["Exit and goto prev group" gnus-summary-prev-group t]
2041         ["Reselect group" gnus-summary-reselect-current-group t]
2042         ["Rescan group" gnus-summary-rescan-group t]
2043         ["Update dribble" gnus-summary-save-newsrc t])))
2044
2045     (gnus-run-hooks 'gnus-summary-menu-hook)))
2046
2047 (defvar gnus-summary-tool-bar-map nil)
2048
2049 ;; Emacs 21 tool bar.  Should be no-op otherwise.
2050 (defun gnus-summary-make-tool-bar ()
2051   (if (and (fboundp 'tool-bar-add-item-from-menu)
2052            (default-value 'tool-bar-mode)
2053            (not gnus-summary-tool-bar-map))
2054       (setq gnus-summary-tool-bar-map
2055             (let ((tool-bar-map (make-sparse-keymap)))
2056               (tool-bar-add-item-from-menu
2057                'gnus-summary-prev-unread "prev-ur" gnus-summary-mode-map)
2058               (tool-bar-add-item-from-menu
2059                'gnus-summary-next-unread "next-ur" gnus-summary-mode-map)
2060               (tool-bar-add-item-from-menu
2061                'gnus-summary-post-news "post" gnus-summary-mode-map)
2062               (tool-bar-add-item-from-menu
2063                'gnus-summary-followup-with-original "fuwo" gnus-summary-mode-map)
2064               (tool-bar-add-item-from-menu
2065                'gnus-summary-followup "followup" gnus-summary-mode-map)
2066               (tool-bar-add-item-from-menu
2067                'gnus-summary-reply-with-original "reply-wo" gnus-summary-mode-map)
2068               (tool-bar-add-item-from-menu
2069                'gnus-summary-reply "reply" gnus-summary-mode-map)
2070               (tool-bar-add-item-from-menu
2071                'gnus-summary-caesar-message "rot13" gnus-summary-mode-map)
2072               (tool-bar-add-item-from-menu
2073                'gnus-uu-decode-uu "uu-decode" gnus-summary-mode-map)
2074               (tool-bar-add-item-from-menu
2075                'gnus-summary-save-article-file "save-aif" gnus-summary-mode-map)
2076               (tool-bar-add-item-from-menu
2077                'gnus-summary-save-article "save-art" gnus-summary-mode-map)
2078               (tool-bar-add-item-from-menu
2079                'gnus-uu-post-news "uu-post" gnus-summary-mode-map)
2080               (tool-bar-add-item-from-menu
2081                'gnus-summary-catchup "catchup" gnus-summary-mode-map)
2082               (tool-bar-add-item-from-menu
2083                'gnus-summary-catchup-and-exit "cu-exit" gnus-summary-mode-map)
2084               (tool-bar-add-item-from-menu
2085                'gnus-summary-exit "exit-summ" gnus-summary-mode-map)
2086               tool-bar-map)))
2087   (if gnus-summary-tool-bar-map
2088       (set (make-local-variable 'tool-bar-map) gnus-summary-tool-bar-map)))
2089
2090 (defun gnus-score-set-default (var value)
2091   "A version of set that updates the GNU Emacs menu-bar."
2092   (set var value)
2093   ;; It is the message that forces the active status to be updated.
2094   (message ""))
2095
2096 (defun gnus-make-score-map (type)
2097   "Make a summary score map of type TYPE."
2098   (if t
2099       nil
2100     (let ((headers '(("author" "from" string)
2101                      ("subject" "subject" string)
2102                      ("article body" "body" string)
2103                      ("article head" "head" string)
2104                      ("xref" "xref" string)
2105                      ("extra header" "extra" string)
2106                      ("lines" "lines" number)
2107                      ("followups to author" "followup" string)))
2108           (types '((number ("less than" <)
2109                            ("greater than" >)
2110                            ("equal" =))
2111                    (string ("substring" s)
2112                            ("exact string" e)
2113                            ("fuzzy string" f)
2114                            ("regexp" r))))
2115           (perms '(("temporary" (current-time-string))
2116                    ("permanent" nil)
2117                    ("immediate" now)))
2118           header)
2119       (list
2120        (apply
2121         'nconc
2122         (list
2123          (if (eq type 'lower)
2124              "Lower score"
2125            "Increase score"))
2126         (let (outh)
2127           (while headers
2128             (setq header (car headers))
2129             (setq outh
2130                   (cons
2131                    (apply
2132                     'nconc
2133                     (list (car header))
2134                     (let ((ts (cdr (assoc (nth 2 header) types)))
2135                           outt)
2136                       (while ts
2137                         (setq outt
2138                               (cons
2139                                (apply
2140                                 'nconc
2141                                 (list (caar ts))
2142                                 (let ((ps perms)
2143                                       outp)
2144                                   (while ps
2145                                     (setq outp
2146                                           (cons
2147                                            (vector
2148                                             (caar ps)
2149                                             (list
2150                                              'gnus-summary-score-entry
2151                                              (nth 1 header)
2152                                              (if (or (string= (nth 1 header)
2153                                                               "head")
2154                                                      (string= (nth 1 header)
2155                                                               "body"))
2156                                                  ""
2157                                                (list 'gnus-summary-header
2158                                                      (nth 1 header)))
2159                                              (list 'quote (nth 1 (car ts)))
2160                                              (list 'gnus-score-delta-default
2161                                                    nil)
2162                                              (nth 1 (car ps))
2163                                              t)
2164                                             t)
2165                                            outp))
2166                                     (setq ps (cdr ps)))
2167                                   (list (nreverse outp))))
2168                                outt))
2169                         (setq ts (cdr ts)))
2170                       (list (nreverse outt))))
2171                    outh))
2172             (setq headers (cdr headers)))
2173           (list (nreverse outh))))))))
2174
2175 \f
2176
2177 (defun gnus-summary-mode (&optional group)
2178   "Major mode for reading articles.
2179
2180 All normal editing commands are switched off.
2181 \\<gnus-summary-mode-map>
2182 Each line in this buffer represents one article.  To read an
2183 article, you can, for instance, type `\\[gnus-summary-next-page]'.  To move forwards
2184 and backwards while displaying articles, type `\\[gnus-summary-next-unread-article]' and `\\[gnus-summary-prev-unread-article]',
2185 respectively.
2186
2187 You can also post articles and send mail from this buffer.  To
2188 follow up an article, type `\\[gnus-summary-followup]'.  To mail a reply to the author
2189 of an article, type `\\[gnus-summary-reply]'.
2190
2191 There are approx. one gazillion commands you can execute in this
2192 buffer; read the info pages for more information (`\\[gnus-info-find-node]').
2193
2194 The following commands are available:
2195
2196 \\{gnus-summary-mode-map}"
2197   (interactive)
2198   (kill-all-local-variables)
2199   (when (gnus-visual-p 'summary-menu 'menu)
2200     (gnus-summary-make-menu-bar)
2201     (gnus-summary-make-tool-bar))
2202   (gnus-summary-make-local-variables)
2203   (let ((gnus-summary-local-variables gnus-newsgroup-variables))
2204     (gnus-summary-make-local-variables))
2205   (gnus-make-thread-indent-array)
2206   (gnus-simplify-mode-line)
2207   (setq major-mode 'gnus-summary-mode)
2208   (setq mode-name "Summary")
2209   (make-local-variable 'minor-mode-alist)
2210   (use-local-map gnus-summary-mode-map)
2211   (buffer-disable-undo)
2212   (setq buffer-read-only t)             ;Disable modification
2213   (setq truncate-lines t)
2214   (setq selective-display t)
2215   (setq selective-display-ellipses t)   ;Display `...'
2216   (gnus-summary-set-display-table)
2217   (gnus-set-default-directory)
2218   (setq gnus-newsgroup-name group)
2219   (make-local-variable 'gnus-summary-line-format)
2220   (make-local-variable 'gnus-summary-line-format-spec)
2221   (make-local-variable 'gnus-summary-dummy-line-format)
2222   (make-local-variable 'gnus-summary-dummy-line-format-spec)
2223   (make-local-variable 'gnus-summary-mark-positions)
2224   (make-local-hook 'pre-command-hook)
2225   (add-hook 'pre-command-hook 'gnus-set-global-variables nil t)
2226   (gnus-run-hooks 'gnus-summary-mode-hook)
2227   (mm-enable-multibyte-mule4)
2228   (gnus-update-format-specifications nil 'summary 'summary-mode 'summary-dummy)
2229   (gnus-update-summary-mark-positions))
2230
2231 (defun gnus-summary-make-local-variables ()
2232   "Make all the local summary buffer variables."
2233   (let (global)
2234     (dolist (local gnus-summary-local-variables)
2235       (if (consp local)
2236           (progn
2237             (if (eq (cdr local) 'global)
2238                 ;; Copy the global value of the variable.
2239                 (setq global (symbol-value (car local)))
2240               ;; Use the value from the list.
2241               (setq global (eval (cdr local))))
2242             (set (make-local-variable (car local)) global))
2243         ;; Simple nil-valued local variable.
2244         (set (make-local-variable local) nil)))))
2245
2246 (defun gnus-summary-clear-local-variables ()
2247   (let ((locals gnus-summary-local-variables))
2248     (while locals
2249       (if (consp (car locals))
2250           (and (vectorp (caar locals))
2251                (set (caar locals) nil))
2252         (and (vectorp (car locals))
2253              (set (car locals) nil)))
2254       (setq locals (cdr locals)))))
2255
2256 ;; Summary data functions.
2257
2258 (defmacro gnus-data-number (data)
2259   `(car ,data))
2260
2261 (defmacro gnus-data-set-number (data number)
2262   `(setcar ,data ,number))
2263
2264 (defmacro gnus-data-mark (data)
2265   `(nth 1 ,data))
2266
2267 (defmacro gnus-data-set-mark (data mark)
2268   `(setcar (nthcdr 1 ,data) ,mark))
2269
2270 (defmacro gnus-data-pos (data)
2271   `(nth 2 ,data))
2272
2273 (defmacro gnus-data-set-pos (data pos)
2274   `(setcar (nthcdr 2 ,data) ,pos))
2275
2276 (defmacro gnus-data-header (data)
2277   `(nth 3 ,data))
2278
2279 (defmacro gnus-data-set-header (data header)
2280   `(setf (nth 3 ,data) ,header))
2281
2282 (defmacro gnus-data-level (data)
2283   `(nth 4 ,data))
2284
2285 (defmacro gnus-data-unread-p (data)
2286   `(= (nth 1 ,data) gnus-unread-mark))
2287
2288 (defmacro gnus-data-read-p (data)
2289   `(/= (nth 1 ,data) gnus-unread-mark))
2290
2291 (defmacro gnus-data-pseudo-p (data)
2292   `(consp (nth 3 ,data)))
2293
2294 (defmacro gnus-data-find (number)
2295   `(assq ,number gnus-newsgroup-data))
2296
2297 (defmacro gnus-data-find-list (number &optional data)
2298   `(let ((bdata ,(or data 'gnus-newsgroup-data)))
2299      (memq (assq ,number bdata)
2300            bdata)))
2301
2302 (defmacro gnus-data-make (number mark pos header level)
2303   `(list ,number ,mark ,pos ,header ,level))
2304
2305 (defun gnus-data-enter (after-article number mark pos header level offset)
2306   (let ((data (gnus-data-find-list after-article)))
2307     (unless data
2308       (error "No such article: %d" after-article))
2309     (setcdr data (cons (gnus-data-make number mark pos header level)
2310                        (cdr data)))
2311     (setq gnus-newsgroup-data-reverse nil)
2312     (gnus-data-update-list (cddr data) offset)))
2313
2314 (defun gnus-data-enter-list (after-article list &optional offset)
2315   (when list
2316     (let ((data (and after-article (gnus-data-find-list after-article)))
2317           (ilist list))
2318       (if (not (or data
2319                    after-article))
2320           (let ((odata gnus-newsgroup-data))
2321             (setq gnus-newsgroup-data (nconc list gnus-newsgroup-data))
2322             (when offset
2323               (gnus-data-update-list odata offset)))
2324         ;; Find the last element in the list to be spliced into the main
2325         ;; list.
2326         (while (cdr list)
2327           (setq list (cdr list)))
2328         (if (not data)
2329             (progn
2330               (setcdr list gnus-newsgroup-data)
2331               (setq gnus-newsgroup-data ilist)
2332               (when offset
2333                 (gnus-data-update-list (cdr list) offset)))
2334           (setcdr list (cdr data))
2335           (setcdr data ilist)
2336           (when offset
2337             (gnus-data-update-list (cdr list) offset))))
2338       (setq gnus-newsgroup-data-reverse nil))))
2339
2340 (defun gnus-data-remove (article &optional offset)
2341   (let ((data gnus-newsgroup-data))
2342     (if (= (gnus-data-number (car data)) article)
2343         (progn
2344           (setq gnus-newsgroup-data (cdr gnus-newsgroup-data)
2345                 gnus-newsgroup-data-reverse nil)
2346           (when offset
2347             (gnus-data-update-list gnus-newsgroup-data offset)))
2348       (while (cdr data)
2349         (when (= (gnus-data-number (cadr data)) article)
2350           (setcdr data (cddr data))
2351           (when offset
2352             (gnus-data-update-list (cdr data) offset))
2353           (setq data nil
2354                 gnus-newsgroup-data-reverse nil))
2355         (setq data (cdr data))))))
2356
2357 (defmacro gnus-data-list (backward)
2358   `(if ,backward
2359        (or gnus-newsgroup-data-reverse
2360            (setq gnus-newsgroup-data-reverse
2361                  (reverse gnus-newsgroup-data)))
2362      gnus-newsgroup-data))
2363
2364 (defun gnus-data-update-list (data offset)
2365   "Add OFFSET to the POS of all data entries in DATA."
2366   (setq gnus-newsgroup-data-reverse nil)
2367   (while data
2368     (setcar (nthcdr 2 (car data)) (+ offset (nth 2 (car data))))
2369     (setq data (cdr data))))
2370
2371 (defun gnus-summary-article-pseudo-p (article)
2372   "Say whether this article is a pseudo article or not."
2373   (not (vectorp (gnus-data-header (gnus-data-find article)))))
2374
2375 (defmacro gnus-summary-article-sparse-p (article)
2376   "Say whether this article is a sparse article or not."
2377   `(memq ,article gnus-newsgroup-sparse))
2378
2379 (defmacro gnus-summary-article-ancient-p (article)
2380   "Say whether this article is a sparse article or not."
2381   `(memq ,article gnus-newsgroup-ancient))
2382
2383 (defun gnus-article-parent-p (number)
2384   "Say whether this article is a parent or not."
2385   (let ((data (gnus-data-find-list number)))
2386     (and (cdr data)                     ; There has to be an article after...
2387          (< (gnus-data-level (car data)) ; And it has to have a higher level.
2388             (gnus-data-level (nth 1 data))))))
2389
2390 (defun gnus-article-children (number)
2391   "Return a list of all children to NUMBER."
2392   (let* ((data (gnus-data-find-list number))
2393          (level (gnus-data-level (car data)))
2394          children)
2395     (setq data (cdr data))
2396     (while (and data
2397                 (= (gnus-data-level (car data)) (1+ level)))
2398       (push (gnus-data-number (car data)) children)
2399       (setq data (cdr data)))
2400     children))
2401
2402 (defmacro gnus-summary-skip-intangible ()
2403   "If the current article is intangible, then jump to a different article."
2404   '(let ((to (get-text-property (point) 'gnus-intangible)))
2405      (and to (gnus-summary-goto-subject to))))
2406
2407 (defmacro gnus-summary-article-intangible-p ()
2408   "Say whether this article is intangible or not."
2409   '(get-text-property (point) 'gnus-intangible))
2410
2411 (defun gnus-article-read-p (article)
2412   "Say whether ARTICLE is read or not."
2413   (not (or (memq article gnus-newsgroup-marked)
2414            (memq article gnus-newsgroup-unreads)
2415            (memq article gnus-newsgroup-unselected)
2416            (memq article gnus-newsgroup-dormant))))
2417
2418 ;; Some summary mode macros.
2419
2420 (defmacro gnus-summary-article-number ()
2421   "The article number of the article on the current line.
2422 If there isn's an article number here, then we return the current
2423 article number."
2424   '(progn
2425      (gnus-summary-skip-intangible)
2426      (or (get-text-property (point) 'gnus-number)
2427          (gnus-summary-last-subject))))
2428
2429 (defmacro gnus-summary-article-header (&optional number)
2430   "Return the header of article NUMBER."
2431   `(gnus-data-header (gnus-data-find
2432                       ,(or number '(gnus-summary-article-number)))))
2433
2434 (defmacro gnus-summary-thread-level (&optional number)
2435   "Return the level of thread that starts with article NUMBER."
2436   `(if (and (eq gnus-summary-make-false-root 'dummy)
2437             (get-text-property (point) 'gnus-intangible))
2438        0
2439      (gnus-data-level (gnus-data-find
2440                        ,(or number '(gnus-summary-article-number))))))
2441
2442 (defmacro gnus-summary-article-mark (&optional number)
2443   "Return the mark of article NUMBER."
2444   `(gnus-data-mark (gnus-data-find
2445                     ,(or number '(gnus-summary-article-number)))))
2446
2447 (defmacro gnus-summary-article-pos (&optional number)
2448   "Return the position of the line of article NUMBER."
2449   `(gnus-data-pos (gnus-data-find
2450                    ,(or number '(gnus-summary-article-number)))))
2451
2452 (defalias 'gnus-summary-subject-string 'gnus-summary-article-subject)
2453 (defmacro gnus-summary-article-subject (&optional number)
2454   "Return current subject string or nil if nothing."
2455   `(let ((headers
2456           ,(if number
2457                `(gnus-data-header (assq ,number gnus-newsgroup-data))
2458              '(gnus-data-header (assq (gnus-summary-article-number)
2459                                       gnus-newsgroup-data)))))
2460      (and headers
2461           (vectorp headers)
2462           (mail-header-subject headers))))
2463
2464 (defmacro gnus-summary-article-score (&optional number)
2465   "Return current article score."
2466   `(or (cdr (assq ,(or number '(gnus-summary-article-number))
2467                   gnus-newsgroup-scored))
2468        gnus-summary-default-score 0))
2469
2470 (defun gnus-summary-article-children (&optional number)
2471   "Return a list of article numbers that are children of article NUMBER."
2472   (let* ((data (gnus-data-find-list (or number (gnus-summary-article-number))))
2473          (level (gnus-data-level (car data)))
2474          l children)
2475     (while (and (setq data (cdr data))
2476                 (> (setq l (gnus-data-level (car data))) level))
2477       (and (= (1+ level) l)
2478            (push (gnus-data-number (car data))
2479                  children)))
2480     (nreverse children)))
2481
2482 (defun gnus-summary-article-parent (&optional number)
2483   "Return the article number of the parent of article NUMBER."
2484   (let* ((data (gnus-data-find-list (or number (gnus-summary-article-number))
2485                                     (gnus-data-list t)))
2486          (level (gnus-data-level (car data))))
2487     (if (zerop level)
2488         ()                              ; This is a root.
2489       ;; We search until we find an article with a level less than
2490       ;; this one.  That function has to be the parent.
2491       (while (and (setq data (cdr data))
2492                   (not (< (gnus-data-level (car data)) level))))
2493       (and data (gnus-data-number (car data))))))
2494
2495 (defun gnus-unread-mark-p (mark)
2496   "Say whether MARK is the unread mark."
2497   (= mark gnus-unread-mark))
2498
2499 (defun gnus-read-mark-p (mark)
2500   "Say whether MARK is one of the marks that mark as read.
2501 This is all marks except unread, ticked, dormant, and expirable."
2502   (not (or (= mark gnus-unread-mark)
2503            (= mark gnus-ticked-mark)
2504            (= mark gnus-dormant-mark)
2505            (= mark gnus-expirable-mark))))
2506
2507 (defmacro gnus-article-mark (number)
2508   "Return the MARK of article NUMBER.
2509 This macro should only be used when computing the mark the \"first\"
2510 time; i.e., when generating the summary lines.  After that,
2511 `gnus-summary-article-mark' should be used to examine the
2512 marks of articles."
2513   `(cond
2514     ((memq ,number gnus-newsgroup-unsendable) gnus-unsendable-mark)
2515     ((memq ,number gnus-newsgroup-undownloaded) gnus-undownloaded-mark)
2516     ((memq ,number gnus-newsgroup-downloadable) gnus-downloadable-mark)
2517     ((memq ,number gnus-newsgroup-unreads) gnus-unread-mark)
2518     ((memq ,number gnus-newsgroup-marked) gnus-ticked-mark)
2519     ((memq ,number gnus-newsgroup-dormant) gnus-dormant-mark)
2520     ((memq ,number gnus-newsgroup-expirable) gnus-expirable-mark)
2521     (t (or (cdr (assq ,number gnus-newsgroup-reads))
2522            gnus-ancient-mark))))
2523
2524 ;; Saving hidden threads.
2525
2526 (put 'gnus-save-hidden-threads 'lisp-indent-function 0)
2527 (put 'gnus-save-hidden-threads 'edebug-form-spec '(body))
2528
2529 (defmacro gnus-save-hidden-threads (&rest forms)
2530   "Save hidden threads, eval FORMS, and restore the hidden threads."
2531   (let ((config (make-symbol "config")))
2532     `(let ((,config (gnus-hidden-threads-configuration)))
2533        (unwind-protect
2534            (save-excursion
2535              ,@forms)
2536          (gnus-restore-hidden-threads-configuration ,config)))))
2537
2538 (defun gnus-data-compute-positions ()
2539   "Compute the positions of all articles."
2540   (setq gnus-newsgroup-data-reverse nil)
2541   (let ((data gnus-newsgroup-data))
2542     (save-excursion
2543       (gnus-save-hidden-threads
2544         (gnus-summary-show-all-threads)
2545         (goto-char (point-min))
2546         (while data
2547           (while (get-text-property (point) 'gnus-intangible)
2548             (forward-line 1))
2549           (gnus-data-set-pos (car data) (+ (point) 3))
2550           (setq data (cdr data))
2551           (forward-line 1))))))
2552
2553 (defun gnus-hidden-threads-configuration ()
2554   "Return the current hidden threads configuration."
2555   (save-excursion
2556     (let (config)
2557       (goto-char (point-min))
2558       (while (search-forward "\r" nil t)
2559         (push (1- (point)) config))
2560       config)))
2561
2562 (defun gnus-restore-hidden-threads-configuration (config)
2563   "Restore hidden threads configuration from CONFIG."
2564   (save-excursion
2565     (let (point buffer-read-only)
2566       (while (setq point (pop config))
2567         (when (and (< point (point-max))
2568                    (goto-char point)
2569                    (eq (char-after) ?\n))
2570           (subst-char-in-region point (1+ point) ?\n ?\r))))))
2571
2572 ;; Various summary mode internalish functions.
2573
2574 (defun gnus-mouse-pick-article (e)
2575   (interactive "e")
2576   (mouse-set-point e)
2577   (gnus-summary-next-page nil t))
2578
2579 (defun gnus-summary-set-display-table ()
2580   "Change the display table.
2581 Odd characters have a tendency to mess
2582 up nicely formatted displays - we make all possible glyphs
2583 display only a single character."
2584
2585   ;; We start from the standard display table, if any.
2586   (let ((table (or (copy-sequence standard-display-table)
2587                    (make-display-table)))
2588         (i 32))
2589     ;; Nix out all the control chars...
2590     (while (>= (setq i (1- i)) 0)
2591       (aset table i [??]))
2592     ;; ... but not newline and cr, of course.  (cr is necessary for the
2593     ;; selective display).
2594     (aset table ?\n nil)
2595     (aset table ?\r nil)
2596     ;; We keep TAB as well.
2597     (aset table ?\t nil)
2598     ;; We nix out any glyphs over 126 that are not set already.
2599     (let ((i 256))
2600       (while (>= (setq i (1- i)) 127)
2601         ;; Only modify if the entry is nil.
2602         (unless (aref table i)
2603           (aset table i [??]))))
2604     (setq buffer-display-table table)))
2605
2606 (defun gnus-summary-setup-buffer (group)
2607   "Initialize summary buffer."
2608   (let ((buffer (concat "*Summary " group "*")))
2609     (if (get-buffer buffer)
2610         (progn
2611           (set-buffer buffer)
2612           (setq gnus-summary-buffer (current-buffer))
2613           (not gnus-newsgroup-prepared))
2614       ;; Fix by Sudish Joseph <joseph@cis.ohio-state.edu>
2615       (setq gnus-summary-buffer (set-buffer (gnus-get-buffer-create buffer)))
2616       (gnus-summary-mode group)
2617       (when gnus-carpal
2618         (gnus-carpal-setup-buffer 'summary))
2619       (unless gnus-single-article-buffer
2620         (make-local-variable 'gnus-article-buffer)
2621         (make-local-variable 'gnus-article-current)
2622         (make-local-variable 'gnus-original-article-buffer))
2623       (setq gnus-newsgroup-name group)
2624       t)))
2625
2626 (defun gnus-set-global-variables ()
2627   "Set the global equivalents of the buffer-local variables.
2628 They are set to the latest values they had.  These reflect the summary
2629 buffer that was in action when the last article was fetched."
2630   (when (eq major-mode 'gnus-summary-mode)
2631     (setq gnus-summary-buffer (current-buffer))
2632     (let ((name gnus-newsgroup-name)
2633           (marked gnus-newsgroup-marked)
2634           (unread gnus-newsgroup-unreads)
2635           (headers gnus-current-headers)
2636           (data gnus-newsgroup-data)
2637           (summary gnus-summary-buffer)
2638           (article-buffer gnus-article-buffer)
2639           (original gnus-original-article-buffer)
2640           (gac gnus-article-current)
2641           (reffed gnus-reffed-article-number)
2642           (score-file gnus-current-score-file)
2643           (default-charset gnus-newsgroup-charset)
2644           vlist)
2645       (let ((locals gnus-newsgroup-variables))
2646         (while locals
2647           (if (consp (car locals))
2648               (push (eval (caar locals)) vlist)
2649             (push (eval (car locals)) vlist))
2650           (setq locals (cdr locals)))
2651         (setq vlist (nreverse vlist)))
2652       (save-excursion
2653         (set-buffer gnus-group-buffer)
2654         (setq gnus-newsgroup-name name
2655               gnus-newsgroup-marked marked
2656               gnus-newsgroup-unreads unread
2657               gnus-current-headers headers
2658               gnus-newsgroup-data data
2659               gnus-article-current gac
2660               gnus-summary-buffer summary
2661               gnus-article-buffer article-buffer
2662               gnus-original-article-buffer original
2663               gnus-reffed-article-number reffed
2664               gnus-current-score-file score-file
2665               gnus-newsgroup-charset default-charset)
2666         (let ((locals gnus-newsgroup-variables))
2667           (while locals
2668             (if (consp (car locals))
2669                 (set (caar locals) (pop vlist))
2670               (set (car locals) (pop vlist)))
2671             (setq locals (cdr locals))))
2672         ;; The article buffer also has local variables.
2673         (when (gnus-buffer-live-p gnus-article-buffer)
2674           (set-buffer gnus-article-buffer)
2675           (setq gnus-summary-buffer summary))))))
2676
2677 (defun gnus-summary-article-unread-p (article)
2678   "Say whether ARTICLE is unread or not."
2679   (memq article gnus-newsgroup-unreads))
2680
2681 (defun gnus-summary-first-article-p (&optional article)
2682   "Return whether ARTICLE is the first article in the buffer."
2683   (if (not (setq article (or article (gnus-summary-article-number))))
2684       nil
2685     (eq article (caar gnus-newsgroup-data))))
2686
2687 (defun gnus-summary-last-article-p (&optional article)
2688   "Return whether ARTICLE is the last article in the buffer."
2689   (if (not (setq article (or article (gnus-summary-article-number))))
2690       ;; All non-existent numbers are the last article.  :-)
2691       t
2692     (not (cdr (gnus-data-find-list article)))))
2693
2694 (defun gnus-make-thread-indent-array ()
2695   (let ((n 200))
2696     (unless (and gnus-thread-indent-array
2697                  (= gnus-thread-indent-level gnus-thread-indent-array-level))
2698       (setq gnus-thread-indent-array (make-vector 201 "")
2699             gnus-thread-indent-array-level gnus-thread-indent-level)
2700       (while (>= n 0)
2701         (aset gnus-thread-indent-array n
2702               (make-string (* n gnus-thread-indent-level) ? ))
2703         (setq n (1- n))))))
2704
2705 (defun gnus-update-summary-mark-positions ()
2706   "Compute where the summary marks are to go."
2707   (save-excursion
2708     (when (gnus-buffer-exists-p gnus-summary-buffer)
2709       (set-buffer gnus-summary-buffer))
2710     (let ((gnus-replied-mark 129)
2711           (gnus-score-below-mark 130)
2712           (gnus-score-over-mark 130)
2713           (gnus-download-mark 131)
2714           (spec gnus-summary-line-format-spec)
2715           gnus-visual pos)
2716       (save-excursion
2717         (gnus-set-work-buffer)
2718         (let ((gnus-summary-line-format-spec spec)
2719               (gnus-newsgroup-downloadable '((0 . t))))
2720           (gnus-summary-insert-line
2721            [0 "" "" "" "" "" 0 0 "" nil]  0 nil 128 t nil "" nil 1)
2722           (goto-char (point-min))
2723           (setq pos (list (cons 'unread (and (search-forward "\200" nil t)
2724                                              (- (point) 2)))))
2725           (goto-char (point-min))
2726           (push (cons 'replied (and (search-forward "\201" nil t)
2727                                     (- (point) 2)))
2728                 pos)
2729           (goto-char (point-min))
2730           (push (cons 'score (and (search-forward "\202" nil t) (- (point) 2)))
2731                 pos)
2732           (goto-char (point-min))
2733           (push (cons 'download
2734                       (and (search-forward "\203" nil t) (- (point) 2)))
2735                 pos)))
2736       (setq gnus-summary-mark-positions pos))))
2737
2738 (defun gnus-summary-insert-dummy-line (gnus-tmp-subject gnus-tmp-number)
2739   "Insert a dummy root in the summary buffer."
2740   (beginning-of-line)
2741   (gnus-add-text-properties
2742    (point) (progn (eval gnus-summary-dummy-line-format-spec) (point))
2743    (list 'gnus-number gnus-tmp-number 'gnus-intangible gnus-tmp-number)))
2744
2745 (defun gnus-summary-from-or-to-or-newsgroups (header)
2746   (let ((to (cdr (assq 'To (mail-header-extra header))))
2747         (newsgroups (cdr (assq 'Newsgroups (mail-header-extra header))))
2748         (mail-parse-charset gnus-newsgroup-charset)
2749         (mail-parse-ignored-charsets
2750          (save-excursion (set-buffer gnus-summary-buffer)
2751                          gnus-newsgroup-ignored-charsets)))
2752     (cond
2753      ((and to
2754            gnus-ignored-from-addresses
2755            (string-match gnus-ignored-from-addresses
2756                          (mail-header-from header)))
2757       (concat "-> "
2758               (or (car (funcall gnus-extract-address-components
2759                                 (funcall
2760                                  gnus-decode-encoded-word-function to)))
2761                   (funcall gnus-decode-encoded-word-function to))))
2762      ((and newsgroups
2763            gnus-ignored-from-addresses
2764            (string-match gnus-ignored-from-addresses
2765                          (mail-header-from header)))
2766       (concat "=> " newsgroups))
2767      (t
2768       (or (car (funcall gnus-extract-address-components
2769                         (mail-header-from header)))
2770           (mail-header-from header))))))
2771
2772 (defun gnus-summary-insert-line (gnus-tmp-header
2773                                  gnus-tmp-level gnus-tmp-current
2774                                  gnus-tmp-unread gnus-tmp-replied
2775                                  gnus-tmp-expirable gnus-tmp-subject-or-nil
2776                                  &optional gnus-tmp-dummy gnus-tmp-score
2777                                  gnus-tmp-process)
2778   (let* ((gnus-tmp-indentation (aref gnus-thread-indent-array gnus-tmp-level))
2779          (gnus-tmp-lines (mail-header-lines gnus-tmp-header))
2780          (gnus-tmp-score (or gnus-tmp-score gnus-summary-default-score 0))
2781          (gnus-tmp-score-char
2782           (if (or (null gnus-summary-default-score)
2783                   (<= (abs (- gnus-tmp-score gnus-summary-default-score))
2784                       gnus-summary-zcore-fuzz))
2785               ?  ;Whitespace
2786             (if (< gnus-tmp-score gnus-summary-default-score)
2787                 gnus-score-below-mark gnus-score-over-mark)))
2788          (gnus-tmp-replied
2789           (cond (gnus-tmp-process gnus-process-mark)
2790                 ((memq gnus-tmp-current gnus-newsgroup-cached)
2791                  gnus-cached-mark)
2792                 (gnus-tmp-replied gnus-replied-mark)
2793                 ((memq gnus-tmp-current gnus-newsgroup-saved)
2794                  gnus-saved-mark)
2795                 (t gnus-unread-mark)))
2796          (gnus-tmp-from (mail-header-from gnus-tmp-header))
2797          (gnus-tmp-name
2798           (cond
2799            ((string-match "<[^>]+> *$" gnus-tmp-from)
2800             (let ((beg (match-beginning 0)))
2801               (or (and (string-match "^\".+\"" gnus-tmp-from)
2802                        (substring gnus-tmp-from 1 (1- (match-end 0))))
2803                   (substring gnus-tmp-from 0 beg))))
2804            ((string-match "(.+)" gnus-tmp-from)
2805             (substring gnus-tmp-from
2806                        (1+ (match-beginning 0)) (1- (match-end 0))))
2807            (t gnus-tmp-from)))
2808          (gnus-tmp-subject (mail-header-subject gnus-tmp-header))
2809          (gnus-tmp-number (mail-header-number gnus-tmp-header))
2810          (gnus-tmp-opening-bracket (if gnus-tmp-dummy ?\< ?\[))
2811          (gnus-tmp-closing-bracket (if gnus-tmp-dummy ?\> ?\]))
2812          (buffer-read-only nil))
2813     (when (string= gnus-tmp-name "")
2814       (setq gnus-tmp-name gnus-tmp-from))
2815     (unless (numberp gnus-tmp-lines)
2816       (setq gnus-tmp-lines 0))
2817     (gnus-put-text-property
2818      (point)
2819      (progn (eval gnus-summary-line-format-spec) (point))
2820      'gnus-number gnus-tmp-number)
2821     (when (gnus-visual-p 'summary-highlight 'highlight)
2822       (forward-line -1)
2823       (gnus-run-hooks 'gnus-summary-update-hook)
2824       (forward-line 1))))
2825
2826 (defun gnus-summary-update-line (&optional dont-update)
2827   "Update summary line after change."
2828   (when (and gnus-summary-default-score
2829              (not gnus-summary-inhibit-highlight))
2830     (let* ((gnus-summary-inhibit-highlight t) ; Prevent recursion.
2831            (article (gnus-summary-article-number))
2832            (score (gnus-summary-article-score article)))
2833       (unless dont-update
2834         (if (and gnus-summary-mark-below
2835                  (< (gnus-summary-article-score)
2836                     gnus-summary-mark-below))
2837             ;; This article has a low score, so we mark it as read.
2838             (when (memq article gnus-newsgroup-unreads)
2839               (gnus-summary-mark-article-as-read gnus-low-score-mark))
2840           (when (eq (gnus-summary-article-mark) gnus-low-score-mark)
2841             ;; This article was previously marked as read on account
2842             ;; of a low score, but now it has risen, so we mark it as
2843             ;; unread.
2844             (gnus-summary-mark-article-as-unread gnus-unread-mark)))
2845         (gnus-summary-update-mark
2846          (if (or (null gnus-summary-default-score)
2847                  (<= (abs (- score gnus-summary-default-score))
2848                      gnus-summary-zcore-fuzz))
2849              ?  ;Whitespace
2850            (if (< score gnus-summary-default-score)
2851                gnus-score-below-mark gnus-score-over-mark))
2852          'score))
2853       ;; Do visual highlighting.
2854       (when (gnus-visual-p 'summary-highlight 'highlight)
2855         (gnus-run-hooks 'gnus-summary-update-hook)))))
2856
2857 (defvar gnus-tmp-new-adopts nil)
2858
2859 (defun gnus-summary-number-of-articles-in-thread (thread &optional level char)
2860   "Return the number of articles in THREAD.
2861 This may be 0 in some cases -- if none of the articles in
2862 the thread are to be displayed."
2863   (let* ((number
2864           ;; Fix by Luc Van Eycken <Luc.VanEycken@esat.kuleuven.ac.be>.
2865           (cond
2866            ((not (listp thread))
2867             1)
2868            ((and (consp thread) (cdr thread))
2869             (apply
2870              '+ 1 (mapcar
2871                    'gnus-summary-number-of-articles-in-thread (cdr thread))))
2872            ((null thread)
2873             1)
2874            ((memq (mail-header-number (car thread)) gnus-newsgroup-limit)
2875             1)
2876            (t 0))))
2877     (when (and level (zerop level) gnus-tmp-new-adopts)
2878       (incf number
2879             (apply '+ (mapcar
2880                        'gnus-summary-number-of-articles-in-thread
2881                        gnus-tmp-new-adopts))))
2882     (if char
2883         (if (> number 1) gnus-not-empty-thread-mark
2884           gnus-empty-thread-mark)
2885       number)))
2886
2887 (defun gnus-summary-set-local-parameters (group)
2888   "Go through the local params of GROUP and set all variable specs in that list."
2889   (let ((params (gnus-group-find-parameter group))
2890         elem)
2891     (while params
2892       (setq elem (car params)
2893             params (cdr params))
2894       (and (consp elem)                 ; Has to be a cons.
2895            (consp (cdr elem))           ; The cdr has to be a list.
2896            (symbolp (car elem))         ; Has to be a symbol in there.
2897            (not (memq (car elem) '(quit-config))) ; Ignore quit-config.
2898            (ignore-errors               ; So we set it.
2899              (make-local-variable (car elem))
2900              (set (car elem) (eval (nth 1 elem))))))))
2901
2902 (defun gnus-summary-read-group (group &optional show-all no-article
2903                                       kill-buffer no-display backward
2904                                       select-articles)
2905   "Start reading news in newsgroup GROUP.
2906 If SHOW-ALL is non-nil, already read articles are also listed.
2907 If NO-ARTICLE is non-nil, no article is selected initially.
2908 If NO-DISPLAY, don't generate a summary buffer."
2909   (let (result)
2910     (while (and group
2911                 (null (setq result
2912                             (let ((gnus-auto-select-next nil))
2913                               (or (gnus-summary-read-group-1
2914                                    group show-all no-article
2915                                    kill-buffer no-display
2916                                    select-articles)
2917                                   (setq show-all nil
2918                                         select-articles nil)))))
2919                 (eq gnus-auto-select-next 'quietly))
2920       (set-buffer gnus-group-buffer)
2921       ;; The entry function called above goes to the next
2922       ;; group automatically, so we go two groups back
2923       ;; if we are searching for the previous group.
2924       (when backward
2925         (gnus-group-prev-unread-group 2))
2926       (if (not (equal group (gnus-group-group-name)))
2927           (setq group (gnus-group-group-name))
2928         (setq group nil)))
2929     result))
2930
2931 (defun gnus-summary-read-group-1 (group show-all no-article
2932                                         kill-buffer no-display
2933                                         &optional select-articles)
2934   ;; Killed foreign groups can't be entered.
2935   (when (and (not (gnus-group-native-p group))
2936              (not (gnus-gethash group gnus-newsrc-hashtb)))
2937     (error "Dead non-native groups can't be entered"))
2938   (gnus-message 5 "Retrieving newsgroup: %s..." group)
2939   (let* ((new-group (gnus-summary-setup-buffer group))
2940          (quit-config (gnus-group-quit-config group))
2941          (did-select (and new-group (gnus-select-newsgroup
2942                                      group show-all select-articles))))
2943     (cond
2944      ;; This summary buffer exists already, so we just select it.
2945      ((not new-group)
2946       (gnus-set-global-variables)
2947       (when kill-buffer
2948         (gnus-kill-or-deaden-summary kill-buffer))
2949       (gnus-configure-windows 'summary 'force)
2950       (gnus-set-mode-line 'summary)
2951       (gnus-summary-position-point)
2952       (message "")
2953       t)
2954      ;; We couldn't select this group.
2955      ((null did-select)
2956       (when (and (eq major-mode 'gnus-summary-mode)
2957                  (not (equal (current-buffer) kill-buffer)))
2958         (kill-buffer (current-buffer))
2959         (if (not quit-config)
2960             (progn
2961               ;; Update the info -- marks might need to be removed,
2962               ;; for instance.
2963               (gnus-summary-update-info)
2964               (set-buffer gnus-group-buffer)
2965               (gnus-group-jump-to-group group)
2966               (gnus-group-next-unread-group 1))
2967           (gnus-handle-ephemeral-exit quit-config)))
2968       (gnus-message 3 "Can't select group")
2969       nil)
2970      ;; The user did a `C-g' while prompting for number of articles,
2971      ;; so we exit this group.
2972      ((eq did-select 'quit)
2973       (and (eq major-mode 'gnus-summary-mode)
2974            (not (equal (current-buffer) kill-buffer))
2975            (kill-buffer (current-buffer)))
2976       (when kill-buffer
2977         (gnus-kill-or-deaden-summary kill-buffer))
2978       (if (not quit-config)
2979           (progn
2980             (set-buffer gnus-group-buffer)
2981             (gnus-group-jump-to-group group)
2982             (gnus-group-next-unread-group 1)
2983             (gnus-configure-windows 'group 'force))
2984         (gnus-handle-ephemeral-exit quit-config))
2985       ;; Finally signal the quit.
2986       (signal 'quit nil))
2987      ;; The group was successfully selected.
2988      (t
2989       (gnus-set-global-variables)
2990       ;; Save the active value in effect when the group was entered.
2991       (setq gnus-newsgroup-active
2992             (gnus-copy-sequence
2993              (gnus-active gnus-newsgroup-name)))
2994       ;; You can change the summary buffer in some way with this hook.
2995       (gnus-run-hooks 'gnus-select-group-hook)
2996       ;; Set any local variables in the group parameters.
2997       (gnus-summary-set-local-parameters gnus-newsgroup-name)
2998       (gnus-update-format-specifications
2999        nil 'summary 'summary-mode 'summary-dummy)
3000       (gnus-update-summary-mark-positions)
3001       ;; Do score processing.
3002       (when gnus-use-scoring
3003         (gnus-possibly-score-headers))
3004       ;; Check whether to fill in the gaps in the threads.
3005       (when gnus-build-sparse-threads
3006         (gnus-build-sparse-threads))
3007       ;; Find the initial limit.
3008       (if gnus-show-threads
3009           (if show-all
3010               (let ((gnus-newsgroup-dormant nil))
3011                 (gnus-summary-initial-limit show-all))
3012             (gnus-summary-initial-limit show-all))
3013         ;; When untreaded, all articles are always shown.
3014         (setq gnus-newsgroup-limit
3015               (mapcar
3016                (lambda (header) (mail-header-number header))
3017                gnus-newsgroup-headers)))
3018       ;; Generate the summary buffer.
3019       (unless no-display
3020         (gnus-summary-prepare))
3021       (when gnus-use-trees
3022         (gnus-tree-open group)
3023         (setq gnus-summary-highlight-line-function
3024               'gnus-tree-highlight-article))
3025       ;; If the summary buffer is empty, but there are some low-scored
3026       ;; articles or some excluded dormants, we include these in the
3027       ;; buffer.
3028       (when (and (zerop (buffer-size))
3029                  (not no-display))
3030         (cond (gnus-newsgroup-dormant
3031                (gnus-summary-limit-include-dormant))
3032               ((and gnus-newsgroup-scored show-all)
3033                (gnus-summary-limit-include-expunged t))))
3034       ;; Function `gnus-apply-kill-file' must be called in this hook.
3035       (gnus-run-hooks 'gnus-apply-kill-hook)
3036       (if (and (zerop (buffer-size))
3037                (not no-display))
3038           (progn
3039             ;; This newsgroup is empty.
3040             (gnus-summary-catchup-and-exit nil t)
3041             (gnus-message 6 "No unread news")
3042             (when kill-buffer
3043               (gnus-kill-or-deaden-summary kill-buffer))
3044             ;; Return nil from this function.
3045             nil)
3046         ;; Hide conversation thread subtrees.  We cannot do this in
3047         ;; gnus-summary-prepare-hook since kill processing may not
3048         ;; work with hidden articles.
3049         (and gnus-show-threads
3050              gnus-thread-hide-subtree
3051              (gnus-summary-hide-all-threads))
3052         (when kill-buffer
3053           (gnus-kill-or-deaden-summary kill-buffer))
3054         ;; Show first unread article if requested.
3055         (if (and (not no-article)
3056                  (not no-display)
3057                  gnus-newsgroup-unreads
3058                  gnus-auto-select-first)
3059             (progn
3060               (gnus-configure-windows 'summary)
3061               (cond
3062                ((eq gnus-auto-select-first 'best)
3063                 (gnus-summary-best-unread-article))
3064                ((eq gnus-auto-select-first t)
3065                 (gnus-summary-first-unread-article))
3066                ((gnus-functionp gnus-auto-select-first)
3067                 (funcall gnus-auto-select-first))))
3068           ;; Don't select any articles, just move point to the first
3069           ;; article in the group.
3070           (goto-char (point-min))
3071           (gnus-summary-position-point)
3072           (gnus-configure-windows 'summary 'force)
3073           (gnus-set-mode-line 'summary))
3074         (when (get-buffer-window gnus-group-buffer t)
3075           ;; Gotta use windows, because recenter does weird stuff if
3076           ;; the current buffer ain't the displayed window.
3077           (let ((owin (selected-window)))
3078             (select-window (get-buffer-window gnus-group-buffer t))
3079             (when (gnus-group-goto-group group)
3080               (recenter))
3081             (select-window owin)))
3082         ;; Mark this buffer as "prepared".
3083         (setq gnus-newsgroup-prepared t)
3084         (gnus-run-hooks 'gnus-summary-prepared-hook)
3085         t)))))
3086
3087 (defun gnus-summary-prepare ()
3088   "Generate the summary buffer."
3089   (interactive)
3090   (let ((buffer-read-only nil))
3091     (erase-buffer)
3092     (setq gnus-newsgroup-data nil
3093           gnus-newsgroup-data-reverse nil)
3094     (gnus-run-hooks 'gnus-summary-generate-hook)
3095     ;; Generate the buffer, either with threads or without.
3096     (when gnus-newsgroup-headers
3097       (gnus-summary-prepare-threads
3098        (if gnus-show-threads
3099            (gnus-sort-gathered-threads
3100             (funcall gnus-summary-thread-gathering-function
3101                      (gnus-sort-threads
3102                       (gnus-cut-threads (gnus-make-threads)))))
3103          ;; Unthreaded display.
3104          (gnus-sort-articles gnus-newsgroup-headers))))
3105     (setq gnus-newsgroup-data (nreverse gnus-newsgroup-data))
3106     ;; Call hooks for modifying summary buffer.
3107     (goto-char (point-min))
3108     (gnus-run-hooks 'gnus-summary-prepare-hook)))
3109
3110 (defsubst gnus-general-simplify-subject (subject)
3111   "Simply subject by the same rules as gnus-gather-threads-by-subject."
3112   (setq subject
3113         (cond
3114          ;; Truncate the subject.
3115          (gnus-simplify-subject-functions
3116           (gnus-map-function gnus-simplify-subject-functions subject))
3117          ((numberp gnus-summary-gather-subject-limit)
3118           (setq subject (gnus-simplify-subject-re subject))
3119           (if (> (length subject) gnus-summary-gather-subject-limit)
3120               (substring subject 0 gnus-summary-gather-subject-limit)
3121             subject))
3122          ;; Fuzzily simplify it.
3123          ((eq 'fuzzy gnus-summary-gather-subject-limit)
3124           (gnus-simplify-subject-fuzzy subject))
3125          ;; Just remove the leading "Re:".
3126          (t
3127           (gnus-simplify-subject-re subject))))
3128
3129   (if (and gnus-summary-gather-exclude-subject
3130            (string-match gnus-summary-gather-exclude-subject subject))
3131       nil                               ; This article shouldn't be gathered
3132     subject))
3133
3134 (defun gnus-summary-simplify-subject-query ()
3135   "Query where the respool algorithm would put this article."
3136   (interactive)
3137   (gnus-summary-select-article)
3138   (message (gnus-general-simplify-subject (gnus-summary-article-subject))))
3139
3140 (defun gnus-gather-threads-by-subject (threads)
3141   "Gather threads by looking at Subject headers."
3142   (if (not gnus-summary-make-false-root)
3143       threads
3144     (let ((hashtb (gnus-make-hashtable 1024))
3145           (prev threads)
3146           (result threads)
3147           subject hthread whole-subject)
3148       (while threads
3149         (setq subject (gnus-general-simplify-subject
3150                        (setq whole-subject (mail-header-subject
3151                                             (caar threads)))))
3152         (when subject
3153           (if (setq hthread (gnus-gethash subject hashtb))
3154               (progn
3155                 ;; We enter a dummy root into the thread, if we
3156                 ;; haven't done that already.
3157                 (unless (stringp (caar hthread))
3158                   (setcar hthread (list whole-subject (car hthread))))
3159                 ;; We add this new gathered thread to this gathered
3160                 ;; thread.
3161                 (setcdr (car hthread)
3162                         (nconc (cdar hthread) (list (car threads))))
3163                 ;; Remove it from the list of threads.
3164                 (setcdr prev (cdr threads))
3165                 (setq threads prev))
3166             ;; Enter this thread into the hash table.
3167             (gnus-sethash subject threads hashtb)))
3168         (setq prev threads)
3169         (setq threads (cdr threads)))
3170       result)))
3171
3172 (defun gnus-gather-threads-by-references (threads)
3173   "Gather threads by looking at References headers."
3174   (let ((idhashtb (gnus-make-hashtable 1024))
3175         (thhashtb (gnus-make-hashtable 1024))
3176         (prev threads)
3177         (result threads)
3178         ids references id gthread gid entered ref)
3179     (while threads
3180       (when (setq references (mail-header-references (caar threads)))
3181         (setq id (mail-header-id (caar threads))
3182               ids (gnus-split-references references)
3183               entered nil)
3184         (while (setq ref (pop ids))
3185           (setq ids (delete ref ids))
3186           (if (not (setq gid (gnus-gethash ref idhashtb)))
3187               (progn
3188                 (gnus-sethash ref id idhashtb)
3189                 (gnus-sethash id threads thhashtb))
3190             (setq gthread (gnus-gethash gid thhashtb))
3191             (unless entered
3192               ;; We enter a dummy root into the thread, if we
3193               ;; haven't done that already.
3194               (unless (stringp (caar gthread))
3195                 (setcar gthread (list (mail-header-subject (caar gthread))
3196                                       (car gthread))))
3197               ;; We add this new gathered thread to this gathered
3198               ;; thread.
3199               (setcdr (car gthread)
3200                       (nconc (cdar gthread) (list (car threads)))))
3201             ;; Add it into the thread hash table.
3202             (gnus-sethash id gthread thhashtb)
3203             (setq entered t)
3204             ;; Remove it from the list of threads.
3205             (setcdr prev (cdr threads))
3206             (setq threads prev))))
3207       (setq prev threads)
3208       (setq threads (cdr threads)))
3209     result))
3210
3211 (defun gnus-sort-gathered-threads (threads)
3212   "Sort subtreads inside each gathered thread by `gnus-sort-gathered-threads-function'."
3213   (let ((result threads))
3214     (while threads
3215       (when (stringp (caar threads))
3216         (setcdr (car threads)
3217                 (sort (cdar threads) gnus-sort-gathered-threads-function)))
3218       (setq threads (cdr threads)))
3219     result))
3220
3221 (defun gnus-thread-loop-p (root thread)
3222   "Say whether ROOT is in THREAD."
3223   (let ((stack (list thread))
3224         (infloop 0)
3225         th)
3226     (while (setq thread (pop stack))
3227       (setq th (cdr thread))
3228       (while (and th
3229                   (not (eq (caar th) root)))
3230         (pop th))
3231       (if th
3232           ;; We have found a loop.
3233           (let (ref-dep)
3234             (setcdr thread (delq (car th) (cdr thread)))
3235             (if (boundp (setq ref-dep (intern "none"
3236                                               gnus-newsgroup-dependencies)))
3237                 (setcdr (symbol-value ref-dep)
3238                         (nconc (cdr (symbol-value ref-dep))
3239                                (list (car th))))
3240               (set ref-dep (list nil (car th))))
3241             (setq infloop 1
3242                   stack nil))
3243         ;; Push all the subthreads onto the stack.
3244         (push (cdr thread) stack)))
3245     infloop))
3246
3247 (defun gnus-make-threads ()
3248   "Go through the dependency hashtb and find the roots.  Return all threads."
3249   (let (threads)
3250     (while (catch 'infloop
3251              (mapatoms
3252               (lambda (refs)
3253                 ;; Deal with self-referencing References loops.
3254                 (when (and (car (symbol-value refs))
3255                            (not (zerop
3256                                  (apply
3257                                   '+
3258                                   (mapcar
3259                                    (lambda (thread)
3260                                      (gnus-thread-loop-p
3261                                       (car (symbol-value refs)) thread))
3262                                    (cdr (symbol-value refs)))))))
3263                   (setq threads nil)
3264                   (throw 'infloop t))
3265                 (unless (car (symbol-value refs))
3266                   ;; These threads do not refer back to any other articles,
3267                   ;; so they're roots.
3268                   (setq threads (append (cdr (symbol-value refs)) threads))))
3269               gnus-newsgroup-dependencies)))
3270     threads))
3271
3272 ;; Build the thread tree.
3273 (defsubst gnus-dependencies-add-header (header dependencies force-new)
3274   "Enter HEADER into the DEPENDENCIES table if it is not already there.
3275
3276 If FORCE-NEW is not nil, enter HEADER into the DEPENDENCIES table even
3277 if it was already present.
3278
3279 If `gnus-summary-ignore-duplicates' is nil then duplicate Message-IDs
3280 will not be entered in the DEPENDENCIES table.  Otherwise duplicate
3281 Message-IDs will be renamed be renamed to a unique Message-ID before
3282 being entered.
3283
3284 Returns HEADER if it was entered in the DEPENDENCIES.  Returns nil otherwise."
3285   (let* ((id (mail-header-id header))
3286          (id-dep (and id (intern id dependencies)))
3287          ref ref-dep ref-header)
3288     ;; Enter this `header' in the `dependencies' table.
3289     (cond
3290      ((not id-dep)
3291       (setq header nil))
3292      ;; The first two cases do the normal part: enter a new `header'
3293      ;; in the `dependencies' table.
3294      ((not (boundp id-dep))
3295       (set id-dep (list header)))
3296      ((null (car (symbol-value id-dep)))
3297       (setcar (symbol-value id-dep) header))
3298
3299      ;; From here the `header' was already present in the
3300      ;; `dependencies' table.
3301      (force-new
3302       ;; Overrides an existing entry;
3303       ;; just set the header part of the entry.
3304       (setcar (symbol-value id-dep) header))
3305
3306      ;; Renames the existing `header' to a unique Message-ID.
3307      ((not gnus-summary-ignore-duplicates)
3308       ;; An article with this Message-ID has already been seen.
3309       ;; We rename the Message-ID.
3310       (set (setq id-dep (intern (setq id (nnmail-message-id)) dependencies))
3311            (list header))
3312       (mail-header-set-id header id))
3313
3314      ;; The last case ignores an existing entry, except it adds any
3315      ;; additional Xrefs (in case the two articles came from different
3316      ;; servers.
3317      ;; Also sets `header' to `nil' meaning that the `dependencies'
3318      ;; table was *not* modified.
3319      (t
3320       (mail-header-set-xref
3321        (car (symbol-value id-dep))
3322        (concat (or (mail-header-xref (car (symbol-value id-dep)))
3323                    "")
3324                (or (mail-header-xref header) "")))
3325       (setq header nil)))
3326
3327     (when header
3328       ;; First check if that we are not creating a References loop.
3329       (setq ref (gnus-parent-id (mail-header-references header)))
3330       (while (and ref
3331                   (setq ref-dep (intern-soft ref dependencies))
3332                   (boundp ref-dep)
3333                   (setq ref-header (car (symbol-value ref-dep))))
3334         (if (string= id ref)
3335             ;; Yuk!  This is a reference loop.  Make the article be a
3336             ;; root article.
3337             (progn
3338               (mail-header-set-references (car (symbol-value id-dep)) "none")
3339               (setq ref nil))
3340           (setq ref (gnus-parent-id (mail-header-references ref-header)))))
3341       (setq ref (gnus-parent-id (mail-header-references header)))
3342       (setq ref-dep (intern (or ref "none") dependencies))
3343       (if (boundp ref-dep)
3344           (setcdr (symbol-value ref-dep)
3345                   (nconc (cdr (symbol-value ref-dep))
3346                          (list (symbol-value id-dep))))
3347         (set ref-dep (list nil (symbol-value id-dep)))))
3348     header))
3349
3350 (defun gnus-build-sparse-threads ()
3351   (let ((headers gnus-newsgroup-headers)
3352         (mail-parse-charset gnus-newsgroup-charset)
3353         (gnus-summary-ignore-duplicates t)
3354         header references generation relations
3355         subject child end new-child date)
3356     ;; First we create an alist of generations/relations, where
3357     ;; generations is how much we trust the relation, and the relation
3358     ;; is parent/child.
3359     (gnus-message 7 "Making sparse threads...")
3360     (save-excursion
3361       (nnheader-set-temp-buffer " *gnus sparse threads*")
3362       (while (setq header (pop headers))
3363         (when (and (setq references (mail-header-references header))
3364                    (not (string= references "")))
3365           (insert references)
3366           (setq child (mail-header-id header)
3367                 subject (mail-header-subject header)
3368                 date (mail-header-date header)
3369                 generation 0)
3370           (while (search-backward ">" nil t)
3371             (setq end (1+ (point)))
3372             (when (search-backward "<" nil t)
3373               (setq new-child (buffer-substring (point) end))
3374               (push (list (incf generation)
3375                           child (setq child new-child)
3376                           subject date)
3377                     relations)))
3378           (when child
3379             (push (list (1+ generation) child nil subject) relations))
3380           (erase-buffer)))
3381       (kill-buffer (current-buffer)))
3382     ;; Sort over trustworthiness.
3383     (mapcar
3384      (lambda (relation)
3385        (when (gnus-dependencies-add-header
3386               (make-full-mail-header
3387                gnus-reffed-article-number
3388                (nth 3 relation) "" (or (nth 4 relation) "")
3389                (nth 1 relation)
3390                (or (nth 2 relation) "") 0 0 "")
3391               gnus-newsgroup-dependencies nil)
3392          (push gnus-reffed-article-number gnus-newsgroup-limit)
3393          (push gnus-reffed-article-number gnus-newsgroup-sparse)
3394          (push (cons gnus-reffed-article-number gnus-sparse-mark)
3395                gnus-newsgroup-reads)
3396          (decf gnus-reffed-article-number)))
3397      (sort relations 'car-less-than-car))
3398     (gnus-message 7 "Making sparse threads...done")))
3399
3400 (defun gnus-build-old-threads ()
3401   ;; Look at all the articles that refer back to old articles, and
3402   ;; fetch the headers for the articles that aren't there.  This will
3403   ;; build complete threads - if the roots haven't been expired by the
3404   ;; server, that is.
3405   (let ((mail-parse-charset gnus-newsgroup-charset)
3406         id heads)
3407     (mapatoms
3408      (lambda (refs)
3409        (when (not (car (symbol-value refs)))
3410          (setq heads (cdr (symbol-value refs)))
3411          (while heads
3412            (if (memq (mail-header-number (caar heads))
3413                      gnus-newsgroup-dormant)
3414                (setq heads (cdr heads))
3415              (setq id (symbol-name refs))
3416              (while (and (setq id (gnus-build-get-header id))
3417                          (not (car (gnus-id-to-thread id)))))
3418              (setq heads nil)))))
3419      gnus-newsgroup-dependencies)))
3420
3421 ;; This function has to be called with point after the article number
3422 ;; on the beginning of the line.
3423 (defsubst gnus-nov-parse-line (number dependencies &optional force-new)
3424   (let ((eol (gnus-point-at-eol))
3425         (buffer (current-buffer))
3426         header)
3427
3428     ;; overview: [num subject from date id refs chars lines misc]
3429     (unwind-protect
3430         (progn
3431           (narrow-to-region (point) eol)
3432           (unless (eobp)
3433             (forward-char))
3434
3435           (setq header
3436                 (make-full-mail-header
3437                  number                 ; number
3438                  (funcall gnus-decode-encoded-word-function
3439                           (nnheader-nov-field)) ; subject
3440                  (funcall gnus-decode-encoded-word-function
3441                           (nnheader-nov-field)) ; from
3442                  (nnheader-nov-field)   ; date
3443                  (nnheader-nov-read-message-id) ; id
3444                  (nnheader-nov-field)   ; refs
3445                  (nnheader-nov-read-integer) ; chars
3446                  (nnheader-nov-read-integer) ; lines
3447                  (unless (eobp)
3448                    (if (looking-at "Xref: ")
3449                        (goto-char (match-end 0)))
3450                    (nnheader-nov-field)) ; Xref
3451                  (nnheader-nov-parse-extra)))) ; extra
3452
3453       (widen))
3454
3455     (when gnus-alter-header-function
3456       (funcall gnus-alter-header-function header))
3457     (gnus-dependencies-add-header header dependencies force-new)))
3458
3459 (defun gnus-build-get-header (id)
3460   "Look through the buffer of NOV lines and find the header to ID.
3461 Enter this line into the dependencies hash table, and return
3462 the id of the parent article (if any)."
3463   (let ((deps gnus-newsgroup-dependencies)
3464         found header)
3465     (prog1
3466         (save-excursion
3467           (set-buffer nntp-server-buffer)
3468           (let ((case-fold-search nil))
3469             (goto-char (point-min))
3470             (while (and (not found)
3471                         (search-forward id nil t))
3472               (beginning-of-line)
3473               (setq found (looking-at
3474                            (format "^[^\t]*\t[^\t]*\t[^\t]*\t[^\t]*\t%s"
3475                                    (regexp-quote id))))
3476               (or found (beginning-of-line 2)))
3477             (when found
3478               (beginning-of-line)
3479               (and
3480                (setq header (gnus-nov-parse-line
3481                              (read (current-buffer)) deps))
3482                (gnus-parent-id (mail-header-references header))))))
3483       (when header
3484         (let ((number (mail-header-number header)))
3485           (push number gnus-newsgroup-limit)
3486           (push header gnus-newsgroup-headers)
3487           (if (memq number gnus-newsgroup-unselected)
3488               (progn
3489                 (push number gnus-newsgroup-unreads)
3490                 (setq gnus-newsgroup-unselected
3491                       (delq number gnus-newsgroup-unselected)))
3492             (push number gnus-newsgroup-ancient)))))))
3493
3494 (defun gnus-build-all-threads ()
3495   "Read all the headers."
3496   (let ((gnus-summary-ignore-duplicates t)
3497         (mail-parse-charset gnus-newsgroup-charset)
3498         (dependencies gnus-newsgroup-dependencies)
3499         header article)
3500     (save-excursion
3501       (set-buffer nntp-server-buffer)
3502       (let ((case-fold-search nil))
3503         (goto-char (point-min))
3504         (while (not (eobp))
3505           (ignore-errors
3506             (setq article (read (current-buffer))
3507                   header (gnus-nov-parse-line article dependencies)))
3508           (when header
3509             (save-excursion
3510               (set-buffer gnus-summary-buffer)
3511               (push header gnus-newsgroup-headers)
3512               (if (memq (setq article (mail-header-number header))
3513                         gnus-newsgroup-unselected)
3514                   (progn
3515                     (push article gnus-newsgroup-unreads)
3516                     (setq gnus-newsgroup-unselected
3517                           (delq article gnus-newsgroup-unselected)))
3518                 (push article gnus-newsgroup-ancient)))
3519             (forward-line 1)))))))
3520
3521 (defun gnus-summary-update-article-line (article header)
3522   "Update the line for ARTICLE using HEADERS."
3523   (let* ((id (mail-header-id header))
3524          (thread (gnus-id-to-thread id)))
3525     (unless thread
3526       (error "Article in no thread"))
3527     ;; Update the thread.
3528     (setcar thread header)
3529     (gnus-summary-goto-subject article)
3530     (let* ((datal (gnus-data-find-list article))
3531            (data (car datal))
3532            (length (when (cdr datal)
3533                      (- (gnus-data-pos data)
3534                         (gnus-data-pos (cadr datal)))))
3535            (buffer-read-only nil)
3536            (level (gnus-summary-thread-level)))
3537       (gnus-delete-line)
3538       (gnus-summary-insert-line
3539        header level nil (gnus-article-mark article)
3540        (memq article gnus-newsgroup-replied)
3541        (memq article gnus-newsgroup-expirable)
3542        ;; Only insert the Subject string when it's different
3543        ;; from the previous Subject string.
3544        (if (and
3545             gnus-show-threads
3546             (gnus-subject-equal
3547              (condition-case ()
3548                  (mail-header-subject
3549                   (gnus-data-header
3550                    (cadr
3551                     (gnus-data-find-list
3552                      article
3553                      (gnus-data-list t)))))
3554                ;; Error on the side of excessive subjects.
3555                (error ""))
3556              (mail-header-subject header)))
3557            ""
3558          (mail-header-subject header))
3559        nil (cdr (assq article gnus-newsgroup-scored))
3560        (memq article gnus-newsgroup-processable))
3561       (when length
3562         (gnus-data-update-list
3563          (cdr datal) (- length (- (gnus-data-pos data) (point))))))))
3564
3565 (defun gnus-summary-update-article (article &optional iheader)
3566   "Update ARTICLE in the summary buffer."
3567   (set-buffer gnus-summary-buffer)
3568   (let* ((header (gnus-summary-article-header article))
3569          (id (mail-header-id header))
3570          (data (gnus-data-find article))
3571          (thread (gnus-id-to-thread id))
3572          (references (mail-header-references header))
3573          (parent
3574           (gnus-id-to-thread
3575            (or (gnus-parent-id
3576                 (when (and references
3577                            (not (equal "" references)))
3578                   references))
3579                "none")))
3580          (buffer-read-only nil)
3581          (old (car thread)))
3582     (when thread
3583       (unless iheader
3584         (setcar thread nil)
3585         (when parent
3586           (delq thread parent)))
3587       (if (gnus-summary-insert-subject id header)
3588           ;; Set the (possibly) new article number in the data structure.
3589           (gnus-data-set-number data (gnus-id-to-article id))
3590         (setcar thread old)
3591         nil))))
3592
3593 (defun gnus-rebuild-thread (id &optional line)
3594   "Rebuild the thread containing ID.
3595 If LINE, insert the rebuilt thread starting on line LINE."
3596   (let ((buffer-read-only nil)
3597         old-pos current thread data)
3598     (if (not gnus-show-threads)
3599         (setq thread (list (car (gnus-id-to-thread id))))
3600       ;; Get the thread this article is part of.
3601       (setq thread (gnus-remove-thread id)))
3602     (setq old-pos (gnus-point-at-bol))
3603     (setq current (save-excursion
3604                     (and (zerop (forward-line -1))
3605                          (gnus-summary-article-number))))
3606     ;; If this is a gathered thread, we have to go some re-gathering.
3607     (when (stringp (car thread))
3608       (let ((subject (car thread))
3609             roots thr)
3610         (setq thread (cdr thread))
3611         (while thread
3612           (unless (memq (setq thr (gnus-id-to-thread
3613                                    (gnus-root-id
3614                                     (mail-header-id (caar thread)))))
3615                         roots)
3616             (push thr roots))
3617           (setq thread (cdr thread)))
3618         ;; We now have all (unique) roots.
3619         (if (= (length roots) 1)
3620             ;; All the loose roots are now one solid root.
3621             (setq thread (car roots))
3622           (setq thread (cons subject (gnus-sort-threads roots))))))
3623     (let (threads)
3624       ;; We then insert this thread into the summary buffer.
3625       (when line
3626         (goto-char (point-min))
3627         (forward-line (1- line)))
3628       (let (gnus-newsgroup-data gnus-newsgroup-threads)
3629         (if gnus-show-threads
3630             (gnus-summary-prepare-threads (gnus-cut-threads (list thread)))
3631           (gnus-summary-prepare-unthreaded thread))
3632         (setq data (nreverse gnus-newsgroup-data))
3633         (setq threads gnus-newsgroup-threads))
3634       ;; We splice the new data into the data structure.
3635       ;;!!! This is kinda bogus.  We assume that in LINE is non-nil,
3636       ;;!!! then we want to insert at the beginning of the buffer.
3637       ;;!!! That happens to be true with Gnus now, but that may
3638       ;;!!! change in the future.  Perhaps.
3639       (gnus-data-enter-list
3640        (if line nil current) data (- (point) old-pos))
3641       (setq gnus-newsgroup-threads
3642             (nconc threads gnus-newsgroup-threads))
3643       (gnus-data-compute-positions))))
3644
3645 (defun gnus-number-to-header (number)
3646   "Return the header for article NUMBER."
3647   (let ((headers gnus-newsgroup-headers))
3648     (while (and headers
3649                 (not (= number (mail-header-number (car headers)))))
3650       (pop headers))
3651     (when headers
3652       (car headers))))
3653
3654 (defun gnus-parent-headers (in-headers &optional generation)
3655   "Return the headers of the GENERATIONeth parent of HEADERS."
3656   (unless generation
3657     (setq generation 1))
3658   (let ((parent t)
3659         (headers in-headers)
3660         references)
3661     (while (and parent
3662                 (not (zerop generation))
3663                 (setq references (mail-header-references headers)))
3664       (setq headers (if (and references
3665                              (setq parent (gnus-parent-id references)))
3666                         (car (gnus-id-to-thread parent))
3667                       nil))
3668       (decf generation))
3669     (and (not (eq headers in-headers))
3670          headers)))
3671
3672 (defun gnus-id-to-thread (id)
3673   "Return the (sub-)thread where ID appears."
3674   (gnus-gethash id gnus-newsgroup-dependencies))
3675
3676 (defun gnus-id-to-article (id)
3677   "Return the article number of ID."
3678   (let ((thread (gnus-id-to-thread id)))
3679     (when (and thread
3680                (car thread))
3681       (mail-header-number (car thread)))))
3682
3683 (defun gnus-id-to-header (id)
3684   "Return the article headers of ID."
3685   (car (gnus-id-to-thread id)))
3686
3687 (defun gnus-article-displayed-root-p (article)
3688   "Say whether ARTICLE is a root(ish) article."
3689   (let ((level (gnus-summary-thread-level article))
3690         (refs (mail-header-references  (gnus-summary-article-header article)))
3691         particle)
3692     (cond
3693      ((null level) nil)
3694      ((zerop level) t)
3695      ((null refs) t)
3696      ((null (gnus-parent-id refs)) t)
3697      ((and (= 1 level)
3698            (null (setq particle (gnus-id-to-article
3699                                  (gnus-parent-id refs))))
3700            (null (gnus-summary-thread-level particle)))))))
3701
3702 (defun gnus-root-id (id)
3703   "Return the id of the root of the thread where ID appears."
3704   (let (last-id prev)
3705     (while (and id (setq prev (car (gnus-id-to-thread id))))
3706       (setq last-id id
3707             id (gnus-parent-id (mail-header-references prev))))
3708     last-id))
3709
3710 (defun gnus-articles-in-thread (thread)
3711   "Return the list of articles in THREAD."
3712   (cons (mail-header-number (car thread))
3713         (apply 'nconc (mapcar 'gnus-articles-in-thread (cdr thread)))))
3714
3715 (defun gnus-remove-thread (id &optional dont-remove)
3716   "Remove the thread that has ID in it."
3717   (let (headers thread last-id)
3718     ;; First go up in this thread until we find the root.
3719     (setq last-id (gnus-root-id id)
3720           headers (message-flatten-list (gnus-id-to-thread last-id)))
3721     ;; We have now found the real root of this thread.  It might have
3722     ;; been gathered into some loose thread, so we have to search
3723     ;; through the threads to find the thread we wanted.
3724     (let ((threads gnus-newsgroup-threads)
3725           sub)
3726       (while threads
3727         (setq sub (car threads))
3728         (if (stringp (car sub))
3729             ;; This is a gathered thread, so we look at the roots
3730             ;; below it to find whether this article is in this
3731             ;; gathered root.
3732             (progn
3733               (setq sub (cdr sub))
3734               (while sub
3735                 (when (member (caar sub) headers)
3736                   (setq thread (car threads)
3737                         threads nil
3738                         sub nil))
3739                 (setq sub (cdr sub))))
3740           ;; It's an ordinary thread, so we check it.
3741           (when (eq (car sub) (car headers))
3742             (setq thread sub
3743                   threads nil)))
3744         (setq threads (cdr threads)))
3745       ;; If this article is in no thread, then it's a root.
3746       (if thread
3747           (unless dont-remove
3748             (setq gnus-newsgroup-threads (delq thread gnus-newsgroup-threads)))
3749         (setq thread (gnus-id-to-thread last-id)))
3750       (when thread
3751         (prog1
3752             thread                      ; We return this thread.
3753           (unless dont-remove
3754             (if (stringp (car thread))
3755                 (progn
3756                   ;; If we use dummy roots, then we have to remove the
3757                   ;; dummy root as well.
3758                   (when (eq gnus-summary-make-false-root 'dummy)
3759                     ;; We go to the dummy root by going to
3760                     ;; the first sub-"thread", and then one line up.
3761                     (gnus-summary-goto-article
3762                      (mail-header-number (caadr thread)))
3763                     (forward-line -1)
3764                     (gnus-delete-line)
3765                     (gnus-data-compute-positions))
3766                   (setq thread (cdr thread))
3767                   (while thread
3768                     (gnus-remove-thread-1 (car thread))
3769                     (setq thread (cdr thread))))
3770               (gnus-remove-thread-1 thread))))))))
3771
3772 (defun gnus-remove-thread-1 (thread)
3773   "Remove the thread THREAD recursively."
3774   (let ((number (mail-header-number (pop thread)))
3775         d)
3776     (setq thread (reverse thread))
3777     (while thread
3778       (gnus-remove-thread-1 (pop thread)))
3779     (when (setq d (gnus-data-find number))
3780       (goto-char (gnus-data-pos d))
3781       (gnus-summary-show-thread)
3782       (gnus-data-remove
3783        number
3784        (- (gnus-point-at-bol)
3785           (prog1
3786               (1+ (gnus-point-at-eol))
3787             (gnus-delete-line)))))))
3788
3789 (defun gnus-sort-threads-1 (threads func)
3790   (sort (mapcar (lambda (thread)
3791                   (cons (car thread)
3792                         (and (cdr thread)
3793                              (gnus-sort-threads-1 (cdr thread) func))))
3794                 threads) func))
3795
3796 (defun gnus-sort-threads (threads)
3797   "Sort THREADS."
3798   (if (not gnus-thread-sort-functions)
3799       threads
3800     (gnus-message 8 "Sorting threads...")
3801     (prog1
3802         (gnus-sort-threads-1 
3803          threads 
3804          (gnus-make-sort-function gnus-thread-sort-functions))
3805       (gnus-message 8 "Sorting threads...done"))))
3806
3807 (defun gnus-sort-articles (articles)
3808   "Sort ARTICLES."
3809   (when gnus-article-sort-functions
3810     (gnus-message 7 "Sorting articles...")
3811     (prog1
3812         (setq gnus-newsgroup-headers
3813               (sort articles (gnus-make-sort-function
3814                               gnus-article-sort-functions)))
3815       (gnus-message 7 "Sorting articles...done"))))
3816
3817 ;; Written by Hallvard B Furuseth <h.b.furuseth@usit.uio.no>.
3818 (defmacro gnus-thread-header (thread)
3819   "Return header of first article in THREAD.
3820 Note that THREAD must never, ever be anything else than a variable -
3821 using some other form will lead to serious barfage."
3822   (or (symbolp thread) (signal 'wrong-type-argument '(symbolp thread)))
3823   ;; (8% speedup to gnus-summary-prepare, just for fun :-)
3824   (list 'byte-code "\10\211:\203\17\0\211@;\203\16\0A@@\207"
3825         (vector thread) 2))
3826
3827 (defsubst gnus-article-sort-by-number (h1 h2)
3828   "Sort articles by article number."
3829   (< (mail-header-number h1)
3830      (mail-header-number h2)))
3831
3832 (defun gnus-thread-sort-by-number (h1 h2)
3833   "Sort threads by root article number."
3834   (gnus-article-sort-by-number
3835    (gnus-thread-header h1) (gnus-thread-header h2)))
3836
3837 (defsubst gnus-article-sort-by-lines (h1 h2)
3838   "Sort articles by article Lines header."
3839   (< (mail-header-lines h1)
3840      (mail-header-lines h2)))
3841
3842 (defun gnus-thread-sort-by-lines (h1 h2)
3843   "Sort threads by root article Lines header."
3844   (gnus-article-sort-by-lines
3845    (gnus-thread-header h1) (gnus-thread-header h2)))
3846
3847 (defsubst gnus-article-sort-by-chars (h1 h2)
3848   "Sort articles by octet length."
3849   (< (mail-header-chars h1)
3850      (mail-header-chars h2)))
3851
3852 (defun gnus-thread-sort-by-chars (h1 h2)
3853   "Sort threads by root article octet length."
3854   (gnus-article-sort-by-chars
3855    (gnus-thread-header h1) (gnus-thread-header h2)))
3856
3857 (defsubst gnus-article-sort-by-author (h1 h2)
3858   "Sort articles by root author."
3859   (string-lessp
3860    (let ((extract (funcall
3861                    gnus-extract-address-components
3862                    (mail-header-from h1))))
3863      (or (car extract) (cadr extract) ""))
3864    (let ((extract (funcall
3865                    gnus-extract-address-components
3866                    (mail-header-from h2))))
3867      (or (car extract) (cadr extract) ""))))
3868
3869 (defun gnus-thread-sort-by-author (h1 h2)
3870   "Sort threads by root author."
3871   (gnus-article-sort-by-author
3872    (gnus-thread-header h1)  (gnus-thread-header h2)))
3873
3874 (defsubst gnus-article-sort-by-subject (h1 h2)
3875   "Sort articles by root subject."
3876   (string-lessp
3877    (downcase (gnus-simplify-subject-re (mail-header-subject h1)))
3878    (downcase (gnus-simplify-subject-re (mail-header-subject h2)))))
3879
3880 (defun gnus-thread-sort-by-subject (h1 h2)
3881   "Sort threads by root subject."
3882   (gnus-article-sort-by-subject
3883    (gnus-thread-header h1) (gnus-thread-header h2)))
3884
3885 (defsubst gnus-article-sort-by-date (h1 h2)
3886   "Sort articles by root article date."
3887   (time-less-p
3888    (gnus-date-get-time (mail-header-date h1))
3889    (gnus-date-get-time (mail-header-date h2))))
3890
3891 (defun gnus-thread-sort-by-date (h1 h2)
3892   "Sort threads by root article date."
3893   (gnus-article-sort-by-date
3894    (gnus-thread-header h1) (gnus-thread-header h2)))
3895
3896 (defsubst gnus-article-sort-by-score (h1 h2)
3897   "Sort articles by root article score.
3898 Unscored articles will be counted as having a score of zero."
3899   (> (or (cdr (assq (mail-header-number h1)
3900                     gnus-newsgroup-scored))
3901          gnus-summary-default-score 0)
3902      (or (cdr (assq (mail-header-number h2)
3903                     gnus-newsgroup-scored))
3904          gnus-summary-default-score 0)))
3905
3906 (defun gnus-thread-sort-by-score (h1 h2)
3907   "Sort threads by root article score."
3908   (gnus-article-sort-by-score
3909    (gnus-thread-header h1) (gnus-thread-header h2)))
3910
3911 (defun gnus-thread-sort-by-total-score (h1 h2)
3912   "Sort threads by the sum of all scores in the thread.
3913 Unscored articles will be counted as having a score of zero."
3914   (> (gnus-thread-total-score h1) (gnus-thread-total-score h2)))
3915
3916 (defun gnus-thread-total-score (thread)
3917   ;; This function find the total score of THREAD.
3918   (cond ((null thread)
3919          0)
3920         ((consp thread)
3921          (if (stringp (car thread))
3922              (apply gnus-thread-score-function 0
3923                     (mapcar 'gnus-thread-total-score-1 (cdr thread)))
3924            (gnus-thread-total-score-1 thread)))
3925         (t
3926          (gnus-thread-total-score-1 (list thread)))))
3927
3928 (defun gnus-thread-total-score-1 (root)
3929   ;; This function find the total score of the thread below ROOT.
3930   (setq root (car root))
3931   (apply gnus-thread-score-function
3932          (or (append
3933               (mapcar 'gnus-thread-total-score
3934                       (cdr (gnus-id-to-thread (mail-header-id root))))
3935               (when (> (mail-header-number root) 0)
3936                 (list (or (cdr (assq (mail-header-number root)
3937                                      gnus-newsgroup-scored))
3938                           gnus-summary-default-score 0))))
3939              (list gnus-summary-default-score)
3940              '(0))))
3941
3942 ;; Added by Per Abrahamsen <amanda@iesd.auc.dk>.
3943 (defvar gnus-tmp-prev-subject nil)
3944 (defvar gnus-tmp-false-parent nil)
3945 (defvar gnus-tmp-root-expunged nil)
3946 (defvar gnus-tmp-dummy-line nil)
3947
3948 (eval-when-compile (defvar gnus-tmp-header))
3949 (defun gnus-extra-header (type &optional header)
3950   "Return the extra header of TYPE."
3951   (or (cdr (assq type (mail-header-extra (or header gnus-tmp-header))))
3952       ""))
3953
3954 (defun gnus-summary-prepare-threads (threads)
3955   "Prepare summary buffer from THREADS and indentation LEVEL.
3956 THREADS is either a list of `(PARENT [(CHILD1 [(GRANDCHILD ...]...) ...])'
3957 or a straight list of headers."
3958   (gnus-message 7 "Generating summary...")
3959
3960   (setq gnus-newsgroup-threads threads)
3961   (beginning-of-line)
3962
3963   (let ((gnus-tmp-level 0)
3964         (default-score (or gnus-summary-default-score 0))
3965         (gnus-visual-p (gnus-visual-p 'summary-highlight 'highlight))
3966         thread number subject stack state gnus-tmp-gathered beg-match
3967         new-roots gnus-tmp-new-adopts thread-end
3968         gnus-tmp-header gnus-tmp-unread
3969         gnus-tmp-replied gnus-tmp-subject-or-nil
3970         gnus-tmp-dummy gnus-tmp-indentation gnus-tmp-lines gnus-tmp-score
3971         gnus-tmp-score-char gnus-tmp-from gnus-tmp-name
3972         gnus-tmp-number gnus-tmp-opening-bracket gnus-tmp-closing-bracket)
3973
3974     (setq gnus-tmp-prev-subject nil)
3975
3976     (if (vectorp (car threads))
3977         ;; If this is a straight (sic) list of headers, then a
3978         ;; threaded summary display isn't required, so we just create
3979         ;; an unthreaded one.
3980         (gnus-summary-prepare-unthreaded threads)
3981
3982       ;; Do the threaded display.
3983
3984       (while (or threads stack gnus-tmp-new-adopts new-roots)
3985
3986         (if (and (= gnus-tmp-level 0)
3987                  (or (not stack)
3988                      (= (caar stack) 0))
3989                  (not gnus-tmp-false-parent)
3990                  (or gnus-tmp-new-adopts new-roots))
3991             (if gnus-tmp-new-adopts
3992                 (setq gnus-tmp-level (if gnus-tmp-root-expunged 0 1)
3993                       thread (list (car gnus-tmp-new-adopts))
3994                       gnus-tmp-header (caar thread)
3995                       gnus-tmp-new-adopts (cdr gnus-tmp-new-adopts))
3996               (when new-roots
3997                 (setq thread (list (car new-roots))
3998                       gnus-tmp-header (caar thread)
3999                       new-roots (cdr new-roots))))
4000
4001           (if threads
4002               ;; If there are some threads, we do them before the
4003               ;; threads on the stack.
4004               (setq thread threads
4005                     gnus-tmp-header (caar thread))
4006             ;; There were no current threads, so we pop something off
4007             ;; the stack.
4008             (setq state (car stack)
4009                   gnus-tmp-level (car state)
4010                   thread (cdr state)
4011                   stack (cdr stack)
4012                   gnus-tmp-header (caar thread))))
4013
4014         (setq gnus-tmp-false-parent nil)
4015         (setq gnus-tmp-root-expunged nil)
4016         (setq thread-end nil)
4017
4018         (if (stringp gnus-tmp-header)
4019             ;; The header is a dummy root.
4020             (cond
4021              ((eq gnus-summary-make-false-root 'adopt)
4022               ;; We let the first article adopt the rest.
4023               (setq gnus-tmp-new-adopts (nconc gnus-tmp-new-adopts
4024                                                (cddar thread)))
4025               (setq gnus-tmp-gathered
4026                     (nconc (mapcar
4027                             (lambda (h) (mail-header-number (car h)))
4028                             (cddar thread))
4029                            gnus-tmp-gathered))
4030               (setq thread (cons (list (caar thread)
4031                                        (cadar thread))
4032                                  (cdr thread)))
4033               (setq gnus-tmp-level -1
4034                     gnus-tmp-false-parent t))
4035              ((eq gnus-summary-make-false-root 'empty)
4036               ;; We print adopted articles with empty subject fields.
4037               (setq gnus-tmp-gathered
4038                     (nconc (mapcar
4039                             (lambda (h) (mail-header-number (car h)))
4040                             (cddar thread))
4041                            gnus-tmp-gathered))
4042               (setq gnus-tmp-level -1))
4043              ((eq gnus-summary-make-false-root 'dummy)
4044               ;; We remember that we probably want to output a dummy
4045               ;; root.
4046               (setq gnus-tmp-dummy-line gnus-tmp-header)
4047               (setq gnus-tmp-prev-subject gnus-tmp-header))
4048              (t
4049               ;; We do not make a root for the gathered
4050               ;; sub-threads at all.
4051               (setq gnus-tmp-level -1)))
4052
4053           (setq number (mail-header-number gnus-tmp-header)
4054                 subject (mail-header-subject gnus-tmp-header))
4055
4056           (cond
4057            ;; If the thread has changed subject, we might want to make
4058            ;; this subthread into a root.
4059            ((and (null gnus-thread-ignore-subject)
4060                  (not (zerop gnus-tmp-level))
4061                  gnus-tmp-prev-subject
4062                  (not (inline
4063                         (gnus-subject-equal gnus-tmp-prev-subject subject))))
4064             (setq new-roots (nconc new-roots (list (car thread)))
4065                   thread-end t
4066                   gnus-tmp-header nil))
4067            ;; If the article lies outside the current limit,
4068            ;; then we do not display it.
4069            ((not (memq number gnus-newsgroup-limit))
4070             (setq gnus-tmp-gathered
4071                   (nconc (mapcar
4072                           (lambda (h) (mail-header-number (car h)))
4073                           (cdar thread))
4074                          gnus-tmp-gathered))
4075             (setq gnus-tmp-new-adopts (if (cdar thread)
4076                                           (append gnus-tmp-new-adopts
4077                                                   (cdar thread))
4078                                         gnus-tmp-new-adopts)
4079                   thread-end t
4080                   gnus-tmp-header nil)
4081             (when (zerop gnus-tmp-level)
4082               (setq gnus-tmp-root-expunged t)))
4083            ;; Perhaps this article is to be marked as read?
4084            ((and gnus-summary-mark-below
4085                  (< (or (cdr (assq number gnus-newsgroup-scored))
4086                         default-score)
4087                     gnus-summary-mark-below)
4088                  ;; Don't touch sparse articles.
4089                  (not (gnus-summary-article-sparse-p number))
4090                  (not (gnus-summary-article-ancient-p number)))
4091             (setq gnus-newsgroup-unreads
4092                   (delq number gnus-newsgroup-unreads))
4093             (if gnus-newsgroup-auto-expire
4094                 (push number gnus-newsgroup-expirable)
4095               (push (cons number gnus-low-score-mark)
4096                     gnus-newsgroup-reads))))
4097
4098           (when gnus-tmp-header
4099             ;; We may have an old dummy line to output before this
4100             ;; article.
4101             (when (and gnus-tmp-dummy-line
4102                        (gnus-subject-equal
4103                         gnus-tmp-dummy-line
4104                         (mail-header-subject gnus-tmp-header)))
4105               (gnus-summary-insert-dummy-line
4106                gnus-tmp-dummy-line (mail-header-number gnus-tmp-header))
4107               (setq gnus-tmp-dummy-line nil))
4108
4109             ;; Compute the mark.
4110             (setq gnus-tmp-unread (gnus-article-mark number))
4111
4112             (push (gnus-data-make number gnus-tmp-unread (1+ (point))
4113                                   gnus-tmp-header gnus-tmp-level)
4114                   gnus-newsgroup-data)
4115
4116             ;; Actually insert the line.
4117             (setq
4118              gnus-tmp-subject-or-nil
4119              (cond
4120               ((and gnus-thread-ignore-subject
4121                     gnus-tmp-prev-subject
4122                     (not (inline (gnus-subject-equal
4123                                   gnus-tmp-prev-subject subject))))
4124                subject)
4125               ((zerop gnus-tmp-level)
4126                (if (and (eq gnus-summary-make-false-root 'empty)
4127                         (memq number gnus-tmp-gathered)
4128                         gnus-tmp-prev-subject
4129                         (inline (gnus-subject-equal
4130                                  gnus-tmp-prev-subject subject)))
4131                    gnus-summary-same-subject
4132                  subject))
4133               (t gnus-summary-same-subject)))
4134             (if (and (eq gnus-summary-make-false-root 'adopt)
4135                      (= gnus-tmp-level 1)
4136                      (memq number gnus-tmp-gathered))
4137                 (setq gnus-tmp-opening-bracket ?\<
4138                       gnus-tmp-closing-bracket ?\>)
4139               (setq gnus-tmp-opening-bracket ?\[
4140                     gnus-tmp-closing-bracket ?\]))
4141             (setq
4142              gnus-tmp-indentation
4143              (aref gnus-thread-indent-array gnus-tmp-level)
4144              gnus-tmp-lines (mail-header-lines gnus-tmp-header)
4145              gnus-tmp-score (or (cdr (assq number gnus-newsgroup-scored))
4146                                 gnus-summary-default-score 0)
4147              gnus-tmp-score-char
4148              (if (or (null gnus-summary-default-score)
4149                      (<= (abs (- gnus-tmp-score gnus-summary-default-score))
4150                          gnus-summary-zcore-fuzz))
4151                  ?  ;Whitespace
4152                (if (< gnus-tmp-score gnus-summary-default-score)
4153                    gnus-score-below-mark gnus-score-over-mark))
4154              gnus-tmp-replied
4155              (cond ((memq number gnus-newsgroup-processable)
4156                     gnus-process-mark)
4157                    ((memq number gnus-newsgroup-cached)
4158                     gnus-cached-mark)
4159                    ((memq number gnus-newsgroup-replied)
4160                     gnus-replied-mark)
4161                    ((memq number gnus-newsgroup-saved)
4162                     gnus-saved-mark)
4163                    (t gnus-unread-mark))
4164              gnus-tmp-from (mail-header-from gnus-tmp-header)
4165              gnus-tmp-name
4166              (cond
4167               ((string-match "<[^>]+> *$" gnus-tmp-from)
4168                (setq beg-match (match-beginning 0))
4169                (or (and (string-match "^\".+\"" gnus-tmp-from)
4170                         (substring gnus-tmp-from 1 (1- (match-end 0))))
4171                    (substring gnus-tmp-from 0 beg-match)))
4172               ((string-match "(.+)" gnus-tmp-from)
4173                (substring gnus-tmp-from
4174                           (1+ (match-beginning 0)) (1- (match-end 0))))
4175               (t gnus-tmp-from)))
4176             (when (string= gnus-tmp-name "")
4177               (setq gnus-tmp-name gnus-tmp-from))
4178             (unless (numberp gnus-tmp-lines)
4179               (setq gnus-tmp-lines 0))
4180             (gnus-put-text-property
4181              (point)
4182              (progn (eval gnus-summary-line-format-spec) (point))
4183              'gnus-number number)
4184             (when gnus-visual-p
4185               (forward-line -1)
4186               (gnus-run-hooks 'gnus-summary-update-hook)
4187               (forward-line 1))
4188
4189             (setq gnus-tmp-prev-subject subject)))
4190
4191         (when (nth 1 thread)
4192           (push (cons (max 0 gnus-tmp-level) (nthcdr 1 thread)) stack))
4193         (incf gnus-tmp-level)
4194         (setq threads (if thread-end nil (cdar thread)))
4195         (unless threads
4196           (setq gnus-tmp-level 0)))))
4197   (gnus-message 7 "Generating summary...done"))
4198
4199 (defun gnus-summary-prepare-unthreaded (headers)
4200   "Generate an unthreaded summary buffer based on HEADERS."
4201   (let (header number mark)
4202
4203     (beginning-of-line)
4204
4205     (while headers
4206       ;; We may have to root out some bad articles...
4207       (when (memq (setq number (mail-header-number
4208                                 (setq header (pop headers))))
4209                   gnus-newsgroup-limit)
4210         ;; Mark article as read when it has a low score.
4211         (when (and gnus-summary-mark-below
4212                    (< (or (cdr (assq number gnus-newsgroup-scored))
4213                           gnus-summary-default-score 0)
4214                       gnus-summary-mark-below)
4215                    (not (gnus-summary-article-ancient-p number)))
4216           (setq gnus-newsgroup-unreads
4217                 (delq number gnus-newsgroup-unreads))
4218           (if gnus-newsgroup-auto-expire
4219               (push number gnus-newsgroup-expirable)
4220             (push (cons number gnus-low-score-mark)
4221                   gnus-newsgroup-reads)))
4222
4223         (setq mark (gnus-article-mark number))
4224         (push (gnus-data-make number mark (1+ (point)) header 0)
4225               gnus-newsgroup-data)
4226         (gnus-summary-insert-line
4227          header 0 number
4228          mark (memq number gnus-newsgroup-replied)
4229          (memq number gnus-newsgroup-expirable)
4230          (mail-header-subject header) nil
4231          (cdr (assq number gnus-newsgroup-scored))
4232          (memq number gnus-newsgroup-processable))))))
4233
4234 (defun gnus-summary-remove-list-identifiers ()
4235   "Remove list identifiers in `gnus-list-identifiers' from articles in the current group."
4236   (let ((regexp (if (stringp gnus-list-identifiers)
4237                     gnus-list-identifiers
4238                   (mapconcat 'identity gnus-list-identifiers " *\\|"))))
4239     (dolist (header gnus-newsgroup-headers)
4240       (when (string-match (concat "\\(\\(\\(Re: +\\)?\\(" regexp 
4241                                   " *\\)\\)+\\(Re: +\\)?\\)")
4242                           (mail-header-subject header))
4243         (mail-header-set-subject
4244          header (concat (substring (mail-header-subject header)
4245                                    0 (match-beginning 1))
4246                         (or
4247                          (match-string 3 (mail-header-subject header))
4248                          (match-string 5 (mail-header-subject header)))
4249                         (substring (mail-header-subject header)
4250                                    (match-end 1))))))))
4251
4252 (defun gnus-select-newsgroup (group &optional read-all select-articles)
4253   "Select newsgroup GROUP.
4254 If READ-ALL is non-nil, all articles in the group are selected.
4255 If SELECT-ARTICLES, only select those articles from GROUP."
4256   (let* ((entry (gnus-gethash group gnus-newsrc-hashtb))
4257          ;;!!! Dirty hack; should be removed.
4258          (gnus-summary-ignore-duplicates
4259           (if (eq (car (gnus-find-method-for-group group)) 'nnvirtual)
4260               t
4261             gnus-summary-ignore-duplicates))
4262          (info (nth 2 entry))
4263          articles fetched-articles cached)
4264
4265     (unless (gnus-check-server
4266              (setq gnus-current-select-method
4267                    (gnus-find-method-for-group group)))
4268       (error "Couldn't open server"))
4269
4270     (or (and entry (not (eq (car entry) t))) ; Either it's active...
4271         (gnus-activate-group group)     ; Or we can activate it...
4272         (progn                          ; Or we bug out.
4273           (when (equal major-mode 'gnus-summary-mode)
4274             (kill-buffer (current-buffer)))
4275           (error "Couldn't activate group %s: %s"
4276                  group (gnus-status-message group))))
4277
4278     (unless (gnus-request-group group t)
4279       (when (equal major-mode 'gnus-summary-mode)
4280         (kill-buffer (current-buffer)))
4281       (error "Couldn't request group %s: %s"
4282              group (gnus-status-message group)))
4283
4284     (setq gnus-newsgroup-name group)
4285     (setq gnus-newsgroup-unselected nil)
4286     (setq gnus-newsgroup-unreads (gnus-list-of-unread-articles group))
4287     (gnus-summary-setup-default-charset)
4288
4289     ;; Adjust and set lists of article marks.
4290     (when info
4291       (gnus-adjust-marked-articles info))
4292
4293     ;; Kludge to avoid having cached articles nixed out in virtual groups.
4294     (when (gnus-virtual-group-p group)
4295       (setq cached gnus-newsgroup-cached))
4296
4297     (setq gnus-newsgroup-unreads
4298           (gnus-set-difference
4299            (gnus-set-difference gnus-newsgroup-unreads gnus-newsgroup-marked)
4300            gnus-newsgroup-dormant))
4301
4302     (setq gnus-newsgroup-processable nil)
4303
4304     (gnus-update-read-articles group gnus-newsgroup-unreads)
4305
4306     (if (setq articles select-articles)
4307         (setq gnus-newsgroup-unselected
4308               (gnus-sorted-intersection
4309                gnus-newsgroup-unreads
4310                (gnus-sorted-complement gnus-newsgroup-unreads articles)))
4311       (setq articles (gnus-articles-to-read group read-all)))
4312
4313     (cond
4314      ((null articles)
4315       ;;(gnus-message 3 "Couldn't select newsgroup -- no articles to display")
4316       'quit)
4317      ((eq articles 0) nil)
4318      (t
4319       ;; Init the dependencies hash table.
4320       (setq gnus-newsgroup-dependencies
4321             (gnus-make-hashtable (length articles)))
4322       (gnus-set-global-variables)
4323       ;; Retrieve the headers and read them in.
4324       (gnus-message 5 "Fetching headers for %s..." gnus-newsgroup-name)
4325       (setq gnus-newsgroup-headers
4326             (if (eq 'nov
4327                     (setq gnus-headers-retrieved-by
4328                           (gnus-retrieve-headers
4329                            articles gnus-newsgroup-name
4330                            ;; We might want to fetch old headers, but
4331                            ;; not if there is only 1 article.
4332                            (and (or (and
4333                                      (not (eq gnus-fetch-old-headers 'some))
4334                                      (not (numberp gnus-fetch-old-headers)))
4335                                     (> (length articles) 1))
4336                                 gnus-fetch-old-headers))))
4337                 (gnus-get-newsgroup-headers-xover
4338                  articles nil nil gnus-newsgroup-name t)
4339               (gnus-get-newsgroup-headers)))
4340       (gnus-message 5 "Fetching headers for %s...done" gnus-newsgroup-name)
4341
4342       ;; Kludge to avoid having cached articles nixed out in virtual groups.
4343       (when cached
4344         (setq gnus-newsgroup-cached cached))
4345
4346       ;; Suppress duplicates?
4347       (when gnus-suppress-duplicates
4348         (gnus-dup-suppress-articles))
4349
4350       ;; Set the initial limit.
4351       (setq gnus-newsgroup-limit (copy-sequence articles))
4352       ;; Remove canceled articles from the list of unread articles.
4353       (setq gnus-newsgroup-unreads
4354             (gnus-set-sorted-intersection
4355              gnus-newsgroup-unreads
4356              (setq fetched-articles
4357                    (mapcar (lambda (headers) (mail-header-number headers))
4358                            gnus-newsgroup-headers))))
4359       ;; Removed marked articles that do not exist.
4360       (gnus-update-missing-marks
4361        (gnus-sorted-complement fetched-articles articles))
4362       ;; We might want to build some more threads first.
4363       (when (and gnus-fetch-old-headers
4364                  (eq gnus-headers-retrieved-by 'nov))
4365         (if (eq gnus-fetch-old-headers 'invisible)
4366             (gnus-build-all-threads)
4367           (gnus-build-old-threads)))
4368       ;; Let the Gnus agent mark articles as read.
4369       (when gnus-agent
4370         (gnus-agent-get-undownloaded-list))
4371       ;; Remove list identifiers from subject
4372       (when gnus-list-identifiers
4373         (gnus-summary-remove-list-identifiers))
4374       ;; Check whether auto-expire is to be done in this group.
4375       (setq gnus-newsgroup-auto-expire
4376             (gnus-group-auto-expirable-p group))
4377       ;; Set up the article buffer now, if necessary.
4378       (unless gnus-single-article-buffer
4379         (gnus-article-setup-buffer))
4380       ;; First and last article in this newsgroup.
4381       (when gnus-newsgroup-headers
4382         (setq gnus-newsgroup-begin
4383               (mail-header-number (car gnus-newsgroup-headers))
4384               gnus-newsgroup-end
4385               (mail-header-number
4386                (gnus-last-element gnus-newsgroup-headers))))
4387       ;; GROUP is successfully selected.
4388       (or gnus-newsgroup-headers t)))))
4389
4390 (defun gnus-articles-to-read (group &optional read-all)
4391   "Find out what articles the user wants to read."
4392   (let* ((articles
4393           ;; Select all articles if `read-all' is non-nil, or if there
4394           ;; are no unread articles.
4395           (if (or read-all
4396                   (and (zerop (length gnus-newsgroup-marked))
4397                        (zerop (length gnus-newsgroup-unreads)))
4398                   (eq (gnus-group-find-parameter group 'display)
4399                       'all))
4400               (or
4401                (gnus-uncompress-range (gnus-active group))
4402                (gnus-cache-articles-in-group group))
4403             (sort (append gnus-newsgroup-dormant gnus-newsgroup-marked
4404                           (copy-sequence gnus-newsgroup-unreads))
4405                   '<)))
4406          (scored-list (gnus-killed-articles gnus-newsgroup-killed articles))
4407          (scored (length scored-list))
4408          (number (length articles))
4409          (marked (+ (length gnus-newsgroup-marked)
4410                     (length gnus-newsgroup-dormant)))
4411          (select
4412           (cond
4413            ((numberp read-all)
4414             read-all)
4415            (t
4416             (condition-case ()
4417                 (cond
4418                  ((and (or (<= scored marked) (= scored number))
4419                        (numberp gnus-large-newsgroup)
4420                        (> number gnus-large-newsgroup))
4421                   (let ((input
4422                          (read-string
4423                           (format
4424                            "How many articles from %s (default %d): "
4425                            (gnus-limit-string gnus-newsgroup-name 35)
4426                            number))))
4427                     (if (string-match "^[ \t]*$" input) number input)))
4428                  ((and (> scored marked) (< scored number)
4429                        (> (- scored number) 20))
4430                   (let ((input
4431                          (read-string
4432                           (format "%s %s (%d scored, %d total): "
4433                                   "How many articles from"
4434                                   group scored number))))
4435                     (if (string-match "^[ \t]*$" input)
4436                         number input)))
4437                  (t number))
4438               (quit
4439                (message "Quit getting the articles to read")
4440                nil))))))
4441     (setq select (if (stringp select) (string-to-number select) select))
4442     (if (or (null select) (zerop select))
4443         select
4444       (if (and (not (zerop scored)) (<= (abs select) scored))
4445           (progn
4446             (setq articles (sort scored-list '<))
4447             (setq number (length articles)))
4448         (setq articles (copy-sequence articles)))
4449
4450       (when (< (abs select) number)
4451         (if (< select 0)
4452             ;; Select the N oldest articles.
4453             (setcdr (nthcdr (1- (abs select)) articles) nil)
4454           ;; Select the N most recent articles.
4455           (setq articles (nthcdr (- number select) articles))))
4456       (setq gnus-newsgroup-unselected
4457             (gnus-sorted-intersection
4458              gnus-newsgroup-unreads
4459              (gnus-sorted-complement gnus-newsgroup-unreads articles)))
4460       (when gnus-alter-articles-to-read-function
4461         (setq gnus-newsgroup-unreads
4462               (sort 
4463                (funcall gnus-alter-articles-to-read-function
4464                         gnus-newsgroup-name gnus-newsgroup-unreads)
4465                '<)))
4466       articles)))
4467
4468 (defun gnus-killed-articles (killed articles)
4469   (let (out)
4470     (while articles
4471       (when (inline (gnus-member-of-range (car articles) killed))
4472         (push (car articles) out))
4473       (setq articles (cdr articles)))
4474     out))
4475
4476 (defun gnus-uncompress-marks (marks)
4477   "Uncompress the mark ranges in MARKS."
4478   (let ((uncompressed '(score bookmark))
4479         out)
4480     (while marks
4481       (if (memq (caar marks) uncompressed)
4482           (push (car marks) out)
4483         (push (cons (caar marks) (gnus-uncompress-range (cdar marks))) out))
4484       (setq marks (cdr marks)))
4485     out))
4486
4487 (defun gnus-adjust-marked-articles (info)
4488   "Set all article lists and remove all marks that are no longer valid."
4489   (let* ((marked-lists (gnus-info-marks info))
4490          (active (gnus-active (gnus-info-group info)))
4491          (min (car active))
4492          (max (cdr active))
4493          (types gnus-article-mark-lists)
4494          (uncompressed '(score bookmark killed))
4495          marks var articles article mark)
4496
4497     (while marked-lists
4498       (setq marks (pop marked-lists))
4499       (set (setq var (intern (format "gnus-newsgroup-%s"
4500                                      (car (rassq (setq mark (car marks))
4501                                                  types)))))
4502            (if (memq (car marks) uncompressed) (cdr marks)
4503              (gnus-uncompress-range (cdr marks))))
4504
4505       (setq articles (symbol-value var))
4506
4507       ;; All articles have to be subsets of the active articles.
4508       (cond
4509        ;; Adjust "simple" lists.
4510        ((memq mark '(tick dormant expire reply save))
4511         (while articles
4512           (when (or (< (setq article (pop articles)) min) (> article max))
4513             (set var (delq article (symbol-value var))))))
4514        ;; Adjust assocs.
4515        ((memq mark uncompressed)
4516         (when (not (listp (cdr (symbol-value var))))
4517           (set var (list (symbol-value var))))
4518         (when (not (listp (cdr articles)))
4519           (setq articles (list articles)))
4520         (while articles
4521           (when (or (not (consp (setq article (pop articles))))
4522                     (< (car article) min)
4523                     (> (car article) max))
4524             (set var (delq article (symbol-value var))))))))))
4525
4526 (defun gnus-update-missing-marks (missing)
4527   "Go through the list of MISSING articles and remove them from the mark lists."
4528   (when missing
4529     (let ((types gnus-article-mark-lists)
4530           var m)
4531       ;; Go through all types.
4532       (while types
4533         (setq var (intern (format "gnus-newsgroup-%s" (car (pop types)))))
4534         (when (symbol-value var)
4535           ;; This list has articles.  So we delete all missing articles
4536           ;; from it.
4537           (setq m missing)
4538           (while m
4539             (set var (delq (pop m) (symbol-value var)))))))))
4540
4541 (defun gnus-update-marks ()
4542   "Enter the various lists of marked articles into the newsgroup info list."
4543   (let ((types gnus-article-mark-lists)
4544         (info (gnus-get-info gnus-newsgroup-name))
4545         (uncompressed '(score bookmark killed))
4546         type list newmarked symbol delta-marks)
4547     (when info
4548       ;; Add all marks lists to the list of marks lists.
4549       (while (setq type (pop types))
4550         (setq list (symbol-value
4551                     (setq symbol
4552                           (intern (format "gnus-newsgroup-%s"
4553                                           (car type))))))
4554
4555         (when list
4556           ;; Get rid of the entries of the articles that have the
4557           ;; default score.
4558           (when (and (eq (cdr type) 'score)
4559                      gnus-save-score
4560                      list)
4561             (let* ((arts list)
4562                    (prev (cons nil list))
4563                    (all prev))
4564               (while arts
4565                 (if (or (not (consp (car arts)))
4566                         (= (cdar arts) gnus-summary-default-score))
4567                     (setcdr prev (cdr arts))
4568                   (setq prev arts))
4569                 (setq arts (cdr arts)))
4570               (setq list (cdr all)))))
4571
4572         (unless (memq (cdr type) uncompressed)
4573           (setq list (gnus-compress-sequence (set symbol (sort list '<)) t)))
4574        
4575         (when (gnus-check-backend-function
4576                'request-set-mark gnus-newsgroup-name)
4577           ;; propagate flags to server, with the following exceptions:
4578           ;; uncompressed:s are not proper flags (they are cons cells)
4579           ;; cache is a internal gnus flag
4580           ;; download are local to one gnus installation (well)
4581           ;; unsend are for nndraft groups only
4582           ;; xxx: generality of this?  this suits nnimap anyway
4583           (unless (memq (cdr type) (append '(cache download unsend)
4584                                            uncompressed))
4585             (let* ((old (cdr (assq (cdr type) (gnus-info-marks info))))
4586                    (del (gnus-remove-from-range (gnus-copy-sequence old) list))
4587                    (add (gnus-remove-from-range
4588                          (gnus-copy-sequence list) old)))
4589               (when add
4590                 (push (list add 'add (list (cdr type))) delta-marks))
4591               (when del
4592                 (push (list del 'del (list (cdr type))) delta-marks)))))
4593           
4594         (when list
4595           (push (cons (cdr type) list) newmarked)))
4596
4597       (when delta-marks
4598         (unless (gnus-check-group gnus-newsgroup-name)
4599           (error "Can't open server for %s" gnus-newsgroup-name))
4600         (gnus-request-set-mark gnus-newsgroup-name delta-marks))
4601           
4602       ;; Enter these new marks into the info of the group.
4603       (if (nthcdr 3 info)
4604           (setcar (nthcdr 3 info) newmarked)
4605         ;; Add the marks lists to the end of the info.
4606         (when newmarked
4607           (setcdr (nthcdr 2 info) (list newmarked))))
4608
4609       ;; Cut off the end of the info if there's nothing else there.
4610       (let ((i 5))
4611         (while (and (> i 2)
4612                     (not (nth i info)))
4613           (when (nthcdr (decf i) info)
4614             (setcdr (nthcdr i info) nil)))))))
4615
4616 (defun gnus-set-mode-line (where)
4617   "Set the mode line of the article or summary buffers.
4618 If WHERE is `summary', the summary mode line format will be used."
4619   ;; Is this mode line one we keep updated?
4620   (when (and (memq where gnus-updated-mode-lines)
4621              (symbol-value
4622               (intern (format "gnus-%s-mode-line-format-spec" where))))
4623     (let (mode-string)
4624       (save-excursion
4625         ;; We evaluate this in the summary buffer since these
4626         ;; variables are buffer-local to that buffer.
4627         (set-buffer gnus-summary-buffer)
4628         ;; We bind all these variables that are used in the `eval' form
4629         ;; below.
4630         (let* ((mformat (symbol-value
4631                          (intern
4632                           (format "gnus-%s-mode-line-format-spec" where))))
4633                (gnus-tmp-group-name (gnus-group-name-decode 
4634                                      gnus-newsgroup-name
4635                                      (gnus-group-name-charset 
4636                                       nil
4637                                       gnus-newsgroup-name)))
4638                (gnus-tmp-article-number (or gnus-current-article 0))
4639                (gnus-tmp-unread gnus-newsgroup-unreads)
4640                (gnus-tmp-unread-and-unticked (length gnus-newsgroup-unreads))
4641                (gnus-tmp-unselected (length gnus-newsgroup-unselected))
4642                (gnus-tmp-unread-and-unselected
4643                 (cond ((and (zerop gnus-tmp-unread-and-unticked)
4644                             (zerop gnus-tmp-unselected))
4645                        "")
4646                       ((zerop gnus-tmp-unselected)
4647                        (format "{%d more}" gnus-tmp-unread-and-unticked))
4648                       (t (format "{%d(+%d) more}"
4649                                  gnus-tmp-unread-and-unticked
4650                                  gnus-tmp-unselected))))
4651                (gnus-tmp-subject
4652                 (if (and gnus-current-headers
4653                          (vectorp gnus-current-headers))
4654                     (gnus-mode-string-quote
4655                      (mail-header-subject gnus-current-headers))
4656                   ""))
4657                bufname-length max-len
4658                gnus-tmp-header);; passed as argument to any user-format-funcs
4659           (setq mode-string (eval mformat))
4660           (setq bufname-length (if (string-match "%b" mode-string)
4661                                    (- (length
4662                                        (buffer-name
4663                                         (if (eq where 'summary)
4664                                             nil
4665                                           (get-buffer gnus-article-buffer))))
4666                                       2)
4667                                  0))
4668           (setq max-len (max 4 (if gnus-mode-non-string-length
4669                                    (- (window-width)
4670                                       gnus-mode-non-string-length
4671                                       bufname-length)
4672                                  (length mode-string))))
4673           ;; We might have to chop a bit of the string off...
4674           (when (> (length mode-string) max-len)
4675             (setq mode-string
4676                   (concat (truncate-string-to-width mode-string (- max-len 3))
4677                           "...")))
4678           ;; Pad the mode string a bit.
4679           (setq mode-string (format (format "%%-%ds" max-len) mode-string))))
4680       ;; Update the mode line.
4681       (setq mode-line-buffer-identification
4682             (gnus-mode-line-buffer-identification (list mode-string)))
4683       (set-buffer-modified-p t))))
4684
4685 (defun gnus-create-xref-hashtb (from-newsgroup headers unreads)
4686   "Go through the HEADERS list and add all Xrefs to a hash table.
4687 The resulting hash table is returned, or nil if no Xrefs were found."
4688   (let* ((virtual (gnus-virtual-group-p from-newsgroup))
4689          (prefix (if virtual "" (gnus-group-real-prefix from-newsgroup)))
4690          (xref-hashtb (gnus-make-hashtable))
4691          start group entry number xrefs header)
4692     (while headers
4693       (setq header (pop headers))
4694       (when (and (setq xrefs (mail-header-xref header))
4695                  (not (memq (setq number (mail-header-number header))
4696                             unreads)))
4697         (setq start 0)
4698         (while (string-match "\\([^ ]+\\)[:/]\\([0-9]+\\)" xrefs start)
4699           (setq start (match-end 0))
4700           (setq group (if prefix
4701                           (concat prefix (substring xrefs (match-beginning 1)
4702                                                     (match-end 1)))
4703                         (substring xrefs (match-beginning 1) (match-end 1))))
4704           (setq number
4705                 (string-to-int (substring xrefs (match-beginning 2)
4706                                           (match-end 2))))
4707           (if (setq entry (gnus-gethash group xref-hashtb))
4708               (setcdr entry (cons number (cdr entry)))
4709             (gnus-sethash group (cons number nil) xref-hashtb)))))
4710     (and start xref-hashtb)))
4711
4712 (defun gnus-mark-xrefs-as-read (from-newsgroup headers unreads)
4713   "Look through all the headers and mark the Xrefs as read."
4714   (let ((virtual (gnus-virtual-group-p from-newsgroup))
4715         name entry info xref-hashtb idlist method nth4)
4716     (save-excursion
4717       (set-buffer gnus-group-buffer)
4718       (when (setq xref-hashtb
4719                   (gnus-create-xref-hashtb from-newsgroup headers unreads))
4720         (mapatoms
4721          (lambda (group)
4722            (unless (string= from-newsgroup (setq name (symbol-name group)))
4723              (setq idlist (symbol-value group))
4724              ;; Dead groups are not updated.
4725              (and (prog1
4726                       (setq entry (gnus-gethash name gnus-newsrc-hashtb)
4727                             info (nth 2 entry))
4728                     (when (stringp (setq nth4 (gnus-info-method info)))
4729                       (setq nth4 (gnus-server-to-method nth4))))
4730                   ;; Only do the xrefs if the group has the same
4731                   ;; select method as the group we have just read.
4732                   (or (gnus-methods-equal-p
4733                        nth4 (gnus-find-method-for-group from-newsgroup))
4734                       virtual
4735                       (equal nth4 (setq method (gnus-find-method-for-group
4736                                                 from-newsgroup)))
4737                       (and (equal (car nth4) (car method))
4738                            (equal (nth 1 nth4) (nth 1 method))))
4739                   gnus-use-cross-reference
4740                   (or (not (eq gnus-use-cross-reference t))
4741                       virtual
4742                       ;; Only do cross-references on subscribed
4743                       ;; groups, if that is what is wanted.
4744                       (<= (gnus-info-level info) gnus-level-subscribed))
4745                   (gnus-group-make-articles-read name idlist))))
4746          xref-hashtb)))))
4747
4748 (defun gnus-compute-read-articles (group articles)
4749   (let* ((entry (gnus-gethash group gnus-newsrc-hashtb))
4750          (info (nth 2 entry))
4751          (active (gnus-active group))
4752          ninfo)
4753     (when entry
4754       ;; First peel off all invalid article numbers.
4755       (when active
4756         (let ((ids articles)
4757               id first)
4758           (while (setq id (pop ids))
4759             (when (and first (> id (cdr active)))
4760               ;; We'll end up in this situation in one particular
4761               ;; obscure situation.  If you re-scan a group and get
4762               ;; a new article that is cross-posted to a different
4763               ;; group that has not been re-scanned, you might get
4764               ;; crossposted article that has a higher number than
4765               ;; Gnus believes possible.  So we re-activate this
4766               ;; group as well.  This might mean doing the
4767               ;; crossposting thingy will *increase* the number
4768               ;; of articles in some groups.  Tsk, tsk.
4769               (setq active (or (gnus-activate-group group) active)))
4770             (when (or (> id (cdr active))
4771                       (< id (car active)))
4772               (setq articles (delq id articles))))))
4773       ;; If the read list is nil, we init it.
4774       (if (and active
4775                (null (gnus-info-read info))
4776                (> (car active) 1))
4777           (setq ninfo (cons 1 (1- (car active))))
4778         (setq ninfo (gnus-info-read info)))
4779       ;; Then we add the read articles to the range.
4780       (gnus-add-to-range
4781        ninfo (setq articles (sort articles '<))))))
4782
4783 (defun gnus-group-make-articles-read (group articles)
4784   "Update the info of GROUP to say that ARTICLES are read."
4785   (let* ((num 0)
4786          (entry (gnus-gethash group gnus-newsrc-hashtb))
4787          (info (nth 2 entry))
4788          (active (gnus-active group))
4789          range)
4790     (when entry
4791       (setq range (gnus-compute-read-articles group articles))
4792       (save-excursion
4793         (set-buffer gnus-group-buffer)
4794         (gnus-undo-register
4795           `(progn
4796              (gnus-info-set-marks ',info ',(gnus-info-marks info) t)
4797              (gnus-info-set-read ',info ',(gnus-info-read info))
4798              (gnus-get-unread-articles-in-group ',info (gnus-active ,group))
4799              (gnus-group-update-group ,group t))))
4800       ;; Add the read articles to the range.
4801       (gnus-info-set-read info range)
4802       ;; Then we have to re-compute how many unread
4803       ;; articles there are in this group.
4804       (when active
4805         (cond
4806          ((not range)
4807           (setq num (- (1+ (cdr active)) (car active))))
4808          ((not (listp (cdr range)))
4809           (setq num (- (cdr active) (- (1+ (cdr range))
4810                                        (car range)))))
4811          (t
4812           (while range
4813             (if (numberp (car range))
4814                 (setq num (1+ num))
4815               (setq num (+ num (- (1+ (cdar range)) (caar range)))))
4816             (setq range (cdr range)))
4817           (setq num (- (cdr active) num))))
4818         ;; Update the number of unread articles.
4819         (setcar entry num)
4820         ;; Update the group buffer.
4821         (gnus-group-update-group group t)))))
4822
4823 (defvar gnus-newsgroup-none-id 0)
4824
4825 (defun gnus-get-newsgroup-headers (&optional dependencies force-new)
4826   (let ((cur nntp-server-buffer)
4827         (dependencies
4828          (or dependencies
4829              (save-excursion (set-buffer gnus-summary-buffer)
4830                              gnus-newsgroup-dependencies)))
4831         headers id end ref
4832         (mail-parse-charset gnus-newsgroup-charset)
4833         (mail-parse-ignored-charsets
4834          (save-excursion (condition-case nil
4835                              (set-buffer gnus-summary-buffer)
4836                            (error))
4837                          gnus-newsgroup-ignored-charsets)))
4838     (save-excursion
4839       (set-buffer nntp-server-buffer)
4840       ;; Translate all TAB characters into SPACE characters.
4841       (subst-char-in-region (point-min) (point-max) ?\t ?  t)
4842       (subst-char-in-region (point-min) (point-max) ?\r ?  t)
4843       (gnus-run-hooks 'gnus-parse-headers-hook)
4844       (let ((case-fold-search t)
4845             in-reply-to header p lines chars)
4846         (goto-char (point-min))
4847         ;; Search to the beginning of the next header.  Error messages
4848         ;; do not begin with 2 or 3.
4849         (while (re-search-forward "^[23][0-9]+ " nil t)
4850           (setq id nil
4851                 ref nil)
4852           ;; This implementation of this function, with nine
4853           ;; search-forwards instead of the one re-search-forward and
4854           ;; a case (which basically was the old function) is actually
4855           ;; about twice as fast, even though it looks messier.  You
4856           ;; can't have everything, I guess.  Speed and elegance
4857           ;; doesn't always go hand in hand.
4858           (setq
4859            header
4860            (vector
4861             ;; Number.
4862             (prog1
4863                 (read cur)
4864               (end-of-line)
4865               (setq p (point))
4866               (narrow-to-region (point)
4867                                 (or (and (search-forward "\n.\n" nil t)
4868                                          (- (point) 2))
4869                                     (point))))
4870             ;; Subject.
4871             (progn
4872               (goto-char p)
4873               (if (search-forward "\nsubject: " nil t)
4874                   (funcall gnus-decode-encoded-word-function
4875                            (nnheader-header-value))
4876                 "(none)"))
4877             ;; From.
4878             (progn
4879               (goto-char p)
4880               (if (or (search-forward "\nfrom: " nil t)
4881                       (search-forward "\nfrom:" nil t))
4882                   (funcall gnus-decode-encoded-word-function
4883                            (nnheader-header-value))
4884                 "(nobody)"))
4885             ;; Date.
4886             (progn
4887               (goto-char p)
4888               (if (search-forward "\ndate: " nil t)
4889                   (nnheader-header-value) ""))
4890             ;; Message-ID.
4891             (progn
4892               (goto-char p)
4893               (setq id (if (re-search-forward
4894                             "^message-id: *\\(<[^\n\t> ]+>\\)" nil t)
4895                            ;; We do it this way to make sure the Message-ID
4896                            ;; is (somewhat) syntactically valid.
4897                            (buffer-substring (match-beginning 1)
4898                                              (match-end 1))
4899                          ;; If there was no message-id, we just fake one
4900                          ;; to make subsequent routines simpler.
4901                          (nnheader-generate-fake-message-id))))
4902             ;; References.
4903             (progn
4904               (goto-char p)
4905               (if (search-forward "\nreferences: " nil t)
4906                   (progn
4907                     (setq end (point))
4908                     (prog1
4909                         (nnheader-header-value)
4910                       (setq ref
4911                             (buffer-substring
4912                              (progn
4913                                (end-of-line)
4914                                (search-backward ">" end t)
4915                                (1+ (point)))
4916                              (progn
4917                                (search-backward "<" end t)
4918                                (point))))))
4919                 ;; Get the references from the in-reply-to header if there
4920                 ;; were no references and the in-reply-to header looks
4921                 ;; promising.
4922                 (if (and (search-forward "\nin-reply-to: " nil t)
4923                          (setq in-reply-to (nnheader-header-value))
4924                          (string-match "<[^>]+>" in-reply-to))
4925                     (let (ref2)
4926                       (setq ref (substring in-reply-to (match-beginning 0)
4927                                            (match-end 0)))
4928                       (while (string-match "<[^>]+>" in-reply-to (match-end 0))
4929                         (setq ref2 (substring in-reply-to (match-beginning 0)
4930                                               (match-end 0)))
4931                         (when (> (length ref2) (length ref))
4932                           (setq ref ref2)))
4933                       ref)
4934                   (setq ref nil))))
4935             ;; Chars.
4936             (progn
4937               (goto-char p)
4938               (if (search-forward "\nchars: " nil t)
4939                   (if (numberp (setq chars (ignore-errors (read cur))))
4940                       chars 0)
4941                 0))
4942             ;; Lines.
4943             (progn
4944               (goto-char p)
4945               (if (search-forward "\nlines: " nil t)
4946                   (if (numberp (setq lines (ignore-errors (read cur))))
4947                       lines 0)
4948                 0))
4949             ;; Xref.
4950             (progn
4951               (goto-char p)
4952               (and (search-forward "\nxref: " nil t)
4953                    (nnheader-header-value)))
4954             ;; Extra.
4955             (when gnus-extra-headers
4956               (let ((extra gnus-extra-headers)
4957                     out)
4958                 (while extra
4959                   (goto-char p)
4960                   (when (search-forward
4961                          (concat "\n" (symbol-name (car extra)) ": ") nil t)
4962                     (push (cons (car extra) (nnheader-header-value))
4963                           out))
4964                   (pop extra))
4965                 out))))
4966           (when (equal id ref)
4967             (setq ref nil))
4968
4969           (when gnus-alter-header-function
4970             (funcall gnus-alter-header-function header)
4971             (setq id (mail-header-id header)
4972                   ref (gnus-parent-id (mail-header-references header))))
4973
4974           (when (setq header
4975                       (gnus-dependencies-add-header
4976                        header dependencies force-new))
4977             (push header headers))
4978           (goto-char (point-max))
4979           (widen))
4980         (nreverse headers)))))
4981
4982 ;; Goes through the xover lines and returns a list of vectors
4983 (defun gnus-get-newsgroup-headers-xover (sequence &optional
4984                                                   force-new dependencies
4985                                                   group also-fetch-heads)
4986   "Parse the news overview data in the server buffer.
4987 Return a list of headers that match SEQUENCE (see
4988 `nntp-retrieve-headers')."
4989   ;; Get the Xref when the users reads the articles since most/some
4990   ;; NNTP servers do not include Xrefs when using XOVER.
4991   (setq gnus-article-internal-prepare-hook '(gnus-article-get-xrefs))
4992   (let ((mail-parse-charset gnus-newsgroup-charset)
4993         (mail-parse-ignored-charsets gnus-newsgroup-ignored-charsets)
4994         (cur nntp-server-buffer)
4995         (dependencies (or dependencies gnus-newsgroup-dependencies))
4996         number headers header)
4997     (save-excursion
4998       (set-buffer nntp-server-buffer)
4999       (subst-char-in-region (point-min) (point-max) ?\r ?  t)
5000       ;; Allow the user to mangle the headers before parsing them.
5001       (gnus-run-hooks 'gnus-parse-headers-hook)
5002       (goto-char (point-min))
5003       (while (not (eobp))
5004         (condition-case ()
5005             (while (and sequence (not (eobp)))
5006               (setq number (read cur))
5007               (while (and sequence
5008                           (< (car sequence) number))
5009                 (setq sequence (cdr sequence)))
5010               (and sequence
5011                    (eq number (car sequence))
5012                    (progn
5013                      (setq sequence (cdr sequence))
5014                      (setq header (inline
5015                                     (gnus-nov-parse-line
5016                                      number dependencies force-new))))
5017                    (push header headers))
5018               (forward-line 1))
5019           (error
5020            (gnus-error 4 "Strange nov line (%d)"
5021                        (count-lines (point-min) (point)))))
5022         (forward-line 1))
5023       ;; A common bug in inn is that if you have posted an article and
5024       ;; then retrieves the active file, it will answer correctly --
5025       ;; the new article is included.  However, a NOV entry for the
5026       ;; article may not have been generated yet, so this may fail.
5027       ;; We work around this problem by retrieving the last few
5028       ;; headers using HEAD.
5029       (if (or (not also-fetch-heads)
5030               (not sequence))
5031           ;; We (probably) got all the headers.
5032           (nreverse headers)
5033         (let ((gnus-nov-is-evil t))
5034           (nconc
5035            (nreverse headers)
5036            (when (gnus-retrieve-headers sequence group)
5037              (gnus-get-newsgroup-headers))))))))
5038
5039 (defun gnus-article-get-xrefs ()
5040   "Fill in the Xref value in `gnus-current-headers', if necessary.
5041 This is meant to be called in `gnus-article-internal-prepare-hook'."
5042   (let ((headers (save-excursion (set-buffer gnus-summary-buffer)
5043                                  gnus-current-headers)))
5044     (or (not gnus-use-cross-reference)
5045         (not headers)
5046         (and (mail-header-xref headers)
5047              (not (string= (mail-header-xref headers) "")))
5048         (let ((case-fold-search t)
5049               xref)
5050           (save-restriction
5051             (nnheader-narrow-to-headers)
5052             (goto-char (point-min))
5053             (when (or (and (not (eobp))
5054                            (eq (downcase (char-after)) ?x)
5055                            (looking-at "Xref:"))
5056                       (search-forward "\nXref:" nil t))
5057               (goto-char (1+ (match-end 0)))
5058               (setq xref (buffer-substring (point)
5059                                            (progn (end-of-line) (point))))
5060               (mail-header-set-xref headers xref)))))))
5061
5062 (defun gnus-summary-insert-subject (id &optional old-header use-old-header)
5063   "Find article ID and insert the summary line for that article.
5064 OLD-HEADER can either be a header or a line number to insert
5065 the subject line on."
5066   (let* ((line (and (numberp old-header) old-header))
5067          (old-header (and (vectorp old-header) old-header))
5068          (header (cond ((and old-header use-old-header)
5069                         old-header)
5070                        ((and (numberp id)
5071                              (gnus-number-to-header id))
5072                         (gnus-number-to-header id))
5073                        (t
5074                         (gnus-read-header id))))
5075          (number (and (numberp id) id))
5076          d)
5077     (when header
5078       ;; Rebuild the thread that this article is part of and go to the
5079       ;; article we have fetched.
5080       (when (and (not gnus-show-threads)
5081                  old-header)
5082         (when (and number
5083                    (setq d (gnus-data-find (mail-header-number old-header))))
5084           (goto-char (gnus-data-pos d))
5085           (gnus-data-remove
5086            number
5087            (- (gnus-point-at-bol)
5088               (prog1
5089                   (1+ (gnus-point-at-eol))
5090                 (gnus-delete-line))))))
5091       (when old-header
5092         (mail-header-set-number header (mail-header-number old-header)))
5093       (setq gnus-newsgroup-sparse
5094             (delq (setq number (mail-header-number header))
5095                   gnus-newsgroup-sparse))
5096       (setq gnus-newsgroup-ancient (delq number gnus-newsgroup-ancient))
5097       (push number gnus-newsgroup-limit)
5098       (gnus-rebuild-thread (mail-header-id header) line)
5099       (gnus-summary-goto-subject number nil t))
5100     (when (and (numberp number)
5101                (> number 0))
5102       ;; We have to update the boundaries even if we can't fetch the
5103       ;; article if ID is a number -- so that the next `P' or `N'
5104       ;; command will fetch the previous (or next) article even
5105       ;; if the one we tried to fetch this time has been canceled.
5106       (when (> number gnus-newsgroup-end)
5107         (setq gnus-newsgroup-end number))
5108       (when (< number gnus-newsgroup-begin)
5109         (setq gnus-newsgroup-begin number))
5110       (setq gnus-newsgroup-unselected
5111             (delq number gnus-newsgroup-unselected)))
5112     ;; Report back a success?
5113     (and header (mail-header-number header))))
5114
5115 ;;; Process/prefix in the summary buffer
5116
5117 (defun gnus-summary-work-articles (n)
5118   "Return a list of articles to be worked upon.
5119 The prefix argument, the list of process marked articles, and the
5120 current article will be taken into consideration."
5121   (save-excursion
5122     (set-buffer gnus-summary-buffer)
5123     (cond
5124      (n
5125       ;; A numerical prefix has been given.
5126       (setq n (prefix-numeric-value n))
5127       (let ((backward (< n 0))
5128             (n (abs (prefix-numeric-value n)))
5129             articles article)
5130         (save-excursion
5131           (while
5132               (and (> n 0)
5133                    (push (setq article (gnus-summary-article-number))
5134                          articles)
5135                    (if backward
5136                        (gnus-summary-find-prev nil article)
5137                      (gnus-summary-find-next nil article)))
5138             (decf n)))
5139         (nreverse articles)))
5140      ((and (gnus-region-active-p) (mark))
5141       (message "region active")
5142       ;; Work on the region between point and mark.
5143       (let ((max (max (point) (mark)))
5144             articles article)
5145         (save-excursion
5146           (goto-char (min (min (point) (mark))))
5147           (while
5148               (and
5149                (push (setq article (gnus-summary-article-number)) articles)
5150                (gnus-summary-find-next nil article)
5151                (< (point) max)))
5152           (nreverse articles))))
5153      (gnus-newsgroup-processable
5154       ;; There are process-marked articles present.
5155       ;; Save current state.
5156       (gnus-summary-save-process-mark)
5157       ;; Return the list.
5158       (reverse gnus-newsgroup-processable))
5159      (t
5160       ;; Just return the current article.
5161       (list (gnus-summary-article-number))))))
5162
5163 (defmacro gnus-summary-iterate (arg &rest forms)
5164   "Iterate over the process/prefixed articles and do FORMS.
5165 ARG is the interactive prefix given to the command.  FORMS will be
5166 executed with point over the summary line of the articles."
5167   (let ((articles (make-symbol "gnus-summary-iterate-articles")))
5168     `(let ((,articles (gnus-summary-work-articles ,arg)))
5169        (while ,articles
5170          (gnus-summary-goto-subject (car ,articles))
5171          ,@forms
5172          (pop ,articles)))))
5173
5174 (put 'gnus-summary-iterate 'lisp-indent-function 1)
5175 (put 'gnus-summary-iterate 'edebug-form-spec '(form body))
5176
5177 (defun gnus-summary-save-process-mark ()
5178   "Push the current set of process marked articles on the stack."
5179   (interactive)
5180   (push (copy-sequence gnus-newsgroup-processable)
5181         gnus-newsgroup-process-stack))
5182
5183 (defun gnus-summary-kill-process-mark ()
5184   "Push the current set of process marked articles on the stack and unmark."
5185   (interactive)
5186   (gnus-summary-save-process-mark)
5187   (gnus-summary-unmark-all-processable))
5188
5189 (defun gnus-summary-yank-process-mark ()
5190   "Pop the last process mark state off the stack and restore it."
5191   (interactive)
5192   (unless gnus-newsgroup-process-stack
5193     (error "Empty mark stack"))
5194   (gnus-summary-process-mark-set (pop gnus-newsgroup-process-stack)))
5195
5196 (defun gnus-summary-process-mark-set (set)
5197   "Make SET into the current process marked articles."
5198   (gnus-summary-unmark-all-processable)
5199   (while set
5200     (gnus-summary-set-process-mark (pop set))))
5201
5202 ;;; Searching and stuff
5203
5204 (defun gnus-summary-search-group (&optional backward use-level)
5205   "Search for next unread newsgroup.
5206 If optional argument BACKWARD is non-nil, search backward instead."
5207   (save-excursion
5208     (set-buffer gnus-group-buffer)
5209     (when (gnus-group-search-forward
5210            backward nil (if use-level (gnus-group-group-level) nil))
5211       (gnus-group-group-name))))
5212
5213 (defun gnus-summary-best-group (&optional exclude-group)
5214   "Find the name of the best unread group.
5215 If EXCLUDE-GROUP, do not go to this group."
5216   (save-excursion
5217     (set-buffer gnus-group-buffer)
5218     (save-excursion
5219       (gnus-group-best-unread-group exclude-group))))
5220
5221 (defun gnus-summary-find-next (&optional unread article backward undownloaded)
5222   (if backward (gnus-summary-find-prev)
5223     (let* ((dummy (gnus-summary-article-intangible-p))
5224            (article (or article (gnus-summary-article-number)))
5225            (arts (gnus-data-find-list article))
5226            result)
5227       (when (and (not dummy)
5228                  (or (not gnus-summary-check-current)
5229                      (not unread)
5230                      (not (gnus-data-unread-p (car arts)))))
5231         (setq arts (cdr arts)))
5232       (when (setq result
5233                   (if unread
5234                       (progn
5235                         (while arts
5236                           (when (or (and undownloaded
5237                                          (eq gnus-undownloaded-mark
5238                                              (gnus-data-mark (car arts))))
5239                                     (gnus-data-unread-p (car arts)))
5240                             (setq result (car arts)
5241                                   arts nil))
5242                           (setq arts (cdr arts)))
5243                         result)
5244                     (car arts)))
5245         (goto-char (gnus-data-pos result))
5246         (gnus-data-number result)))))
5247
5248 (defun gnus-summary-find-prev (&optional unread article)
5249   (let* ((eobp (eobp))
5250          (article (or article (gnus-summary-article-number)))
5251          (arts (gnus-data-find-list article (gnus-data-list 'rev)))
5252          result)
5253     (when (and (not eobp)
5254                (or (not gnus-summary-check-current)
5255                    (not unread)
5256                    (not (gnus-data-unread-p (car arts)))))
5257       (setq arts (cdr arts)))
5258     (when (setq result
5259                 (if unread
5260                     (progn
5261                       (while arts
5262                         (when (gnus-data-unread-p (car arts))
5263                           (setq result (car arts)
5264                                 arts nil))
5265                         (setq arts (cdr arts)))
5266                       result)
5267                   (car arts)))
5268       (goto-char (gnus-data-pos result))
5269       (gnus-data-number result))))
5270
5271 (defun gnus-summary-find-subject (subject &optional unread backward article)
5272   (let* ((simp-subject (gnus-simplify-subject-fully subject))
5273          (article (or article (gnus-summary-article-number)))
5274          (articles (gnus-data-list backward))
5275          (arts (gnus-data-find-list article articles))
5276          result)
5277     (when (or (not gnus-summary-check-current)
5278               (not unread)
5279               (not (gnus-data-unread-p (car arts))))
5280       (setq arts (cdr arts)))
5281     (while arts
5282       (and (or (not unread)
5283                (gnus-data-unread-p (car arts)))
5284            (vectorp (gnus-data-header (car arts)))
5285            (gnus-subject-equal
5286             simp-subject (mail-header-subject (gnus-data-header (car arts))) t)
5287            (setq result (car arts)
5288                  arts nil))
5289       (setq arts (cdr arts)))
5290     (and result
5291          (goto-char (gnus-data-pos result))
5292          (gnus-data-number result))))
5293
5294 (defun gnus-summary-search-forward (&optional unread subject backward)
5295   "Search forward for an article.
5296 If UNREAD, look for unread articles.  If SUBJECT, look for
5297 articles with that subject.  If BACKWARD, search backward instead."
5298   (cond (subject (gnus-summary-find-subject subject unread backward))
5299         (backward (gnus-summary-find-prev unread))
5300         (t (gnus-summary-find-next unread))))
5301
5302 (defun gnus-recenter (&optional n)
5303   "Center point in window and redisplay frame.
5304 Also do horizontal recentering."
5305   (interactive "P")
5306   (when (and gnus-auto-center-summary
5307              (not (eq gnus-auto-center-summary 'vertical)))
5308     (gnus-horizontal-recenter))
5309   (recenter n))
5310
5311 (defun gnus-summary-recenter ()
5312   "Center point in the summary window.
5313 If `gnus-auto-center-summary' is nil, or the article buffer isn't
5314 displayed, no centering will be performed."
5315   ;; Suggested by earle@mahendo.JPL.NASA.GOV (Greg Earle).
5316   ;; Recenter only when requested.  Suggested by popovich@park.cs.columbia.edu.
5317   (interactive)
5318   (let* ((top (cond ((< (window-height) 4) 0)
5319                     ((< (window-height) 7) 1)
5320                     (t (if (numberp gnus-auto-center-summary)
5321                            gnus-auto-center-summary
5322                          2))))
5323          (height (1- (window-height)))
5324          (bottom (save-excursion (goto-char (point-max))
5325                                  (forward-line (- height))
5326                                  (point)))
5327          (window (get-buffer-window (current-buffer))))
5328     ;; The user has to want it.
5329     (when gnus-auto-center-summary
5330       (when (get-buffer-window gnus-article-buffer)
5331         ;; Only do recentering when the article buffer is displayed,
5332         ;; Set the window start to either `bottom', which is the biggest
5333         ;; possible valid number, or the second line from the top,
5334         ;; whichever is the least.
5335         (let ((top-pos (save-excursion (forward-line (- top)) (point))))
5336           (if (> bottom top-pos)
5337               ;; Keep the second line from the top visible
5338               (set-window-start window top-pos t)
5339             ;; Try to keep the bottom line visible; if it's partially
5340             ;; obscured, either scroll one more line to make it fully
5341             ;; visible, or revert to using TOP-POS.
5342             (save-excursion
5343               (goto-char (point-max))
5344               (forward-line -1)
5345               (let ((last-line-start (point)))
5346                 (goto-char bottom)
5347                 (set-window-start window (point) t)
5348                 (when (not (pos-visible-in-window-p last-line-start window))
5349                   (forward-line 1)
5350                   (set-window-start window (min (point) top-pos) t)))))))
5351       ;; Do horizontal recentering while we're at it.
5352       (when (and (get-buffer-window (current-buffer) t)
5353                  (not (eq gnus-auto-center-summary 'vertical)))
5354         (let ((selected (selected-window)))
5355           (select-window (get-buffer-window (current-buffer) t))
5356           (gnus-summary-position-point)
5357           (gnus-horizontal-recenter)
5358           (select-window selected))))))
5359
5360 (defun gnus-summary-jump-to-group (newsgroup)
5361   "Move point to NEWSGROUP in group mode buffer."
5362   ;; Keep update point of group mode buffer if visible.
5363   (if (eq (current-buffer) (get-buffer gnus-group-buffer))
5364       (save-window-excursion
5365         ;; Take care of tree window mode.
5366         (when (get-buffer-window gnus-group-buffer)
5367           (pop-to-buffer gnus-group-buffer))
5368         (gnus-group-jump-to-group newsgroup))
5369     (save-excursion
5370       ;; Take care of tree window mode.
5371       (if (get-buffer-window gnus-group-buffer)
5372           (pop-to-buffer gnus-group-buffer)
5373         (set-buffer gnus-group-buffer))
5374       (gnus-group-jump-to-group newsgroup))))
5375
5376 ;; This function returns a list of article numbers based on the
5377 ;; difference between the ranges of read articles in this group and
5378 ;; the range of active articles.
5379 (defun gnus-list-of-unread-articles (group)
5380   (let* ((read (gnus-info-read (gnus-get-info group)))
5381          (active (or (gnus-active group) (gnus-activate-group group)))
5382          (last (cdr active))
5383          first nlast unread)
5384     ;; If none are read, then all are unread.
5385     (if (not read)
5386         (setq first (car active))
5387       ;; If the range of read articles is a single range, then the
5388       ;; first unread article is the article after the last read
5389       ;; article.  Sounds logical, doesn't it?
5390       (if (and (not (listp (cdr read)))
5391                (or (< (car read) (car active))
5392                    (progn (setq read (list read))
5393                           nil)))
5394           (setq first (max (car active) (1+ (cdr read))))
5395         ;; `read' is a list of ranges.
5396         (when (/= (setq nlast (or (and (numberp (car read)) (car read))
5397                                   (caar read)))
5398                   1)
5399           (setq first (car active)))
5400         (while read
5401           (when first
5402             (while (< first nlast)
5403               (push first unread)
5404               (setq first (1+ first))))
5405           (setq first (1+ (if (atom (car read)) (car read) (cdar read))))
5406           (setq nlast (if (atom (cadr read)) (cadr read) (caadr read)))
5407           (setq read (cdr read)))))
5408     ;; And add the last unread articles.
5409     (while (<= first last)
5410       (push first unread)
5411       (setq first (1+ first)))
5412     ;; Return the list of unread articles.
5413     (delq 0 (nreverse unread))))
5414
5415 (defun gnus-list-of-read-articles (group)
5416   "Return a list of unread, unticked and non-dormant articles."
5417   (let* ((info (gnus-get-info group))
5418          (marked (gnus-info-marks info))
5419          (active (gnus-active group)))
5420     (and info active
5421          (gnus-set-difference
5422           (gnus-sorted-complement
5423            (gnus-uncompress-range active)
5424            (gnus-list-of-unread-articles group))
5425           (append
5426            (gnus-uncompress-range (cdr (assq 'dormant marked)))
5427            (gnus-uncompress-range (cdr (assq 'tick marked))))))))
5428
5429 ;; Various summary commands
5430
5431 (defun gnus-summary-select-article-buffer ()
5432   "Reconfigure windows to show article buffer."
5433   (interactive)
5434   (if (not (gnus-buffer-live-p gnus-article-buffer))
5435       (error "There is no article buffer for this summary buffer")
5436     (gnus-configure-windows 'article)
5437     (select-window (get-buffer-window gnus-article-buffer))))
5438
5439 (defun gnus-summary-universal-argument (arg)
5440   "Perform any operation on all articles that are process/prefixed."
5441   (interactive "P")
5442   (let ((articles (gnus-summary-work-articles arg))
5443         func article)
5444     (if (eq
5445          (setq
5446           func
5447           (key-binding
5448            (read-key-sequence
5449             (substitute-command-keys
5450              "\\<gnus-summary-mode-map>\\[gnus-summary-universal-argument]"))))
5451          'undefined)
5452         (gnus-error 1 "Undefined key")
5453       (save-excursion
5454         (while articles
5455           (gnus-summary-goto-subject (setq article (pop articles)))
5456           (let (gnus-newsgroup-processable)
5457             (command-execute func))
5458           (gnus-summary-remove-process-mark article)))))
5459   (gnus-summary-position-point))
5460
5461 (defun gnus-summary-toggle-truncation (&optional arg)
5462   "Toggle truncation of summary lines.
5463 With arg, turn line truncation on iff arg is positive."
5464   (interactive "P")
5465   (setq truncate-lines
5466         (if (null arg) (not truncate-lines)
5467           (> (prefix-numeric-value arg) 0)))
5468   (redraw-display))
5469
5470 (defun gnus-summary-reselect-current-group (&optional all rescan)
5471   "Exit and then reselect the current newsgroup.
5472 The prefix argument ALL means to select all articles."
5473   (interactive "P")
5474   (when (gnus-ephemeral-group-p gnus-newsgroup-name)
5475     (error "Ephemeral groups can't be reselected"))
5476   (let ((current-subject (gnus-summary-article-number))
5477         (group gnus-newsgroup-name))
5478     (setq gnus-newsgroup-begin nil)
5479     (gnus-summary-exit)
5480     ;; We have to adjust the point of group mode buffer because
5481     ;; point was moved to the next unread newsgroup by exiting.
5482     (gnus-summary-jump-to-group group)
5483     (when rescan
5484       (save-excursion
5485         (gnus-group-get-new-news-this-group 1)))
5486     (gnus-group-read-group all t)
5487     (gnus-summary-goto-subject current-subject nil t)))
5488
5489 (defun gnus-summary-rescan-group (&optional all)
5490   "Exit the newsgroup, ask for new articles, and select the newsgroup."
5491   (interactive "P")
5492   (gnus-summary-reselect-current-group all t))
5493
5494 (defun gnus-summary-update-info (&optional non-destructive)
5495   (save-excursion
5496     (let ((group gnus-newsgroup-name))
5497       (when group
5498         (when gnus-newsgroup-kill-headers
5499           (setq gnus-newsgroup-killed
5500                 (gnus-compress-sequence
5501                  (nconc
5502                   (gnus-set-sorted-intersection
5503                    (gnus-uncompress-range gnus-newsgroup-killed)
5504                    (setq gnus-newsgroup-unselected
5505                          (sort gnus-newsgroup-unselected '<)))
5506                   (setq gnus-newsgroup-unreads
5507                         (sort gnus-newsgroup-unreads '<)))
5508                  t)))
5509         (unless (listp (cdr gnus-newsgroup-killed))
5510           (setq gnus-newsgroup-killed (list gnus-newsgroup-killed)))
5511         (let ((headers gnus-newsgroup-headers))
5512           ;; Set the new ranges of read articles.
5513           (save-excursion
5514             (set-buffer gnus-group-buffer)
5515             (gnus-undo-force-boundary))
5516           (gnus-update-read-articles
5517            group (append gnus-newsgroup-unreads gnus-newsgroup-unselected))
5518           ;; Set the current article marks.
5519           (let ((gnus-newsgroup-scored
5520                  (if (and (not gnus-save-score)
5521                           (not non-destructive))
5522                      nil
5523                    gnus-newsgroup-scored)))
5524             (save-excursion
5525               (gnus-update-marks)))
5526           ;; Do the cross-ref thing.
5527           (when gnus-use-cross-reference
5528             (gnus-mark-xrefs-as-read group headers gnus-newsgroup-unreads))
5529           ;; Do not switch windows but change the buffer to work.
5530           (set-buffer gnus-group-buffer)
5531           (unless (gnus-ephemeral-group-p group)
5532             (gnus-group-update-group group)))))))
5533
5534 (defun gnus-summary-save-newsrc (&optional force)
5535   "Save the current number of read/marked articles in the dribble buffer.
5536 The dribble buffer will then be saved.
5537 If FORCE (the prefix), also save the .newsrc file(s)."
5538   (interactive "P")
5539   (gnus-summary-update-info t)
5540   (if force
5541       (gnus-save-newsrc-file)
5542     (gnus-dribble-save)))
5543
5544 (defun gnus-summary-exit (&optional temporary)
5545   "Exit reading current newsgroup, and then return to group selection mode.
5546 `gnus-exit-group-hook' is called with no arguments if that value is non-nil."
5547   (interactive)
5548   (gnus-set-global-variables)
5549   (when (gnus-buffer-live-p gnus-article-buffer)
5550     (save-excursion
5551       (set-buffer gnus-article-buffer)
5552       (mm-destroy-parts gnus-article-mime-handles)
5553       ;; Set it to nil for safety reason.
5554       (setq gnus-article-mime-handle-alist nil)
5555       (setq gnus-article-mime-handles nil)))
5556   (gnus-kill-save-kill-buffer)
5557   (gnus-async-halt-prefetch)
5558   (let* ((group gnus-newsgroup-name)
5559          (quit-config (gnus-group-quit-config gnus-newsgroup-name))
5560          (mode major-mode)
5561          (group-point nil)
5562          (buf (current-buffer)))
5563     (unless quit-config
5564       ;; Do adaptive scoring, and possibly save score files.
5565       (when gnus-newsgroup-adaptive
5566         (gnus-score-adaptive))
5567       (when gnus-use-scoring
5568         (gnus-score-save)))
5569     (gnus-run-hooks 'gnus-summary-prepare-exit-hook)
5570     ;; If we have several article buffers, we kill them at exit.
5571     (unless gnus-single-article-buffer
5572       (gnus-kill-buffer gnus-original-article-buffer)
5573       (setq gnus-article-current nil))
5574     (when gnus-use-cache
5575       (gnus-cache-possibly-remove-articles)
5576       (gnus-cache-save-buffers))
5577     (gnus-async-prefetch-remove-group group)
5578     (when gnus-suppress-duplicates
5579       (gnus-dup-enter-articles))
5580     (when gnus-use-trees
5581       (gnus-tree-close group))
5582     (when gnus-use-cache
5583       (gnus-cache-write-active))
5584     ;; Remove entries for this group.
5585     (nnmail-purge-split-history (gnus-group-real-name group))
5586     ;; Make all changes in this group permanent.
5587     (unless quit-config
5588       (gnus-run-hooks 'gnus-exit-group-hook)
5589       (gnus-summary-update-info))
5590     (gnus-close-group group)
5591     ;; Make sure where we were, and go to next newsgroup.
5592     (set-buffer gnus-group-buffer)
5593     (unless quit-config
5594       (gnus-group-jump-to-group group))
5595     (gnus-run-hooks 'gnus-summary-exit-hook)
5596     (unless (or quit-config
5597                 ;; If this group has disappeared from the summary
5598                 ;; buffer, don't skip forwards.
5599                 (not (string= group (gnus-group-group-name))))
5600       (gnus-group-next-unread-group 1))
5601     (setq group-point (point))
5602     (if temporary
5603         nil                             ;Nothing to do.
5604       ;; If we have several article buffers, we kill them at exit.
5605       (unless gnus-single-article-buffer
5606         (gnus-kill-buffer gnus-article-buffer)
5607         (gnus-kill-buffer gnus-original-article-buffer)
5608         (setq gnus-article-current nil))
5609       (set-buffer buf)
5610       (if (not gnus-kill-summary-on-exit)
5611           (gnus-deaden-summary)
5612         ;; We set all buffer-local variables to nil.  It is unclear why
5613         ;; this is needed, but if we don't, buffer-local variables are
5614         ;; not garbage-collected, it seems.  This would the lead to en
5615         ;; ever-growing Emacs.
5616         (gnus-summary-clear-local-variables)
5617         (let ((gnus-summary-local-variables gnus-newsgroup-variables))
5618           (gnus-summary-clear-local-variables))
5619         (when (get-buffer gnus-article-buffer)
5620           (bury-buffer gnus-article-buffer))
5621         ;; We clear the global counterparts of the buffer-local
5622         ;; variables as well, just to be on the safe side.
5623         (set-buffer gnus-group-buffer)
5624         (gnus-summary-clear-local-variables)
5625         (let ((gnus-summary-local-variables gnus-newsgroup-variables))
5626           (gnus-summary-clear-local-variables))
5627         ;; Return to group mode buffer.
5628         (when (eq mode 'gnus-summary-mode)
5629           (gnus-kill-buffer buf)))
5630       (setq gnus-current-select-method gnus-select-method)
5631       (pop-to-buffer gnus-group-buffer)
5632       (if (not quit-config)
5633           (progn
5634             (goto-char group-point)
5635             (gnus-configure-windows 'group 'force))
5636         (gnus-handle-ephemeral-exit quit-config))
5637       ;; Clear the current group name.
5638       (unless quit-config
5639         (setq gnus-newsgroup-name nil)))))
5640
5641 (defalias 'gnus-summary-quit 'gnus-summary-exit-no-update)
5642 (defun gnus-summary-exit-no-update (&optional no-questions)
5643   "Quit reading current newsgroup without updating read article info."
5644   (interactive)
5645   (let* ((group gnus-newsgroup-name)
5646          (quit-config (gnus-group-quit-config group)))
5647     (when (or no-questions
5648               gnus-expert-user
5649               (gnus-y-or-n-p "Discard changes to this group and exit? "))
5650       (gnus-async-halt-prefetch)
5651       (mapcar 'funcall
5652               (delq 'gnus-summary-expire-articles
5653                     (copy-sequence gnus-summary-prepare-exit-hook)))
5654       (when (gnus-buffer-live-p gnus-article-buffer)
5655         (save-excursion
5656           (set-buffer gnus-article-buffer)
5657           (mm-destroy-parts gnus-article-mime-handles)
5658           ;; Set it to nil for safety reason.
5659           (setq gnus-article-mime-handle-alist nil)
5660           (setq gnus-article-mime-handles nil)))
5661       ;; If we have several article buffers, we kill them at exit.
5662       (unless gnus-single-article-buffer
5663         (gnus-kill-buffer gnus-article-buffer)
5664         (gnus-kill-buffer gnus-original-article-buffer)
5665         (setq gnus-article-current nil))
5666       (if (not gnus-kill-summary-on-exit)
5667           (gnus-deaden-summary)
5668         (gnus-close-group group)
5669         (gnus-summary-clear-local-variables)
5670         (let ((gnus-summary-local-variables gnus-newsgroup-variables))
5671           (gnus-summary-clear-local-variables))
5672         (set-buffer gnus-group-buffer)
5673         (gnus-summary-clear-local-variables)
5674         (let ((gnus-summary-local-variables gnus-newsgroup-variables))
5675           (gnus-summary-clear-local-variables))
5676         (when (get-buffer gnus-summary-buffer)
5677           (kill-buffer gnus-summary-buffer)))
5678       (unless gnus-single-article-buffer
5679         (setq gnus-article-current nil))
5680       (when gnus-use-trees
5681         (gnus-tree-close group))
5682       (gnus-async-prefetch-remove-group group)
5683       (when (get-buffer gnus-article-buffer)
5684         (bury-buffer gnus-article-buffer))
5685       ;; Return to the group buffer.
5686       (gnus-configure-windows 'group 'force)
5687       ;; Clear the current group name.
5688       (setq gnus-newsgroup-name nil)
5689       (when (equal (gnus-group-group-name) group)
5690         (gnus-group-next-unread-group 1))
5691       (when quit-config
5692         (gnus-handle-ephemeral-exit quit-config)))))
5693
5694 (defun gnus-handle-ephemeral-exit (quit-config)
5695   "Handle movement when leaving an ephemeral group.
5696 The state which existed when entering the ephemeral is reset."
5697   (if (not (buffer-name (car quit-config)))
5698       (gnus-configure-windows 'group 'force)
5699     (set-buffer (car quit-config))
5700     (cond ((eq major-mode 'gnus-summary-mode)
5701            (gnus-set-global-variables))
5702           ((eq major-mode 'gnus-article-mode)
5703            (save-excursion
5704              ;; The `gnus-summary-buffer' variable may point
5705              ;; to the old summary buffer when using a single
5706              ;; article buffer.
5707              (unless (gnus-buffer-live-p gnus-summary-buffer)
5708                (set-buffer gnus-group-buffer))
5709              (set-buffer gnus-summary-buffer)
5710              (gnus-set-global-variables))))
5711     (if (or (eq (cdr quit-config) 'article)
5712             (eq (cdr quit-config) 'pick))
5713         (progn
5714           ;; The current article may be from the ephemeral group
5715           ;; thus it is best that we reload this article
5716           (gnus-summary-show-article)
5717           (if (and (boundp 'gnus-pick-mode) (symbol-value 'gnus-pick-mode))
5718               (gnus-configure-windows 'pick 'force)
5719             (gnus-configure-windows (cdr quit-config) 'force)))
5720       (gnus-configure-windows (cdr quit-config) 'force))
5721     (when (eq major-mode 'gnus-summary-mode)
5722       (gnus-summary-next-subject 1 nil t)
5723       (gnus-summary-recenter)
5724       (gnus-summary-position-point))))
5725
5726 ;;; Dead summaries.
5727
5728 (defvar gnus-dead-summary-mode-map nil)
5729
5730 (unless gnus-dead-summary-mode-map
5731   (setq gnus-dead-summary-mode-map (make-keymap))
5732   (suppress-keymap gnus-dead-summary-mode-map)
5733   (substitute-key-definition
5734    'undefined 'gnus-summary-wake-up-the-dead gnus-dead-summary-mode-map)
5735   (let ((keys '("\C-d" "\r" "\177" [delete])))
5736     (while keys
5737       (define-key gnus-dead-summary-mode-map
5738         (pop keys) 'gnus-summary-wake-up-the-dead))))
5739
5740 (defvar gnus-dead-summary-mode nil
5741   "Minor mode for Gnus summary buffers.")
5742
5743 (defun gnus-dead-summary-mode (&optional arg)
5744   "Minor mode for Gnus summary buffers."
5745   (interactive "P")
5746   (when (eq major-mode 'gnus-summary-mode)
5747     (make-local-variable 'gnus-dead-summary-mode)
5748     (setq gnus-dead-summary-mode
5749           (if (null arg) (not gnus-dead-summary-mode)
5750             (> (prefix-numeric-value arg) 0)))
5751     (when gnus-dead-summary-mode
5752       (gnus-add-minor-mode
5753        'gnus-dead-summary-mode " Dead" gnus-dead-summary-mode-map))))
5754
5755 (defun gnus-deaden-summary ()
5756   "Make the current summary buffer into a dead summary buffer."
5757   ;; Kill any previous dead summary buffer.
5758   (when (and gnus-dead-summary
5759              (buffer-name gnus-dead-summary))
5760     (save-excursion
5761       (set-buffer gnus-dead-summary)
5762       (when gnus-dead-summary-mode
5763         (kill-buffer (current-buffer)))))
5764   ;; Make this the current dead summary.
5765   (setq gnus-dead-summary (current-buffer))
5766   (gnus-dead-summary-mode 1)
5767   (let ((name (buffer-name)))
5768     (when (string-match "Summary" name)
5769       (rename-buffer
5770        (concat (substring name 0 (match-beginning 0)) "Dead "
5771                (substring name (match-beginning 0)))
5772        t)
5773       (bury-buffer))))
5774
5775 (defun gnus-kill-or-deaden-summary (buffer)
5776   "Kill or deaden the summary BUFFER."
5777   (save-excursion
5778     (when (and (buffer-name buffer)
5779                (not gnus-single-article-buffer))
5780       (save-excursion
5781         (set-buffer buffer)
5782         (gnus-kill-buffer gnus-article-buffer)
5783         (gnus-kill-buffer gnus-original-article-buffer)))
5784     (cond (gnus-kill-summary-on-exit
5785            (when (and gnus-use-trees
5786                       (gnus-buffer-exists-p buffer))
5787              (save-excursion
5788                (set-buffer buffer)
5789                (gnus-tree-close gnus-newsgroup-name)))
5790            (gnus-kill-buffer buffer))
5791           ((gnus-buffer-exists-p buffer)
5792            (save-excursion
5793              (set-buffer buffer)
5794              (gnus-deaden-summary))))))
5795
5796 (defun gnus-summary-wake-up-the-dead (&rest args)
5797   "Wake up the dead summary buffer."
5798   (interactive)
5799   (gnus-dead-summary-mode -1)
5800   (let ((name (buffer-name)))
5801     (when (string-match "Dead " name)
5802       (rename-buffer
5803        (concat (substring name 0 (match-beginning 0))
5804                (substring name (match-end 0)))
5805        t)))
5806   (gnus-message 3 "This dead summary is now alive again"))
5807
5808 ;; Suggested by Andrew Eskilsson <pi92ae@pt.hk-r.se>.
5809 (defun gnus-summary-fetch-faq (&optional faq-dir)
5810   "Fetch the FAQ for the current group.
5811 If FAQ-DIR (the prefix), prompt for a directory to search for the faq
5812 in."
5813   (interactive
5814    (list
5815     (when current-prefix-arg
5816       (completing-read
5817        "Faq dir: " (and (listp gnus-group-faq-directory)
5818                         (mapcar (lambda (file) (list file))
5819                                 gnus-group-faq-directory))))))
5820   (let (gnus-faq-buffer)
5821     (when (setq gnus-faq-buffer
5822                 (gnus-group-fetch-faq gnus-newsgroup-name faq-dir))
5823       (gnus-configure-windows 'summary-faq))))
5824
5825 ;; Suggested by Per Abrahamsen <amanda@iesd.auc.dk>.
5826 (defun gnus-summary-describe-group (&optional force)
5827   "Describe the current newsgroup."
5828   (interactive "P")
5829   (gnus-group-describe-group force gnus-newsgroup-name))
5830
5831 (defun gnus-summary-describe-briefly ()
5832   "Describe summary mode commands briefly."
5833   (interactive)
5834   (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")))
5835
5836 ;; Walking around group mode buffer from summary mode.
5837
5838 (defun gnus-summary-next-group (&optional no-article target-group backward)
5839   "Exit current newsgroup and then select next unread newsgroup.
5840 If prefix argument NO-ARTICLE is non-nil, no article is selected
5841 initially.  If NEXT-GROUP, go to this group.  If BACKWARD, go to
5842 previous group instead."
5843   (interactive "P")
5844   ;; Stop pre-fetching.
5845   (gnus-async-halt-prefetch)
5846   (let ((current-group gnus-newsgroup-name)
5847         (current-buffer (current-buffer))
5848         entered)
5849     ;; First we semi-exit this group to update Xrefs and all variables.
5850     ;; We can't do a real exit, because the window conf must remain
5851     ;; the same in case the user is prompted for info, and we don't
5852     ;; want the window conf to change before that...
5853     (gnus-summary-exit t)
5854     (while (not entered)
5855       ;; Then we find what group we are supposed to enter.
5856       (set-buffer gnus-group-buffer)
5857       (gnus-group-jump-to-group current-group)
5858       (setq target-group
5859             (or target-group
5860                 (if (eq gnus-keep-same-level 'best)
5861                     (gnus-summary-best-group gnus-newsgroup-name)
5862                   (gnus-summary-search-group backward gnus-keep-same-level))))
5863       (if (not target-group)
5864           ;; There are no further groups, so we return to the group
5865           ;; buffer.
5866           (progn
5867             (gnus-message 5 "Returning to the group buffer")
5868             (setq entered t)
5869             (when (gnus-buffer-live-p current-buffer)
5870               (set-buffer current-buffer)
5871               (gnus-summary-exit))
5872             (gnus-run-hooks 'gnus-group-no-more-groups-hook))
5873         ;; We try to enter the target group.
5874         (gnus-group-jump-to-group target-group)
5875         (let ((unreads (gnus-group-group-unread)))
5876           (if (and (or (eq t unreads)
5877                        (and unreads (not (zerop unreads))))
5878                    (gnus-summary-read-group
5879                     target-group nil no-article
5880                     (and (buffer-name current-buffer) current-buffer)
5881                     nil backward))
5882               (setq entered t)
5883             (setq current-group target-group
5884                   target-group nil)))))))
5885
5886 (defun gnus-summary-prev-group (&optional no-article)
5887   "Exit current newsgroup and then select previous unread newsgroup.
5888 If prefix argument NO-ARTICLE is non-nil, no article is selected initially."
5889   (interactive "P")
5890   (gnus-summary-next-group no-article nil t))
5891
5892 ;; Walking around summary lines.
5893
5894 (defun gnus-summary-first-subject (&optional unread undownloaded)
5895   "Go to the first unread subject.
5896 If UNREAD is non-nil, go to the first unread article.
5897 Returns the article selected or nil if there are no unread articles."
5898   (interactive "P")
5899   (prog1
5900       (cond
5901        ;; Empty summary.
5902        ((null gnus-newsgroup-data)
5903         (gnus-message 3 "No articles in the group")
5904         nil)
5905        ;; Pick the first article.
5906        ((not unread)
5907         (goto-char (gnus-data-pos (car gnus-newsgroup-data)))
5908         (gnus-data-number (car gnus-newsgroup-data)))
5909        ;; No unread articles.
5910        ((null gnus-newsgroup-unreads)
5911         (gnus-message 3 "No more unread articles")
5912         nil)
5913        ;; Find the first unread article.
5914        (t
5915         (let ((data gnus-newsgroup-data))
5916           (while (and data
5917                       (and (not (and undownloaded
5918                                      (eq gnus-undownloaded-mark
5919                                          (gnus-data-mark (car data)))))
5920                            (not (gnus-data-unread-p (car data)))))
5921             (setq data (cdr data)))
5922           (when data
5923             (goto-char (gnus-data-pos (car data)))
5924             (gnus-data-number (car data))))))
5925     (gnus-summary-position-point)))
5926
5927 (defun gnus-summary-next-subject (n &optional unread dont-display)
5928   "Go to next N'th summary line.
5929 If N is negative, go to the previous N'th subject line.
5930 If UNREAD is non-nil, only unread articles are selected.
5931 The difference between N and the actual number of steps taken is
5932 returned."
5933   (interactive "p")
5934   (let ((backward (< n 0))
5935         (n (abs n)))
5936     (while (and (> n 0)
5937                 (if backward
5938                     (gnus-summary-find-prev unread)
5939                   (gnus-summary-find-next unread)))
5940       (unless (zerop (setq n (1- n)))
5941         (gnus-summary-show-thread)))
5942     (when (/= 0 n)
5943       (gnus-message 7 "No more%s articles"
5944                     (if unread " unread" "")))
5945     (unless dont-display
5946       (gnus-summary-recenter)
5947       (gnus-summary-position-point))
5948     n))
5949
5950 (defun gnus-summary-next-unread-subject (n)
5951   "Go to next N'th unread summary line."
5952   (interactive "p")
5953   (gnus-summary-next-subject n t))
5954
5955 (defun gnus-summary-prev-subject (n &optional unread)
5956   "Go to previous N'th summary line.
5957 If optional argument UNREAD is non-nil, only unread article is selected."
5958   (interactive "p")
5959   (gnus-summary-next-subject (- n) unread))
5960
5961 (defun gnus-summary-prev-unread-subject (n)
5962   "Go to previous N'th unread summary line."
5963   (interactive "p")
5964   (gnus-summary-next-subject (- n) t))
5965
5966 (defun gnus-summary-goto-subject (article &optional force silent)
5967   "Go the subject line of ARTICLE.
5968 If FORCE, also allow jumping to articles not currently shown."
5969   (interactive "nArticle number: ")
5970   (let ((b (point))
5971         (data (gnus-data-find article)))
5972     ;; We read in the article if we have to.
5973     (and (not data)
5974          force
5975          (gnus-summary-insert-subject
5976           article
5977           (if (or (numberp force) (vectorp force)) force)
5978           t)
5979          (setq data (gnus-data-find article)))
5980     (goto-char b)
5981     (if (not data)
5982         (progn
5983           (unless silent
5984             (gnus-message 3 "Can't find article %d" article))
5985           nil)
5986       (goto-char (gnus-data-pos data))
5987       (gnus-summary-position-point)
5988       article)))
5989
5990 ;; Walking around summary lines with displaying articles.
5991
5992 (defun gnus-summary-expand-window (&optional arg)
5993   "Make the summary buffer take up the entire Emacs frame.
5994 Given a prefix, will force an `article' buffer configuration."
5995   (interactive "P")
5996   (if arg
5997       (gnus-configure-windows 'article 'force)
5998     (gnus-configure-windows 'summary 'force)))
5999
6000 (defun gnus-summary-display-article (article &optional all-header)
6001   "Display ARTICLE in article buffer."
6002   (when (gnus-buffer-live-p gnus-article-buffer)
6003     (with-current-buffer gnus-article-buffer
6004       (mm-enable-multibyte-mule4)))
6005   (gnus-set-global-variables)
6006   (when (gnus-buffer-live-p gnus-article-buffer)
6007     (with-current-buffer gnus-article-buffer
6008       (setq gnus-article-charset gnus-newsgroup-charset)
6009       (setq gnus-article-ignored-charsets gnus-newsgroup-ignored-charsets)
6010       (mm-enable-multibyte-mule4)))
6011   (if (null article)
6012       nil
6013     (prog1
6014         (if gnus-summary-display-article-function
6015             (funcall gnus-summary-display-article-function article all-header)
6016           (gnus-article-prepare article all-header))
6017       (gnus-run-hooks 'gnus-select-article-hook)
6018       (when (and gnus-current-article
6019                  (not (zerop gnus-current-article)))
6020         (gnus-summary-goto-subject gnus-current-article))
6021       (gnus-summary-recenter)
6022       (when (and gnus-use-trees gnus-show-threads)
6023         (gnus-possibly-generate-tree article)
6024         (gnus-highlight-selected-tree article))
6025       ;; Successfully display article.
6026       (gnus-article-set-window-start
6027        (cdr (assq article gnus-newsgroup-bookmarks))))))
6028
6029 (defun gnus-summary-select-article (&optional all-headers force pseudo article)
6030   "Select the current article.
6031 If ALL-HEADERS is non-nil, show all header fields.  If FORCE is
6032 non-nil, the article will be re-fetched even if it already present in
6033 the article buffer.  If PSEUDO is non-nil, pseudo-articles will also
6034 be displayed."
6035   ;; Make sure we are in the summary buffer to work around bbdb bug.
6036   (unless (eq major-mode 'gnus-summary-mode)
6037     (set-buffer gnus-summary-buffer))
6038   (let ((article (or article (gnus-summary-article-number)))
6039         (all-headers (not (not all-headers))) ;Must be T or NIL.
6040         gnus-summary-display-article-function)
6041     (and (not pseudo)
6042          (gnus-summary-article-pseudo-p article)
6043          (error "This is a pseudo-article"))
6044     (save-excursion
6045       (set-buffer gnus-summary-buffer)
6046       (if (or (and gnus-single-article-buffer
6047                    (or (null gnus-current-article)
6048                        (null gnus-article-current)
6049                        (null (get-buffer gnus-article-buffer))
6050                        (not (eq article (cdr gnus-article-current)))
6051                        (not (equal (car gnus-article-current)
6052                                    gnus-newsgroup-name))))
6053               (and (not gnus-single-article-buffer)
6054                    (or (null gnus-current-article)
6055                        (not (eq gnus-current-article article))))
6056               force)
6057           ;; The requested article is different from the current article.
6058           (progn
6059             (gnus-summary-display-article article all-headers)
6060             (when (gnus-buffer-live-p gnus-article-buffer)
6061                (with-current-buffer gnus-article-buffer
6062                 (if (not gnus-article-decoded-p) ;; a local variable
6063                     (mm-disable-multibyte-mule4))))
6064             (when (or all-headers gnus-show-all-headers)
6065               (gnus-article-show-all-headers))
6066             (gnus-article-set-window-start
6067              (cdr (assq article gnus-newsgroup-bookmarks)))
6068             article)
6069         (when (or all-headers gnus-show-all-headers)
6070           (gnus-article-show-all-headers))
6071         'old))))
6072
6073 (defun gnus-summary-force-verify-and-decrypt ()
6074   (interactive)
6075   (let ((mm-verify-option 'known)
6076         (mm-decrypt-option 'known))
6077     (gnus-summary-select-article nil 'force)))
6078
6079 (defun gnus-summary-set-current-mark (&optional current-mark)
6080   "Obsolete function."
6081   nil)
6082
6083 (defun gnus-summary-next-article (&optional unread subject backward push)
6084   "Select the next article.
6085 If UNREAD, only unread articles are selected.
6086 If SUBJECT, only articles with SUBJECT are selected.
6087 If BACKWARD, the previous article is selected instead of the next."
6088   (interactive "P")
6089   (cond
6090    ;; Is there such an article?
6091    ((and (gnus-summary-search-forward unread subject backward)
6092          (or (gnus-summary-display-article (gnus-summary-article-number))
6093              (eq (gnus-summary-article-mark) gnus-canceled-mark)))
6094     (gnus-summary-position-point))
6095    ;; If not, we try the first unread, if that is wanted.
6096    ((and subject
6097          gnus-auto-select-same
6098          (gnus-summary-first-unread-article))
6099     (gnus-summary-position-point)
6100     (gnus-message 6 "Wrapped"))
6101    ;; Try to get next/previous article not displayed in this group.
6102    ((and gnus-auto-extend-newsgroup
6103          (not unread) (not subject))
6104     (gnus-summary-goto-article
6105      (if backward (1- gnus-newsgroup-begin) (1+ gnus-newsgroup-end))
6106      nil (count-lines (point-min) (point))))
6107    ;; Go to next/previous group.
6108    (t
6109     (unless (gnus-ephemeral-group-p gnus-newsgroup-name)
6110       (gnus-summary-jump-to-group gnus-newsgroup-name))
6111     (let ((cmd last-command-char)
6112           (point
6113            (save-excursion
6114              (set-buffer gnus-group-buffer)
6115              (point)))
6116           (group
6117            (if (eq gnus-keep-same-level 'best)
6118                (gnus-summary-best-group gnus-newsgroup-name)
6119              (gnus-summary-search-group backward gnus-keep-same-level))))
6120       ;; For some reason, the group window gets selected.  We change
6121       ;; it back.
6122       (select-window (get-buffer-window (current-buffer)))
6123       ;; Select next unread newsgroup automagically.
6124       (cond
6125        ((or (not gnus-auto-select-next)
6126             (not cmd))
6127         (gnus-message 7 "No more%s articles" (if unread " unread" "")))
6128        ((or (eq gnus-auto-select-next 'quietly)
6129             (and (eq gnus-auto-select-next 'slightly-quietly)
6130                  push)
6131             (and (eq gnus-auto-select-next 'almost-quietly)
6132                  (gnus-summary-last-article-p)))
6133         ;; Select quietly.
6134         (if (gnus-ephemeral-group-p gnus-newsgroup-name)
6135             (gnus-summary-exit)
6136           (gnus-message 7 "No more%s articles (%s)..."
6137                         (if unread " unread" "")
6138                         (if group (concat "selecting " group)
6139                           "exiting"))
6140           (gnus-summary-next-group nil group backward)))
6141        (t
6142         (when (gnus-key-press-event-p last-input-event)
6143           (gnus-summary-walk-group-buffer
6144            gnus-newsgroup-name cmd unread backward point))))))))
6145
6146 (defun gnus-summary-walk-group-buffer (from-group cmd unread backward start)
6147   (let ((keystrokes '((?\C-n (gnus-group-next-unread-group 1))
6148                       (?\C-p (gnus-group-prev-unread-group 1))))
6149         (cursor-in-echo-area t)
6150         keve key group ended)
6151     (save-excursion
6152       (set-buffer gnus-group-buffer)
6153       (goto-char start)
6154       (setq group
6155             (if (eq gnus-keep-same-level 'best)
6156                 (gnus-summary-best-group gnus-newsgroup-name)
6157               (gnus-summary-search-group backward gnus-keep-same-level))))
6158     (while (not ended)
6159       (gnus-message
6160        5 "No more%s articles%s" (if unread " unread" "")
6161        (if (and group
6162                 (not (gnus-ephemeral-group-p gnus-newsgroup-name)))
6163            (format " (Type %s for %s [%s])"
6164                    (single-key-description cmd) group
6165                    (car (gnus-gethash group gnus-newsrc-hashtb)))
6166          (format " (Type %s to exit %s)"
6167                  (single-key-description cmd)
6168                  gnus-newsgroup-name)))
6169       ;; Confirm auto selection.
6170       (setq key (car (setq keve (gnus-read-event-char))))
6171       (setq ended t)
6172       (cond
6173        ((assq key keystrokes)
6174         (let ((obuf (current-buffer)))
6175           (switch-to-buffer gnus-group-buffer)
6176           (when group
6177             (gnus-group-jump-to-group group))
6178           (eval (cadr (assq key keystrokes)))
6179           (setq group (gnus-group-group-name))
6180           (switch-to-buffer obuf))
6181         (setq ended nil))
6182        ((equal key cmd)
6183         (if (or (not group)
6184                 (gnus-ephemeral-group-p gnus-newsgroup-name))
6185             (gnus-summary-exit)
6186           (gnus-summary-next-group nil group backward)))
6187        (t
6188         (push (cdr keve) unread-command-events))))))
6189
6190 (defun gnus-summary-next-unread-article ()
6191   "Select unread article after current one."
6192   (interactive)
6193   (gnus-summary-next-article
6194    (or (not (eq gnus-summary-goto-unread 'never))
6195        (gnus-summary-last-article-p (gnus-summary-article-number)))
6196    (and gnus-auto-select-same
6197         (gnus-summary-article-subject))))
6198
6199 (defun gnus-summary-prev-article (&optional unread subject)
6200   "Select the article after the current one.
6201 If UNREAD is non-nil, only unread articles are selected."
6202   (interactive "P")
6203   (gnus-summary-next-article unread subject t))
6204
6205 (defun gnus-summary-prev-unread-article ()
6206   "Select unread article before current one."
6207   (interactive)
6208   (gnus-summary-prev-article
6209    (or (not (eq gnus-summary-goto-unread 'never))
6210        (gnus-summary-first-article-p (gnus-summary-article-number)))
6211    (and gnus-auto-select-same
6212         (gnus-summary-article-subject))))
6213
6214 (defun gnus-summary-next-page (&optional lines circular)
6215   "Show next page of the selected article.
6216 If at the end of the current article, select the next article.
6217 LINES says how many lines should be scrolled up.
6218
6219 If CIRCULAR is non-nil, go to the start of the article instead of
6220 selecting the next article when reaching the end of the current
6221 article."
6222   (interactive "P")
6223   (setq gnus-summary-buffer (current-buffer))
6224   (gnus-set-global-variables)
6225   (let ((article (gnus-summary-article-number))
6226         (article-window (get-buffer-window gnus-article-buffer t))
6227         endp)
6228     ;; If the buffer is empty, we have no article.
6229     (unless article
6230       (error "No article to select"))
6231     (gnus-configure-windows 'article)
6232     (if (eq (cdr (assq article gnus-newsgroup-reads)) gnus-canceled-mark)
6233         (if (and (eq gnus-summary-goto-unread 'never)
6234                  (not (gnus-summary-last-article-p article)))
6235             (gnus-summary-next-article)
6236           (gnus-summary-next-unread-article))
6237       (if (or (null gnus-current-article)
6238               (null gnus-article-current)
6239               (/= article (cdr gnus-article-current))
6240               (not (equal (car gnus-article-current) gnus-newsgroup-name)))
6241           ;; Selected subject is different from current article's.
6242           (gnus-summary-display-article article)
6243         (when article-window
6244           (gnus-eval-in-buffer-window gnus-article-buffer
6245             (setq endp (gnus-article-next-page lines)))
6246           (when endp
6247             (cond (circular
6248                    (gnus-summary-beginning-of-article))
6249                   (lines
6250                    (gnus-message 3 "End of message"))
6251                   ((null lines)
6252                    (if (and (eq gnus-summary-goto-unread 'never)
6253                             (not (gnus-summary-last-article-p article)))
6254                        (gnus-summary-next-article)
6255                      (gnus-summary-next-unread-article))))))))
6256     (gnus-summary-recenter)
6257     (gnus-summary-position-point)))
6258
6259 (defun gnus-summary-prev-page (&optional lines move)
6260   "Show previous page of selected article.
6261 Argument LINES specifies lines to be scrolled down.
6262 If MOVE, move to the previous unread article if point is at
6263 the beginning of the buffer."
6264   (interactive "P")
6265   (let ((article (gnus-summary-article-number))
6266         (article-window (get-buffer-window gnus-article-buffer t))
6267         endp)
6268     (gnus-configure-windows 'article)
6269     (if (or (null gnus-current-article)
6270             (null gnus-article-current)
6271             (/= article (cdr gnus-article-current))
6272             (not (equal (car gnus-article-current) gnus-newsgroup-name)))
6273         ;; Selected subject is different from current article's.
6274         (gnus-summary-display-article article)
6275       (gnus-summary-recenter)
6276       (when article-window
6277         (gnus-eval-in-buffer-window gnus-article-buffer
6278           (setq endp (gnus-article-prev-page lines)))
6279         (when (and move endp)
6280           (cond (lines
6281                  (gnus-message 3 "Beginning of message"))
6282                 ((null lines)
6283                  (if (and (eq gnus-summary-goto-unread 'never)
6284                           (not (gnus-summary-first-article-p article)))
6285                      (gnus-summary-prev-article)
6286                    (gnus-summary-prev-unread-article))))))))
6287   (gnus-summary-position-point))
6288
6289 (defun gnus-summary-prev-page-or-article (&optional lines)
6290   "Show previous page of selected article.
6291 Argument LINES specifies lines to be scrolled down.
6292 If at the beginning of the article, go to the next article."
6293   (interactive "P")
6294   (gnus-summary-prev-page lines t))
6295
6296 (defun gnus-summary-scroll-up (lines)
6297   "Scroll up (or down) one line current article.
6298 Argument LINES specifies lines to be scrolled up (or down if negative)."
6299   (interactive "p")
6300   (gnus-configure-windows 'article)
6301   (gnus-summary-show-thread)
6302   (when (eq (gnus-summary-select-article nil nil 'pseudo) 'old)
6303     (gnus-eval-in-buffer-window gnus-article-buffer
6304       (cond ((> lines 0)
6305              (when (gnus-article-next-page lines)
6306                (gnus-message 3 "End of message")))
6307             ((< lines 0)
6308              (gnus-article-prev-page (- lines))))))
6309   (gnus-summary-recenter)
6310   (gnus-summary-position-point))
6311
6312 (defun gnus-summary-scroll-down (lines)
6313   "Scroll down (or up) one line current article.
6314 Argument LINES specifies lines to be scrolled down (or up if negative)."
6315   (interactive "p")
6316   (gnus-summary-scroll-up (- lines)))
6317
6318 (defun gnus-summary-next-same-subject ()
6319   "Select next article which has the same subject as current one."
6320   (interactive)
6321   (gnus-summary-next-article nil (gnus-summary-article-subject)))
6322
6323 (defun gnus-summary-prev-same-subject ()
6324   "Select previous article which has the same subject as current one."
6325   (interactive)
6326   (gnus-summary-prev-article nil (gnus-summary-article-subject)))
6327
6328 (defun gnus-summary-next-unread-same-subject ()
6329   "Select next unread article which has the same subject as current one."
6330   (interactive)
6331   (gnus-summary-next-article t (gnus-summary-article-subject)))
6332
6333 (defun gnus-summary-prev-unread-same-subject ()
6334   "Select previous unread article which has the same subject as current one."
6335   (interactive)
6336   (gnus-summary-prev-article t (gnus-summary-article-subject)))
6337
6338 (defun gnus-summary-first-unread-article ()
6339   "Select the first unread article.
6340 Return nil if there are no unread articles."
6341   (interactive)
6342   (prog1
6343       (when (gnus-summary-first-subject t)
6344         (gnus-summary-show-thread)
6345         (gnus-summary-first-subject t)
6346         (gnus-summary-display-article (gnus-summary-article-number)))
6347     (gnus-summary-position-point)))
6348
6349 (defun gnus-summary-first-unread-subject ()
6350   "Place the point on the subject line of the first unread article.
6351 Return nil if there are no unread articles."
6352   (interactive)
6353   (prog1
6354       (when (gnus-summary-first-subject t)
6355         (gnus-summary-show-thread)
6356         (gnus-summary-first-subject t))
6357     (gnus-summary-position-point)))
6358
6359 (defun gnus-summary-first-article ()
6360   "Select the first article.
6361 Return nil if there are no articles."
6362   (interactive)
6363   (prog1
6364       (when (gnus-summary-first-subject)
6365         (gnus-summary-show-thread)
6366         (gnus-summary-first-subject)
6367         (gnus-summary-display-article (gnus-summary-article-number)))
6368     (gnus-summary-position-point)))
6369
6370 (defun gnus-summary-best-unread-article ()
6371   "Select the unread article with the highest score."
6372   (interactive)
6373   (let ((best -1000000)
6374         (data gnus-newsgroup-data)
6375         article score)
6376     (while data
6377       (and (gnus-data-unread-p (car data))
6378            (> (setq score
6379                     (gnus-summary-article-score (gnus-data-number (car data))))
6380               best)
6381            (setq best score
6382                  article (gnus-data-number (car data))))
6383       (setq data (cdr data)))
6384     (prog1
6385         (if article
6386             (gnus-summary-goto-article article)
6387           (error "No unread articles"))
6388       (gnus-summary-position-point))))
6389
6390 (defun gnus-summary-last-subject ()
6391   "Go to the last displayed subject line in the group."
6392   (let ((article (gnus-data-number (car (gnus-data-list t)))))
6393     (when article
6394       (gnus-summary-goto-subject article))))
6395
6396 (defun gnus-summary-goto-article (article &optional all-headers force)
6397   "Fetch ARTICLE (article number or Message-ID) and display it if it exists.
6398 If ALL-HEADERS is non-nil, no header lines are hidden.
6399 If FORCE, go to the article even if it isn't displayed.  If FORCE
6400 is a number, it is the line the article is to be displayed on."
6401   (interactive
6402    (list
6403     (completing-read
6404      "Article number or Message-ID: "
6405      (mapcar (lambda (number) (list (int-to-string number)))
6406              gnus-newsgroup-limit))
6407     current-prefix-arg
6408     t))
6409   (prog1
6410       (if (and (stringp article)
6411                (string-match "@" article))
6412           (gnus-summary-refer-article article)
6413         (when (stringp article)
6414           (setq article (string-to-number article)))
6415         (if (gnus-summary-goto-subject article force)
6416             (gnus-summary-display-article article all-headers)
6417           (gnus-message 4 "Couldn't go to article %s" article) nil))
6418     (gnus-summary-position-point)))
6419
6420 (defun gnus-summary-goto-last-article ()
6421   "Go to the previously read article."
6422   (interactive)
6423   (prog1
6424       (when gnus-last-article
6425         (gnus-summary-goto-article gnus-last-article nil t))
6426     (gnus-summary-position-point)))
6427
6428 (defun gnus-summary-pop-article (number)
6429   "Pop one article off the history and go to the previous.
6430 NUMBER articles will be popped off."
6431   (interactive "p")
6432   (let (to)
6433     (setq gnus-newsgroup-history
6434           (cdr (setq to (nthcdr number gnus-newsgroup-history))))
6435     (if to
6436         (gnus-summary-goto-article (car to) nil t)
6437       (error "Article history empty")))
6438   (gnus-summary-position-point))
6439
6440 ;; Summary commands and functions for limiting the summary buffer.
6441
6442 (defun gnus-summary-limit-to-articles (n)
6443   "Limit the summary buffer to the next N articles.
6444 If not given a prefix, use the process marked articles instead."
6445   (interactive "P")
6446   (prog1
6447       (let ((articles (gnus-summary-work-articles n)))
6448         (setq gnus-newsgroup-processable nil)
6449         (gnus-summary-limit articles))
6450     (gnus-summary-position-point)))
6451
6452 (defun gnus-summary-pop-limit (&optional total)
6453   "Restore the previous limit.
6454 If given a prefix, remove all limits."
6455   (interactive "P")
6456   (when total
6457     (setq gnus-newsgroup-limits
6458           (list (mapcar (lambda (h) (mail-header-number h))
6459                         gnus-newsgroup-headers))))
6460   (unless gnus-newsgroup-limits
6461     (error "No limit to pop"))
6462   (prog1
6463       (gnus-summary-limit nil 'pop)
6464     (gnus-summary-position-point)))
6465
6466 (defun gnus-summary-limit-to-subject (subject &optional header)
6467   "Limit the summary buffer to articles that have subjects that match a regexp."
6468   (interactive "sLimit to subject (regexp): ")
6469   (unless header
6470     (setq header "subject"))
6471   (when (not (equal "" subject))
6472     (prog1
6473         (let ((articles (gnus-summary-find-matching
6474                          (or header "subject") subject 'all)))
6475           (unless articles
6476             (error "Found no matches for \"%s\"" subject))
6477           (gnus-summary-limit articles))
6478       (gnus-summary-position-point))))
6479
6480 (defun gnus-summary-limit-to-author (from)
6481   "Limit the summary buffer to articles that have authors that match a regexp."
6482   (interactive "sLimit to author (regexp): ")
6483   (gnus-summary-limit-to-subject from "from"))
6484
6485 (defun gnus-summary-limit-to-age (age &optional younger-p)
6486   "Limit the summary buffer to articles that are older than (or equal) AGE days.
6487 If YOUNGER-P (the prefix) is non-nil, limit the summary buffer to
6488 articles that are younger than AGE days."
6489   (interactive
6490    (let ((younger current-prefix-arg)
6491          (days-got nil)
6492          days)
6493      (while (not days-got)
6494        (setq days (if younger
6495                       (read-string "Limit to articles within (in days): ")
6496                     (read-string "Limit to articles older than (in days): ")))
6497        (when (> (length days) 0)
6498          (setq days (read days)))
6499        (if (numberp days)
6500            (setq days-got t)
6501          (message "Please enter a number.")
6502          (sleep-for 1)))
6503      (list days younger)))
6504   (prog1
6505       (let ((data gnus-newsgroup-data)
6506             (cutoff (days-to-time age))
6507             articles d date is-younger)
6508         (while (setq d (pop data))
6509           (when (and (vectorp (gnus-data-header d))
6510                      (setq date (mail-header-date (gnus-data-header d))))
6511             (setq is-younger (time-less-p
6512                               (time-since (condition-case ()
6513                                               (date-to-time date)
6514                                             (error '(0 0))))
6515                               cutoff))
6516             (when (if younger-p
6517                       is-younger
6518                     (not is-younger))
6519               (push (gnus-data-number d) articles))))
6520         (gnus-summary-limit (nreverse articles)))
6521     (gnus-summary-position-point)))
6522
6523 (defun gnus-summary-limit-to-extra (header regexp)
6524   "Limit the summary buffer to articles that match an 'extra' header."
6525   (interactive
6526    (let ((header
6527           (intern
6528            (gnus-completing-read
6529             (symbol-name (car gnus-extra-headers))
6530             "Limit extra header:"
6531             (mapcar (lambda (x)
6532                       (cons (symbol-name x) x))
6533                     gnus-extra-headers)
6534             nil
6535             t))))
6536      (list header
6537            (read-string (format "Limit to header %s (regexp): " header)))))
6538   (when (not (equal "" regexp))
6539     (prog1
6540         (let ((articles (gnus-summary-find-matching
6541                          (cons 'extra header) regexp 'all)))
6542           (unless articles
6543             (error "Found no matches for \"%s\"" regexp))
6544           (gnus-summary-limit articles))
6545       (gnus-summary-position-point))))
6546
6547 (defalias 'gnus-summary-delete-marked-as-read 'gnus-summary-limit-to-unread)
6548 (make-obsolete
6549  'gnus-summary-delete-marked-as-read 'gnus-summary-limit-to-unread)
6550
6551 (defun gnus-summary-limit-to-unread (&optional all)
6552   "Limit the summary buffer to articles that are not marked as read.
6553 If ALL is non-nil, limit strictly to unread articles."
6554   (interactive "P")
6555   (if all
6556       (gnus-summary-limit-to-marks (char-to-string gnus-unread-mark))
6557     (gnus-summary-limit-to-marks
6558      ;; Concat all the marks that say that an article is read and have
6559      ;; those removed.
6560      (list gnus-del-mark gnus-read-mark gnus-ancient-mark
6561            gnus-killed-mark gnus-kill-file-mark
6562            gnus-low-score-mark gnus-expirable-mark
6563            gnus-canceled-mark gnus-catchup-mark gnus-sparse-mark
6564            gnus-duplicate-mark gnus-souped-mark)
6565      'reverse)))
6566
6567 (defalias 'gnus-summary-delete-marked-with 'gnus-summary-limit-exclude-marks)
6568 (make-obsolete 'gnus-summary-delete-marked-with
6569                'gnus-summary-limit-exlude-marks)
6570
6571 (defun gnus-summary-limit-exclude-marks (marks &optional reverse)
6572   "Exclude articles that are marked with MARKS (e.g. \"DK\").
6573 If REVERSE, limit the summary buffer to articles that are marked
6574 with MARKS.  MARKS can either be a string of marks or a list of marks.
6575 Returns how many articles were removed."
6576   (interactive "sMarks: ")
6577   (gnus-summary-limit-to-marks marks t))
6578
6579 (defun gnus-summary-limit-to-marks (marks &optional reverse)
6580   "Limit the summary buffer to articles that are marked with MARKS (e.g. \"DK\").
6581 If REVERSE (the prefix), limit the summary buffer to articles that are
6582 not marked with MARKS.  MARKS can either be a string of marks or a
6583 list of marks.
6584 Returns how many articles were removed."
6585   (interactive "sMarks: \nP")
6586   (prog1
6587       (let ((data gnus-newsgroup-data)
6588             (marks (if (listp marks) marks
6589                      (append marks nil))) ; Transform to list.
6590             articles)
6591         (while data
6592           (when (if reverse (not (memq (gnus-data-mark (car data)) marks))
6593                   (memq (gnus-data-mark (car data)) marks))
6594             (push (gnus-data-number (car data)) articles))
6595           (setq data (cdr data)))
6596         (gnus-summary-limit articles))
6597     (gnus-summary-position-point)))
6598
6599 (defun gnus-summary-limit-to-score (&optional score)
6600   "Limit to articles with score at or above SCORE."
6601   (interactive "P")
6602   (setq score (if score
6603                   (prefix-numeric-value score)
6604                 (or gnus-summary-default-score 0)))
6605   (let ((data gnus-newsgroup-data)
6606         articles)
6607     (while data
6608       (when (>= (gnus-summary-article-score (gnus-data-number (car data)))
6609                 score)
6610         (push (gnus-data-number (car data)) articles))
6611       (setq data (cdr data)))
6612     (prog1
6613         (gnus-summary-limit articles)
6614       (gnus-summary-position-point))))
6615
6616 (defun gnus-summary-limit-include-thread (id)
6617   "Display all the hidden articles that in the current thread."
6618   (interactive (list (mail-header-id (gnus-summary-article-header))))
6619   (let ((articles (gnus-articles-in-thread
6620                    (gnus-id-to-thread (gnus-root-id id)))))
6621     (prog1
6622         (gnus-summary-limit (nconc articles gnus-newsgroup-limit))
6623       (gnus-summary-position-point))))
6624
6625 (defun gnus-summary-limit-include-dormant ()
6626   "Display all the hidden articles that are marked as dormant.
6627 Note that this command only works on a subset of the articles currently
6628 fetched for this group."
6629   (interactive)
6630   (unless gnus-newsgroup-dormant
6631     (error "There are no dormant articles in this group"))
6632   (prog1
6633       (gnus-summary-limit (append gnus-newsgroup-dormant gnus-newsgroup-limit))
6634     (gnus-summary-position-point)))
6635
6636 (defun gnus-summary-limit-exclude-dormant ()
6637   "Hide all dormant articles."
6638   (interactive)
6639   (prog1
6640       (gnus-summary-limit-to-marks (list gnus-dormant-mark) 'reverse)
6641     (gnus-summary-position-point)))
6642
6643 (defun gnus-summary-limit-exclude-childless-dormant ()
6644   "Hide all dormant articles that have no children."
6645   (interactive)
6646   (let ((data (gnus-data-list t))
6647         articles d children)
6648     ;; Find all articles that are either not dormant or have
6649     ;; children.
6650     (while (setq d (pop data))
6651       (when (or (not (= (gnus-data-mark d) gnus-dormant-mark))
6652                 (and (setq children
6653                            (gnus-article-children (gnus-data-number d)))
6654                      (let (found)
6655                        (while children
6656                          (when (memq (car children) articles)
6657                            (setq children nil
6658                                  found t))
6659                          (pop children))
6660                        found)))
6661         (push (gnus-data-number d) articles)))
6662     ;; Do the limiting.
6663     (prog1
6664         (gnus-summary-limit articles)
6665       (gnus-summary-position-point))))
6666
6667 (defun gnus-summary-limit-mark-excluded-as-read (&optional all)
6668   "Mark all unread excluded articles as read.
6669 If ALL, mark even excluded ticked and dormants as read."
6670   (interactive "P")
6671   (let ((articles (gnus-sorted-complement
6672                    (sort
6673                     (mapcar (lambda (h) (mail-header-number h))
6674                             gnus-newsgroup-headers)
6675                     '<)
6676                    (sort gnus-newsgroup-limit '<)))
6677         article)
6678     (setq gnus-newsgroup-unreads
6679           (gnus-intersection gnus-newsgroup-unreads gnus-newsgroup-limit))
6680     (if all
6681         (setq gnus-newsgroup-dormant nil
6682               gnus-newsgroup-marked nil
6683               gnus-newsgroup-reads
6684               (nconc
6685                (mapcar (lambda (n) (cons n gnus-catchup-mark)) articles)
6686                gnus-newsgroup-reads))
6687       (while (setq article (pop articles))
6688         (unless (or (memq article gnus-newsgroup-dormant)
6689                     (memq article gnus-newsgroup-marked))
6690           (push (cons article gnus-catchup-mark) gnus-newsgroup-reads))))))
6691
6692 (defun gnus-summary-limit (articles &optional pop)
6693   (if pop
6694       ;; We pop the previous limit off the stack and use that.
6695       (setq articles (car gnus-newsgroup-limits)
6696             gnus-newsgroup-limits (cdr gnus-newsgroup-limits))
6697     ;; We use the new limit, so we push the old limit on the stack.
6698     (push gnus-newsgroup-limit gnus-newsgroup-limits))
6699   ;; Set the limit.
6700   (setq gnus-newsgroup-limit articles)
6701   (let ((total (length gnus-newsgroup-data))
6702         (data (gnus-data-find-list (gnus-summary-article-number)))
6703         (gnus-summary-mark-below nil)   ; Inhibit this.
6704         found)
6705     ;; This will do all the work of generating the new summary buffer
6706     ;; according to the new limit.
6707     (gnus-summary-prepare)
6708     ;; Hide any threads, possibly.
6709     (and gnus-show-threads
6710          gnus-thread-hide-subtree
6711          (gnus-summary-hide-all-threads))
6712     ;; Try to return to the article you were at, or one in the
6713     ;; neighborhood.
6714     (when data
6715       ;; We try to find some article after the current one.
6716       (while data
6717         (when (gnus-summary-goto-subject (gnus-data-number (car data)) nil t)
6718           (setq data nil
6719                 found t))
6720         (setq data (cdr data))))
6721     (unless found
6722       ;; If there is no data, that means that we were after the last
6723       ;; article.  The same goes when we can't find any articles
6724       ;; after the current one.
6725       (goto-char (point-max))
6726       (gnus-summary-find-prev))
6727     (gnus-set-mode-line 'summary)
6728     ;; We return how many articles were removed from the summary
6729     ;; buffer as a result of the new limit.
6730     (- total (length gnus-newsgroup-data))))
6731
6732 (defsubst gnus-invisible-cut-children (threads)
6733   (let ((num 0))
6734     (while threads
6735       (when (memq (mail-header-number (caar threads)) gnus-newsgroup-limit)
6736         (incf num))
6737       (pop threads))
6738     (< num 2)))
6739
6740 (defsubst gnus-cut-thread (thread)
6741   "Go forwards in the thread until we find an article that we want to display."
6742   (when (or (eq gnus-fetch-old-headers 'some)
6743             (eq gnus-fetch-old-headers 'invisible)
6744             (numberp gnus-fetch-old-headers)
6745             (eq gnus-build-sparse-threads 'some)
6746             (eq gnus-build-sparse-threads 'more))
6747     ;; Deal with old-fetched headers and sparse threads.
6748     (while (and
6749             thread
6750             (or
6751              (gnus-summary-article-sparse-p (mail-header-number (car thread)))
6752              (gnus-summary-article-ancient-p
6753               (mail-header-number (car thread))))
6754             (if (or (<= (length (cdr thread)) 1)
6755                     (eq gnus-fetch-old-headers 'invisible))
6756                 (setq gnus-newsgroup-limit
6757                       (delq (mail-header-number (car thread))
6758                             gnus-newsgroup-limit)
6759                       thread (cadr thread))
6760               (when (gnus-invisible-cut-children (cdr thread))
6761                 (let ((th (cdr thread)))
6762                   (while th
6763                     (if (memq (mail-header-number (caar th))
6764                               gnus-newsgroup-limit)
6765                         (setq thread (car th)
6766                               th nil)
6767                       (setq th (cdr th))))))))))
6768   thread)
6769
6770 (defun gnus-cut-threads (threads)
6771   "Cut off all uninteresting articles from the beginning of threads."
6772   (when (or (eq gnus-fetch-old-headers 'some)
6773             (eq gnus-fetch-old-headers 'invisible)
6774             (numberp gnus-fetch-old-headers)
6775             (eq gnus-build-sparse-threads 'some)
6776             (eq gnus-build-sparse-threads 'more))
6777     (let ((th threads))
6778       (while th
6779         (setcar th (gnus-cut-thread (car th)))
6780         (setq th (cdr th)))))
6781   ;; Remove nixed out threads.
6782   (delq nil threads))
6783
6784 (defun gnus-summary-initial-limit (&optional show-if-empty)
6785   "Figure out what the initial limit is supposed to be on group entry.
6786 This entails weeding out unwanted dormants, low-scored articles,
6787 fetch-old-headers verbiage, and so on."
6788   ;; Most groups have nothing to remove.
6789   (if (or gnus-inhibit-limiting
6790           (and (null gnus-newsgroup-dormant)
6791                (not (eq gnus-fetch-old-headers 'some))
6792                (not (numberp gnus-fetch-old-headers))
6793                (not (eq gnus-fetch-old-headers 'invisible))
6794                (null gnus-summary-expunge-below)
6795                (not (eq gnus-build-sparse-threads 'some))
6796                (not (eq gnus-build-sparse-threads 'more))
6797                (null gnus-thread-expunge-below)
6798                (not gnus-use-nocem)))
6799       ()                                ; Do nothing.
6800     (push gnus-newsgroup-limit gnus-newsgroup-limits)
6801     (setq gnus-newsgroup-limit nil)
6802     (mapatoms
6803      (lambda (node)
6804        (unless (car (symbol-value node))
6805          ;; These threads have no parents -- they are roots.
6806          (let ((nodes (cdr (symbol-value node)))
6807                thread)
6808            (while nodes
6809              (if (and gnus-thread-expunge-below
6810                       (< (gnus-thread-total-score (car nodes))
6811                          gnus-thread-expunge-below))
6812                  (gnus-expunge-thread (pop nodes))
6813                (setq thread (pop nodes))
6814                (gnus-summary-limit-children thread))))))
6815      gnus-newsgroup-dependencies)
6816     ;; If this limitation resulted in an empty group, we might
6817     ;; pop the previous limit and use it instead.
6818     (when (and (not gnus-newsgroup-limit)
6819                show-if-empty)
6820       (setq gnus-newsgroup-limit (pop gnus-newsgroup-limits)))
6821     gnus-newsgroup-limit))
6822
6823 (defun gnus-summary-limit-children (thread)
6824   "Return 1 if this subthread is visible and 0 if it is not."
6825   ;; First we get the number of visible children to this thread.  This
6826   ;; is done by recursing down the thread using this function, so this
6827   ;; will really go down to a leaf article first, before slowly
6828   ;; working its way up towards the root.
6829   (when thread
6830     (let ((children
6831            (if (cdr thread)
6832                (apply '+ (mapcar 'gnus-summary-limit-children
6833                                  (cdr thread)))
6834              0))
6835           (number (mail-header-number (car thread)))
6836           score)
6837       (if (and
6838            (not (memq number gnus-newsgroup-marked))
6839            (or
6840             ;; If this article is dormant and has absolutely no visible
6841             ;; children, then this article isn't visible.
6842             (and (memq number gnus-newsgroup-dormant)
6843                  (zerop children))
6844             ;; If this is "fetch-old-headered" and there is no
6845             ;; visible children, then we don't want this article.
6846             (and (or (eq gnus-fetch-old-headers 'some)
6847                      (numberp gnus-fetch-old-headers))
6848                  (gnus-summary-article-ancient-p number)
6849                  (zerop children))
6850             ;; If this is "fetch-old-headered" and `invisible', then
6851             ;; we don't want this article.
6852             (and (eq gnus-fetch-old-headers 'invisible)
6853                  (gnus-summary-article-ancient-p number))
6854             ;; If this is a sparsely inserted article with no children,
6855             ;; we don't want it.
6856             (and (eq gnus-build-sparse-threads 'some)
6857                  (gnus-summary-article-sparse-p number)
6858                  (zerop children))
6859             ;; If we use expunging, and this article is really
6860             ;; low-scored, then we don't want this article.
6861             (when (and gnus-summary-expunge-below
6862                        (< (setq score
6863                                 (or (cdr (assq number gnus-newsgroup-scored))
6864                                     gnus-summary-default-score))
6865                           gnus-summary-expunge-below))
6866               ;; We increase the expunge-tally here, but that has
6867               ;; nothing to do with the limits, really.
6868               (incf gnus-newsgroup-expunged-tally)
6869               ;; We also mark as read here, if that's wanted.
6870               (when (and gnus-summary-mark-below
6871                          (< score gnus-summary-mark-below))
6872                 (setq gnus-newsgroup-unreads
6873                       (delq number gnus-newsgroup-unreads))
6874                 (if gnus-newsgroup-auto-expire
6875                     (push number gnus-newsgroup-expirable)
6876                   (push (cons number gnus-low-score-mark)
6877                         gnus-newsgroup-reads)))
6878               t)
6879             ;; Check NoCeM things.
6880             (if (and gnus-use-nocem
6881                      (gnus-nocem-unwanted-article-p
6882                       (mail-header-id (car thread))))
6883                 (progn
6884                   (setq gnus-newsgroup-unreads
6885                         (delq number gnus-newsgroup-unreads))
6886                   t))))
6887           ;; Nope, invisible article.
6888           0
6889         ;; Ok, this article is to be visible, so we add it to the limit
6890         ;; and return 1.
6891         (push number gnus-newsgroup-limit)
6892         1))))
6893
6894 (defun gnus-expunge-thread (thread)
6895   "Mark all articles in THREAD as read."
6896   (let* ((number (mail-header-number (car thread))))
6897     (incf gnus-newsgroup-expunged-tally)
6898     ;; We also mark as read here, if that's wanted.
6899     (setq gnus-newsgroup-unreads
6900           (delq number gnus-newsgroup-unreads))
6901     (if gnus-newsgroup-auto-expire
6902         (push number gnus-newsgroup-expirable)
6903       (push (cons number gnus-low-score-mark)
6904             gnus-newsgroup-reads)))
6905   ;; Go recursively through all subthreads.
6906   (mapcar 'gnus-expunge-thread (cdr thread)))
6907
6908 ;; Summary article oriented commands
6909
6910 (defun gnus-summary-refer-parent-article (n)
6911   "Refer parent article N times.
6912 If N is negative, go to ancestor -N instead.
6913 The difference between N and the number of articles fetched is returned."
6914   (interactive "p")
6915   (let ((skip 1)
6916         error header ref)
6917     (when (not (natnump n))
6918       (setq skip (abs n)
6919             n 1))
6920     (while (and (> n 0)
6921                 (not error))
6922       (setq header (gnus-summary-article-header))
6923       (if (and (eq (mail-header-number header)
6924                    (cdr gnus-article-current))
6925                (equal gnus-newsgroup-name
6926                       (car gnus-article-current)))
6927           ;; If we try to find the parent of the currently
6928           ;; displayed article, then we take a look at the actual
6929           ;; References header, since this is slightly more
6930           ;; reliable than the References field we got from the
6931           ;; server.
6932           (save-excursion
6933             (set-buffer gnus-original-article-buffer)
6934             (nnheader-narrow-to-headers)
6935             (unless (setq ref (message-fetch-field "references"))
6936               (setq ref (message-fetch-field "in-reply-to")))
6937             (widen))
6938         (setq ref
6939               ;; It's not the current article, so we take a bet on
6940               ;; the value we got from the server.
6941               (mail-header-references header)))
6942       (if (and ref
6943                (not (equal ref "")))
6944           (unless (gnus-summary-refer-article (gnus-parent-id ref skip))
6945             (gnus-message 1 "Couldn't find parent"))
6946         (gnus-message 1 "No references in article %d"
6947                       (gnus-summary-article-number))
6948         (setq error t))
6949       (decf n))
6950     (gnus-summary-position-point)
6951     n))
6952
6953 (defun gnus-summary-refer-references ()
6954   "Fetch all articles mentioned in the References header.
6955 Return the number of articles fetched."
6956   (interactive)
6957   (let ((ref (mail-header-references (gnus-summary-article-header)))
6958         (current (gnus-summary-article-number))
6959         (n 0))
6960     (if (or (not ref)
6961             (equal ref ""))
6962         (error "No References in the current article")
6963       ;; For each Message-ID in the References header...
6964       (while (string-match "<[^>]*>" ref)
6965         (incf n)
6966         ;; ... fetch that article.
6967         (gnus-summary-refer-article
6968          (prog1 (match-string 0 ref)
6969            (setq ref (substring ref (match-end 0))))))
6970       (gnus-summary-goto-subject current)
6971       (gnus-summary-position-point)
6972       n)))
6973
6974 (defun gnus-summary-refer-thread (&optional limit)
6975   "Fetch all articles in the current thread.
6976 If LIMIT (the numerical prefix), fetch that many old headers instead
6977 of what's specified by the `gnus-refer-thread-limit' variable."
6978   (interactive "P")
6979   (let ((id (mail-header-id (gnus-summary-article-header)))
6980         (limit (if limit (prefix-numeric-value limit)
6981                  gnus-refer-thread-limit)))
6982     ;; We want to fetch LIMIT *old* headers, but we also have to
6983     ;; re-fetch all the headers in the current buffer, because many of
6984     ;; them may be undisplayed.  So we adjust LIMIT.
6985     (when (numberp limit)
6986       (incf limit (- gnus-newsgroup-end gnus-newsgroup-begin)))
6987     (unless (eq gnus-fetch-old-headers 'invisible)
6988       (gnus-message 5 "Fetching headers for %s..." gnus-newsgroup-name)
6989       ;; Retrieve the headers and read them in.
6990       (if (eq (gnus-retrieve-headers
6991                (list gnus-newsgroup-end) gnus-newsgroup-name limit)
6992               'nov)
6993           (gnus-build-all-threads)
6994         (error "Can't fetch thread from backends that don't support NOV"))
6995       (gnus-message 5 "Fetching headers for %s...done" gnus-newsgroup-name))
6996     (gnus-summary-limit-include-thread id)))
6997
6998 (defun gnus-summary-refer-article (message-id)
6999   "Fetch an article specified by MESSAGE-ID."
7000   (interactive "sMessage-ID: ")
7001   (when (and (stringp message-id)
7002              (not (zerop (length message-id))))
7003     ;; Construct the correct Message-ID if necessary.
7004     ;; Suggested by tale@pawl.rpi.edu.
7005     (unless (string-match "^<" message-id)
7006       (setq message-id (concat "<" message-id)))
7007     (unless (string-match ">$" message-id)
7008       (setq message-id (concat message-id ">")))
7009     (let* ((header (gnus-id-to-header message-id))
7010            (sparse (and header
7011                         (gnus-summary-article-sparse-p
7012                          (mail-header-number header))
7013                         (memq (mail-header-number header)
7014                               gnus-newsgroup-limit)))
7015            number)
7016       (cond
7017        ;; If the article is present in the buffer we just go to it.
7018        ((and header
7019              (or (not (gnus-summary-article-sparse-p
7020                        (mail-header-number header)))
7021                  sparse))
7022         (prog1
7023             (gnus-summary-goto-article
7024              (mail-header-number header) nil t)
7025           (when sparse
7026             (gnus-summary-update-article (mail-header-number header)))))
7027        (t
7028         ;; We fetch the article.
7029         (catch 'found
7030           (dolist (gnus-override-method (gnus-refer-article-methods))
7031             (gnus-check-server gnus-override-method)
7032             ;; Fetch the header, and display the article.
7033             (when (setq number (gnus-summary-insert-subject message-id))
7034               (gnus-summary-select-article nil nil nil number)
7035               (throw 'found t)))
7036           (gnus-message 3 "Couldn't fetch article %s" message-id)))))))
7037
7038 (defun gnus-refer-article-methods ()
7039   "Return a list of referrable methods."
7040   (cond
7041    ;; No method, so we default to current and native.
7042    ((null gnus-refer-article-method)
7043     (list gnus-current-select-method gnus-select-method))
7044    ;; Current.
7045    ((eq 'current gnus-refer-article-method)
7046     (list gnus-current-select-method))
7047    ;; List of select methods.
7048    ((not (and (symbolp (car gnus-refer-article-method))
7049               (assq (car gnus-refer-article-method) nnoo-definition-alist)))
7050     (let (out)
7051       (dolist (method gnus-refer-article-method)
7052         (push (if (eq 'current method)
7053                   gnus-current-select-method
7054                 method)
7055               out))
7056       (nreverse out)))
7057    ;; One single select method.
7058    (t
7059     (list gnus-refer-article-method))))
7060
7061 (defun gnus-summary-edit-parameters ()
7062   "Edit the group parameters of the current group."
7063   (interactive)
7064   (gnus-group-edit-group gnus-newsgroup-name 'params))
7065
7066 (defun gnus-summary-customize-parameters ()
7067   "Customize the group parameters of the current group."
7068   (interactive)
7069   (gnus-group-customize gnus-newsgroup-name))
7070
7071 (defun gnus-summary-enter-digest-group (&optional force)
7072   "Enter an nndoc group based on the current article.
7073 If FORCE, force a digest interpretation.  If not, try
7074 to guess what the document format is."
7075   (interactive "P")
7076   (let ((conf gnus-current-window-configuration))
7077     (save-excursion
7078       (gnus-summary-select-article))
7079     (setq gnus-current-window-configuration conf)
7080     (let* ((name (format "%s-%d"
7081                          (gnus-group-prefixed-name
7082                           gnus-newsgroup-name (list 'nndoc ""))
7083                          (save-excursion
7084                            (set-buffer gnus-summary-buffer)
7085                            gnus-current-article)))
7086            (ogroup gnus-newsgroup-name)
7087            (params (append (gnus-info-params (gnus-get-info ogroup))
7088                            (list (cons 'to-group ogroup))
7089                            (list (cons 'save-article-group ogroup))))
7090            (case-fold-search t)
7091            (buf (current-buffer))
7092            dig to-address)
7093       (save-excursion
7094         (set-buffer gnus-original-article-buffer)
7095         ;; Have the digest group inherit the main mail address of
7096         ;; the parent article.
7097         (when (setq to-address (or (message-fetch-field "reply-to")
7098                                    (message-fetch-field "from")))
7099           (setq params (append 
7100                         (list (cons 'to-address 
7101                                     (funcall gnus-decode-encoded-word-function
7102                                              to-address))))))
7103         (setq dig (nnheader-set-temp-buffer " *gnus digest buffer*"))
7104         (insert-buffer-substring gnus-original-article-buffer)
7105         ;; Remove lines that may lead nndoc to misinterpret the
7106         ;; document type.
7107         (narrow-to-region
7108          (goto-char (point-min))
7109          (or (search-forward "\n\n" nil t) (point)))
7110         (goto-char (point-min))
7111         (delete-matching-lines "^Path:\\|^From ")
7112         (widen))
7113       (unwind-protect
7114           (if (let ((gnus-newsgroup-ephemeral-charset gnus-newsgroup-charset)
7115                     (gnus-newsgroup-ephemeral-ignored-charsets
7116                      gnus-newsgroup-ignored-charsets))
7117                 (gnus-group-read-ephemeral-group
7118                  name `(nndoc ,name (nndoc-address ,(get-buffer dig))
7119                               (nndoc-article-type
7120                                ,(if force 'mbox 'guess))) t))
7121               ;; Make all postings to this group go to the parent group.
7122               (nconc (gnus-info-params (gnus-get-info name))
7123                      params)
7124             ;; Couldn't select this doc group.
7125             (switch-to-buffer buf)
7126             (gnus-set-global-variables)
7127             (gnus-configure-windows 'summary)
7128             (gnus-message 3 "Article couldn't be entered?"))
7129         (kill-buffer dig)))))
7130
7131 (defun gnus-summary-read-document (n)
7132   "Open a new group based on the current article(s).
7133 This will allow you to read digests and other similar
7134 documents as newsgroups.
7135 Obeys the standard process/prefix convention."
7136   (interactive "P")
7137   (let* ((articles (gnus-summary-work-articles n))
7138          (ogroup gnus-newsgroup-name)
7139          (params (append (gnus-info-params (gnus-get-info ogroup))
7140                          (list (cons 'to-group ogroup))))
7141          article group egroup groups vgroup)
7142     (while (setq article (pop articles))
7143       (setq group (format "%s-%d" gnus-newsgroup-name article))
7144       (gnus-summary-remove-process-mark article)
7145       (when (gnus-summary-display-article article)
7146         (save-excursion
7147           (with-temp-buffer
7148             (insert-buffer-substring gnus-original-article-buffer)
7149             ;; Remove some headers that may lead nndoc to make
7150             ;; the wrong guess.
7151             (message-narrow-to-head)
7152             (goto-char (point-min))
7153             (delete-matching-lines "^\\(Path\\):\\|^From ")
7154             (widen)
7155             (if (setq egroup
7156                       (gnus-group-read-ephemeral-group
7157                        group `(nndoc ,group (nndoc-address ,(current-buffer))
7158                                      (nndoc-article-type guess))
7159                        t nil t))
7160                 (progn
7161                   ;; Make all postings to this group go to the parent group.
7162                   (nconc (gnus-info-params (gnus-get-info egroup))
7163                          params)
7164                   (push egroup groups))
7165               ;; Couldn't select this doc group.
7166               (gnus-error 3 "Article couldn't be entered"))))))
7167     ;; Now we have selected all the documents.
7168     (cond
7169      ((not groups)
7170       (error "None of the articles could be interpreted as documents"))
7171      ((gnus-group-read-ephemeral-group
7172        (setq vgroup (format
7173                      "nnvirtual:%s-%s" gnus-newsgroup-name
7174                      (format-time-string "%Y%m%dT%H%M%S" (current-time))))
7175        `(nnvirtual ,vgroup (nnvirtual-component-groups ,groups))
7176        t
7177        (cons (current-buffer) 'summary)))
7178      (t
7179       (error "Couldn't select virtual nndoc group")))))
7180
7181 (defun gnus-summary-isearch-article (&optional regexp-p)
7182   "Do incremental search forward on the current article.
7183 If REGEXP-P (the prefix) is non-nil, do regexp isearch."
7184   (interactive "P")
7185   (gnus-summary-select-article)
7186   (gnus-configure-windows 'article)
7187   (gnus-eval-in-buffer-window gnus-article-buffer
7188     (save-restriction
7189       (widen)
7190       (isearch-forward regexp-p))))
7191
7192 (defun gnus-summary-search-article-forward (regexp &optional backward)
7193   "Search for an article containing REGEXP forward.
7194 If BACKWARD, search backward instead."
7195   (interactive
7196    (list (read-string
7197           (format "Search article %s (regexp%s): "
7198                   (if current-prefix-arg "backward" "forward")
7199                   (if gnus-last-search-regexp
7200                       (concat ", default " gnus-last-search-regexp)
7201                     "")))
7202          current-prefix-arg))
7203   (if (string-equal regexp "")
7204       (setq regexp (or gnus-last-search-regexp ""))
7205     (setq gnus-last-search-regexp regexp)
7206     (setq gnus-article-before-search gnus-current-article))
7207   ;; Intentionally set gnus-last-article.
7208   (setq gnus-last-article gnus-article-before-search)
7209   (let ((gnus-last-article gnus-last-article))
7210     (if (gnus-summary-search-article regexp backward)
7211         (gnus-summary-show-thread)
7212       (error "Search failed: \"%s\"" regexp))))
7213
7214 (defun gnus-summary-search-article-backward (regexp)
7215   "Search for an article containing REGEXP backward."
7216   (interactive
7217    (list (read-string
7218           (format "Search article backward (regexp%s): "
7219                   (if gnus-last-search-regexp
7220                       (concat ", default " gnus-last-search-regexp)
7221                     "")))))
7222   (gnus-summary-search-article-forward regexp 'backward))
7223
7224 (defun gnus-summary-search-article (regexp &optional backward)
7225   "Search for an article containing REGEXP.
7226 Optional argument BACKWARD means do search for backward.
7227 `gnus-select-article-hook' is not called during the search."
7228   ;; We have to require this here to make sure that the following
7229   ;; dynamic binding isn't shadowed by autoloading.
7230   (require 'gnus-async)
7231   (require 'gnus-art)
7232   (let ((gnus-select-article-hook nil)  ;Disable hook.
7233         (gnus-article-prepare-hook nil)
7234         (gnus-mark-article-hook nil)    ;Inhibit marking as read.
7235         (gnus-use-article-prefetch nil)
7236         (gnus-xmas-force-redisplay nil) ;Inhibit XEmacs redisplay.
7237         (gnus-use-trees nil)            ;Inhibit updating tree buffer.
7238         (sum (current-buffer))
7239         (gnus-display-mime-function nil)
7240         (found nil)
7241         point)
7242     (gnus-save-hidden-threads
7243       (gnus-summary-select-article)
7244       (set-buffer gnus-article-buffer)
7245       (goto-char (window-point (get-buffer-window (current-buffer))))
7246       (when backward
7247         (forward-line -1))
7248       (while (not found)
7249         (gnus-message 7 "Searching article: %d..." (cdr gnus-article-current))
7250         (if (if backward
7251                 (re-search-backward regexp nil t)
7252               (re-search-forward regexp nil t))
7253             ;; We found the regexp.
7254             (progn
7255               (setq found 'found)
7256               (beginning-of-line)
7257               (set-window-start
7258                (get-buffer-window (current-buffer))
7259                (point))
7260               (forward-line 1)
7261               (set-window-point
7262                (get-buffer-window (current-buffer))
7263                (point))
7264               (set-buffer sum)
7265               (setq point (point)))
7266           ;; We didn't find it, so we go to the next article.
7267           (set-buffer sum)
7268           (setq found 'not)
7269           (while (eq found 'not)
7270             (if (not (if backward (gnus-summary-find-prev)
7271                        (gnus-summary-find-next)))
7272                 ;; No more articles.
7273                 (setq found t)
7274               ;; Select the next article and adjust point.
7275               (unless (gnus-summary-article-sparse-p
7276                        (gnus-summary-article-number))
7277                 (setq found nil)
7278                 (gnus-summary-select-article)
7279                 (set-buffer gnus-article-buffer)
7280                 (widen)
7281                 (goto-char (if backward (point-max) (point-min))))))))
7282       (gnus-message 7 ""))
7283     ;; Return whether we found the regexp.
7284     (when (eq found 'found)
7285       (goto-char point)
7286       (gnus-summary-show-thread)
7287       (gnus-summary-goto-subject gnus-current-article)
7288       (gnus-summary-position-point)
7289       t)))
7290
7291 (defun gnus-summary-find-matching (header regexp &optional backward unread
7292                                           not-case-fold)
7293   "Return a list of all articles that match REGEXP on HEADER.
7294 The search stars on the current article and goes forwards unless
7295 BACKWARD is non-nil.  If BACKWARD is `all', do all articles.
7296 If UNREAD is non-nil, only unread articles will
7297 be taken into consideration.  If NOT-CASE-FOLD, case won't be folded
7298 in the comparisons."
7299   (let ((data (if (eq backward 'all) gnus-newsgroup-data
7300                 (gnus-data-find-list
7301                  (gnus-summary-article-number) (gnus-data-list backward))))
7302         (case-fold-search (not not-case-fold))
7303         articles d func)
7304     (if (consp header)
7305         (if (eq (car header) 'extra)
7306             (setq func
7307                   `(lambda (h)
7308                      (or (cdr (assq ',(cdr header) (mail-header-extra h)))
7309                          "")))
7310           (error "%s is an invalid header" header))
7311       (unless (fboundp (intern (concat "mail-header-" header)))
7312         (error "%s is not a valid header" header))
7313       (setq func `(lambda (h) (,(intern (concat "mail-header-" header)) h))))
7314     (while data
7315       (setq d (car data))
7316       (and (or (not unread)             ; We want all articles...
7317                (gnus-data-unread-p d))  ; Or just unreads.
7318            (vectorp (gnus-data-header d)) ; It's not a pseudo.
7319            (string-match regexp (funcall func (gnus-data-header d))) ; Match.
7320            (push (gnus-data-number d) articles)) ; Success!
7321       (setq data (cdr data)))
7322     (nreverse articles)))
7323
7324 (defun gnus-summary-execute-command (header regexp command &optional backward)
7325   "Search forward for an article whose HEADER matches REGEXP and execute COMMAND.
7326 If HEADER is an empty string (or nil), the match is done on the entire
7327 article.  If BACKWARD (the prefix) is non-nil, search backward instead."
7328   (interactive
7329    (list (let ((completion-ignore-case t))
7330            (completing-read
7331             "Header name: "
7332             (mapcar (lambda (string) (list string))
7333                     '("Number" "Subject" "From" "Lines" "Date"
7334                       "Message-ID" "Xref" "References" "Body"))
7335             nil 'require-match))
7336          (read-string "Regexp: ")
7337          (read-key-sequence "Command: ")
7338          current-prefix-arg))
7339   (when (equal header "Body")
7340     (setq header ""))
7341   ;; Hidden thread subtrees must be searched as well.
7342   (gnus-summary-show-all-threads)
7343   ;; We don't want to change current point nor window configuration.
7344   (save-excursion
7345     (save-window-excursion
7346       (gnus-message 6 "Executing %s..." (key-description command))
7347       ;; We'd like to execute COMMAND interactively so as to give arguments.
7348       (gnus-execute header regexp
7349                     `(call-interactively ',(key-binding command))
7350                     backward)
7351       (gnus-message 6 "Executing %s...done" (key-description command)))))
7352
7353 (defun gnus-summary-beginning-of-article ()
7354   "Scroll the article back to the beginning."
7355   (interactive)
7356   (gnus-summary-select-article)
7357   (gnus-configure-windows 'article)
7358   (gnus-eval-in-buffer-window gnus-article-buffer
7359     (widen)
7360     (goto-char (point-min))
7361     (when gnus-page-broken
7362       (gnus-narrow-to-page))))
7363
7364 (defun gnus-summary-end-of-article ()
7365   "Scroll to the end of the article."
7366   (interactive)
7367   (gnus-summary-select-article)
7368   (gnus-configure-windows 'article)
7369   (gnus-eval-in-buffer-window gnus-article-buffer
7370     (widen)
7371     (goto-char (point-max))
7372     (recenter -3)
7373     (when gnus-page-broken
7374       (gnus-narrow-to-page))))
7375
7376 (defun gnus-summary-print-article (&optional filename n)
7377   "Generate and print a PostScript image of the N next (mail) articles.
7378
7379 If N is negative, print the N previous articles.  If N is nil and articles
7380 have been marked with the process mark, print these instead.
7381
7382 If the optional first argument FILENAME is nil, send the image to the
7383 printer.  If FILENAME is a string, save the PostScript image in a file with
7384 that name.  If FILENAME is a number, prompt the user for the name of the file
7385 to save in."
7386   (interactive (list (ps-print-preprint current-prefix-arg)
7387                      current-prefix-arg))
7388   (dolist (article (gnus-summary-work-articles n))
7389     (gnus-summary-select-article nil nil 'pseudo article)
7390     (gnus-eval-in-buffer-window gnus-article-buffer
7391       (let ((buffer (generate-new-buffer " *print*")))
7392         (unwind-protect
7393             (progn
7394               (copy-to-buffer buffer (point-min) (point-max))
7395               (set-buffer buffer)
7396               (gnus-article-delete-invisible-text)
7397               (let ((ps-left-header
7398                      (list
7399                       (concat "("
7400                               (mail-header-subject gnus-current-headers) ")")
7401                       (concat "("
7402                               (mail-header-from gnus-current-headers) ")")))
7403                     (ps-right-header
7404                      (list
7405                       "/pagenumberstring load"
7406                       (concat "("
7407                               (mail-header-date gnus-current-headers) ")"))))
7408                 (gnus-run-hooks 'gnus-ps-print-hook)
7409                 (save-excursion
7410                   (ps-print-buffer-with-faces filename))))
7411           (kill-buffer buffer))))))
7412
7413 (defun gnus-summary-show-article (&optional arg)
7414   "Force re-fetching of the current article.
7415 If ARG (the prefix) is a number, show the article with the charset
7416 defined in `gnus-summary-show-article-charset-alist', or the charset
7417 inputed.
7418 If ARG (the prefix) is non-nil and not a number, show the raw article
7419 without any article massaging functions being run."
7420   (interactive "P")
7421   (cond
7422    ((numberp arg)
7423     (let ((gnus-newsgroup-charset
7424            (or (cdr (assq arg gnus-summary-show-article-charset-alist))
7425                (read-coding-system "Charset: ")))
7426           (gnus-newsgroup-ignored-charsets 'gnus-all))
7427       (gnus-summary-select-article nil 'force)
7428       (let ((deps gnus-newsgroup-dependencies)
7429             head header)
7430         (save-excursion
7431           (set-buffer gnus-original-article-buffer)
7432           (save-restriction
7433             (message-narrow-to-head)
7434             (setq head (buffer-string)))
7435           (with-temp-buffer
7436             (insert (format "211 %d Article retrieved.\n"
7437                             (cdr gnus-article-current)))
7438             (insert head)
7439             (insert ".\n")
7440             (let ((nntp-server-buffer (current-buffer)))
7441               (setq header (car (gnus-get-newsgroup-headers deps t))))))
7442         (gnus-data-set-header
7443          (gnus-data-find (cdr gnus-article-current))
7444          header)
7445         (gnus-summary-update-article-line
7446          (cdr gnus-article-current) header))))
7447    ((not arg)
7448     ;; Select the article the normal way.
7449     (gnus-summary-select-article nil 'force))
7450    (t
7451     ;; We have to require this here to make sure that the following
7452     ;; dynamic binding isn't shadowed by autoloading.
7453     (require 'gnus-async)
7454     (require 'gnus-art)
7455     ;; Bind the article treatment functions to nil.
7456     (let ((gnus-have-all-headers t)
7457           gnus-article-prepare-hook
7458           gnus-article-decode-hook
7459           gnus-display-mime-function
7460           gnus-break-pages)
7461       ;; Destroy any MIME parts.
7462       (when (gnus-buffer-live-p gnus-article-buffer)
7463         (save-excursion
7464           (set-buffer gnus-article-buffer)
7465           (mm-destroy-parts gnus-article-mime-handles)
7466           ;; Set it to nil for safety reason.
7467           (setq gnus-article-mime-handle-alist nil)
7468           (setq gnus-article-mime-handles nil)))
7469       (gnus-summary-select-article nil 'force))))
7470   (gnus-summary-goto-subject gnus-current-article)
7471   (gnus-summary-position-point))
7472
7473 (defun gnus-summary-verbose-headers (&optional arg)
7474   "Toggle permanent full header display.
7475 If ARG is a positive number, turn header display on.
7476 If ARG is a negative number, turn header display off."
7477   (interactive "P")
7478   (setq gnus-show-all-headers
7479         (cond ((or (not (numberp arg))
7480                    (zerop arg))
7481                (not gnus-show-all-headers))
7482               ((natnump arg)
7483                t)))
7484   (gnus-summary-show-article))
7485
7486 (defun gnus-summary-toggle-header (&optional arg)
7487   "Show the headers if they are hidden, or hide them if they are shown.
7488 If ARG is a positive number, show the entire header.
7489 If ARG is a negative number, hide the unwanted header lines."
7490   (interactive "P")
7491   (save-excursion
7492     (set-buffer gnus-article-buffer)
7493     (save-restriction
7494       (let* ((buffer-read-only nil)
7495              (inhibit-point-motion-hooks t)
7496              hidden e)
7497         (setq hidden
7498               (if (numberp arg)
7499                   (>= arg 0)
7500                 (save-restriction
7501                   (article-narrow-to-head)
7502                   (gnus-article-hidden-text-p 'headers))))
7503         (goto-char (point-min))
7504         (when (search-forward "\n\n" nil t)
7505           (delete-region (point-min) (1- (point))))
7506         (goto-char (point-min))
7507         (save-excursion
7508           (set-buffer gnus-original-article-buffer)
7509           (goto-char (point-min))
7510           (setq e (1- (or (search-forward "\n\n" nil t) (point-max)))))
7511         (insert-buffer-substring gnus-original-article-buffer 1 e)
7512         (save-restriction
7513           (narrow-to-region (point-min) (point))
7514           (article-decode-encoded-words)
7515           (if  hidden
7516               (let ((gnus-treat-hide-headers nil)
7517                     (gnus-treat-hide-boring-headers nil))
7518                 (setq gnus-article-wash-types
7519                       (delq 'headers gnus-article-wash-types))
7520                 (gnus-treat-article 'head))
7521             (gnus-treat-article 'head)))
7522         (gnus-set-mode-line 'article)))))
7523
7524 (defun gnus-summary-show-all-headers ()
7525   "Make all header lines visible."
7526   (interactive)
7527   (gnus-article-show-all-headers))
7528
7529 (defun gnus-summary-caesar-message (&optional arg)
7530   "Caesar rotate the current article by 13.
7531 The numerical prefix specifies how many places to rotate each letter
7532 forward."
7533   (interactive "P")
7534   (gnus-summary-select-article)
7535   (let ((mail-header-separator ""))
7536     (gnus-eval-in-buffer-window gnus-article-buffer
7537       (save-restriction
7538         (widen)
7539         (let ((start (window-start))
7540               buffer-read-only)
7541           (message-caesar-buffer-body arg)
7542           (set-window-start (get-buffer-window (current-buffer)) start))))))
7543
7544 (defun gnus-summary-stop-page-breaking ()
7545   "Stop page breaking in the current article."
7546   (interactive)
7547   (gnus-summary-select-article)
7548   (gnus-eval-in-buffer-window gnus-article-buffer
7549     (widen)
7550     (when (gnus-visual-p 'page-marker)
7551       (let ((buffer-read-only nil))
7552         (gnus-remove-text-with-property 'gnus-prev)
7553         (gnus-remove-text-with-property 'gnus-next))
7554       (setq gnus-page-broken nil))))
7555
7556 (defun gnus-summary-move-article (&optional n to-newsgroup
7557                                             select-method action)
7558   "Move the current article to a different newsgroup.
7559 If N is a positive number, move the N next articles.
7560 If N is a negative number, move the N previous articles.
7561 If N is nil and any articles have been marked with the process mark,
7562 move those articles instead.
7563 If TO-NEWSGROUP is string, do not prompt for a newsgroup to move to.
7564 If SELECT-METHOD is non-nil, do not move to a specific newsgroup, but
7565 re-spool using this method.
7566
7567 For this function to work, both the current newsgroup and the
7568 newsgroup that you want to move to have to support the `request-move'
7569 and `request-accept' functions.
7570
7571 ACTION can be either `move' (the default), `crosspost' or `copy'."
7572   (interactive "P")
7573   (unless action
7574     (setq action 'move))
7575   ;; Disable marking as read.
7576   (let (gnus-mark-article-hook)
7577     (save-window-excursion
7578       (gnus-summary-select-article)))
7579   ;; Check whether the source group supports the required functions.
7580   (cond ((and (eq action 'move)
7581               (not (gnus-check-backend-function
7582                     'request-move-article gnus-newsgroup-name)))
7583          (error "The current group does not support article moving"))
7584         ((and (eq action 'crosspost)
7585               (not (gnus-check-backend-function
7586                     'request-replace-article gnus-newsgroup-name)))
7587          (error "The current group does not support article editing")))
7588   (let ((articles (gnus-summary-work-articles n))
7589         (prefix (if (gnus-check-backend-function
7590                     'request-move-article gnus-newsgroup-name)
7591                     (gnus-group-real-prefix gnus-newsgroup-name)
7592                   ""))
7593         (names '((move "Move" "Moving")
7594                  (copy "Copy" "Copying")
7595                  (crosspost "Crosspost" "Crossposting")))
7596         (copy-buf (save-excursion
7597                     (nnheader-set-temp-buffer " *copy article*")))
7598         art-group to-method new-xref article to-groups)
7599     (unless (assq action names)
7600       (error "Unknown action %s" action))
7601     ;; Read the newsgroup name.
7602     (when (and (not to-newsgroup)
7603                (not select-method))
7604       (setq to-newsgroup
7605             (gnus-read-move-group-name
7606              (cadr (assq action names))
7607              (symbol-value (intern (format "gnus-current-%s-group" action)))
7608              articles prefix))
7609       (set (intern (format "gnus-current-%s-group" action)) to-newsgroup))
7610     (setq to-method (or select-method
7611                         (gnus-server-to-method
7612                          (gnus-group-method to-newsgroup))))
7613     ;; Check the method we are to move this article to...
7614     (unless (gnus-check-backend-function
7615              'request-accept-article (car to-method))
7616       (error "%s does not support article copying" (car to-method)))
7617     (unless (gnus-check-server to-method)
7618       (error "Can't open server %s" (car to-method)))
7619     (gnus-message 6 "%s to %s: %s..."
7620                   (caddr (assq action names))
7621                   (or (car select-method) to-newsgroup) articles)
7622     (while articles
7623       (setq article (pop articles))
7624       (setq
7625        art-group
7626        (cond
7627         ;; Move the article.
7628         ((eq action 'move)
7629          ;; Remove this article from future suppression.
7630          (gnus-dup-unsuppress-article article)
7631          (gnus-request-move-article
7632           article                       ; Article to move
7633           gnus-newsgroup-name           ; From newsgroup
7634           (nth 1 (gnus-find-method-for-group
7635                   gnus-newsgroup-name)) ; Server
7636           (list 'gnus-request-accept-article
7637                 to-newsgroup (list 'quote select-method)
7638                 (not articles) t)       ; Accept form
7639           (not articles)))              ; Only save nov last time
7640         ;; Copy the article.
7641         ((eq action 'copy)
7642          (save-excursion
7643            (set-buffer copy-buf)
7644            (when (gnus-request-article-this-buffer article gnus-newsgroup-name)
7645              (gnus-request-accept-article
7646               to-newsgroup select-method (not articles) t))))
7647         ;; Crosspost the article.
7648         ((eq action 'crosspost)
7649          (let ((xref (message-tokenize-header
7650                       (mail-header-xref (gnus-summary-article-header article))
7651                       " ")))
7652            (setq new-xref (concat (gnus-group-real-name gnus-newsgroup-name)
7653                                   ":" article))
7654            (unless xref
7655              (setq xref (list (system-name))))
7656            (setq new-xref
7657                  (concat
7658                   (mapconcat 'identity
7659                              (delete "Xref:" (delete new-xref xref))
7660                              " ")
7661                   " " new-xref))
7662            (save-excursion
7663              (set-buffer copy-buf)
7664              ;; First put the article in the destination group.
7665              (gnus-request-article-this-buffer article gnus-newsgroup-name)
7666              (when (consp (setq art-group
7667                                 (gnus-request-accept-article
7668                                  to-newsgroup select-method (not articles))))
7669                (setq new-xref (concat new-xref " " (car art-group)
7670                                       ":" (cdr art-group)))
7671                ;; Now we have the new Xrefs header, so we insert
7672                ;; it and replace the new article.
7673                (nnheader-replace-header "Xref" new-xref)
7674                (gnus-request-replace-article
7675                 (cdr art-group) to-newsgroup (current-buffer))
7676                art-group))))))
7677       (cond
7678        ((not art-group)
7679         (gnus-message 1 "Couldn't %s article %s: %s"
7680                       (cadr (assq action names)) article
7681                       (nnheader-get-report (car to-method))))
7682        ((eq art-group 'junk)
7683         (when (eq action 'move)
7684           (gnus-summary-mark-article article gnus-canceled-mark)
7685           (gnus-message 4 "Deleted article %s" article)))
7686        (t
7687         (let* ((pto-group (gnus-group-prefixed-name
7688                            (car art-group) to-method))
7689                (entry
7690                 (gnus-gethash pto-group gnus-newsrc-hashtb))
7691                (info (nth 2 entry))
7692                (to-group (gnus-info-group info))
7693                to-marks)
7694           ;; Update the group that has been moved to.
7695           (when (and info
7696                      (memq action '(move copy)))
7697             (unless (member to-group to-groups)
7698               (push to-group to-groups))
7699
7700             (unless (memq article gnus-newsgroup-unreads)
7701               (push 'read to-marks)
7702               (gnus-info-set-read
7703                info (gnus-add-to-range (gnus-info-read info)
7704                                        (list (cdr art-group)))))
7705
7706             ;; See whether the article is to be put in the cache.
7707             (let ((marks gnus-article-mark-lists)
7708                   (to-article (cdr art-group)))
7709
7710               ;; Enter the article into the cache in the new group,
7711               ;; if that is required.
7712               (when gnus-use-cache
7713                 (gnus-cache-possibly-enter-article
7714                  to-group to-article
7715                  (memq article gnus-newsgroup-marked)
7716                  (memq article gnus-newsgroup-dormant)
7717                  (memq article gnus-newsgroup-unreads)))
7718
7719               (when gnus-preserve-marks
7720                 ;; Copy any marks over to the new group.
7721                 (when (and (equal to-group gnus-newsgroup-name)
7722                            (not (memq article gnus-newsgroup-unreads)))
7723                   ;; Mark this article as read in this group.
7724                   (push (cons to-article gnus-read-mark) gnus-newsgroup-reads)
7725                   (setcdr (gnus-active to-group) to-article)
7726                   (setcdr gnus-newsgroup-active to-article))
7727
7728                 (while marks
7729                   (when (memq article (symbol-value
7730                                        (intern (format "gnus-newsgroup-%s"
7731                                                        (caar marks)))))
7732                     (push (cdar marks) to-marks)
7733                     ;; If the other group is the same as this group,
7734                     ;; then we have to add the mark to the list.
7735                     (when (equal to-group gnus-newsgroup-name)
7736                       (set (intern (format "gnus-newsgroup-%s" (caar marks)))
7737                            (cons to-article
7738                                  (symbol-value
7739                                   (intern (format "gnus-newsgroup-%s"
7740                                                   (caar marks)))))))
7741                     ;; Copy the marks to other group.
7742                     (gnus-add-marked-articles
7743                      to-group (cdar marks) (list to-article) info))
7744                   (setq marks (cdr marks)))
7745
7746                 (gnus-request-set-mark to-group (list (list (list to-article)
7747                                                             'set
7748                                                             to-marks))))
7749
7750               (gnus-dribble-enter
7751                (concat "(gnus-group-set-info '"
7752                        (gnus-prin1-to-string (gnus-get-info to-group))
7753                        ")"))))
7754
7755           ;; Update the Xref header in this article to point to
7756           ;; the new crossposted article we have just created.
7757           (when (eq action 'crosspost)
7758             (save-excursion
7759               (set-buffer copy-buf)
7760               (gnus-request-article-this-buffer article gnus-newsgroup-name)
7761               (nnheader-replace-header "Xref" new-xref)
7762               (gnus-request-replace-article
7763                article gnus-newsgroup-name (current-buffer)))))
7764
7765         ;;;!!!Why is this necessary?
7766         (set-buffer gnus-summary-buffer)
7767
7768         (gnus-summary-goto-subject article)
7769         (when (eq action 'move)
7770           (gnus-summary-mark-article article gnus-canceled-mark))))
7771       (gnus-summary-remove-process-mark article))
7772     ;; Re-activate all groups that have been moved to.
7773     (while to-groups
7774       (save-excursion
7775         (set-buffer gnus-group-buffer)
7776         (when (gnus-group-goto-group (car to-groups) t)
7777           (gnus-group-get-new-news-this-group 1 t))
7778         (pop to-groups)))
7779
7780     (gnus-kill-buffer copy-buf)
7781     (gnus-summary-position-point)
7782     (gnus-set-mode-line 'summary)))
7783
7784 (defun gnus-summary-copy-article (&optional n to-newsgroup select-method)
7785   "Move the current article to a different newsgroup.
7786 If TO-NEWSGROUP is string, do not prompt for a newsgroup to move to.
7787 If SELECT-METHOD is non-nil, do not move to a specific newsgroup, but
7788 re-spool using this method."
7789   (interactive "P")
7790   (gnus-summary-move-article n to-newsgroup select-method 'copy))
7791
7792 (defun gnus-summary-crosspost-article (&optional n)
7793   "Crosspost the current article to some other group."
7794   (interactive "P")
7795   (gnus-summary-move-article n nil nil 'crosspost))
7796
7797 (defcustom gnus-summary-respool-default-method nil
7798   "Default method for respooling an article.
7799 If nil, use to the current newsgroup method."
7800   :type '(choice (gnus-select-method :value (nnml ""))
7801                  (const nil))
7802   :group 'gnus-summary-mail)
7803
7804 (defun gnus-summary-respool-article (&optional n method)
7805   "Respool the current article.
7806 The article will be squeezed through the mail spooling process again,
7807 which means that it will be put in some mail newsgroup or other
7808 depending on `nnmail-split-methods'.
7809 If N is a positive number, respool the N next articles.
7810 If N is a negative number, respool the N previous articles.
7811 If N is nil and any articles have been marked with the process mark,
7812 respool those articles instead.
7813
7814 Respooling can be done both from mail groups and \"real\" newsgroups.
7815 In the former case, the articles in question will be moved from the
7816 current group into whatever groups they are destined to.  In the
7817 latter case, they will be copied into the relevant groups."
7818   (interactive
7819    (list current-prefix-arg
7820          (let* ((methods (gnus-methods-using 'respool))
7821                 (methname
7822                  (symbol-name (or gnus-summary-respool-default-method
7823                                   (car (gnus-find-method-for-group
7824                                         gnus-newsgroup-name)))))
7825                 (method
7826                  (gnus-completing-read
7827                   methname "What backend do you want to use when respooling?"
7828                   methods nil t nil 'gnus-mail-method-history))
7829                 ms)
7830            (cond
7831             ((zerop (length (setq ms (gnus-servers-using-backend
7832                                       (intern method)))))
7833              (list (intern method) ""))
7834             ((= 1 (length ms))
7835              (car ms))
7836             (t
7837              (let ((ms-alist (mapcar (lambda (m) (cons (cadr m) m)) ms)))
7838                (cdr (assoc (completing-read "Server name: " ms-alist nil t)
7839                            ms-alist))))))))
7840   (unless method
7841     (error "No method given for respooling"))
7842   (if (assoc (symbol-name
7843               (car (gnus-find-method-for-group gnus-newsgroup-name)))
7844              (gnus-methods-using 'respool))
7845       (gnus-summary-move-article n nil method)
7846     (gnus-summary-copy-article n nil method)))
7847
7848 (defun gnus-summary-import-article (file)
7849   "Import an arbitrary file into a mail newsgroup."
7850   (interactive "fImport file: ")
7851   (let ((group gnus-newsgroup-name)
7852         (now (current-time))
7853         atts lines)
7854     (unless (gnus-check-backend-function 'request-accept-article group)
7855       (error "%s does not support article importing" group))
7856     (or (file-readable-p file)
7857         (not (file-regular-p file))
7858         (error "Can't read %s" file))
7859     (save-excursion
7860       (set-buffer (gnus-get-buffer-create " *import file*"))
7861       (erase-buffer)
7862       (nnheader-insert-file-contents file)
7863       (goto-char (point-min))
7864       (unless (nnheader-article-p)
7865         ;; This doesn't look like an article, so we fudge some headers.
7866         (setq atts (file-attributes file)
7867               lines (count-lines (point-min) (point-max)))
7868         (insert "From: " (read-string "From: ") "\n"
7869                 "Subject: " (read-string "Subject: ") "\n"
7870                 "Date: " (message-make-date (nth 5 atts))
7871                 "\n"
7872                 "Message-ID: " (message-make-message-id) "\n"
7873                 "Lines: " (int-to-string lines) "\n"
7874                 "Chars: " (int-to-string (nth 7 atts)) "\n\n"))
7875       (gnus-request-accept-article group nil t)
7876       (kill-buffer (current-buffer)))))
7877
7878 (defun gnus-summary-article-posted-p ()
7879   "Say whether the current (mail) article is available from news as well.
7880 This will be the case if the article has both been mailed and posted."
7881   (interactive)
7882   (let ((id (mail-header-references (gnus-summary-article-header)))
7883         (gnus-override-method (car (gnus-refer-article-methods))))
7884     (if (gnus-request-head id "")
7885         (gnus-message 2 "The current message was found on %s"
7886                       gnus-override-method)
7887       (gnus-message 2 "The current message couldn't be found on %s"
7888                     gnus-override-method)
7889       nil)))
7890
7891 (defun gnus-summary-expire-articles (&optional now)
7892   "Expire all articles that are marked as expirable in the current group."
7893   (interactive)
7894   (when (gnus-check-backend-function
7895          'request-expire-articles gnus-newsgroup-name)
7896     ;; This backend supports expiry.
7897     (let* ((total (gnus-group-total-expirable-p gnus-newsgroup-name))
7898            (expirable (if total
7899                           (progn
7900                             ;; We need to update the info for
7901                             ;; this group for `gnus-list-of-read-articles'
7902                             ;; to give us the right answer.
7903                             (gnus-run-hooks 'gnus-exit-group-hook)
7904                             (gnus-summary-update-info)
7905                             (gnus-list-of-read-articles gnus-newsgroup-name))
7906                         (setq gnus-newsgroup-expirable
7907                               (sort gnus-newsgroup-expirable '<))))
7908            (expiry-wait (if now 'immediate
7909                           (gnus-group-find-parameter
7910                            gnus-newsgroup-name 'expiry-wait)))
7911            (nnmail-expiry-target
7912             (or (gnus-group-find-parameter gnus-newsgroup-name 'expiry-target)
7913                 nnmail-expiry-target))
7914            es)
7915       (when expirable
7916         ;; There are expirable articles in this group, so we run them
7917         ;; through the expiry process.
7918         (gnus-message 6 "Expiring articles...")
7919         (unless (gnus-check-group gnus-newsgroup-name)
7920           (error "Can't open server for %s" gnus-newsgroup-name))
7921         ;; The list of articles that weren't expired is returned.
7922         (save-excursion
7923           (if expiry-wait
7924               (let ((nnmail-expiry-wait-function nil)
7925                     (nnmail-expiry-wait expiry-wait))
7926                 (setq es (gnus-request-expire-articles
7927                           expirable gnus-newsgroup-name)))
7928             (setq es (gnus-request-expire-articles
7929                       expirable gnus-newsgroup-name)))
7930           (unless total
7931             (setq gnus-newsgroup-expirable es))
7932           ;; We go through the old list of expirable, and mark all
7933           ;; really expired articles as nonexistent.
7934           (unless (eq es expirable)     ;If nothing was expired, we don't mark.
7935             (let ((gnus-use-cache nil))
7936               (while expirable
7937                 (unless (memq (car expirable) es)
7938                   (when (gnus-data-find (car expirable))
7939                     (gnus-summary-mark-article
7940                      (car expirable) gnus-canceled-mark)))
7941                 (setq expirable (cdr expirable))))))
7942         (gnus-message 6 "Expiring articles...done")))))
7943
7944 (defun gnus-summary-expire-articles-now ()
7945   "Expunge all expirable articles in the current group.
7946 This means that *all* articles that are marked as expirable will be
7947 deleted forever, right now."
7948   (interactive)
7949   (or gnus-expert-user
7950       (gnus-yes-or-no-p
7951        "Are you really, really, really sure you want to delete all these messages? ")
7952       (error "Phew!"))
7953   (gnus-summary-expire-articles t))
7954
7955 ;; Suggested by Jack Vinson <vinson@unagi.cis.upenn.edu>.
7956 (defun gnus-summary-delete-article (&optional n)
7957   "Delete the N next (mail) articles.
7958 This command actually deletes articles.  This is not a marking
7959 command.  The article will disappear forever from your life, never to
7960 return.
7961 If N is negative, delete backwards.
7962 If N is nil and articles have been marked with the process mark,
7963 delete these instead."
7964   (interactive "P")
7965   (unless (gnus-check-backend-function 'request-expire-articles
7966                                        gnus-newsgroup-name)
7967     (error "The current newsgroup does not support article deletion"))
7968   (unless (gnus-check-server (gnus-find-method-for-group gnus-newsgroup-name))
7969     (error "Couldn't open server"))
7970   ;; Compute the list of articles to delete.
7971   (let ((articles (sort (copy-sequence (gnus-summary-work-articles n)) '<))
7972         not-deleted)
7973     (if (and gnus-novice-user
7974              (not (gnus-yes-or-no-p
7975                    (format "Do you really want to delete %s forever? "
7976                            (if (> (length articles) 1)
7977                                (format "these %s articles" (length articles))
7978                              "this article")))))
7979         ()
7980       ;; Delete the articles.
7981       (setq not-deleted (gnus-request-expire-articles
7982                          articles gnus-newsgroup-name 'force))
7983       (while articles
7984         (gnus-summary-remove-process-mark (car articles))
7985         ;; The backend might not have been able to delete the article
7986         ;; after all.
7987         (unless (memq (car articles) not-deleted)
7988           (gnus-summary-mark-article (car articles) gnus-canceled-mark))
7989         (setq articles (cdr articles)))
7990       (when not-deleted
7991         (gnus-message 4 "Couldn't delete articles %s" not-deleted)))
7992     (gnus-summary-position-point)
7993     (gnus-set-mode-line 'summary)
7994     not-deleted))
7995
7996 (defun gnus-summary-edit-article (&optional arg)
7997   "Edit the current article.
7998 This will have permanent effect only in mail groups.
7999 If ARG is nil, edit the decoded articles.
8000 If ARG is 1, edit the raw articles. 
8001 If ARG is 2, edit the raw articles even in read-only groups.
8002 If ARG is 3, edit the articles with the current handles.
8003 Otherwise, allow editing of articles even in read-only
8004 groups."
8005   (interactive "P")
8006   (let (force raw current-handles)
8007     (cond 
8008      ((null arg))
8009      ((eq arg 1) (setq raw t))
8010      ((eq arg 2) (setq raw t
8011                        force t))
8012      ((eq arg 3) (setq current-handles 
8013                        (and (gnus-buffer-live-p gnus-article-buffer)
8014                             (with-current-buffer gnus-article-buffer
8015                               (prog1
8016                                   gnus-article-mime-handles
8017                                   (setq gnus-article-mime-handles nil))))))
8018      (t (setq force t)))
8019     (if (and raw (not force) (equal gnus-newsgroup-name "nndraft:drafts"))
8020         (error "Can't edit the raw article in group nndraft:drafts."))
8021     (save-excursion
8022       (set-buffer gnus-summary-buffer)
8023       (let ((mail-parse-charset gnus-newsgroup-charset)
8024             (mail-parse-ignored-charsets gnus-newsgroup-ignored-charsets))
8025         (gnus-set-global-variables)
8026         (when (and (not force)
8027                    (gnus-group-read-only-p))
8028           (error "The current newsgroup does not support article editing"))
8029         (gnus-summary-show-article t)
8030         (when (and (not raw) (gnus-buffer-live-p gnus-article-buffer))
8031           (with-current-buffer gnus-article-buffer
8032             (mm-enable-multibyte-mule4)))
8033         (if (equal gnus-newsgroup-name "nndraft:drafts")
8034             (setq raw t))
8035         (gnus-article-edit-article
8036          (if raw 'ignore 
8037            `(lambda () 
8038               (let ((mbl mml-buffer-list))
8039                 (setq mml-buffer-list nil)
8040                 (mime-to-mml ,'current-handles)
8041                 (make-local-hook 'kill-buffer-hook)
8042                 (let ((mbl1 mml-buffer-list))
8043                   (setq mml-buffer-list mbl)
8044                   (set (make-local-variable 'mml-buffer-list) mbl1))
8045                 (add-hook 'kill-buffer-hook 'mml-destroy-buffers t t))))
8046          `(lambda (no-highlight)
8047             (let ((mail-parse-charset ',gnus-newsgroup-charset)
8048                   (message-options message-options)
8049                   (message-options-set-recipient)
8050                   (mail-parse-ignored-charsets 
8051                    ',gnus-newsgroup-ignored-charsets))
8052               ,(if (not raw) '(progn 
8053                                 (mml-to-mime)
8054                                 (mml-destroy-buffers)
8055                                 (remove-hook 'kill-buffer-hook 
8056                                              'mml-destroy-buffers t)
8057                                 (kill-local-variable 'mml-buffer-list)))
8058               (gnus-summary-edit-article-done
8059                ,(or (mail-header-references gnus-current-headers) "")
8060                ,(gnus-group-read-only-p) 
8061                ,gnus-summary-buffer no-highlight))))))))
8062
8063 (defalias 'gnus-summary-edit-article-postpone 'gnus-article-edit-exit)
8064
8065 (defun gnus-summary-edit-article-done (&optional references read-only buffer
8066                                                  no-highlight)
8067   "Make edits to the current article permanent."
8068   (interactive)
8069   (save-excursion
8070     ;; The buffer restriction contains the entire article if it exists.
8071     (when (article-goto-body)
8072       (let ((lines (count-lines (point) (point-max)))
8073             (length (- (point-max) (point)))
8074             (case-fold-search t)
8075             (body (copy-marker (point))))
8076         (goto-char (point-min))
8077         (when (re-search-forward "^content-length:[ \t]\\([0-9]+\\)" body t)
8078           (delete-region (match-beginning 1) (match-end 1))
8079           (insert (number-to-string length)))
8080         (goto-char (point-min))
8081         (when (re-search-forward
8082                "^x-content-length:[ \t]\\([0-9]+\\)" body t)
8083           (delete-region (match-beginning 1) (match-end 1))
8084           (insert (number-to-string length)))
8085         (goto-char (point-min))
8086         (when (re-search-forward "^lines:[ \t]\\([0-9]+\\)" body t)
8087           (delete-region (match-beginning 1) (match-end 1))
8088           (insert (number-to-string lines))))))
8089   ;; Replace the article.
8090   (let ((buf (current-buffer)))
8091     (with-temp-buffer
8092       (insert-buffer-substring buf)
8093       
8094       (if (and (not read-only)
8095                (not (gnus-request-replace-article
8096                      (cdr gnus-article-current) (car gnus-article-current)
8097                      (current-buffer) t)))
8098           (error "Couldn't replace article")
8099         ;; Update the summary buffer.
8100         (if (and references
8101                  (equal (message-tokenize-header references " ")
8102                         (message-tokenize-header
8103                          (or (message-fetch-field "references") "") " ")))
8104             ;; We only have to update this line.
8105             (save-excursion
8106               (save-restriction
8107                 (message-narrow-to-head)
8108                 (let ((head (buffer-string))
8109                       header)
8110                   (with-temp-buffer
8111                     (insert (format "211 %d Article retrieved.\n"
8112                                     (cdr gnus-article-current)))
8113                     (insert head)
8114                     (insert ".\n")
8115                     (let ((nntp-server-buffer (current-buffer)))
8116                       (setq header (car (gnus-get-newsgroup-headers
8117                                          (save-excursion
8118                                            (set-buffer gnus-summary-buffer)
8119                                            gnus-newsgroup-dependencies)
8120                                          t))))
8121                     (save-excursion
8122                       (set-buffer gnus-summary-buffer)
8123                       (gnus-data-set-header
8124                        (gnus-data-find (cdr gnus-article-current))
8125                        header)
8126                       (gnus-summary-update-article-line
8127                        (cdr gnus-article-current) header))))))
8128           ;; Update threads.
8129           (set-buffer (or buffer gnus-summary-buffer))
8130           (gnus-summary-update-article (cdr gnus-article-current)))
8131         ;; Prettify the article buffer again.
8132         (unless no-highlight
8133           (save-excursion
8134             (set-buffer gnus-article-buffer)
8135             ;;;!!! Fix this -- article should be rehighlighted.
8136             ;;;(gnus-run-hooks 'gnus-article-display-hook)
8137             (set-buffer gnus-original-article-buffer)
8138             (gnus-request-article
8139              (cdr gnus-article-current)
8140              (car gnus-article-current) (current-buffer))))
8141         ;; Prettify the summary buffer line.
8142         (when (gnus-visual-p 'summary-highlight 'highlight)
8143           (gnus-run-hooks 'gnus-visual-mark-article-hook))))))
8144
8145 (defun gnus-summary-edit-wash (key)
8146   "Perform editing command KEY in the article buffer."
8147   (interactive
8148    (list
8149     (progn
8150       (message "%s" (concat (this-command-keys) "- "))
8151       (read-char))))
8152   (message "")
8153   (gnus-summary-edit-article)
8154   (execute-kbd-macro (concat (this-command-keys) key))
8155   (gnus-article-edit-done))
8156
8157 ;;; Respooling
8158
8159 (defun gnus-summary-respool-query (&optional silent trace)
8160   "Query where the respool algorithm would put this article."
8161   (interactive)
8162   (let (gnus-mark-article-hook)
8163     (gnus-summary-select-article)
8164     (save-excursion
8165       (set-buffer gnus-original-article-buffer)
8166       (save-restriction
8167         (message-narrow-to-head)
8168         (let ((groups (nnmail-article-group 'identity trace)))
8169           (unless silent
8170             (if groups
8171                 (message "This message would go to %s"
8172                          (mapconcat 'car groups ", "))
8173               (message "This message would go to no groups"))
8174             groups))))))
8175
8176 (defun gnus-summary-respool-trace ()
8177   "Trace where the respool algorithm would put this article.
8178 Display a buffer showing all fancy splitting patterns which matched."
8179   (interactive)
8180   (gnus-summary-respool-query nil t))
8181
8182 ;; Summary marking commands.
8183
8184 (defun gnus-summary-kill-same-subject-and-select (&optional unmark)
8185   "Mark articles which has the same subject as read, and then select the next.
8186 If UNMARK is positive, remove any kind of mark.
8187 If UNMARK is negative, tick articles."
8188   (interactive "P")
8189   (when unmark
8190     (setq unmark (prefix-numeric-value unmark)))
8191   (let ((count
8192          (gnus-summary-mark-same-subject
8193           (gnus-summary-article-subject) unmark)))
8194     ;; Select next unread article.  If auto-select-same mode, should
8195     ;; select the first unread article.
8196     (gnus-summary-next-article t (and gnus-auto-select-same
8197                                       (gnus-summary-article-subject)))
8198     (gnus-message 7 "%d article%s marked as %s"
8199                   count (if (= count 1) " is" "s are")
8200                   (if unmark "unread" "read"))))
8201
8202 (defun gnus-summary-kill-same-subject (&optional unmark)
8203   "Mark articles which has the same subject as read.
8204 If UNMARK is positive, remove any kind of mark.
8205 If UNMARK is negative, tick articles."
8206   (interactive "P")
8207   (when unmark
8208     (setq unmark (prefix-numeric-value unmark)))
8209   (let ((count
8210          (gnus-summary-mark-same-subject
8211           (gnus-summary-article-subject) unmark)))
8212     ;; If marked as read, go to next unread subject.
8213     (when (null unmark)
8214       ;; Go to next unread subject.
8215       (gnus-summary-next-subject 1 t))
8216     (gnus-message 7 "%d articles are marked as %s"
8217                   count (if unmark "unread" "read"))))
8218
8219 (defun gnus-summary-mark-same-subject (subject &optional unmark)
8220   "Mark articles with same SUBJECT as read, and return marked number.
8221 If optional argument UNMARK is positive, remove any kinds of marks.
8222 If optional argument UNMARK is negative, mark articles as unread instead."
8223   (let ((count 1))
8224     (save-excursion
8225       (cond
8226        ((null unmark)                   ; Mark as read.
8227         (while (and
8228                 (progn
8229                   (gnus-summary-mark-article-as-read gnus-killed-mark)
8230                   (gnus-summary-show-thread) t)
8231                 (gnus-summary-find-subject subject))
8232           (setq count (1+ count))))
8233        ((> unmark 0)                    ; Tick.
8234         (while (and
8235                 (progn
8236                   (gnus-summary-mark-article-as-unread gnus-ticked-mark)
8237                   (gnus-summary-show-thread) t)
8238                 (gnus-summary-find-subject subject))
8239           (setq count (1+ count))))
8240        (t                               ; Mark as unread.
8241         (while (and
8242                 (progn
8243                   (gnus-summary-mark-article-as-unread gnus-unread-mark)
8244                   (gnus-summary-show-thread) t)
8245                 (gnus-summary-find-subject subject))
8246           (setq count (1+ count)))))
8247       (gnus-set-mode-line 'summary)
8248       ;; Return the number of marked articles.
8249       count)))
8250
8251 (defun gnus-summary-mark-as-processable (n &optional unmark)
8252   "Set the process mark on the next N articles.
8253 If N is negative, mark backward instead.  If UNMARK is non-nil, remove
8254 the process mark instead.  The difference between N and the actual
8255 number of articles marked is returned."
8256   (interactive "p")
8257   (let ((backward (< n 0))
8258         (n (abs n)))
8259     (while (and
8260             (> n 0)
8261             (if unmark
8262                 (gnus-summary-remove-process-mark
8263                  (gnus-summary-article-number))
8264               (gnus-summary-set-process-mark (gnus-summary-article-number)))
8265             (zerop (gnus-summary-next-subject (if backward -1 1) nil t)))
8266       (setq n (1- n)))
8267     (when (/= 0 n)
8268       (gnus-message 7 "No more articles"))
8269     (gnus-summary-recenter)
8270     (gnus-summary-position-point)
8271     n))
8272
8273 (defun gnus-summary-unmark-as-processable (n)
8274   "Remove the process mark from the next N articles.
8275 If N is negative, unmark backward instead.  The difference between N and
8276 the actual number of articles unmarked is returned."
8277   (interactive "p")
8278   (gnus-summary-mark-as-processable n t))
8279
8280 (defun gnus-summary-unmark-all-processable ()
8281   "Remove the process mark from all articles."
8282   (interactive)
8283   (save-excursion
8284     (while gnus-newsgroup-processable
8285       (gnus-summary-remove-process-mark (car gnus-newsgroup-processable))))
8286   (gnus-summary-position-point))
8287
8288 (defun gnus-summary-mark-as-expirable (n)
8289   "Mark N articles forward as expirable.
8290 If N is negative, mark backward instead.  The difference between N and
8291 the actual number of articles marked is returned."
8292   (interactive "p")
8293   (gnus-summary-mark-forward n gnus-expirable-mark))
8294
8295 (defun gnus-summary-mark-article-as-replied (article)
8296   "Mark ARTICLE replied and update the summary line."
8297   (push article gnus-newsgroup-replied)
8298   (let ((buffer-read-only nil))
8299     (when (gnus-summary-goto-subject article nil t)
8300       (gnus-summary-update-secondary-mark article))))
8301
8302 (defun gnus-summary-set-bookmark (article)
8303   "Set a bookmark in current article."
8304   (interactive (list (gnus-summary-article-number)))
8305   (when (or (not (get-buffer gnus-article-buffer))
8306             (not gnus-current-article)
8307             (not gnus-article-current)
8308             (not (equal gnus-newsgroup-name (car gnus-article-current))))
8309     (error "No current article selected"))
8310   ;; Remove old bookmark, if one exists.
8311   (let ((old (assq article gnus-newsgroup-bookmarks)))
8312     (when old
8313       (setq gnus-newsgroup-bookmarks
8314             (delq old gnus-newsgroup-bookmarks))))
8315   ;; Set the new bookmark, which is on the form
8316   ;; (article-number . line-number-in-body).
8317   (push
8318    (cons article
8319          (save-excursion
8320            (set-buffer gnus-article-buffer)
8321            (count-lines
8322             (min (point)
8323                  (save-excursion
8324                    (goto-char (point-min))
8325                    (search-forward "\n\n" nil t)
8326                    (point)))
8327             (point))))
8328    gnus-newsgroup-bookmarks)
8329   (gnus-message 6 "A bookmark has been added to the current article."))
8330
8331 (defun gnus-summary-remove-bookmark (article)
8332   "Remove the bookmark from the current article."
8333   (interactive (list (gnus-summary-article-number)))
8334   ;; Remove old bookmark, if one exists.
8335   (let ((old (assq article gnus-newsgroup-bookmarks)))
8336     (if old
8337         (progn
8338           (setq gnus-newsgroup-bookmarks
8339                 (delq old gnus-newsgroup-bookmarks))
8340           (gnus-message 6 "Removed bookmark."))
8341       (gnus-message 6 "No bookmark in current article."))))
8342
8343 ;; Suggested by Daniel Quinlan <quinlan@best.com>.
8344 (defun gnus-summary-mark-as-dormant (n)
8345   "Mark N articles forward as dormant.
8346 If N is negative, mark backward instead.  The difference between N and
8347 the actual number of articles marked is returned."
8348   (interactive "p")
8349   (gnus-summary-mark-forward n gnus-dormant-mark))
8350
8351 (defun gnus-summary-set-process-mark (article)
8352   "Set the process mark on ARTICLE and update the summary line."
8353   (setq gnus-newsgroup-processable
8354         (cons article
8355               (delq article gnus-newsgroup-processable)))
8356   (when (gnus-summary-goto-subject article)
8357     (gnus-summary-show-thread)
8358     (gnus-summary-goto-subject article)
8359     (gnus-summary-update-secondary-mark article)))
8360
8361 (defun gnus-summary-remove-process-mark (article)
8362   "Remove the process mark from ARTICLE and update the summary line."
8363   (setq gnus-newsgroup-processable (delq article gnus-newsgroup-processable))
8364   (when (gnus-summary-goto-subject article)
8365     (gnus-summary-show-thread)
8366     (gnus-summary-goto-subject article)
8367     (gnus-summary-update-secondary-mark article)))
8368
8369 (defun gnus-summary-set-saved-mark (article)
8370   "Set the process mark on ARTICLE and update the summary line."
8371   (push article gnus-newsgroup-saved)
8372   (when (gnus-summary-goto-subject article)
8373     (gnus-summary-update-secondary-mark article)))
8374
8375 (defun gnus-summary-mark-forward (n &optional mark no-expire)
8376   "Mark N articles as read forwards.
8377 If N is negative, mark backwards instead.  Mark with MARK, ?r by default.
8378 The difference between N and the actual number of articles marked is
8379 returned.
8380 Iff NO-EXPIRE, auto-expiry will be inhibited."
8381   (interactive "p")
8382   (gnus-summary-show-thread)
8383   (let ((backward (< n 0))
8384         (gnus-summary-goto-unread
8385          (and gnus-summary-goto-unread
8386               (not (eq gnus-summary-goto-unread 'never))
8387               (not (memq mark (list gnus-unread-mark
8388                                     gnus-ticked-mark gnus-dormant-mark)))))
8389         (n (abs n))
8390         (mark (or mark gnus-del-mark)))
8391     (while (and (> n 0)
8392                 (gnus-summary-mark-article nil mark no-expire)
8393                 (zerop (gnus-summary-next-subject
8394                         (if backward -1 1)
8395                         (and gnus-summary-goto-unread
8396                              (not (eq gnus-summary-goto-unread 'never)))
8397                         t)))
8398       (setq n (1- n)))
8399     (when (/= 0 n)
8400       (gnus-message 7 "No more %sarticles" (if mark "" "unread ")))
8401     (gnus-summary-recenter)
8402     (gnus-summary-position-point)
8403     (gnus-set-mode-line 'summary)
8404     n))
8405
8406 (defun gnus-summary-mark-article-as-read (mark)
8407   "Mark the current article quickly as read with MARK."
8408   (let ((article (gnus-summary-article-number)))
8409     (setq gnus-newsgroup-unreads (delq article gnus-newsgroup-unreads))
8410     (setq gnus-newsgroup-marked (delq article gnus-newsgroup-marked))
8411     (setq gnus-newsgroup-dormant (delq article gnus-newsgroup-dormant))
8412     (push (cons article mark) gnus-newsgroup-reads)
8413     ;; Possibly remove from cache, if that is used.
8414     (when gnus-use-cache
8415       (gnus-cache-enter-remove-article article))
8416     ;; Allow the backend to change the mark.
8417     (setq mark (gnus-request-update-mark gnus-newsgroup-name article mark))
8418     ;; Check for auto-expiry.
8419     (when (and gnus-newsgroup-auto-expire
8420                (memq mark gnus-auto-expirable-marks))
8421       (setq mark gnus-expirable-mark)
8422       ;; Let the backend know about the mark change.
8423       (setq mark (gnus-request-update-mark gnus-newsgroup-name article mark))
8424       (push article gnus-newsgroup-expirable))
8425     ;; Set the mark in the buffer.
8426     (gnus-summary-update-mark mark 'unread)
8427     t))
8428
8429 (defun gnus-summary-mark-article-as-unread (mark)
8430   "Mark the current article quickly as unread with MARK."
8431   (let* ((article (gnus-summary-article-number))
8432          (old-mark (gnus-summary-article-mark article)))
8433     ;; Allow the backend to change the mark.
8434     (setq mark (gnus-request-update-mark gnus-newsgroup-name article mark))
8435     (if (eq mark old-mark)
8436         t
8437       (if (<= article 0)
8438           (progn
8439             (gnus-error 1 "Can't mark negative article numbers")
8440             nil)
8441         (setq gnus-newsgroup-marked (delq article gnus-newsgroup-marked))
8442         (setq gnus-newsgroup-dormant (delq article gnus-newsgroup-dormant))
8443         (setq gnus-newsgroup-expirable (delq article gnus-newsgroup-expirable))
8444         (setq gnus-newsgroup-reads (delq article gnus-newsgroup-reads))
8445         (cond ((= mark gnus-ticked-mark)
8446                (push article gnus-newsgroup-marked))
8447               ((= mark gnus-dormant-mark)
8448                (push article gnus-newsgroup-dormant))
8449               (t
8450                (push article gnus-newsgroup-unreads)))
8451         (gnus-pull article gnus-newsgroup-reads)
8452
8453         ;; See whether the article is to be put in the cache.
8454         (and gnus-use-cache
8455              (vectorp (gnus-summary-article-header article))
8456              (save-excursion
8457                (gnus-cache-possibly-enter-article
8458                 gnus-newsgroup-name article
8459                 (= mark gnus-ticked-mark)
8460                 (= mark gnus-dormant-mark) (= mark gnus-unread-mark))))
8461
8462         ;; Fix the mark.
8463         (gnus-summary-update-mark mark 'unread)
8464         t))))
8465
8466 (defun gnus-summary-mark-article (&optional article mark no-expire)
8467   "Mark ARTICLE with MARK.  MARK can be any character.
8468 Four MARK strings are reserved: `? ' (unread), `?!' (ticked),
8469 `??' (dormant) and `?E' (expirable).
8470 If MARK is nil, then the default character `?r' is used.
8471 If ARTICLE is nil, then the article on the current line will be
8472 marked.
8473 Iff NO-EXPIRE, auto-expiry will be inhibited."
8474   ;; The mark might be a string.
8475   (when (stringp mark)
8476     (setq mark (aref mark 0)))
8477   ;; If no mark is given, then we check auto-expiring.
8478   (when (null mark)
8479     (setq mark gnus-del-mark))
8480   (when (and (not no-expire)
8481              gnus-newsgroup-auto-expire
8482              (memq mark gnus-auto-expirable-marks))
8483     (setq mark gnus-expirable-mark))
8484   (let ((article (or article (gnus-summary-article-number)))
8485         (old-mark (gnus-summary-article-mark article)))
8486     ;; Allow the backend to change the mark.
8487     (setq mark (gnus-request-update-mark gnus-newsgroup-name article mark))
8488     (if (eq mark old-mark)
8489         t
8490       (unless article
8491         (error "No article on current line"))
8492       (if (not (if (or (= mark gnus-unread-mark)
8493                        (= mark gnus-ticked-mark)
8494                        (= mark gnus-dormant-mark))
8495                    (gnus-mark-article-as-unread article mark)
8496                  (gnus-mark-article-as-read article mark)))
8497           t
8498         ;; See whether the article is to be put in the cache.
8499         (and gnus-use-cache
8500              (not (= mark gnus-canceled-mark))
8501              (vectorp (gnus-summary-article-header article))
8502              (save-excursion
8503                (gnus-cache-possibly-enter-article
8504                 gnus-newsgroup-name article
8505                 (= mark gnus-ticked-mark)
8506                 (= mark gnus-dormant-mark) (= mark gnus-unread-mark))))
8507
8508         (when (gnus-summary-goto-subject article nil t)
8509           (let ((buffer-read-only nil))
8510             (gnus-summary-show-thread)
8511             ;; Fix the mark.
8512             (gnus-summary-update-mark mark 'unread)
8513             t))))))
8514
8515 (defun gnus-summary-update-secondary-mark (article)
8516   "Update the secondary (read, process, cache) mark."
8517   (gnus-summary-update-mark
8518    (cond ((memq article gnus-newsgroup-processable)
8519           gnus-process-mark)
8520          ((memq article gnus-newsgroup-cached)
8521           gnus-cached-mark)
8522          ((memq article gnus-newsgroup-replied)
8523           gnus-replied-mark)
8524          ((memq article gnus-newsgroup-saved)
8525           gnus-saved-mark)
8526          (t gnus-unread-mark))
8527    'replied)
8528   (when (gnus-visual-p 'summary-highlight 'highlight)
8529     (gnus-run-hooks 'gnus-summary-update-hook))
8530   t)
8531
8532 (defun gnus-summary-update-mark (mark type)
8533   (let ((forward (cdr (assq type gnus-summary-mark-positions)))
8534         (buffer-read-only nil))
8535     (re-search-backward "[\n\r]" (gnus-point-at-bol) 'move-to-limit)
8536     (when forward
8537       (when (looking-at "\r")
8538         (incf forward))
8539       (when (<= (+ forward (point)) (point-max))
8540         ;; Go to the right position on the line.
8541         (goto-char (+ forward (point)))
8542         ;; Replace the old mark with the new mark.
8543         (subst-char-in-region (point) (1+ (point)) (char-after) mark)
8544         ;; Optionally update the marks by some user rule.
8545         (when (eq type 'unread)
8546           (gnus-data-set-mark
8547            (gnus-data-find (gnus-summary-article-number)) mark)
8548           (gnus-summary-update-line (eq mark gnus-unread-mark)))))))
8549
8550 (defun gnus-mark-article-as-read (article &optional mark)
8551   "Enter ARTICLE in the pertinent lists and remove it from others."
8552   ;; Make the article expirable.
8553   (let ((mark (or mark gnus-del-mark)))
8554     (if (= mark gnus-expirable-mark)
8555         (push article gnus-newsgroup-expirable)
8556       (setq gnus-newsgroup-expirable (delq article gnus-newsgroup-expirable)))
8557     ;; Remove from unread and marked lists.
8558     (setq gnus-newsgroup-unreads (delq article gnus-newsgroup-unreads))
8559     (setq gnus-newsgroup-marked (delq article gnus-newsgroup-marked))
8560     (setq gnus-newsgroup-dormant (delq article gnus-newsgroup-dormant))
8561     (push (cons article mark) gnus-newsgroup-reads)
8562     ;; Possibly remove from cache, if that is used.
8563     (when gnus-use-cache
8564       (gnus-cache-enter-remove-article article))
8565     t))
8566
8567 (defun gnus-mark-article-as-unread (article &optional mark)
8568   "Enter ARTICLE in the pertinent lists and remove it from others."
8569   (let ((mark (or mark gnus-ticked-mark)))
8570     (if (<= article 0)
8571         (progn
8572           (gnus-error 1 "Can't mark negative article numbers")
8573           nil)
8574       (setq gnus-newsgroup-marked (delq article gnus-newsgroup-marked)
8575             gnus-newsgroup-dormant (delq article gnus-newsgroup-dormant)
8576             gnus-newsgroup-expirable (delq article gnus-newsgroup-expirable)
8577             gnus-newsgroup-unreads (delq article gnus-newsgroup-unreads))
8578
8579       ;; Unsuppress duplicates?
8580       (when gnus-suppress-duplicates
8581         (gnus-dup-unsuppress-article article))
8582
8583       (cond ((= mark gnus-ticked-mark)
8584              (push article gnus-newsgroup-marked))
8585             ((= mark gnus-dormant-mark)
8586              (push article gnus-newsgroup-dormant))
8587             (t
8588              (push article gnus-newsgroup-unreads)))
8589       (gnus-pull article gnus-newsgroup-reads)
8590       t)))
8591
8592 (defalias 'gnus-summary-mark-as-unread-forward
8593   'gnus-summary-tick-article-forward)
8594 (make-obsolete 'gnus-summary-mark-as-unread-forward
8595                'gnus-summary-tick-article-forward)
8596 (defun gnus-summary-tick-article-forward (n)
8597   "Tick N articles forwards.
8598 If N is negative, tick backwards instead.
8599 The difference between N and the number of articles ticked is returned."
8600   (interactive "p")
8601   (gnus-summary-mark-forward n gnus-ticked-mark))
8602
8603 (defalias 'gnus-summary-mark-as-unread-backward
8604   'gnus-summary-tick-article-backward)
8605 (make-obsolete 'gnus-summary-mark-as-unread-backward
8606                'gnus-summary-tick-article-backward)
8607 (defun gnus-summary-tick-article-backward (n)
8608   "Tick N articles backwards.
8609 The difference between N and the number of articles ticked is returned."
8610   (interactive "p")
8611   (gnus-summary-mark-forward (- n) gnus-ticked-mark))
8612
8613 (defalias 'gnus-summary-mark-as-unread 'gnus-summary-tick-article)
8614 (make-obsolete 'gnus-summary-mark-as-unread 'gnus-summary-tick-article)
8615 (defun gnus-summary-tick-article (&optional article clear-mark)
8616   "Mark current article as unread.
8617 Optional 1st argument ARTICLE specifies article number to be marked as unread.
8618 Optional 2nd argument CLEAR-MARK remove any kinds of mark."
8619   (interactive)
8620   (gnus-summary-mark-article article (if clear-mark gnus-unread-mark
8621                                        gnus-ticked-mark)))
8622
8623 (defun gnus-summary-mark-as-read-forward (n)
8624   "Mark N articles as read forwards.
8625 If N is negative, mark backwards instead.
8626 The difference between N and the actual number of articles marked is
8627 returned."
8628   (interactive "p")
8629   (gnus-summary-mark-forward n gnus-del-mark gnus-inhibit-user-auto-expire))
8630
8631 (defun gnus-summary-mark-as-read-backward (n)
8632   "Mark the N articles as read backwards.
8633 The difference between N and the actual number of articles marked is
8634 returned."
8635   (interactive "p")
8636   (gnus-summary-mark-forward
8637    (- n) gnus-del-mark gnus-inhibit-user-auto-expire))
8638
8639 (defun gnus-summary-mark-as-read (&optional article mark)
8640   "Mark current article as read.
8641 ARTICLE specifies the article to be marked as read.
8642 MARK specifies a string to be inserted at the beginning of the line."
8643   (gnus-summary-mark-article article mark))
8644
8645 (defun gnus-summary-clear-mark-forward (n)
8646   "Clear marks from N articles forward.
8647 If N is negative, clear backward instead.
8648 The difference between N and the number of marks cleared is returned."
8649   (interactive "p")
8650   (gnus-summary-mark-forward n gnus-unread-mark))
8651
8652 (defun gnus-summary-clear-mark-backward (n)
8653   "Clear marks from N articles backward.
8654 The difference between N and the number of marks cleared is returned."
8655   (interactive "p")
8656   (gnus-summary-mark-forward (- n) gnus-unread-mark))
8657
8658 (defun gnus-summary-mark-unread-as-read ()
8659   "Intended to be used by `gnus-summary-mark-article-hook'."
8660   (when (memq gnus-current-article gnus-newsgroup-unreads)
8661     (gnus-summary-mark-article gnus-current-article gnus-read-mark)))
8662
8663 (defun gnus-summary-mark-read-and-unread-as-read ()
8664   "Intended to be used by `gnus-summary-mark-article-hook'."
8665   (let ((mark (gnus-summary-article-mark)))
8666     (when (or (gnus-unread-mark-p mark)
8667               (gnus-read-mark-p mark))
8668       (gnus-summary-mark-article gnus-current-article gnus-read-mark))))
8669
8670 (defun gnus-summary-mark-region-as-read (point mark all)
8671   "Mark all unread articles between point and mark as read.
8672 If given a prefix, mark all articles between point and mark as read,
8673 even ticked and dormant ones."
8674   (interactive "r\nP")
8675   (save-excursion
8676     (let (article)
8677       (goto-char point)
8678       (beginning-of-line)
8679       (while (and
8680               (< (point) mark)
8681               (progn
8682                 (when (or all
8683                           (memq (setq article (gnus-summary-article-number))
8684                                 gnus-newsgroup-unreads))
8685                   (gnus-summary-mark-article article gnus-del-mark))
8686                 t)
8687               (gnus-summary-find-next))))))
8688
8689 (defun gnus-summary-mark-below (score mark)
8690   "Mark articles with score less than SCORE with MARK."
8691   (interactive "P\ncMark: ")
8692   (setq score (if score
8693                   (prefix-numeric-value score)
8694                 (or gnus-summary-default-score 0)))
8695   (save-excursion
8696     (set-buffer gnus-summary-buffer)
8697     (goto-char (point-min))
8698     (while
8699         (progn
8700           (and (< (gnus-summary-article-score) score)
8701                (gnus-summary-mark-article nil mark))
8702           (gnus-summary-find-next)))))
8703
8704 (defun gnus-summary-kill-below (&optional score)
8705   "Mark articles with score below SCORE as read."
8706   (interactive "P")
8707   (gnus-summary-mark-below score gnus-killed-mark))
8708
8709 (defun gnus-summary-clear-above (&optional score)
8710   "Clear all marks from articles with score above SCORE."
8711   (interactive "P")
8712   (gnus-summary-mark-above score gnus-unread-mark))
8713
8714 (defun gnus-summary-tick-above (&optional score)
8715   "Tick all articles with score above SCORE."
8716   (interactive "P")
8717   (gnus-summary-mark-above score gnus-ticked-mark))
8718
8719 (defun gnus-summary-mark-above (score mark)
8720   "Mark articles with score over SCORE with MARK."
8721   (interactive "P\ncMark: ")
8722   (setq score (if score
8723                   (prefix-numeric-value score)
8724                 (or gnus-summary-default-score 0)))
8725   (save-excursion
8726     (set-buffer gnus-summary-buffer)
8727     (goto-char (point-min))
8728     (while (and (progn
8729                   (when (> (gnus-summary-article-score) score)
8730                     (gnus-summary-mark-article nil mark))
8731                   t)
8732                 (gnus-summary-find-next)))))
8733
8734 ;; Suggested by Daniel Quinlan <quinlan@best.com>.
8735 (defalias 'gnus-summary-show-all-expunged 'gnus-summary-limit-include-expunged)
8736 (defun gnus-summary-limit-include-expunged (&optional no-error)
8737   "Display all the hidden articles that were expunged for low scores."
8738   (interactive)
8739   (let ((buffer-read-only nil))
8740     (let ((scored gnus-newsgroup-scored)
8741           headers h)
8742       (while scored
8743         (unless (gnus-summary-goto-subject (caar scored))
8744           (and (setq h (gnus-summary-article-header (caar scored)))
8745                (< (cdar scored) gnus-summary-expunge-below)
8746                (push h headers)))
8747         (setq scored (cdr scored)))
8748       (if (not headers)
8749           (when (not no-error)
8750             (error "No expunged articles hidden"))
8751         (goto-char (point-min))
8752         (gnus-summary-prepare-unthreaded (nreverse headers))
8753         (goto-char (point-min))
8754         (gnus-summary-position-point)
8755         t))))
8756
8757 (defun gnus-summary-catchup (&optional all quietly to-here not-mark)
8758   "Mark all unread articles in this newsgroup as read.
8759 If prefix argument ALL is non-nil, ticked and dormant articles will
8760 also be marked as read.
8761 If QUIETLY is non-nil, no questions will be asked.
8762 If TO-HERE is non-nil, it should be a point in the buffer.  All
8763 articles before this point will be marked as read.
8764 Note that this function will only catch up the unread article
8765 in the current summary buffer limitation.
8766 The number of articles marked as read is returned."
8767   (interactive "P")
8768   (prog1
8769       (save-excursion
8770         (when (or quietly
8771                   (not gnus-interactive-catchup) ;Without confirmation?
8772                   gnus-expert-user
8773                   (gnus-y-or-n-p
8774                    (if all
8775                        "Mark absolutely all articles as read? "
8776                      "Mark all unread articles as read? ")))
8777           (if (and not-mark
8778                    (not gnus-newsgroup-adaptive)
8779                    (not gnus-newsgroup-auto-expire)
8780                    (not gnus-suppress-duplicates)
8781                    (or (not gnus-use-cache)
8782                        (eq gnus-use-cache 'passive)))
8783               (progn
8784                 (when all
8785                   (setq gnus-newsgroup-marked nil
8786                         gnus-newsgroup-dormant nil))
8787                 (setq gnus-newsgroup-unreads gnus-newsgroup-downloadable))
8788             ;; We actually mark all articles as canceled, which we
8789             ;; have to do when using auto-expiry or adaptive scoring.
8790             (gnus-summary-show-all-threads)
8791             (when (gnus-summary-first-subject (not all) t)
8792               (while (and
8793                       (if to-here (< (point) to-here) t)
8794                       (gnus-summary-mark-article-as-read gnus-catchup-mark)
8795                       (gnus-summary-find-next (not all) nil nil t))))
8796             (gnus-set-mode-line 'summary))
8797           t))
8798     (gnus-summary-position-point)))
8799
8800 (defun gnus-summary-catchup-to-here (&optional all)
8801   "Mark all unticked articles before the current one as read.
8802 If ALL is non-nil, also mark ticked and dormant articles as read."
8803   (interactive "P")
8804   (save-excursion
8805     (gnus-save-hidden-threads
8806       (let ((beg (point)))
8807         ;; We check that there are unread articles.
8808         (when (or all (gnus-summary-find-prev))
8809           (gnus-summary-catchup all t beg)))))
8810   (gnus-summary-position-point))
8811
8812 (defun gnus-summary-catchup-all (&optional quietly)
8813   "Mark all articles in this newsgroup as read."
8814   (interactive "P")
8815   (gnus-summary-catchup t quietly))
8816
8817 (defun gnus-summary-catchup-and-exit (&optional all quietly)
8818   "Mark all unread articles in this group as read, then exit.
8819 If prefix argument ALL is non-nil, all articles are marked as read."
8820   (interactive "P")
8821   (when (gnus-summary-catchup all quietly nil 'fast)
8822     ;; Select next newsgroup or exit.
8823     (if (and (not (gnus-group-quit-config gnus-newsgroup-name))
8824              (eq gnus-auto-select-next 'quietly))
8825         (gnus-summary-next-group nil)
8826       (gnus-summary-exit))))
8827
8828 (defun gnus-summary-catchup-all-and-exit (&optional quietly)
8829   "Mark all articles in this newsgroup as read, and then exit."
8830   (interactive "P")
8831   (gnus-summary-catchup-and-exit t quietly))
8832
8833 (defun gnus-summary-catchup-and-goto-next-group (&optional all)
8834   "Mark all articles in this group as read and select the next group.
8835 If given a prefix, mark all articles, unread as well as ticked, as
8836 read."
8837   (interactive "P")
8838   (save-excursion
8839     (gnus-summary-catchup all))
8840   (gnus-summary-next-group))
8841
8842 ;;;
8843 ;;; with article
8844 ;;;
8845
8846 (defmacro gnus-with-article (article &rest forms)
8847   "Select ARTICLE and perform FORMS in the original article buffer.
8848 Then replace the article with the result."
8849   `(progn
8850      ;; We don't want the article to be marked as read.
8851      (let (gnus-mark-article-hook)
8852        (gnus-summary-select-article t t nil ,article))
8853      (set-buffer gnus-original-article-buffer)
8854      ,@forms
8855      (if (not (gnus-check-backend-function
8856                'request-replace-article (car gnus-article-current)))
8857          (gnus-message 5 "Read-only group; not replacing")
8858        (unless (gnus-request-replace-article
8859                 ,article (car gnus-article-current)
8860                 (current-buffer) t)
8861          (error "Couldn't replace article")))
8862      ;; The cache and backlog have to be flushed somewhat.
8863      (when gnus-keep-backlog
8864        (gnus-backlog-remove-article
8865         (car gnus-article-current) (cdr gnus-article-current)))
8866      (when gnus-use-cache
8867        (gnus-cache-update-article
8868         (car gnus-article-current) (cdr gnus-article-current)))))
8869
8870 (put 'gnus-with-article 'lisp-indent-function 1)
8871 (put 'gnus-with-article 'edebug-form-spec '(form body))
8872
8873 ;; Thread-based commands.
8874
8875 (defun gnus-summary-articles-in-thread (&optional article)
8876   "Return a list of all articles in the current thread.
8877 If ARTICLE is non-nil, return all articles in the thread that starts
8878 with that article."
8879   (let* ((article (or article (gnus-summary-article-number)))
8880          (data (gnus-data-find-list article))
8881          (top-level (gnus-data-level (car data)))
8882          (top-subject
8883           (cond ((null gnus-thread-operation-ignore-subject)
8884                  (gnus-simplify-subject-re
8885                   (mail-header-subject (gnus-data-header (car data)))))
8886                 ((eq gnus-thread-operation-ignore-subject 'fuzzy)
8887                  (gnus-simplify-subject-fuzzy
8888                   (mail-header-subject (gnus-data-header (car data)))))
8889                 (t nil)))
8890          (end-point (save-excursion
8891                       (if (gnus-summary-go-to-next-thread)
8892                           (point) (point-max))))
8893          articles)
8894     (while (and data
8895                 (< (gnus-data-pos (car data)) end-point))
8896       (when (or (not top-subject)
8897                 (string= top-subject
8898                          (if (eq gnus-thread-operation-ignore-subject 'fuzzy)
8899                              (gnus-simplify-subject-fuzzy
8900                               (mail-header-subject
8901                                (gnus-data-header (car data))))
8902                            (gnus-simplify-subject-re
8903                             (mail-header-subject
8904                              (gnus-data-header (car data)))))))
8905         (push (gnus-data-number (car data)) articles))
8906       (unless (and (setq data (cdr data))
8907                    (> (gnus-data-level (car data)) top-level))
8908         (setq data nil)))
8909     ;; Return the list of articles.
8910     (nreverse articles)))
8911
8912 (defun gnus-summary-rethread-current ()
8913   "Rethread the thread the current article is part of."
8914   (interactive)
8915   (let* ((gnus-show-threads t)
8916          (article (gnus-summary-article-number))
8917          (id (mail-header-id (gnus-summary-article-header)))
8918          (gnus-newsgroup-threads (list (gnus-id-to-thread (gnus-root-id id)))))
8919     (unless id
8920       (error "No article on the current line"))
8921     (gnus-rebuild-thread id)
8922     (gnus-summary-goto-subject article)))
8923
8924 (defun gnus-summary-reparent-thread ()
8925   "Make the current article child of the marked (or previous) article.
8926
8927 Note that the re-threading will only work if `gnus-thread-ignore-subject'
8928 is non-nil or the Subject: of both articles are the same."
8929   (interactive)
8930   (unless (not (gnus-group-read-only-p))
8931     (error "The current newsgroup does not support article editing"))
8932   (unless (<= (length gnus-newsgroup-processable) 1)
8933     (error "No more than one article may be marked"))
8934   (save-window-excursion
8935     (let ((gnus-article-buffer " *reparent*")
8936           (current-article (gnus-summary-article-number))
8937           ;; First grab the marked article, otherwise one line up.
8938           (parent-article (if (not (null gnus-newsgroup-processable))
8939                               (car gnus-newsgroup-processable)
8940                             (save-excursion
8941                               (if (eq (forward-line -1) 0)
8942                                   (gnus-summary-article-number)
8943                                 (error "Beginning of summary buffer"))))))
8944       (unless (not (eq current-article parent-article))
8945         (error "An article may not be self-referential"))
8946       (let ((message-id (mail-header-id
8947                          (gnus-summary-article-header parent-article))))
8948         (unless (and message-id (not (equal message-id "")))
8949           (error "No message-id in desired parent"))
8950         (gnus-with-article current-article
8951           (save-restriction
8952             (goto-char (point-min))
8953             (message-narrow-to-head)
8954             (if (re-search-forward "^References: " nil t)
8955                 (progn
8956                   (re-search-forward "^[^ \t]" nil t)
8957                   (forward-line -1)
8958                   (end-of-line)
8959                   (insert " " message-id))
8960               (insert "References: " message-id "\n"))))
8961         (set-buffer gnus-summary-buffer)
8962         (gnus-summary-unmark-all-processable)
8963         (gnus-summary-update-article current-article)
8964         (gnus-summary-rethread-current)
8965         (gnus-message 3 "Article %d is now the child of article %d"
8966                       current-article parent-article)))))
8967
8968 (defun gnus-summary-toggle-threads (&optional arg)
8969   "Toggle showing conversation threads.
8970 If ARG is positive number, turn showing conversation threads on."
8971   (interactive "P")
8972   (let ((current (or (gnus-summary-article-number) gnus-newsgroup-end)))
8973     (setq gnus-show-threads
8974           (if (null arg) (not gnus-show-threads)
8975             (> (prefix-numeric-value arg) 0)))
8976     (gnus-summary-prepare)
8977     (gnus-summary-goto-subject current)
8978     (gnus-message 6 "Threading is now %s" (if gnus-show-threads "on" "off"))
8979     (gnus-summary-position-point)))
8980
8981 (defun gnus-summary-show-all-threads ()
8982   "Show all threads."
8983   (interactive)
8984   (save-excursion
8985     (let ((buffer-read-only nil))
8986       (subst-char-in-region (point-min) (point-max) ?\^M ?\n t)))
8987   (gnus-summary-position-point))
8988
8989 (defun gnus-summary-show-thread ()
8990   "Show thread subtrees.
8991 Returns nil if no thread was there to be shown."
8992   (interactive)
8993   (let ((buffer-read-only nil)
8994         (orig (point))
8995         ;; first goto end then to beg, to have point at beg after let
8996         (end (progn (end-of-line) (point)))
8997         (beg (progn (beginning-of-line) (point))))
8998     (prog1
8999         ;; Any hidden lines here?
9000         (search-forward "\r" end t)
9001       (subst-char-in-region beg end ?\^M ?\n t)
9002       (goto-char orig)
9003       (gnus-summary-position-point))))
9004
9005 (defun gnus-summary-hide-all-threads ()
9006   "Hide all thread subtrees."
9007   (interactive)
9008   (save-excursion
9009     (goto-char (point-min))
9010     (gnus-summary-hide-thread)
9011     (while (zerop (gnus-summary-next-thread 1 t))
9012       (gnus-summary-hide-thread)))
9013   (gnus-summary-position-point))
9014
9015 (defun gnus-summary-hide-thread ()
9016   "Hide thread subtrees.
9017 Returns nil if no threads were there to be hidden."
9018   (interactive)
9019   (let ((buffer-read-only nil)
9020         (start (point))
9021         (article (gnus-summary-article-number)))
9022     (goto-char start)
9023     ;; Go forward until either the buffer ends or the subthread
9024     ;; ends.
9025     (when (and (not (eobp))
9026                (or (zerop (gnus-summary-next-thread 1 t))
9027                    (goto-char (point-max))))
9028       (prog1
9029           (if (and (> (point) start)
9030                    (search-backward "\n" start t))
9031               (progn
9032                 (subst-char-in-region start (point) ?\n ?\^M)
9033                 (gnus-summary-goto-subject article))
9034             (goto-char start)
9035             nil)))))
9036
9037 (defun gnus-summary-go-to-next-thread (&optional previous)
9038   "Go to the same level (or less) next thread.
9039 If PREVIOUS is non-nil, go to previous thread instead.
9040 Return the article number moved to, or nil if moving was impossible."
9041   (let ((level (gnus-summary-thread-level))
9042         (way (if previous -1 1))
9043         (beg (point)))
9044     (forward-line way)
9045     (while (and (not (eobp))
9046                 (< level (gnus-summary-thread-level)))
9047       (forward-line way))
9048     (if (eobp)
9049         (progn
9050           (goto-char beg)
9051           nil)
9052       (setq beg (point))
9053       (prog1
9054           (gnus-summary-article-number)
9055         (goto-char beg)))))
9056
9057 (defun gnus-summary-next-thread (n &optional silent)
9058   "Go to the same level next N'th thread.
9059 If N is negative, search backward instead.
9060 Returns the difference between N and the number of skips actually
9061 done.
9062
9063 If SILENT, don't output messages."
9064   (interactive "p")
9065   (let ((backward (< n 0))
9066         (n (abs n)))
9067     (while (and (> n 0)
9068                 (gnus-summary-go-to-next-thread backward))
9069       (decf n))
9070     (unless silent
9071       (gnus-summary-position-point))
9072     (when (and (not silent) (/= 0 n))
9073       (gnus-message 7 "No more threads"))
9074     n))
9075
9076 (defun gnus-summary-prev-thread (n)
9077   "Go to the same level previous N'th thread.
9078 Returns the difference between N and the number of skips actually
9079 done."
9080   (interactive "p")
9081   (gnus-summary-next-thread (- n)))
9082
9083 (defun gnus-summary-go-down-thread ()
9084   "Go down one level in the current thread."
9085   (let ((children (gnus-summary-article-children)))
9086     (when children
9087       (gnus-summary-goto-subject (car children)))))
9088
9089 (defun gnus-summary-go-up-thread ()
9090   "Go up one level in the current thread."
9091   (let ((parent (gnus-summary-article-parent)))
9092     (when parent
9093       (gnus-summary-goto-subject parent))))
9094
9095 (defun gnus-summary-down-thread (n)
9096   "Go down thread N steps.
9097 If N is negative, go up instead.
9098 Returns the difference between N and how many steps down that were
9099 taken."
9100   (interactive "p")
9101   (let ((up (< n 0))
9102         (n (abs n)))
9103     (while (and (> n 0)
9104                 (if up (gnus-summary-go-up-thread)
9105                   (gnus-summary-go-down-thread)))
9106       (setq n (1- n)))
9107     (gnus-summary-position-point)
9108     (when (/= 0 n)
9109       (gnus-message 7 "Can't go further"))
9110     n))
9111
9112 (defun gnus-summary-up-thread (n)
9113   "Go up thread N steps.
9114 If N is negative, go up instead.
9115 Returns the difference between N and how many steps down that were
9116 taken."
9117   (interactive "p")
9118   (gnus-summary-down-thread (- n)))
9119
9120 (defun gnus-summary-top-thread ()
9121   "Go to the top of the thread."
9122   (interactive)
9123   (while (gnus-summary-go-up-thread))
9124   (gnus-summary-article-number))
9125
9126 (defun gnus-summary-kill-thread (&optional unmark)
9127   "Mark articles under current thread as read.
9128 If the prefix argument is positive, remove any kinds of marks.
9129 If the prefix argument is negative, tick articles instead."
9130   (interactive "P")
9131   (when unmark
9132     (setq unmark (prefix-numeric-value unmark)))
9133   (let ((articles (gnus-summary-articles-in-thread)))
9134     (save-excursion
9135       ;; Expand the thread.
9136       (gnus-summary-show-thread)
9137       ;; Mark all the articles.
9138       (while articles
9139         (gnus-summary-goto-subject (car articles))
9140         (cond ((null unmark)
9141                (gnus-summary-mark-article-as-read gnus-killed-mark))
9142               ((> unmark 0)
9143                (gnus-summary-mark-article-as-unread gnus-unread-mark))
9144               (t
9145                (gnus-summary-mark-article-as-unread gnus-ticked-mark)))
9146         (setq articles (cdr articles))))
9147     ;; Hide killed subtrees.
9148     (and (null unmark)
9149          gnus-thread-hide-killed
9150          (gnus-summary-hide-thread))
9151     ;; If marked as read, go to next unread subject.
9152     (when (null unmark)
9153       ;; Go to next unread subject.
9154       (gnus-summary-next-subject 1 t)))
9155   (gnus-set-mode-line 'summary))
9156
9157 ;; Summary sorting commands
9158
9159 (defun gnus-summary-sort-by-number (&optional reverse)
9160   "Sort the summary buffer by article number.
9161 Argument REVERSE means reverse order."
9162   (interactive "P")
9163   (gnus-summary-sort 'number reverse))
9164
9165 (defun gnus-summary-sort-by-author (&optional reverse)
9166   "Sort the summary buffer by author name alphabetically.
9167 If `case-fold-search' is non-nil, case of letters is ignored.
9168 Argument REVERSE means reverse order."
9169   (interactive "P")
9170   (gnus-summary-sort 'author reverse))
9171
9172 (defun gnus-summary-sort-by-subject (&optional reverse)
9173   "Sort the summary buffer by subject alphabetically.  `Re:'s are ignored.
9174 If `case-fold-search' is non-nil, case of letters is ignored.
9175 Argument REVERSE means reverse order."
9176   (interactive "P")
9177   (gnus-summary-sort 'subject reverse))
9178
9179 (defun gnus-summary-sort-by-date (&optional reverse)
9180   "Sort the summary buffer by date.
9181 Argument REVERSE means reverse order."
9182   (interactive "P")
9183   (gnus-summary-sort 'date reverse))
9184
9185 (defun gnus-summary-sort-by-score (&optional reverse)
9186   "Sort the summary buffer by score.
9187 Argument REVERSE means reverse order."
9188   (interactive "P")
9189   (gnus-summary-sort 'score reverse))
9190
9191 (defun gnus-summary-sort-by-lines (&optional reverse)
9192   "Sort the summary buffer by the number of lines.
9193 Argument REVERSE means reverse order."
9194   (interactive "P")
9195   (gnus-summary-sort 'lines reverse))
9196
9197 (defun gnus-summary-sort-by-chars (&optional reverse)
9198   "Sort the summary buffer by article length.
9199 Argument REVERSE means reverse order."
9200   (interactive "P")
9201   (gnus-summary-sort 'chars reverse))
9202
9203 (defun gnus-summary-sort (predicate reverse)
9204   "Sort summary buffer by PREDICATE.  REVERSE means reverse order."
9205   (let* ((thread (intern (format "gnus-thread-sort-by-%s" predicate)))
9206          (article (intern (format "gnus-article-sort-by-%s" predicate)))
9207          (gnus-thread-sort-functions
9208           (if (not reverse)
9209               thread
9210             `(lambda (t1 t2)
9211                (,thread t2 t1))))
9212          (gnus-sort-gathered-threads-function
9213           gnus-thread-sort-functions)
9214          (gnus-article-sort-functions
9215           (if (not reverse)
9216               article
9217             `(lambda (t1 t2)
9218                (,article t2 t1))))
9219          (buffer-read-only)
9220          (gnus-summary-prepare-hook nil))
9221     ;; We do the sorting by regenerating the threads.
9222     (gnus-summary-prepare)
9223     ;; Hide subthreads if needed.
9224     (when (and gnus-show-threads gnus-thread-hide-subtree)
9225       (gnus-summary-hide-all-threads))))
9226
9227 ;; Summary saving commands.
9228
9229 (defun gnus-summary-save-article (&optional n not-saved)
9230   "Save the current article using the default saver function.
9231 If N is a positive number, save the N next articles.
9232 If N is a negative number, save the N previous articles.
9233 If N is nil and any articles have been marked with the process mark,
9234 save those articles instead.
9235 The variable `gnus-default-article-saver' specifies the saver function."
9236   (interactive "P")
9237   (let* ((articles (gnus-summary-work-articles n))
9238          (save-buffer (save-excursion
9239                         (nnheader-set-temp-buffer " *Gnus Save*")))
9240          (num (length articles))
9241          header file)
9242     (dolist (article articles)
9243       (setq header (gnus-summary-article-header article))
9244       (if (not (vectorp header))
9245           ;; This is a pseudo-article.
9246           (if (assq 'name header)
9247               (gnus-copy-file (cdr (assq 'name header)))
9248             (gnus-message 1 "Article %d is unsaveable" article))
9249         ;; This is a real article.
9250         (save-window-excursion
9251           (gnus-summary-select-article t nil nil article))
9252         (save-excursion
9253           (set-buffer save-buffer)
9254           (erase-buffer)
9255           (insert-buffer-substring gnus-original-article-buffer))
9256         (setq file (gnus-article-save save-buffer file num))
9257         (gnus-summary-remove-process-mark article)
9258         (unless not-saved
9259           (gnus-summary-set-saved-mark article))))
9260     (gnus-kill-buffer save-buffer)
9261     (gnus-summary-position-point)
9262     (gnus-set-mode-line 'summary)
9263     n))
9264
9265 (defun gnus-summary-pipe-output (&optional arg)
9266   "Pipe the current article to a subprocess.
9267 If N is a positive number, pipe the N next articles.
9268 If N is a negative number, pipe the N previous articles.
9269 If N is nil and any articles have been marked with the process mark,
9270 pipe those articles instead."
9271   (interactive "P")
9272   (let ((gnus-default-article-saver 'gnus-summary-save-in-pipe))
9273     (gnus-summary-save-article arg t))
9274   (gnus-configure-windows 'pipe))
9275
9276 (defun gnus-summary-save-article-mail (&optional arg)
9277   "Append the current article to an mail file.
9278 If N is a positive number, save the N next articles.
9279 If N is a negative number, save the N previous articles.
9280 If N is nil and any articles have been marked with the process mark,
9281 save those articles instead."
9282   (interactive "P")
9283   (let ((gnus-default-article-saver 'gnus-summary-save-in-mail))
9284     (gnus-summary-save-article arg)))
9285
9286 (defun gnus-summary-save-article-rmail (&optional arg)
9287   "Append the current article to an rmail file.
9288 If N is a positive number, save the N next articles.
9289 If N is a negative number, save the N previous articles.
9290 If N is nil and any articles have been marked with the process mark,
9291 save those articles instead."
9292   (interactive "P")
9293   (let ((gnus-default-article-saver 'gnus-summary-save-in-rmail))
9294     (gnus-summary-save-article arg)))
9295
9296 (defun gnus-summary-save-article-file (&optional arg)
9297   "Append the current article to a file.
9298 If N is a positive number, save the N next articles.
9299 If N is a negative number, save the N previous articles.
9300 If N is nil and any articles have been marked with the process mark,
9301 save those articles instead."
9302   (interactive "P")
9303   (let ((gnus-default-article-saver 'gnus-summary-save-in-file))
9304     (gnus-summary-save-article arg)))
9305
9306 (defun gnus-summary-write-article-file (&optional arg)
9307   "Write the current article to a file, deleting the previous file.
9308 If N is a positive number, save the N next articles.
9309 If N is a negative number, save the N previous articles.
9310 If N is nil and any articles have been marked with the process mark,
9311 save those articles instead."
9312   (interactive "P")
9313   (let ((gnus-default-article-saver 'gnus-summary-write-to-file))
9314     (gnus-summary-save-article arg)))
9315
9316 (defun gnus-summary-save-article-body-file (&optional arg)
9317   "Append the current article body to a file.
9318 If N is a positive number, save the N next articles.
9319 If N is a negative number, save the N previous articles.
9320 If N is nil and any articles have been marked with the process mark,
9321 save those articles instead."
9322   (interactive "P")
9323   (let ((gnus-default-article-saver 'gnus-summary-save-body-in-file))
9324     (gnus-summary-save-article arg)))
9325
9326 (defun gnus-summary-pipe-message (program)
9327   "Pipe the current article through PROGRAM."
9328   (interactive "sProgram: ")
9329   (gnus-summary-select-article)
9330   (let ((mail-header-separator ""))
9331     (gnus-eval-in-buffer-window gnus-article-buffer
9332       (save-restriction
9333         (widen)
9334         (let ((start (window-start))
9335               buffer-read-only)
9336           (message-pipe-buffer-body program)
9337           (set-window-start (get-buffer-window (current-buffer)) start))))))
9338
9339 (defun gnus-get-split-value (methods)
9340   "Return a value based on the split METHODS."
9341   (let (split-name method result match)
9342     (when methods
9343       (save-excursion
9344         (set-buffer gnus-original-article-buffer)
9345         (save-restriction
9346           (nnheader-narrow-to-headers)
9347           (while (and methods (not split-name))
9348             (goto-char (point-min))
9349             (setq method (pop methods))
9350             (setq match (car method))
9351             (when (cond
9352                    ((stringp match)
9353                     ;; Regular expression.
9354                     (ignore-errors
9355                       (re-search-forward match nil t)))
9356                    ((gnus-functionp match)
9357                     ;; Function.
9358                     (save-restriction
9359                       (widen)
9360                       (setq result (funcall match gnus-newsgroup-name))))
9361                    ((consp match)
9362                     ;; Form.
9363                     (save-restriction
9364                       (widen)
9365                       (setq result (eval match)))))
9366               (setq split-name (cdr method))
9367               (cond ((stringp result)
9368                      (push (expand-file-name
9369                             result gnus-article-save-directory)
9370                            split-name))
9371                     ((consp result)
9372                      (setq split-name (append result split-name)))))))))
9373     (nreverse split-name)))
9374
9375 (defun gnus-valid-move-group-p (group)
9376   (and (boundp group)
9377        (symbol-name group)
9378        (symbol-value group)
9379        (gnus-get-function (gnus-find-method-for-group
9380                            (symbol-name group)) 'request-accept-article t)))
9381
9382 (defun gnus-read-move-group-name (prompt default articles prefix)
9383   "Read a group name."
9384   (let* ((split-name (gnus-get-split-value gnus-move-split-methods))
9385          (minibuffer-confirm-incomplete nil) ; XEmacs
9386          (prom
9387           (format "%s %s to:"
9388                   prompt
9389                   (if (> (length articles) 1)
9390                       (format "these %d articles" (length articles))
9391                     "this article")))
9392          (to-newsgroup
9393           (cond
9394            ((null split-name)
9395             (gnus-completing-read default prom
9396                                   gnus-active-hashtb
9397                                   'gnus-valid-move-group-p
9398                                   nil prefix
9399                                   'gnus-group-history))
9400            ((= 1 (length split-name))
9401             (gnus-completing-read (car split-name) prom
9402                                   gnus-active-hashtb
9403                                   'gnus-valid-move-group-p
9404                                   nil nil
9405                                   'gnus-group-history))
9406            (t
9407             (gnus-completing-read nil prom
9408                                   (mapcar (lambda (el) (list el))
9409                                           (nreverse split-name))
9410                                   nil nil nil
9411                                   'gnus-group-history))))
9412          (to-method (gnus-server-to-method (gnus-group-method to-newsgroup))))
9413     (when to-newsgroup
9414       (if (or (string= to-newsgroup "")
9415               (string= to-newsgroup prefix))
9416           (setq to-newsgroup default))
9417       (unless to-newsgroup
9418         (error "No group name entered"))
9419       (or (gnus-active to-newsgroup)
9420           (gnus-activate-group to-newsgroup nil nil to-method)
9421           (if (gnus-y-or-n-p (format "No such group: %s.  Create it? "
9422                                      to-newsgroup))
9423               (or (and (gnus-request-create-group to-newsgroup to-method)
9424                        (gnus-activate-group
9425                         to-newsgroup nil nil to-method)
9426                        (gnus-subscribe-group to-newsgroup))
9427                   (error "Couldn't create group %s" to-newsgroup)))
9428           (error "No such group: %s" to-newsgroup)))
9429     to-newsgroup))
9430
9431 (defun gnus-summary-save-parts (type dir n &optional reverse)
9432   "Save parts matching TYPE to DIR.
9433 If REVERSE, save parts that do not match TYPE."
9434   (interactive
9435    (list (read-string "Save parts of type: " 
9436                       (or (car gnus-summary-save-parts-type-history)
9437                           gnus-summary-save-parts-default-mime)
9438                       'gnus-summary-save-parts-type-history)
9439          (setq gnus-summary-save-parts-last-directory
9440                (read-file-name "Save to directory: " 
9441                                gnus-summary-save-parts-last-directory
9442                                nil t))
9443          current-prefix-arg))
9444   (gnus-summary-iterate n
9445     (let ((gnus-display-mime-function nil)
9446           (gnus-inhibit-treatment t))
9447       (gnus-summary-select-article))
9448     (save-excursion
9449       (set-buffer gnus-article-buffer)
9450       (let ((handles (or gnus-article-mime-handles
9451                          (mm-dissect-buffer) (mm-uu-dissect))))
9452         (when handles
9453           (gnus-summary-save-parts-1 type dir handles reverse)
9454           (unless gnus-article-mime-handles ;; Don't destroy this case.
9455             (mm-destroy-parts handles)))))))
9456
9457 (defun gnus-summary-save-parts-1 (type dir handle reverse)
9458   (if (stringp (car handle))
9459       (mapcar (lambda (h) (gnus-summary-save-parts-1 type dir h reverse))
9460               (cdr handle))
9461     (when (if reverse
9462               (not (string-match type (mm-handle-media-type handle)))
9463             (string-match type (mm-handle-media-type handle)))
9464       (let ((file (expand-file-name
9465                    (file-name-nondirectory
9466                     (or
9467                      (mail-content-type-get
9468                       (mm-handle-disposition handle) 'filename)
9469                      (concat gnus-newsgroup-name
9470                              "." (number-to-string
9471                                   (cdr gnus-article-current)))))
9472                    dir)))
9473         (unless (file-exists-p file)
9474           (mm-save-part-to-file handle file))))))
9475
9476 ;; Summary extract commands
9477
9478 (defun gnus-summary-insert-pseudos (pslist &optional not-view)
9479   (let ((buffer-read-only nil)
9480         (article (gnus-summary-article-number))
9481         after-article b e)
9482     (unless (gnus-summary-goto-subject article)
9483       (error "No such article: %d" article))
9484     (gnus-summary-position-point)
9485     ;; If all commands are to be bunched up on one line, we collect
9486     ;; them here.
9487     (unless gnus-view-pseudos-separately
9488       (let ((ps (setq pslist (sort pslist 'gnus-pseudos<)))
9489             files action)
9490         (while ps
9491           (setq action (cdr (assq 'action (car ps))))
9492           (setq files (list (cdr (assq 'name (car ps)))))
9493           (while (and ps (cdr ps)
9494                       (string= (or action "1")
9495                                (or (cdr (assq 'action (cadr ps))) "2")))
9496             (push (cdr (assq 'name (cadr ps))) files)
9497             (setcdr ps (cddr ps)))
9498           (when files
9499             (when (not (string-match "%s" action))
9500               (push " " files))
9501             (push " " files)
9502             (when (assq 'execute (car ps))
9503               (setcdr (assq 'execute (car ps))
9504                       (funcall (if (string-match "%s" action)
9505                                    'format 'concat)
9506                                action
9507                                (mapconcat
9508                                 (lambda (f)
9509                                   (if (equal f " ")
9510                                       f
9511                                     (mm-quote-arg f)))
9512                                 files " ")))))
9513           (setq ps (cdr ps)))))
9514     (if (and gnus-view-pseudos (not not-view))
9515         (while pslist
9516           (when (assq 'execute (car pslist))
9517             (gnus-execute-command (cdr (assq 'execute (car pslist)))
9518                                   (eq gnus-view-pseudos 'not-confirm)))
9519           (setq pslist (cdr pslist)))
9520       (save-excursion
9521         (while pslist
9522           (setq after-article (or (cdr (assq 'article (car pslist)))
9523                                   (gnus-summary-article-number)))
9524           (gnus-summary-goto-subject after-article)
9525           (forward-line 1)
9526           (setq b (point))
9527           (insert "    " (file-name-nondirectory
9528                           (cdr (assq 'name (car pslist))))
9529                   ": " (or (cdr (assq 'execute (car pslist))) "") "\n")
9530           (setq e (point))
9531           (forward-line -1)             ; back to `b'
9532           (gnus-add-text-properties
9533            b (1- e) (list 'gnus-number gnus-reffed-article-number
9534                           gnus-mouse-face-prop gnus-mouse-face))
9535           (gnus-data-enter
9536            after-article gnus-reffed-article-number
9537            gnus-unread-mark b (car pslist) 0 (- e b))
9538           (push gnus-reffed-article-number gnus-newsgroup-unreads)
9539           (setq gnus-reffed-article-number (1- gnus-reffed-article-number))
9540           (setq pslist (cdr pslist)))))))
9541
9542 (defun gnus-pseudos< (p1 p2)
9543   (let ((c1 (cdr (assq 'action p1)))
9544         (c2 (cdr (assq 'action p2))))
9545     (and c1 c2 (string< c1 c2))))
9546
9547 (defun gnus-request-pseudo-article (props)
9548   (cond ((assq 'execute props)
9549          (gnus-execute-command (cdr (assq 'execute props)))))
9550   (let ((gnus-current-article (gnus-summary-article-number)))
9551     (gnus-run-hooks 'gnus-mark-article-hook)))
9552
9553 (defun gnus-execute-command (command &optional automatic)
9554   (save-excursion
9555     (gnus-article-setup-buffer)
9556     (set-buffer gnus-article-buffer)
9557     (setq buffer-read-only nil)
9558     (let ((command (if automatic command
9559                      (read-string "Command: " (cons command 0)))))
9560       (erase-buffer)
9561       (insert "$ " command "\n\n")
9562       (if gnus-view-pseudo-asynchronously
9563           (start-process "gnus-execute" (current-buffer) shell-file-name
9564                          shell-command-switch command)
9565         (call-process shell-file-name nil t nil
9566                       shell-command-switch command)))))
9567
9568 ;; Summary kill commands.
9569
9570 (defun gnus-summary-edit-global-kill (article)
9571   "Edit the \"global\" kill file."
9572   (interactive (list (gnus-summary-article-number)))
9573   (gnus-group-edit-global-kill article))
9574
9575 (defun gnus-summary-edit-local-kill ()
9576   "Edit a local kill file applied to the current newsgroup."
9577   (interactive)
9578   (setq gnus-current-headers (gnus-summary-article-header))
9579   (gnus-group-edit-local-kill
9580    (gnus-summary-article-number) gnus-newsgroup-name))
9581
9582 ;;; Header reading.
9583
9584 (defun gnus-read-header (id &optional header)
9585   "Read the headers of article ID and enter them into the Gnus system."
9586   (let ((group gnus-newsgroup-name)
9587         (gnus-override-method
9588          (or
9589           gnus-override-method
9590           (and (gnus-news-group-p gnus-newsgroup-name)
9591                (car (gnus-refer-article-methods)))))
9592         where)
9593     ;; First we check to see whether the header in question is already
9594     ;; fetched.
9595     (if (stringp id)
9596         ;; This is a Message-ID.
9597         (setq header (or header (gnus-id-to-header id)))
9598       ;; This is an article number.
9599       (setq header (or header (gnus-summary-article-header id))))
9600     (if (and header
9601              (not (gnus-summary-article-sparse-p (mail-header-number header))))
9602         ;; We have found the header.
9603         header
9604       ;; If this is a sparse article, we have to nix out its
9605       ;; previous entry in the thread hashtb.
9606       (when (and header
9607                  (gnus-summary-article-sparse-p (mail-header-number header)))
9608         (let* ((parent (gnus-parent-id (mail-header-references header)))
9609                (thread (and parent (gnus-id-to-thread parent))))
9610           (when thread
9611             (delq (assq header thread) thread))))
9612       ;; We have to really fetch the header to this article.
9613       (save-excursion
9614         (set-buffer nntp-server-buffer)
9615         (when (setq where (gnus-request-head id group))
9616           (nnheader-fold-continuation-lines)
9617           (goto-char (point-max))
9618           (insert ".\n")
9619           (goto-char (point-min))
9620           (insert "211 ")
9621           (princ (cond
9622                   ((numberp id) id)
9623                   ((cdr where) (cdr where))
9624                   (header (mail-header-number header))
9625                   (t gnus-reffed-article-number))
9626                  (current-buffer))
9627           (insert " Article retrieved.\n"))
9628         (if (or (not where)
9629                 (not (setq header (car (gnus-get-newsgroup-headers nil t)))))
9630             ()                          ; Malformed head.
9631           (unless (gnus-summary-article-sparse-p (mail-header-number header))
9632             (when (and (stringp id)
9633                        (not (string= (gnus-group-real-name group)
9634                                      (car where))))
9635               ;; If we fetched by Message-ID and the article came
9636               ;; from a different group, we fudge some bogus article
9637               ;; numbers for this article.
9638               (mail-header-set-number header gnus-reffed-article-number))
9639             (save-excursion
9640               (set-buffer gnus-summary-buffer)
9641               (decf gnus-reffed-article-number)
9642               (gnus-remove-header (mail-header-number header))
9643               (push header gnus-newsgroup-headers)
9644               (setq gnus-current-headers header)
9645               (push (mail-header-number header) gnus-newsgroup-limit)))
9646           header)))))
9647
9648 (defun gnus-remove-header (number)
9649   "Remove header NUMBER from `gnus-newsgroup-headers'."
9650   (if (and gnus-newsgroup-headers
9651            (= number (mail-header-number (car gnus-newsgroup-headers))))
9652       (pop gnus-newsgroup-headers)
9653     (let ((headers gnus-newsgroup-headers))
9654       (while (and (cdr headers)
9655                   (not (= number (mail-header-number (cadr headers)))))
9656         (pop headers))
9657       (when (cdr headers)
9658         (setcdr headers (cddr headers))))))
9659
9660 ;;;
9661 ;;; summary highlights
9662 ;;;
9663
9664 (defun gnus-highlight-selected-summary ()
9665   "Highlight selected article in summary buffer."
9666   ;; Added by Per Abrahamsen <amanda@iesd.auc.dk>.
9667   (when gnus-summary-selected-face
9668     (save-excursion
9669       (let* ((beg (progn (beginning-of-line) (point)))
9670              (end (progn (end-of-line) (point)))
9671              ;; Fix by Mike Dugan <dugan@bucrf16.bu.edu>.
9672              (from (if (get-text-property beg gnus-mouse-face-prop)
9673                        beg
9674                      (or (next-single-property-change
9675                           beg gnus-mouse-face-prop nil end)
9676                          beg)))
9677              (to
9678               (if (= from end)
9679                   (- from 2)
9680                 (or (next-single-property-change
9681                      from gnus-mouse-face-prop nil end)
9682                     end))))
9683         ;; If no mouse-face prop on line we will have to = from = end,
9684         ;; so we highlight the entire line instead.
9685         (when (= (+ to 2) from)
9686           (setq from beg)
9687           (setq to end))
9688         (if gnus-newsgroup-selected-overlay
9689             ;; Move old overlay.
9690             (gnus-move-overlay
9691              gnus-newsgroup-selected-overlay from to (current-buffer))
9692           ;; Create new overlay.
9693           (gnus-overlay-put
9694            (setq gnus-newsgroup-selected-overlay (gnus-make-overlay from to))
9695            'face gnus-summary-selected-face))))))
9696
9697 ;; New implementation by Christian Limpach <Christian.Limpach@nice.ch>.
9698 (defun gnus-summary-highlight-line ()
9699   "Highlight current line according to `gnus-summary-highlight'."
9700   (let* ((list gnus-summary-highlight)
9701          (p (point))
9702          (end (progn (end-of-line) (point)))
9703          ;; now find out where the line starts and leave point there.
9704          (beg (progn (beginning-of-line) (point)))
9705          (article (gnus-summary-article-number))
9706          (score (or (cdr (assq (or article gnus-current-article)
9707                                gnus-newsgroup-scored))
9708                     gnus-summary-default-score 0))
9709          (mark (or (gnus-summary-article-mark) gnus-unread-mark))
9710          (inhibit-read-only t))
9711     ;; Eval the cars of the lists until we find a match.
9712     (let ((default gnus-summary-default-score))
9713       (while (and list
9714                   (not (eval (caar list))))
9715         (setq list (cdr list))))
9716     (let ((face (cdar list)))
9717       (unless (eq face (get-text-property beg 'face))
9718         (gnus-put-text-property-excluding-characters-with-faces
9719          beg end 'face
9720          (setq face (if (boundp face) (symbol-value face) face)))
9721         (when gnus-summary-highlight-line-function
9722           (funcall gnus-summary-highlight-line-function article face))))
9723     (goto-char p)))
9724
9725 (defun gnus-update-read-articles (group unread &optional compute)
9726   "Update the list of read articles in GROUP."
9727   (let* ((active (or gnus-newsgroup-active (gnus-active group)))
9728          (entry (gnus-gethash group gnus-newsrc-hashtb))
9729          (info (nth 2 entry))
9730          (prev 1)
9731          (unread (sort (copy-sequence unread) '<))
9732          read)
9733     (if (or (not info) (not active))
9734         ;; There is no info on this group if it was, in fact,
9735         ;; killed.  Gnus stores no information on killed groups, so
9736         ;; there's nothing to be done.
9737         ;; One could store the information somewhere temporarily,
9738         ;; perhaps...  Hmmm...
9739         ()
9740       ;; Remove any negative articles numbers.
9741       (while (and unread (< (car unread) 0))
9742         (setq unread (cdr unread)))
9743       ;; Remove any expired article numbers
9744       (while (and unread (< (car unread) (car active)))
9745         (setq unread (cdr unread)))
9746       ;; Compute the ranges of read articles by looking at the list of
9747       ;; unread articles.
9748       (while unread
9749         (when (/= (car unread) prev)
9750           (push (if (= prev (1- (car unread))) prev
9751                   (cons prev (1- (car unread))))
9752                 read))
9753         (setq prev (1+ (car unread)))
9754         (setq unread (cdr unread)))
9755       (when (<= prev (cdr active))
9756         (push (cons prev (cdr active)) read))
9757       (setq read (if (> (length read) 1) (nreverse read) read))
9758       (if compute
9759           read
9760         (save-excursion
9761           (let (setmarkundo)
9762             ;; Propagate the read marks to the backend.
9763             (when (gnus-check-backend-function 'request-set-mark group)
9764               (let ((del (gnus-remove-from-range (gnus-info-read info) read))
9765                     (add (gnus-remove-from-range read (gnus-info-read info))))
9766                 (when (or add del)
9767                   (unless (gnus-check-group group)
9768                     (error "Can't open server for %s" group))
9769                   (gnus-request-set-mark
9770                    group (delq nil (list (if add (list add 'add '(read)))
9771                                          (if del (list del 'del '(read))))))
9772                   (setq setmarkundo
9773                         `(gnus-request-set-mark
9774                           ,group
9775                           ',(delq nil (list
9776                                        (if del (list del 'add '(read)))
9777                                        (if add (list add 'del '(read))))))))))
9778             (set-buffer gnus-group-buffer)
9779             (gnus-undo-register
9780               `(progn
9781                  (gnus-info-set-marks ',info ',(gnus-info-marks info) t)
9782                  (gnus-info-set-read ',info ',(gnus-info-read info))
9783                  (gnus-get-unread-articles-in-group ',info 
9784                                                     (gnus-active ,group))
9785                  (gnus-group-update-group ,group t)
9786                  ,setmarkundo))))
9787         ;; Enter this list into the group info.
9788         (gnus-info-set-read info read)
9789         ;; Set the number of unread articles in gnus-newsrc-hashtb.
9790         (gnus-get-unread-articles-in-group info (gnus-active group))
9791         t))))
9792
9793 (defun gnus-offer-save-summaries ()
9794   "Offer to save all active summary buffers."
9795   (save-excursion
9796     (let ((buflist (buffer-list))
9797           buffers bufname)
9798       ;; Go through all buffers and find all summaries.
9799       (while buflist
9800         (and (setq bufname (buffer-name (car buflist)))
9801              (string-match "Summary" bufname)
9802              (save-excursion
9803                (set-buffer bufname)
9804                ;; We check that this is, indeed, a summary buffer.
9805                (and (eq major-mode 'gnus-summary-mode)
9806                     ;; Also make sure this isn't bogus.
9807                     gnus-newsgroup-prepared
9808                     ;; Also make sure that this isn't a dead summary buffer.
9809                     (not gnus-dead-summary-mode)))
9810              (push bufname buffers))
9811         (setq buflist (cdr buflist)))
9812       ;; Go through all these summary buffers and offer to save them.
9813       (when buffers
9814         (map-y-or-n-p
9815          "Update summary buffer %s? "
9816          (lambda (buf)
9817            (switch-to-buffer buf)
9818            (gnus-summary-exit))
9819          buffers)))))
9820
9821 (defun gnus-summary-setup-default-charset ()
9822   "Setup newsgroup default charset."
9823   (if (equal gnus-newsgroup-name "nndraft:drafts")
9824       (setq gnus-newsgroup-charset nil)
9825     (let* ((name (and gnus-newsgroup-name
9826                       (gnus-group-real-name gnus-newsgroup-name)))
9827            (ignored-charsets
9828             (or gnus-newsgroup-ephemeral-ignored-charsets
9829                 (append
9830                  (and gnus-newsgroup-name
9831                       (or (gnus-group-find-parameter gnus-newsgroup-name
9832                                                      'ignored-charsets t)
9833                           (let ((alist gnus-group-ignored-charsets-alist)
9834                                 elem (charsets nil))
9835                             (while (setq elem (pop alist))
9836                               (when (and name
9837                                          (string-match (car elem) name))
9838                                 (setq alist nil
9839                                       charsets (cdr elem))))
9840                             charsets)))
9841                  gnus-newsgroup-ignored-charsets))))
9842       (setq gnus-newsgroup-charset
9843             (or gnus-newsgroup-ephemeral-charset
9844                 (and gnus-newsgroup-name
9845                      (or (gnus-group-find-parameter gnus-newsgroup-name 'charset)
9846                          (let ((alist gnus-group-charset-alist)
9847                                elem charset)
9848                            (while (setq elem (pop alist))
9849                              (when (and name
9850                                         (string-match (car elem) name))
9851                                (setq alist nil
9852                                      charset (cadr elem))))
9853                            charset)))
9854                 gnus-default-charset))
9855       (set (make-local-variable 'gnus-newsgroup-ignored-charsets)
9856            ignored-charsets))))
9857
9858 ;;;
9859 ;;; Mime Commands
9860 ;;;
9861
9862 (defun gnus-summary-display-buttonized (&optional show-all-parts)
9863   "Display the current article buffer fully MIME-buttonized.
9864 If SHOW-ALL-PARTS (the prefix) is non-nil, all multipart/* parts are
9865 treated as multipart/mixed."
9866   (interactive "P")
9867   (require 'gnus-art)
9868   (let ((gnus-unbuttonized-mime-types nil)
9869         (gnus-mime-display-multipart-as-mixed show-all-parts))
9870     (gnus-summary-show-article)))
9871
9872 (defun gnus-summary-repair-multipart (article)
9873   "Add a Content-Type header to a multipart article without one."
9874   (interactive (list (gnus-summary-article-number)))
9875   (gnus-with-article article
9876     (message-narrow-to-head)
9877     (message-remove-header "Mime-Version")
9878     (goto-char (point-max))
9879     (insert "Mime-Version: 1.0\n")
9880     (widen)
9881     (when (search-forward "\n--" nil t)
9882       (let ((separator (buffer-substring (point) (gnus-point-at-eol))))
9883         (message-narrow-to-head)
9884         (message-remove-header "Content-Type")
9885         (goto-char (point-max))
9886         (insert (format "Content-Type: multipart/mixed; boundary=\"%s\"\n"
9887                         separator))
9888         (widen))))
9889   (let (gnus-mark-article-hook)
9890     (gnus-summary-select-article t t nil article)))
9891
9892 (defun gnus-summary-toggle-display-buttonized ()
9893   "Toggle the buttonizing of the article buffer."
9894   (interactive)
9895   (require 'gnus-art)
9896   (if (setq gnus-inhibit-mime-unbuttonizing
9897             (not gnus-inhibit-mime-unbuttonizing))
9898       (let ((gnus-unbuttonized-mime-types nil))
9899         (gnus-summary-show-article))
9900     (gnus-summary-show-article)))
9901
9902 ;;;
9903 ;;; Generic summary marking commands
9904 ;;;
9905
9906 (defvar gnus-summary-marking-alist
9907   '((read gnus-del-mark "d")
9908     (unread gnus-unread-mark "u")
9909     (ticked gnus-ticked-mark "!")
9910     (dormant gnus-dormant-mark "?")
9911     (expirable gnus-expirable-mark "e"))
9912   "An alist of names/marks/keystrokes.")
9913
9914 (defvar gnus-summary-generic-mark-map (make-sparse-keymap))
9915 (defvar gnus-summary-mark-map)
9916
9917 (defun gnus-summary-make-all-marking-commands ()
9918   (define-key gnus-summary-mark-map "M" gnus-summary-generic-mark-map)
9919   (dolist (elem gnus-summary-marking-alist)
9920     (apply 'gnus-summary-make-marking-command elem)))
9921
9922 (defun gnus-summary-make-marking-command (name mark keystroke)
9923   (let ((map (make-sparse-keymap)))
9924     (define-key gnus-summary-generic-mark-map keystroke map)
9925     (dolist (lway `((next "next" next nil "n")
9926                     (next-unread "next unread" next t "N")
9927                     (prev "previous" prev nil "p")
9928                     (prev-unread "previous unread" prev t "P")
9929                     (nomove "" nil nil ,keystroke)))
9930       (let ((func (gnus-summary-make-marking-command-1
9931                    mark (car lway) lway name)))
9932         (setq func (eval func))
9933         (define-key map (nth 4 lway) func)))))
9934       
9935 (defun gnus-summary-make-marking-command-1 (mark way lway name)
9936   `(defun ,(intern
9937             (format "gnus-summary-put-mark-as-%s%s"
9938                     name (if (eq way 'nomove)
9939                              ""
9940                            (concat "-" (symbol-name way)))))
9941      (n)
9942      ,(format
9943        "Mark the current article as %s%s.
9944 If N, the prefix, then repeat N times.
9945 If N is negative, move in reverse order.
9946 The difference between N and the actual number of articles marked is
9947 returned."
9948        name (cadr lway))
9949      (interactive "p")
9950      (gnus-summary-generic-mark n ,mark ',(nth 2 lway) ,(nth 3 lway))))
9951     
9952 (defun gnus-summary-generic-mark (n mark move unread)
9953   "Mark N articles with MARK."
9954   (unless (eq major-mode 'gnus-summary-mode)
9955     (error "This command can only be used in the summary buffer"))
9956   (gnus-summary-show-thread)
9957   (let ((nummove
9958          (cond
9959           ((eq move 'next) 1)
9960           ((eq move 'prev) -1)
9961           (t 0))))
9962     (if (zerop nummove)
9963         (setq n 1)
9964       (when (< n 0)
9965         (setq n (abs n)
9966               nummove (* -1 nummove))))
9967     (while (and (> n 0)
9968                 (gnus-summary-mark-article nil mark)
9969                 (zerop (gnus-summary-next-subject nummove unread t)))
9970       (setq n (1- n)))
9971     (when (/= 0 n)
9972       (gnus-message 7 "No more %sarticles" (if mark "" "unread ")))
9973     (gnus-summary-recenter)
9974     (gnus-summary-position-point)
9975     (gnus-set-mode-line 'summary)
9976     n))
9977
9978 (gnus-summary-make-all-marking-commands)
9979
9980 (gnus-ems-redefine)
9981
9982 (provide 'gnus-sum)
9983
9984 (run-hooks 'gnus-sum-load-hook)
9985
9986 ;;; gnus-sum.el ends here