1ed79489c32027d291fec98c57a208b2da8b8ceb
[gnus] / lisp / gnus-sum.el
1 ;;; gnus-sum.el --- summary mode commands for Gnus
2 ;; Copyright (C) 1996,97 Free Software Foundation, Inc.
3
4 ;; Author: Lars Magne Ingebrigtsen <larsi@ifi.uio.no>
5 ;; Keywords: news
6
7 ;; This file is part of GNU Emacs.
8
9 ;; GNU Emacs is free software; you can redistribute it and/or modify
10 ;; it under the terms of the GNU General Public License as published by
11 ;; the Free Software Foundation; either version 2, or (at your option)
12 ;; any later version.
13
14 ;; GNU Emacs is distributed in the hope that it will be useful,
15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 ;; GNU General Public License for more details.
18
19 ;; You should have received a copy of the GNU General Public License
20 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
21 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 ;; Boston, MA 02111-1307, USA.
23
24 ;;; Commentary:
25
26 ;;; Code:
27
28 (eval-when-compile (require 'cl))
29
30 (require 'gnus)
31 (require 'gnus-group)
32 (require 'gnus-spec)
33 (require 'gnus-range)
34 (require 'gnus-int)
35 (require 'gnus-undo)
36
37 (defcustom gnus-kill-summary-on-exit t
38   "*If non-nil, kill the summary buffer when you exit from it.
39 If nil, the summary will become a \"*Dead Summary*\" buffer, and
40 it will be killed sometime later."
41   :group 'gnus-summary-exit
42   :type 'boolean)
43
44 (defcustom gnus-fetch-old-headers nil
45   "*Non-nil means that Gnus will try to build threads by grabbing old headers.
46 If an unread article in the group refers to an older, already read (or
47 just marked as read) article, the old article will not normally be
48 displayed in the Summary buffer.  If this variable is non-nil, Gnus
49 will attempt to grab the headers to the old articles, and thereby
50 build complete threads.  If it has the value `some', only enough
51 headers to connect otherwise loose threads will be displayed.
52 This variable can also be a number.  In that case, no more than that
53 number of old headers will be fetched.
54
55 The server has to support NOV for any of this to work."
56   :group 'gnus-thread
57   :type '(choice (const :tag "off" nil)
58                  (const some)
59                  number
60                  (sexp :menu-tag "other" t)))
61
62 (defcustom gnus-summary-make-false-root 'adopt
63   "*nil means that Gnus won't gather loose threads.
64 If the root of a thread has expired or been read in a previous
65 session, the information necessary to build a complete thread has been
66 lost.  Instead of having many small sub-threads from this original thread
67 scattered all over the summary buffer, Gnus can gather them.
68
69 If non-nil, Gnus will try to gather all loose sub-threads from an
70 original thread into one large thread.
71
72 If this variable is non-nil, it should be one of `none', `adopt',
73 `dummy' or `empty'.
74
75 If this variable is `none', Gnus will not make a false root, but just
76 present the sub-threads after another.
77 If this variable is `dummy', Gnus will create a dummy root that will
78 have all the sub-threads as children.
79 If this variable is `adopt', Gnus will make one of the \"children\"
80 the parent and mark all the step-children as such.
81 If this variable is `empty', the \"children\" are printed with empty
82 subject fields.  (Or rather, they will be printed with a string
83 given by the `gnus-summary-same-subject' variable.)"
84   :group 'gnus-thread
85   :type '(choice (const :tag "off" nil)
86                  (const none)
87                  (const dummy)
88                  (const adopt)
89                  (const empty)))
90
91 (defcustom gnus-summary-gather-exclude-subject "^ *$\\|^(none)$"
92   "*A regexp to match subjects to be excluded from loose thread gathering.
93 As loose thread gathering is done on subjects only, that means that
94 there can be many false gatherings performed.  By rooting out certain
95 common subjects, gathering might become saner."
96   :group 'gnus-thread
97   :type 'regexp)
98
99 (defcustom gnus-summary-gather-subject-limit nil
100   "*Maximum length of subject comparisons when gathering loose threads.
101 Use nil to compare full subjects.  Setting this variable to a low
102 number will help gather threads that have been corrupted by
103 newsreaders chopping off subject lines, but it might also mean that
104 unrelated articles that have subject that happen to begin with the
105 same few characters will be incorrectly gathered.
106
107 If this variable is `fuzzy', Gnus will use a fuzzy algorithm when
108 comparing subjects."
109   :group 'gnus-thread
110   :type '(choice (const :tag "off" nil)
111                  (const fuzzy)
112                  (sexp :menu-tag "on" t)))
113
114 (defcustom gnus-simplify-ignored-prefixes nil
115   "*Regexp, matches for which are removed from subject lines when simplifying fuzzily."
116   :group 'gnus-thread
117   :type '(choice (const :tag "off" nil)
118                  regexp))
119
120 (defcustom gnus-build-sparse-threads nil
121   "*If non-nil, fill in the gaps in threads.
122 If `some', only fill in the gaps that are needed to tie loose threads
123 together.  If `more', fill in all leaf nodes that Gnus can find.  If
124 non-nil and non-`some', fill in all gaps that Gnus manages to guess."
125   :group 'gnus-thread
126   :type '(choice (const :tag "off" nil)
127                  (const some)
128                  (const more)
129                  (sexp :menu-tag "all" t)))
130
131 (defcustom gnus-summary-thread-gathering-function
132   'gnus-gather-threads-by-subject
133   "Function used for gathering loose threads.
134 There are two pre-defined functions: `gnus-gather-threads-by-subject',
135 which only takes Subjects into consideration; and
136 `gnus-gather-threads-by-references', which compared the References
137 headers of the articles to find matches."
138   :group 'gnus-thread
139   :type '(radio (function-item gnus-gather-threads-by-subject)
140                 (function-item gnus-gather-threads-by-references)
141                 (function :tag "other")))
142
143 ;; Added by Per Abrahamsen <amanda@iesd.auc.dk>.
144 (defcustom gnus-summary-same-subject ""
145   "*String indicating that the current article has the same subject as the previous.
146 This variable will only be used if the value of
147 `gnus-summary-make-false-root' is `empty'."
148   :group 'gnus-summary-format
149   :type 'string)
150
151 (defcustom gnus-summary-goto-unread t
152   "*If t, marking commands will go to the next unread article.
153 If `never', commands that usually go to the next unread article, will
154 go to the next article, whether it is read or not.
155 If nil, only the marking commands will go to the next (un)read article."
156   :group 'gnus-summary-marks
157   :link '(custom-manual "(gnus)Setting Marks")
158   :type '(choice (const :tag "off" nil)
159                  (const never)
160                  (sexp :menu-tag "on" t)))
161
162 (defcustom gnus-summary-default-score 0
163   "*Default article score level.
164 All scores generated by the score files will be added to this score.
165 If this variable is nil, scoring will be disabled."
166   :group 'gnus-score-default
167   :type '(choice (const :tag "disable")
168                  integer))
169
170 (defcustom gnus-summary-zcore-fuzz 0
171   "*Fuzziness factor for the zcore in the summary buffer.
172 Articles with scores closer than this to `gnus-summary-default-score'
173 will not be marked."
174   :group 'gnus-summary-format
175   :type 'integer)
176
177 (defcustom gnus-simplify-subject-fuzzy-regexp nil
178   "*Strings to be removed when doing fuzzy matches.
179 This can either be a regular expression or list of regular expressions
180 that will be removed from subject strings if fuzzy subject
181 simplification is selected."
182   :group 'gnus-thread
183   :type '(repeat regexp))
184
185 (defcustom gnus-show-threads t
186   "*If non-nil, display threads in summary mode."
187   :group 'gnus-thread
188   :type 'boolean)
189
190 (defcustom gnus-thread-hide-subtree nil
191   "*If non-nil, hide all threads initially.
192 If threads are hidden, you have to run the command
193 `gnus-summary-show-thread' by hand or use `gnus-select-article-hook'
194 to expose hidden threads."
195   :group 'gnus-thread
196   :type 'boolean)
197
198 (defcustom gnus-thread-hide-killed t
199   "*If non-nil, hide killed threads automatically."
200   :group 'gnus-thread
201   :type 'boolean)
202
203 (defcustom gnus-thread-ignore-subject nil
204   "*If non-nil, ignore subjects and do all threading based on the Reference header.
205 If nil, which is the default, articles that have different subjects
206 from their parents will start separate threads."
207   :group 'gnus-thread
208   :type 'boolean)
209
210 (defcustom gnus-thread-operation-ignore-subject t
211   "*If non-nil, subjects will be ignored when doing thread commands.
212 This affects commands like `gnus-summary-kill-thread' and
213 `gnus-summary-lower-thread'.
214
215 If this variable is nil, articles in the same thread with different
216 subjects will not be included in the operation in question.  If this
217 variable is `fuzzy', only articles that have subjects that are fuzzily
218 equal will be included."
219   :group 'gnus-thread
220   :type '(choice (const :tag "off" nil)
221                  (const fuzzy)
222                  (sexp :tag "on" t)))
223
224 (defcustom gnus-thread-indent-level 4
225   "*Number that says how much each sub-thread should be indented."
226   :group 'gnus-thread
227   :type 'integer)
228
229 (defcustom gnus-auto-extend-newsgroup t
230   "*If non-nil, extend newsgroup forward and backward when requested."
231   :group 'gnus-summary-choose
232   :type 'boolean)
233
234 (defcustom gnus-auto-select-first t
235   "*If nil, don't select the first unread article when entering a group.
236 If this variable is `best', select the highest-scored unread article
237 in the group.  If neither nil nor `best', select the first unread
238 article.
239
240 If you want to prevent automatic selection of the first unread article
241 in some newsgroups, set the variable to nil in
242 `gnus-select-group-hook'."
243   :group 'gnus-group-select
244   :type '(choice (const :tag "none" nil)
245                  (const best)
246                  (sexp :menu-tag "first" t)))
247
248 (defcustom gnus-auto-select-next t
249   "*If non-nil, offer to go to the next group from the end of the previous.
250 If the value is t and the next newsgroup is empty, Gnus will exit
251 summary mode and go back to group mode.  If the value is neither nil
252 nor t, Gnus will select the following unread newsgroup.  In
253 particular, if the value is the symbol `quietly', the next unread
254 newsgroup will be selected without any confirmation, and if it is
255 `almost-quietly', the next group will be selected without any
256 confirmation if you are located on the last article in the group.
257 Finally, if this variable is `slightly-quietly', the `Z n' command
258 will go to the next group without confirmation."
259   :group 'gnus-summary-maneuvering
260   :type '(choice (const :tag "off" nil)
261                  (const quietly)
262                  (const almost-quietly)
263                  (const slightly-quietly)
264                  (sexp :menu-tag "on" t)))
265
266 (defcustom gnus-auto-select-same nil
267   "*If non-nil, select the next article with the same subject."
268   :group 'gnus-summary-maneuvering
269   :type 'boolean)
270
271 (defcustom gnus-summary-check-current nil
272   "*If non-nil, consider the current article when moving.
273 The \"unread\" movement commands will stay on the same line if the
274 current article is unread."
275   :group 'gnus-summary-maneuvering
276   :type 'boolean)
277
278 (defcustom gnus-auto-center-summary t
279   "*If non-nil, always center the current summary buffer.
280 In particular, if `vertical' do only vertical recentering.  If non-nil
281 and non-`vertical', do both horizontal and vertical recentering."
282   :group 'gnus-summary-maneuvering
283   :type '(choice (const :tag "none" nil)
284                  (const vertical)
285                  (sexp :menu-tag "both" t)))
286
287 (defcustom gnus-show-all-headers nil
288   "*If non-nil, don't hide any headers."
289   :group 'gnus-article-hiding
290   :group 'gnus-article-headers
291   :type 'boolean)
292
293 (defcustom gnus-summary-ignore-duplicates nil
294   "*If non-nil, ignore articles with identical Message-ID headers."
295   :group 'gnus-summary
296   :type 'boolean)
297   
298 (defcustom gnus-single-article-buffer t
299   "*If non-nil, display all articles in the same buffer.
300 If nil, each group will get its own article buffer."
301   :group 'gnus-article-various
302   :type 'boolean)
303
304 (defcustom gnus-break-pages t
305   "*If non-nil, do page breaking on articles.
306 The page delimiter is specified by the `gnus-page-delimiter'
307 variable."
308   :group 'gnus-article-various
309   :type 'boolean)
310
311 (defcustom gnus-show-mime nil
312   "*If non-nil, do mime processing of articles.
313 The articles will simply be fed to the function given by
314 `gnus-show-mime-method'."
315   :group 'gnus-article-mime
316   :type 'boolean)
317
318 (defcustom gnus-move-split-methods nil
319   "*Variable used to suggest where articles are to be moved to.
320 It uses the same syntax as the `gnus-split-methods' variable."
321   :group 'gnus-summary-mail
322   :type '(repeat (choice (list function)
323                          (cons regexp (repeat string))
324                          sexp)))
325
326 (defcustom gnus-unread-mark ? 
327   "*Mark used for unread articles."
328   :group 'gnus-summary-marks
329   :type 'character)
330
331 (defcustom gnus-ticked-mark ?!
332   "*Mark used for ticked articles."
333   :group 'gnus-summary-marks
334   :type 'character)
335
336 (defcustom gnus-dormant-mark ??
337   "*Mark used for dormant articles."
338   :group 'gnus-summary-marks
339   :type 'character)
340
341 (defcustom gnus-del-mark ?r
342   "*Mark used for del'd articles."
343   :group 'gnus-summary-marks
344   :type 'character)
345
346 (defcustom gnus-read-mark ?R
347   "*Mark used for read articles."
348   :group 'gnus-summary-marks
349   :type 'character)
350
351 (defcustom gnus-expirable-mark ?E
352   "*Mark used for expirable articles."
353   :group 'gnus-summary-marks
354   :type 'character)
355
356 (defcustom gnus-killed-mark ?K
357   "*Mark used for killed articles."
358   :group 'gnus-summary-marks
359   :type 'character)
360
361 (defcustom gnus-souped-mark ?F
362   "*Mark used for killed articles."
363   :group 'gnus-summary-marks
364   :type 'character)
365
366 (defcustom gnus-kill-file-mark ?X
367   "*Mark used for articles killed by kill files."
368   :group 'gnus-summary-marks
369   :type 'character)
370
371 (defcustom gnus-low-score-mark ?Y
372   "*Mark used for articles with a low score."
373   :group 'gnus-summary-marks
374   :type 'character)
375
376 (defcustom gnus-catchup-mark ?C
377   "*Mark used for articles that are caught up."
378   :group 'gnus-summary-marks
379   :type 'character)
380
381 (defcustom gnus-replied-mark ?A
382   "*Mark used for articles that have been replied to."
383   :group 'gnus-summary-marks
384   :type 'character)
385
386 (defcustom gnus-cached-mark ?*
387   "*Mark used for articles that are in the cache."
388   :group 'gnus-summary-marks
389   :type 'character)
390
391 (defcustom gnus-saved-mark ?S
392   "*Mark used for articles that have been saved to."
393   :group 'gnus-summary-marks
394   :type 'character)
395
396 (defcustom gnus-ancient-mark ?O
397   "*Mark used for ancient articles."
398   :group 'gnus-summary-marks
399   :type 'character)
400
401 (defcustom gnus-sparse-mark ?Q
402   "*Mark used for sparsely reffed articles."
403   :group 'gnus-summary-marks
404   :type 'character)
405
406 (defcustom gnus-canceled-mark ?G
407   "*Mark used for canceled articles."
408   :group 'gnus-summary-marks
409   :type 'character)
410
411 (defcustom gnus-duplicate-mark ?M
412   "*Mark used for duplicate articles."
413   :group 'gnus-summary-marks
414   :type 'character)
415
416 (defcustom gnus-score-over-mark ?+
417   "*Score mark used for articles with high scores."
418   :group 'gnus-summary-marks
419   :type 'character)
420
421 (defcustom gnus-score-below-mark ?-
422   "*Score mark used for articles with low scores."
423   :group 'gnus-summary-marks
424   :type 'character)
425
426 (defcustom gnus-empty-thread-mark ? 
427   "*There is no thread under the article."
428   :group 'gnus-summary-marks
429   :type 'character)
430
431 (defcustom gnus-not-empty-thread-mark ?=
432   "*There is a thread under the article."
433   :group 'gnus-summary-marks
434   :type 'character)
435
436 (defcustom gnus-view-pseudo-asynchronously nil
437   "*If non-nil, Gnus will view pseudo-articles asynchronously."
438   :group 'gnus-extract-view
439   :type 'boolean)
440
441 (defcustom gnus-view-pseudos nil
442   "*If `automatic', pseudo-articles will be viewed automatically.
443 If `not-confirm', pseudos will be viewed automatically, and the user
444 will not be asked to confirm the command."
445   :group 'gnus-extract-view
446   :type '(choice (const :tag "off" nil)
447                  (const automatic)
448                  (const not-confirm)))
449
450 (defcustom gnus-view-pseudos-separately t
451   "*If non-nil, one pseudo-article will be created for each file to be viewed.
452 If nil, all files that use the same viewing command will be given as a
453 list of parameters to that command."
454   :group 'gnus-extract-view
455   :type 'boolean)
456
457 (defcustom gnus-insert-pseudo-articles t
458   "*If non-nil, insert pseudo-articles when decoding articles."
459   :group 'gnus-extract-view
460   :type 'boolean)
461
462 (defcustom gnus-summary-dummy-line-format
463   "*  %(:                          :%) %S\n"
464   "*The format specification for the dummy roots in the summary buffer.
465 It works along the same lines as a normal formatting string,
466 with some simple extensions.
467
468 %S  The subject"
469   :group 'gnus-threading
470   :type 'string)
471
472 (defcustom gnus-summary-mode-line-format "Gnus: %%b [%A] %Z"
473   "*The format specification for the summary mode line.
474 It works along the same lines as a normal formatting string,
475 with some simple extensions:
476
477 %G  Group name
478 %p  Unprefixed group name
479 %A  Current article number
480 %V  Gnus version
481 %U  Number of unread articles in the group
482 %e  Number of unselected articles in the group
483 %Z  A string with unread/unselected article counts
484 %g  Shortish group name
485 %S  Subject of the current article
486 %u  User-defined spec
487 %s  Current score file name
488 %d  Number of dormant articles
489 %r  Number of articles that have been marked as read in this session
490 %E  Number of articles expunged by the score files"
491   :group 'gnus-summary-format
492   :type 'string)
493
494 (defcustom gnus-summary-mark-below 0
495   "*Mark all articles with a score below this variable as read.
496 This variable is local to each summary buffer and usually set by the
497 score file."
498   :group 'gnus-score-default
499   :type 'integer)
500
501 (defcustom gnus-article-sort-functions '(gnus-article-sort-by-number)
502   "*List of functions used for sorting articles in the summary buffer.
503 This variable is only used when not using a threaded display."
504   :group 'gnus-summary-sort
505   :type '(repeat (choice (function-item gnus-article-sort-by-number)
506                          (function-item gnus-article-sort-by-author)
507                          (function-item gnus-article-sort-by-subject)
508                          (function-item gnus-article-sort-by-date)
509                          (function-item gnus-article-sort-by-score)
510                          (function :tag "other"))))
511
512 (defcustom gnus-thread-sort-functions '(gnus-thread-sort-by-number)
513   "*List of functions used for sorting threads in the summary buffer.
514 By default, threads are sorted by article number.
515
516 Each function takes two threads and return non-nil if the first thread
517 should be sorted before the other.  If you use more than one function,
518 the primary sort function should be the last.  You should probably
519 always include `gnus-thread-sort-by-number' in the list of sorting
520 functions -- preferably first.
521
522 Ready-made functions include `gnus-thread-sort-by-number',
523 `gnus-thread-sort-by-author', `gnus-thread-sort-by-subject',
524 `gnus-thread-sort-by-date', `gnus-thread-sort-by-score' and
525 `gnus-thread-sort-by-total-score' (see `gnus-thread-score-function')."
526   :group 'gnus-summary-sort
527   :type '(repeat (choice (function-item gnus-thread-sort-by-number)
528                          (function-item gnus-thread-sort-by-author)
529                          (function-item gnus-thread-sort-by-subject)
530                          (function-item gnus-thread-sort-by-date)
531                          (function-item gnus-thread-sort-by-score)
532                          (function-item gnus-thread-sort-by-total-score)
533                          (function :tag "other"))))
534
535 (defcustom gnus-thread-score-function '+
536   "*Function used for calculating the total score of a thread.
537
538 The function is called with the scores of the article and each
539 subthread and should then return the score of the thread.
540
541 Some functions you can use are `+', `max', or `min'."
542   :group 'gnus-summary-sort
543   :type 'function)
544
545 (defcustom gnus-summary-expunge-below nil
546   "All articles that have a score less than this variable will be expunged."
547   :group 'gnus-score-default
548   :type '(choice (const :tag "off" nil)
549                  integer))
550
551 (defcustom gnus-thread-expunge-below nil
552   "All threads that have a total score less than this variable will be expunged.
553 See `gnus-thread-score-function' for en explanation of what a
554 \"thread score\" is."
555   :group 'gnus-treading
556   :group 'gnus-score-default
557   :type '(choice (const :tag "off" nil)
558                  integer))
559
560 (defcustom gnus-summary-mode-hook nil
561   "*A hook for Gnus summary mode.
562 This hook is run before any variables are set in the summary buffer."
563   :group 'gnus-summary-various
564   :type 'hook)
565
566 (defcustom gnus-summary-menu-hook nil
567   "*Hook run after the creation of the summary mode menu."
568   :group 'gnus-summary-visual
569   :type 'hook)
570
571 (defcustom gnus-summary-exit-hook nil
572   "*A hook called on exit from the summary buffer.
573 It will be called with point in the group buffer."
574   :group 'gnus-summary-exit
575   :type 'hook)
576
577 (defcustom gnus-summary-prepare-hook nil
578   "*A hook called after the summary buffer has been generated.
579 If you want to modify the summary buffer, you can use this hook."
580   :group 'gnus-summary-various
581   :type 'hook)
582
583 (defcustom gnus-summary-generate-hook nil
584   "*A hook run just before generating the summary buffer.
585 This hook is commonly used to customize threading variables and the
586 like."
587   :group 'gnus-summary-various
588   :type 'hook)
589
590 (defcustom gnus-select-group-hook nil
591   "*A hook called when a newsgroup is selected.
592
593 If you'd like to simplify subjects like the
594 `gnus-summary-next-same-subject' command does, you can use the
595 following hook:
596
597  (setq gnus-select-group-hook
598       (list
599         (lambda ()
600           (mapcar (lambda (header)
601                      (mail-header-set-subject
602                       header
603                       (gnus-simplify-subject
604                        (mail-header-subject header) 're-only)))
605                   gnus-newsgroup-headers))))"
606   :group 'gnus-group-select
607   :type 'hook)
608
609 (defcustom gnus-select-article-hook nil
610   "*A hook called when an article is selected."
611   :group 'gnus-summary-choose
612   :type 'hook)
613
614 (defcustom gnus-visual-mark-article-hook
615   (list 'gnus-highlight-selected-summary)
616   "*Hook run after selecting an article in the summary buffer.
617 It is meant to be used for highlighting the article in some way.  It
618 is not run if `gnus-visual' is nil."
619   :group 'gnus-summary-visual
620   :type 'hook)
621
622 ;; 1997/5/4 by MORIOKA Tomohiko <morioka@jaist.ac.jp>
623 (defcustom gnus-structured-field-decoder 'identity
624   "Function to decode non-ASCII characters in structured field for summary."
625   :group 'gnus-various
626   :type 'function)
627
628 (defcustom gnus-unstructured-field-decoder 'identity
629   "Function to decode non-ASCII characters in unstructured field for summary."
630   :group 'gnus-various
631   :type 'function)
632
633 (defcustom gnus-parse-headers-hook
634   (list 'gnus-hack-decode-rfc1522 'gnus-decode-rfc1522)
635   "*A hook called before parsing the headers."
636   :group 'gnus-various
637   :type 'hook)
638
639 (defcustom gnus-exit-group-hook nil
640   "*A hook called when exiting (not quitting) summary mode."
641   :group 'gnus-various
642   :type 'hook)
643
644 (defcustom gnus-summary-update-hook
645   (list 'gnus-summary-highlight-line)
646   "*A hook called when a summary line is changed.
647 The hook will not be called if `gnus-visual' is nil.
648
649 The default function `gnus-summary-highlight-line' will
650 highlight the line according to the `gnus-summary-highlight'
651 variable."
652   :group 'gnus-summary-visual
653   :type 'hook)
654
655 (defcustom gnus-mark-article-hook '(gnus-summary-mark-read-and-unread-as-read)
656   "*A hook called when an article is selected for the first time.
657 The hook is intended to mark an article as read (or unread)
658 automatically when it is selected."
659   :group 'gnus-summary-choose
660   :type 'hook)
661
662 (defcustom gnus-group-no-more-groups-hook nil
663   "*A hook run when returning to group mode having no more (unread) groups."
664   :group 'gnus-group-select
665   :type 'hook)
666
667 (defcustom gnus-ps-print-hook nil
668   "*A hook run before ps-printing something from Gnus."
669   :group 'gnus-summary
670   :type 'hook)
671
672 (defcustom gnus-summary-selected-face 'gnus-summary-selected-face
673   "Face used for highlighting the current article in the summary buffer."
674   :group 'gnus-summary-visual
675   :type 'face)
676
677 (defcustom gnus-summary-highlight
678   '(((= mark gnus-canceled-mark)
679      . gnus-summary-cancelled-face)
680     ((and (> score default)
681           (or (= mark gnus-dormant-mark)
682               (= mark gnus-ticked-mark)))
683      . gnus-summary-high-ticked-face)
684     ((and (< score default)
685           (or (= mark gnus-dormant-mark)
686               (= mark gnus-ticked-mark)))
687      . gnus-summary-low-ticked-face)
688     ((or (= mark gnus-dormant-mark)
689          (= mark gnus-ticked-mark))
690      . gnus-summary-normal-ticked-face)
691     ((and (> score default) (= mark gnus-ancient-mark))
692      . gnus-summary-high-ancient-face)
693     ((and (< score default) (= mark gnus-ancient-mark))
694      . gnus-summary-low-ancient-face)
695     ((= mark gnus-ancient-mark)
696      . gnus-summary-normal-ancient-face)
697     ((and (> score default) (= mark gnus-unread-mark))
698      . gnus-summary-high-unread-face)
699     ((and (< score default) (= mark gnus-unread-mark))
700      . gnus-summary-low-unread-face)
701     ((and (= mark gnus-unread-mark))
702      . gnus-summary-normal-unread-face)
703     ((> score default)
704      . gnus-summary-high-read-face)
705     ((< score default)
706      . gnus-summary-low-read-face)
707     (t
708      . gnus-summary-normal-read-face))
709   "Controls the highlighting of summary buffer lines.
710
711 A list of (FORM . FACE) pairs.  When deciding how a a particular
712 summary line should be displayed, each form is evaluated.  The content
713 of the face field after the first true form is used.  You can change
714 how those summary lines are displayed, by editing the face field.
715
716 You can use the following variables in the FORM field.
717
718 score:   The articles score
719 default: The default article score.
720 below:   The score below which articles are automatically marked as read.
721 mark:    The articles mark."
722   :group 'gnus-summary-visual
723   :type '(repeat (cons (sexp :tag "Form" nil)
724                        face)))
725
726
727 ;;; Internal variables
728
729 (defvar gnus-scores-exclude-files nil)
730 (defvar gnus-page-broken nil)
731
732 (defvar gnus-original-article nil)
733 (defvar gnus-article-internal-prepare-hook nil)
734 (defvar gnus-newsgroup-process-stack nil)
735
736 (defvar gnus-thread-indent-array nil)
737 (defvar gnus-thread-indent-array-level gnus-thread-indent-level)
738
739 ;; Avoid highlighting in kill files.
740 (defvar gnus-summary-inhibit-highlight nil)
741 (defvar gnus-newsgroup-selected-overlay nil)
742 (defvar gnus-inhibit-limiting nil)
743 (defvar gnus-newsgroup-adaptive-score-file nil)
744 (defvar gnus-current-score-file nil)
745 (defvar gnus-current-move-group nil)
746 (defvar gnus-current-copy-group nil)
747 (defvar gnus-current-crosspost-group nil)
748
749 (defvar gnus-newsgroup-dependencies nil)
750 (defvar gnus-newsgroup-adaptive nil)
751 (defvar gnus-summary-display-article-function nil)
752 (defvar gnus-summary-highlight-line-function nil
753   "Function called after highlighting a summary line.")
754
755 (defvar gnus-summary-line-format-alist
756   `((?N ,(macroexpand '(mail-header-number gnus-tmp-header)) ?d)
757     (?S ,(macroexpand '(mail-header-subject gnus-tmp-header)) ?s)
758     (?s gnus-tmp-subject-or-nil ?s)
759     (?n gnus-tmp-name ?s)
760     (?A (car (cdr (funcall gnus-extract-address-components gnus-tmp-from)))
761         ?s)
762     (?a (or (car (funcall gnus-extract-address-components gnus-tmp-from))
763             gnus-tmp-from) ?s)
764     (?F gnus-tmp-from ?s)
765     (?x ,(macroexpand '(mail-header-xref gnus-tmp-header)) ?s)
766     (?D ,(macroexpand '(mail-header-date gnus-tmp-header)) ?s)
767     (?d (gnus-dd-mmm (mail-header-date gnus-tmp-header)) ?s)
768     (?o (gnus-date-iso8601 gnus-tmp-header) ?s)
769     (?M ,(macroexpand '(mail-header-id gnus-tmp-header)) ?s)
770     (?r ,(macroexpand '(mail-header-references gnus-tmp-header)) ?s)
771     (?c (or (mail-header-chars gnus-tmp-header) 0) ?d)
772     (?L gnus-tmp-lines ?d)
773     (?I gnus-tmp-indentation ?s)
774     (?T (if (= gnus-tmp-level 0) "" (make-string (frame-width) ? )) ?s)
775     (?R gnus-tmp-replied ?c)
776     (?\[ gnus-tmp-opening-bracket ?c)
777     (?\] gnus-tmp-closing-bracket ?c)
778     (?\> (make-string gnus-tmp-level ? ) ?s)
779     (?\< (make-string (max 0 (- 20 gnus-tmp-level)) ? ) ?s)
780     (?i gnus-tmp-score ?d)
781     (?z gnus-tmp-score-char ?c)
782     (?l (bbb-grouplens-score gnus-tmp-header) ?s)
783     (?V (gnus-thread-total-score (and (boundp 'thread) (car thread))) ?d)
784     (?U gnus-tmp-unread ?c)
785     (?t (gnus-summary-number-of-articles-in-thread
786          (and (boundp 'thread) (car thread)) gnus-tmp-level)
787         ?d)
788     (?e (gnus-summary-number-of-articles-in-thread
789          (and (boundp 'thread) (car thread)) gnus-tmp-level t)
790         ?c)
791     (?u gnus-tmp-user-defined ?s)
792     (?P (gnus-pick-line-number) ?d))
793   "An alist of format specifications that can appear in summary lines,
794 and what variables they correspond with, along with the type of the
795 variable (string, integer, character, etc).")
796
797 (defvar gnus-summary-dummy-line-format-alist
798   `((?S gnus-tmp-subject ?s)
799     (?N gnus-tmp-number ?d)
800     (?u gnus-tmp-user-defined ?s)))
801
802 (defvar gnus-summary-mode-line-format-alist
803   `((?G gnus-tmp-group-name ?s)
804     (?g (gnus-short-group-name gnus-tmp-group-name) ?s)
805     (?p (gnus-group-real-name gnus-tmp-group-name) ?s)
806     (?A gnus-tmp-article-number ?d)
807     (?Z gnus-tmp-unread-and-unselected ?s)
808     (?V gnus-version ?s)
809     (?U gnus-tmp-unread-and-unticked ?d)
810     (?S gnus-tmp-subject ?s)
811     (?e gnus-tmp-unselected ?d)
812     (?u gnus-tmp-user-defined ?s)
813     (?d (length gnus-newsgroup-dormant) ?d)
814     (?t (length gnus-newsgroup-marked) ?d)
815     (?r (length gnus-newsgroup-reads) ?d)
816     (?E gnus-newsgroup-expunged-tally ?d)
817     (?s (gnus-current-score-file-nondirectory) ?s)))
818
819 (defvar gnus-last-search-regexp nil
820   "Default regexp for article search command.")
821
822 (defvar gnus-last-shell-command nil
823   "Default shell command on article.")
824
825 (defvar gnus-newsgroup-begin nil)
826 (defvar gnus-newsgroup-end nil)
827 (defvar gnus-newsgroup-last-rmail nil)
828 (defvar gnus-newsgroup-last-mail nil)
829 (defvar gnus-newsgroup-last-folder nil)
830 (defvar gnus-newsgroup-last-file nil)
831 (defvar gnus-newsgroup-auto-expire nil)
832 (defvar gnus-newsgroup-active nil)
833
834 (defvar gnus-newsgroup-data nil)
835 (defvar gnus-newsgroup-data-reverse nil)
836 (defvar gnus-newsgroup-limit nil)
837 (defvar gnus-newsgroup-limits nil)
838
839 (defvar gnus-newsgroup-unreads nil
840   "List of unread articles in the current newsgroup.")
841
842 (defvar gnus-newsgroup-unselected nil
843   "List of unselected unread articles in the current newsgroup.")
844
845 (defvar gnus-newsgroup-reads nil
846   "Alist of read articles and article marks in the current newsgroup.")
847
848 (defvar gnus-newsgroup-expunged-tally nil)
849
850 (defvar gnus-newsgroup-marked nil
851   "List of ticked articles in the current newsgroup (a subset of unread art).")
852
853 (defvar gnus-newsgroup-killed nil
854   "List of ranges of articles that have been through the scoring process.")
855
856 (defvar gnus-newsgroup-cached nil
857   "List of articles that come from the article cache.")
858
859 (defvar gnus-newsgroup-saved nil
860   "List of articles that have been saved.")
861
862 (defvar gnus-newsgroup-kill-headers nil)
863
864 (defvar gnus-newsgroup-replied nil
865   "List of articles that have been replied to in the current newsgroup.")
866
867 (defvar gnus-newsgroup-expirable nil
868   "List of articles in the current newsgroup that can be expired.")
869
870 (defvar gnus-newsgroup-processable nil
871   "List of articles in the current newsgroup that can be processed.")
872
873 (defvar gnus-newsgroup-bookmarks nil
874   "List of articles in the current newsgroup that have bookmarks.")
875
876 (defvar gnus-newsgroup-dormant nil
877   "List of dormant articles in the current newsgroup.")
878
879 (defvar gnus-newsgroup-scored nil
880   "List of scored articles in the current newsgroup.")
881
882 (defvar gnus-newsgroup-headers nil
883   "List of article headers in the current newsgroup.")
884
885 (defvar gnus-newsgroup-threads nil)
886
887 (defvar gnus-newsgroup-prepared nil
888   "Whether the current group has been prepared properly.")
889
890 (defvar gnus-newsgroup-ancient nil
891   "List of `gnus-fetch-old-headers' articles in the current newsgroup.")
892
893 (defvar gnus-newsgroup-sparse nil)
894
895 (defvar gnus-current-article nil)
896 (defvar gnus-article-current nil)
897 (defvar gnus-current-headers nil)
898 (defvar gnus-have-all-headers nil)
899 (defvar gnus-last-article nil)
900 (defvar gnus-newsgroup-history nil)
901
902 (defconst gnus-summary-local-variables
903   '(gnus-newsgroup-name
904     gnus-newsgroup-begin gnus-newsgroup-end
905     gnus-newsgroup-last-rmail gnus-newsgroup-last-mail
906     gnus-newsgroup-last-folder gnus-newsgroup-last-file
907     gnus-newsgroup-auto-expire gnus-newsgroup-unreads
908     gnus-newsgroup-unselected gnus-newsgroup-marked
909     gnus-newsgroup-reads gnus-newsgroup-saved
910     gnus-newsgroup-replied gnus-newsgroup-expirable
911     gnus-newsgroup-processable gnus-newsgroup-killed
912     gnus-newsgroup-bookmarks gnus-newsgroup-dormant
913     gnus-newsgroup-headers gnus-newsgroup-threads
914     gnus-newsgroup-prepared gnus-summary-highlight-line-function
915     gnus-current-article gnus-current-headers gnus-have-all-headers
916     gnus-last-article gnus-article-internal-prepare-hook
917     gnus-newsgroup-dependencies gnus-newsgroup-selected-overlay
918     gnus-newsgroup-scored gnus-newsgroup-kill-headers
919     gnus-thread-expunge-below
920     gnus-score-alist gnus-current-score-file gnus-summary-expunge-below
921     (gnus-summary-mark-below . global)
922     gnus-newsgroup-active gnus-scores-exclude-files
923     gnus-newsgroup-history gnus-newsgroup-ancient
924     gnus-newsgroup-sparse gnus-newsgroup-process-stack
925     (gnus-newsgroup-adaptive . gnus-use-adaptive-scoring)
926     gnus-newsgroup-adaptive-score-file (gnus-reffed-article-number . -1)
927     (gnus-newsgroup-expunged-tally . 0)
928     gnus-cache-removable-articles gnus-newsgroup-cached
929     gnus-newsgroup-data gnus-newsgroup-data-reverse
930     gnus-newsgroup-limit gnus-newsgroup-limits)
931   "Variables that are buffer-local to the summary buffers.")
932
933 ;; Byte-compiler warning.
934 (defvar gnus-article-mode-map)
935
936 ;; Subject simplification.
937
938 (defsubst gnus-simplify-subject-re (subject)
939   "Remove \"Re:\" from subject lines."
940   (if (string-match "^[Rr][Ee]: *" subject)
941       (substring subject (match-end 0))
942     subject))
943
944 (defun gnus-simplify-subject (subject &optional re-only)
945   "Remove `Re:' and words in parentheses.
946 If RE-ONLY is non-nil, strip leading `Re:'s only."
947   (let ((case-fold-search t))           ;Ignore case.
948     ;; Remove `Re:', `Re^N:', `Re(n)', and `Re[n]:'.
949     (when (string-match "\\`\\(re\\([[(^][0-9]+[])]?\\)?:[ \t]*\\)+" subject)
950       (setq subject (substring subject (match-end 0))))
951     ;; Remove uninteresting prefixes.
952     (when (and (not re-only)
953                gnus-simplify-ignored-prefixes
954                (string-match gnus-simplify-ignored-prefixes subject))
955       (setq subject (substring subject (match-end 0))))
956     ;; Remove words in parentheses from end.
957     (unless re-only
958       (while (string-match "[ \t\n]*([^()]*)[ \t\n]*\\'" subject)
959         (setq subject (substring subject 0 (match-beginning 0)))))
960     ;; Return subject string.
961     subject))
962
963 ;; Remove any leading "re:"s, any trailing paren phrases, and simplify
964 ;; all whitespace.
965 (defsubst gnus-simplify-buffer-fuzzy-step (regexp &optional newtext)
966   (goto-char (point-min))
967   (while (re-search-forward regexp nil t)
968       (replace-match (or newtext ""))))
969
970 (defun gnus-simplify-buffer-fuzzy ()
971   "Simplify string in the buffer fuzzily.
972 The string in the accessible portion of the current buffer is simplified.
973 It is assumed to be a single-line subject.
974 Whitespace is generally cleaned up, and miscellaneous leading/trailing
975 matter is removed.  Additional things can be deleted by setting
976 gnus-simplify-subject-fuzzy-regexp."
977   (let ((case-fold-search t)
978         (modified-tick))
979     (gnus-simplify-buffer-fuzzy-step "\t" " ")
980
981     (while (not (eq modified-tick (buffer-modified-tick)))
982       (setq modified-tick (buffer-modified-tick))
983       (cond
984        ((listp gnus-simplify-subject-fuzzy-regexp)
985         (mapcar 'gnus-simplify-buffer-fuzzy-step
986                 gnus-simplify-subject-fuzzy-regexp))
987        (gnus-simplify-subject-fuzzy-regexp
988         (gnus-simplify-buffer-fuzzy-step gnus-simplify-subject-fuzzy-regexp)))
989       (gnus-simplify-buffer-fuzzy-step "^ *\\[[-+?*!][-+?*!]\\] *")
990       (gnus-simplify-buffer-fuzzy-step
991        "^ *\\(re\\|fw\\|fwd\\)[[{(^0-9]*[])}]?[:;] *")
992       (gnus-simplify-buffer-fuzzy-step "^[[].*:\\( .*\\)[]]$" "\\1"))
993
994     (gnus-simplify-buffer-fuzzy-step " *[[{(][^()\n]*[]})] *$")
995     (gnus-simplify-buffer-fuzzy-step "  +" " ")
996     (gnus-simplify-buffer-fuzzy-step " $")
997     (gnus-simplify-buffer-fuzzy-step "^ +")))
998
999 (defun gnus-simplify-subject-fuzzy (subject)
1000   "Simplify a subject string fuzzily.
1001 See gnus-simplify-buffer-fuzzy for details."
1002   (save-excursion
1003     (gnus-set-work-buffer)
1004     (let ((case-fold-search t))
1005       (insert subject)
1006       (inline (gnus-simplify-buffer-fuzzy))
1007       (buffer-string))))
1008
1009 (defsubst gnus-simplify-subject-fully (subject)
1010   "Simplify a subject string according to gnus-summary-gather-subject-limit."
1011   (cond
1012    ((null gnus-summary-gather-subject-limit)
1013     (gnus-simplify-subject-re subject))
1014    ((eq gnus-summary-gather-subject-limit 'fuzzy)
1015     (gnus-simplify-subject-fuzzy subject))
1016    ((numberp gnus-summary-gather-subject-limit)
1017     (gnus-limit-string (gnus-simplify-subject-re subject)
1018                        gnus-summary-gather-subject-limit))
1019    (t
1020     subject)))
1021
1022 (defsubst gnus-subject-equal (s1 s2 &optional simple-first)
1023   "Check whether two subjects are equal.  If optional argument
1024 simple-first is t, first argument is already simplified."
1025   (cond
1026    ((null simple-first)
1027     (equal (gnus-simplify-subject-fully s1)
1028            (gnus-simplify-subject-fully s2)))
1029    (t
1030     (equal s1
1031            (gnus-simplify-subject-fully s2)))))
1032
1033 (defun gnus-summary-bubble-group ()
1034   "Increase the score of the current group.
1035 This is a handy function to add to `gnus-summary-exit-hook' to
1036 increase the score of each group you read."
1037   (gnus-group-add-score gnus-newsgroup-name))
1038
1039 \f
1040 ;;;
1041 ;;; Gnus summary mode
1042 ;;;
1043
1044 (put 'gnus-summary-mode 'mode-class 'special)
1045
1046 (when t
1047   ;; Non-orthogonal keys
1048
1049   (gnus-define-keys gnus-summary-mode-map
1050     " " gnus-summary-next-page
1051     "\177" gnus-summary-prev-page
1052     [delete] gnus-summary-prev-page
1053     "\r" gnus-summary-scroll-up
1054     "n" gnus-summary-next-unread-article
1055     "p" gnus-summary-prev-unread-article
1056     "N" gnus-summary-next-article
1057     "P" gnus-summary-prev-article
1058     "\M-\C-n" gnus-summary-next-same-subject
1059     "\M-\C-p" gnus-summary-prev-same-subject
1060     "\M-n" gnus-summary-next-unread-subject
1061     "\M-p" gnus-summary-prev-unread-subject
1062     "." gnus-summary-first-unread-article
1063     "," gnus-summary-best-unread-article
1064     "\M-s" gnus-summary-search-article-forward
1065     "\M-r" gnus-summary-search-article-backward
1066     "<" gnus-summary-beginning-of-article
1067     ">" gnus-summary-end-of-article
1068     "j" gnus-summary-goto-article
1069     "^" gnus-summary-refer-parent-article
1070     "\M-^" gnus-summary-refer-article
1071     "u" gnus-summary-tick-article-forward
1072     "!" gnus-summary-tick-article-forward
1073     "U" gnus-summary-tick-article-backward
1074     "d" gnus-summary-mark-as-read-forward
1075     "D" gnus-summary-mark-as-read-backward
1076     "E" gnus-summary-mark-as-expirable
1077     "\M-u" gnus-summary-clear-mark-forward
1078     "\M-U" gnus-summary-clear-mark-backward
1079     "k" gnus-summary-kill-same-subject-and-select
1080     "\C-k" gnus-summary-kill-same-subject
1081     "\M-\C-k" gnus-summary-kill-thread
1082     "\M-\C-l" gnus-summary-lower-thread
1083     "e" gnus-summary-edit-article
1084     "#" gnus-summary-mark-as-processable
1085     "\M-#" gnus-summary-unmark-as-processable
1086     "\M-\C-t" gnus-summary-toggle-threads
1087     "\M-\C-s" gnus-summary-show-thread
1088     "\M-\C-h" gnus-summary-hide-thread
1089     "\M-\C-f" gnus-summary-next-thread
1090     "\M-\C-b" gnus-summary-prev-thread
1091     "\M-\C-u" gnus-summary-up-thread
1092     "\M-\C-d" gnus-summary-down-thread
1093     "&" gnus-summary-execute-command
1094     "c" gnus-summary-catchup-and-exit
1095     "\C-w" gnus-summary-mark-region-as-read
1096     "\C-t" gnus-summary-toggle-truncation
1097     "?" gnus-summary-mark-as-dormant
1098     "\C-c\M-\C-s" gnus-summary-limit-include-expunged
1099     "\C-c\C-s\C-n" gnus-summary-sort-by-number
1100     "\C-c\C-s\C-l" gnus-summary-sort-by-lines
1101     "\C-c\C-s\C-a" gnus-summary-sort-by-author
1102     "\C-c\C-s\C-s" gnus-summary-sort-by-subject
1103     "\C-c\C-s\C-d" gnus-summary-sort-by-date
1104     "\C-c\C-s\C-i" gnus-summary-sort-by-score
1105     "=" gnus-summary-expand-window
1106     "\C-x\C-s" gnus-summary-reselect-current-group
1107     "\M-g" gnus-summary-rescan-group
1108     "w" gnus-summary-stop-page-breaking
1109     "\C-c\C-r" gnus-summary-caesar-message
1110     "\M-t" gnus-summary-toggle-mime
1111     "f" gnus-summary-followup
1112     "F" gnus-summary-followup-with-original
1113     "C" gnus-summary-cancel-article
1114     "r" gnus-summary-reply
1115     "R" gnus-summary-reply-with-original
1116     "\C-c\C-f" gnus-summary-mail-forward
1117     "o" gnus-summary-save-article
1118     "\C-o" gnus-summary-save-article-mail
1119     "|" gnus-summary-pipe-output
1120     "\M-k" gnus-summary-edit-local-kill
1121     "\M-K" gnus-summary-edit-global-kill
1122     ;; "V" gnus-version
1123     "\C-c\C-d" gnus-summary-describe-group
1124     "q" gnus-summary-exit
1125     "Q" gnus-summary-exit-no-update
1126     "\C-c\C-i" gnus-info-find-node
1127     gnus-mouse-2 gnus-mouse-pick-article
1128     "m" gnus-summary-mail-other-window
1129     "a" gnus-summary-post-news
1130     "x" gnus-summary-limit-to-unread
1131     "s" gnus-summary-isearch-article
1132     "t" gnus-article-hide-headers
1133     "g" gnus-summary-show-article
1134     "l" gnus-summary-goto-last-article
1135     "\C-c\C-v\C-v" gnus-uu-decode-uu-view
1136     "\C-d" gnus-summary-enter-digest-group
1137     "\M-\C-d" gnus-summary-read-document
1138     "\C-c\C-b" gnus-bug
1139     "*" gnus-cache-enter-article
1140     "\M-*" gnus-cache-remove-article
1141     "\M-&" gnus-summary-universal-argument
1142     "\C-l" gnus-recenter
1143     "I" gnus-summary-increase-score
1144     "L" gnus-summary-lower-score
1145
1146     "V" gnus-summary-score-map
1147     "X" gnus-uu-extract-map
1148     "S" gnus-summary-send-map)
1149
1150   ;; Sort of orthogonal keymap
1151   (gnus-define-keys (gnus-summary-mark-map "M" gnus-summary-mode-map)
1152     "t" gnus-summary-tick-article-forward
1153     "!" gnus-summary-tick-article-forward
1154     "d" gnus-summary-mark-as-read-forward
1155     "r" gnus-summary-mark-as-read-forward
1156     "c" gnus-summary-clear-mark-forward
1157     " " gnus-summary-clear-mark-forward
1158     "e" gnus-summary-mark-as-expirable
1159     "x" gnus-summary-mark-as-expirable
1160     "?" gnus-summary-mark-as-dormant
1161     "b" gnus-summary-set-bookmark
1162     "B" gnus-summary-remove-bookmark
1163     "#" gnus-summary-mark-as-processable
1164     "\M-#" gnus-summary-unmark-as-processable
1165     "S" gnus-summary-limit-include-expunged
1166     "C" gnus-summary-catchup
1167     "H" gnus-summary-catchup-to-here
1168     "\C-c" gnus-summary-catchup-all
1169     "k" gnus-summary-kill-same-subject-and-select
1170     "K" gnus-summary-kill-same-subject
1171     "P" gnus-uu-mark-map)
1172
1173   (gnus-define-keys (gnus-summary-mscore-map "V" gnus-summary-mark-map)
1174     "c" gnus-summary-clear-above
1175     "u" gnus-summary-tick-above
1176     "m" gnus-summary-mark-above
1177     "k" gnus-summary-kill-below)
1178
1179   (gnus-define-keys (gnus-summary-limit-map "/" gnus-summary-mode-map)
1180     "/" gnus-summary-limit-to-subject
1181     "n" gnus-summary-limit-to-articles
1182     "w" gnus-summary-pop-limit
1183     "s" gnus-summary-limit-to-subject
1184     "a" gnus-summary-limit-to-author
1185     "u" gnus-summary-limit-to-unread
1186     "m" gnus-summary-limit-to-marks
1187     "v" gnus-summary-limit-to-score
1188     "D" gnus-summary-limit-include-dormant
1189     "d" gnus-summary-limit-exclude-dormant
1190     "t" gnus-summary-limit-to-age
1191     "E" gnus-summary-limit-include-expunged
1192     "c" gnus-summary-limit-exclude-childless-dormant
1193     "C" gnus-summary-limit-mark-excluded-as-read)
1194
1195   (gnus-define-keys (gnus-summary-goto-map "G" gnus-summary-mode-map)
1196     "n" gnus-summary-next-unread-article
1197     "p" gnus-summary-prev-unread-article
1198     "N" gnus-summary-next-article
1199     "P" gnus-summary-prev-article
1200     "\C-n" gnus-summary-next-same-subject
1201     "\C-p" gnus-summary-prev-same-subject
1202     "\M-n" gnus-summary-next-unread-subject
1203     "\M-p" gnus-summary-prev-unread-subject
1204     "f" gnus-summary-first-unread-article
1205     "b" gnus-summary-best-unread-article
1206     "j" gnus-summary-goto-article
1207     "g" gnus-summary-goto-subject
1208     "l" gnus-summary-goto-last-article
1209     "o" gnus-summary-pop-article)
1210
1211   (gnus-define-keys (gnus-summary-thread-map "T" gnus-summary-mode-map)
1212     "k" gnus-summary-kill-thread
1213     "l" gnus-summary-lower-thread
1214     "i" gnus-summary-raise-thread
1215     "T" gnus-summary-toggle-threads
1216     "t" gnus-summary-rethread-current
1217     "^" gnus-summary-reparent-thread
1218     "s" gnus-summary-show-thread
1219     "S" gnus-summary-show-all-threads
1220     "h" gnus-summary-hide-thread
1221     "H" gnus-summary-hide-all-threads
1222     "n" gnus-summary-next-thread
1223     "p" gnus-summary-prev-thread
1224     "u" gnus-summary-up-thread
1225     "o" gnus-summary-top-thread
1226     "d" gnus-summary-down-thread
1227     "#" gnus-uu-mark-thread
1228     "\M-#" gnus-uu-unmark-thread)
1229
1230   (gnus-define-keys (gnus-summary-buffer-map "Y" gnus-summary-mode-map)
1231     "g" gnus-summary-prepare
1232     "c" gnus-summary-insert-cached-articles)
1233
1234   (gnus-define-keys (gnus-summary-exit-map "Z" gnus-summary-mode-map)
1235     "c" gnus-summary-catchup-and-exit
1236     "C" gnus-summary-catchup-all-and-exit
1237     "E" gnus-summary-exit-no-update
1238     "Q" gnus-summary-exit
1239     "Z" gnus-summary-exit
1240     "n" gnus-summary-catchup-and-goto-next-group
1241     "R" gnus-summary-reselect-current-group
1242     "G" gnus-summary-rescan-group
1243     "N" gnus-summary-next-group
1244     "s" gnus-summary-save-newsrc
1245     "P" gnus-summary-prev-group)
1246
1247   (gnus-define-keys (gnus-summary-article-map "A" gnus-summary-mode-map)
1248     " " gnus-summary-next-page
1249     "n" gnus-summary-next-page
1250     "\177" gnus-summary-prev-page
1251     [delete] gnus-summary-prev-page
1252     "p" gnus-summary-prev-page
1253     "\r" gnus-summary-scroll-up
1254     "<" gnus-summary-beginning-of-article
1255     ">" gnus-summary-end-of-article
1256     "b" gnus-summary-beginning-of-article
1257     "e" gnus-summary-end-of-article
1258     "^" gnus-summary-refer-parent-article
1259     "r" gnus-summary-refer-parent-article
1260     "R" gnus-summary-refer-references
1261     "g" gnus-summary-show-article
1262     "s" gnus-summary-isearch-article
1263     "P" gnus-summary-print-article)
1264
1265   (gnus-define-keys (gnus-summary-wash-map "W" gnus-summary-mode-map)
1266     "b" gnus-article-add-buttons
1267     "B" gnus-article-add-buttons-to-head
1268     "o" gnus-article-treat-overstrike
1269     "e" gnus-article-emphasize
1270     "w" gnus-article-fill-cited-article
1271     "c" gnus-article-remove-cr
1272     "q" gnus-article-de-quoted-unreadable
1273     "f" gnus-article-display-x-face
1274     "l" gnus-summary-stop-page-breaking
1275     "r" gnus-summary-caesar-message
1276     "t" gnus-article-hide-headers
1277     "v" gnus-summary-verbose-headers
1278     "m" gnus-summary-toggle-mime
1279     "h" gnus-article-treat-html)
1280
1281   (gnus-define-keys (gnus-summary-wash-hide-map "W" gnus-summary-wash-map)
1282     "a" gnus-article-hide
1283     "h" gnus-article-hide-headers
1284     "b" gnus-article-hide-boring-headers
1285     "s" gnus-article-hide-signature
1286     "c" gnus-article-hide-citation
1287     "p" gnus-article-hide-pgp
1288     "P" gnus-article-hide-pem
1289     "\C-c" gnus-article-hide-citation-maybe)
1290
1291   (gnus-define-keys (gnus-summary-wash-highlight-map "H" gnus-summary-wash-map)
1292     "a" gnus-article-highlight
1293     "h" gnus-article-highlight-headers
1294     "c" gnus-article-highlight-citation
1295     "s" gnus-article-highlight-signature)
1296
1297   (gnus-define-keys (gnus-summary-wash-time-map "T" gnus-summary-wash-map)
1298     "z" gnus-article-date-ut
1299     "u" gnus-article-date-ut
1300     "l" gnus-article-date-local
1301     "e" gnus-article-date-lapsed
1302     "o" gnus-article-date-original
1303     "s" gnus-article-date-user)
1304
1305   (gnus-define-keys (gnus-summary-wash-empty-map "E" gnus-summary-wash-map)
1306     "t" gnus-article-remove-trailing-blank-lines
1307     "l" gnus-article-strip-leading-blank-lines
1308     "m" gnus-article-strip-multiple-blank-lines
1309     "a" gnus-article-strip-blank-lines
1310     "s" gnus-article-strip-leading-space)
1311
1312   (gnus-define-keys (gnus-summary-help-map "H" gnus-summary-mode-map)
1313     "v" gnus-version
1314     "f" gnus-summary-fetch-faq
1315     "d" gnus-summary-describe-group
1316     "h" gnus-summary-describe-briefly
1317     "i" gnus-info-find-node)
1318
1319   (gnus-define-keys (gnus-summary-backend-map "B" gnus-summary-mode-map)
1320     "e" gnus-summary-expire-articles
1321     "\M-\C-e" gnus-summary-expire-articles-now
1322     "\177" gnus-summary-delete-article
1323     [delete] gnus-summary-delete-article
1324     "m" gnus-summary-move-article
1325     "r" gnus-summary-respool-article
1326     "w" gnus-summary-edit-article
1327     "c" gnus-summary-copy-article
1328     "B" gnus-summary-crosspost-article
1329     "q" gnus-summary-respool-query
1330     "i" gnus-summary-import-article
1331     "p" gnus-summary-article-posted-p)
1332
1333   (gnus-define-keys (gnus-summary-save-map "O" gnus-summary-mode-map)
1334     "o" gnus-summary-save-article
1335     "m" gnus-summary-save-article-mail
1336     "F" gnus-summary-write-article-file
1337     "r" gnus-summary-save-article-rmail
1338     "f" gnus-summary-save-article-file
1339     "b" gnus-summary-save-article-body-file
1340     "h" gnus-summary-save-article-folder
1341     "v" gnus-summary-save-article-vm
1342     "p" gnus-summary-pipe-output
1343     "s" gnus-soup-add-article))
1344
1345 (defun gnus-summary-make-menu-bar ()
1346   (gnus-turn-off-edit-menu 'summary)
1347
1348   (unless (boundp 'gnus-summary-misc-menu)
1349
1350     (easy-menu-define
1351      gnus-summary-kill-menu gnus-summary-mode-map ""
1352      (cons
1353       "Score"
1354       (nconc
1355        (list
1356         ["Enter score..." gnus-summary-score-entry t]
1357         ["Customize" gnus-score-customize t])
1358        (gnus-make-score-map 'increase)
1359        (gnus-make-score-map 'lower)
1360        '(("Mark"
1361           ["Kill below" gnus-summary-kill-below t]
1362           ["Mark above" gnus-summary-mark-above t]
1363           ["Tick above" gnus-summary-tick-above t]
1364           ["Clear above" gnus-summary-clear-above t])
1365          ["Current score" gnus-summary-current-score t]
1366          ["Set score" gnus-summary-set-score t]
1367          ["Switch current score file..." gnus-score-change-score-file t]
1368          ["Set mark below..." gnus-score-set-mark-below t]
1369          ["Set expunge below..." gnus-score-set-expunge-below t]
1370          ["Edit current score file" gnus-score-edit-current-scores t]
1371          ["Edit score file" gnus-score-edit-file t]
1372          ["Trace score" gnus-score-find-trace t]
1373          ["Find words" gnus-score-find-favourite-words t]
1374          ["Rescore buffer" gnus-summary-rescore t]
1375          ["Increase score..." gnus-summary-increase-score t]
1376          ["Lower score..." gnus-summary-lower-score t]))))
1377
1378     '(("Default header"
1379        ["Ask" (gnus-score-set-default 'gnus-score-default-header nil)
1380         :style radio
1381         :selected (null gnus-score-default-header)]
1382        ["From" (gnus-score-set-default 'gnus-score-default-header 'a)
1383         :style radio
1384         :selected (eq gnus-score-default-header 'a)]
1385        ["Subject" (gnus-score-set-default 'gnus-score-default-header 's)
1386         :style radio
1387         :selected (eq gnus-score-default-header 's)]
1388        ["Article body"
1389         (gnus-score-set-default 'gnus-score-default-header 'b)
1390         :style radio
1391         :selected (eq gnus-score-default-header 'b )]
1392        ["All headers"
1393         (gnus-score-set-default 'gnus-score-default-header 'h)
1394         :style radio
1395         :selected (eq gnus-score-default-header 'h )]
1396        ["Message-ID" (gnus-score-set-default 'gnus-score-default-header 'i)
1397         :style radio
1398         :selected (eq gnus-score-default-header 'i )]
1399        ["Thread" (gnus-score-set-default 'gnus-score-default-header 't)
1400         :style radio
1401         :selected (eq gnus-score-default-header 't )]
1402        ["Crossposting"
1403         (gnus-score-set-default 'gnus-score-default-header 'x)
1404         :style radio
1405         :selected (eq gnus-score-default-header 'x )]
1406        ["Lines" (gnus-score-set-default 'gnus-score-default-header 'l)
1407         :style radio
1408         :selected (eq gnus-score-default-header 'l )]
1409        ["Date" (gnus-score-set-default 'gnus-score-default-header 'd)
1410         :style radio
1411         :selected (eq gnus-score-default-header 'd )]
1412        ["Followups to author"
1413         (gnus-score-set-default 'gnus-score-default-header 'f)
1414         :style radio
1415         :selected (eq gnus-score-default-header 'f )])
1416       ("Default type"
1417        ["Ask" (gnus-score-set-default 'gnus-score-default-type nil)
1418         :style radio
1419         :selected (null gnus-score-default-type)]
1420        ;; The `:active' key is commented out in the following,
1421        ;; because the GNU Emacs hack to support radio buttons use
1422        ;; active to indicate which button is selected.
1423        ["Substring" (gnus-score-set-default 'gnus-score-default-type 's)
1424         :style radio
1425         ;; :active (not (memq gnus-score-default-header '(l d)))
1426         :selected (eq gnus-score-default-type 's)]
1427        ["Regexp" (gnus-score-set-default 'gnus-score-default-type 'r)
1428         :style radio
1429         ;; :active (not (memq gnus-score-default-header '(l d)))
1430         :selected (eq gnus-score-default-type 'r)]
1431        ["Exact" (gnus-score-set-default 'gnus-score-default-type 'e)
1432         :style radio
1433         ;; :active (not (memq gnus-score-default-header '(l d)))
1434         :selected (eq gnus-score-default-type 'e)]
1435        ["Fuzzy" (gnus-score-set-default 'gnus-score-default-type 'f)
1436         :style radio
1437         ;; :active (not (memq gnus-score-default-header '(l d)))
1438         :selected (eq gnus-score-default-type 'f)]
1439        ["Before date" (gnus-score-set-default 'gnus-score-default-type 'b)
1440         :style radio
1441         ;; :active (eq (gnus-score-default-header 'd))
1442         :selected (eq gnus-score-default-type 'b)]
1443        ["At date" (gnus-score-set-default 'gnus-score-default-type 'n)
1444         :style radio
1445         ;; :active (eq (gnus-score-default-header 'd))
1446         :selected (eq gnus-score-default-type 'n)]
1447        ["After date" (gnus-score-set-default 'gnus-score-default-type 'a)
1448         :style radio
1449         ;; :active (eq (gnus-score-default-header 'd))
1450         :selected (eq gnus-score-default-type 'a)]
1451        ["Less than number"
1452         (gnus-score-set-default 'gnus-score-default-type '<)
1453         :style radio
1454         ;; :active (eq (gnus-score-default-header 'l))
1455         :selected (eq gnus-score-default-type '<)]
1456        ["Equal to number"
1457         (gnus-score-set-default 'gnus-score-default-type '=)
1458         :style radio
1459         ;; :active (eq (gnus-score-default-header 'l))
1460         :selected (eq gnus-score-default-type '=)]
1461        ["Greater than number"
1462         (gnus-score-set-default 'gnus-score-default-type '>)
1463         :style radio
1464         ;; :active (eq (gnus-score-default-header 'l))
1465         :selected (eq gnus-score-default-type '>)])
1466       ["Default fold" gnus-score-default-fold-toggle
1467        :style toggle
1468        :selected gnus-score-default-fold]
1469       ("Default duration"
1470        ["Ask" (gnus-score-set-default 'gnus-score-default-duration nil)
1471         :style radio
1472         :selected (null gnus-score-default-duration)]
1473        ["Permanent"
1474         (gnus-score-set-default 'gnus-score-default-duration 'p)
1475         :style radio
1476         :selected (eq gnus-score-default-duration 'p)]
1477        ["Temporary"
1478         (gnus-score-set-default 'gnus-score-default-duration 't)
1479         :style radio
1480         :selected (eq gnus-score-default-duration 't)]
1481        ["Immediate"
1482         (gnus-score-set-default 'gnus-score-default-duration 'i)
1483         :style radio
1484         :selected (eq gnus-score-default-duration 'i)]))
1485
1486     (easy-menu-define
1487      gnus-summary-article-menu gnus-summary-mode-map ""
1488      '("Article"
1489        ("Hide"
1490         ["All" gnus-article-hide t]
1491         ["Headers" gnus-article-hide-headers t]
1492         ["Signature" gnus-article-hide-signature t]
1493         ["Citation" gnus-article-hide-citation t]
1494         ["PGP" gnus-article-hide-pgp t]
1495         ["Boring headers" gnus-article-hide-boring-headers t])
1496        ("Highlight"
1497         ["All" gnus-article-highlight t]
1498         ["Headers" gnus-article-highlight-headers t]
1499         ["Signature" gnus-article-highlight-signature t]
1500         ["Citation" gnus-article-highlight-citation t])
1501        ("Date"
1502         ["Local" gnus-article-date-local t]
1503         ["UT" gnus-article-date-ut t]
1504         ["Original" gnus-article-date-original t]
1505         ["Lapsed" gnus-article-date-lapsed t]
1506         ["User-defined" gnus-article-date-user t])
1507        ("Washing"
1508         ("Remove Blanks"
1509          ["Leading" gnus-article-strip-leading-blank-lines t]
1510          ["Multiple" gnus-article-strip-multiple-blank-lines t]
1511          ["Trailing" gnus-article-remove-trailing-blank-lines t]
1512          ["All of the above" gnus-article-strip-blank-lines t]
1513          ["Leading space" gnus-article-strip-leading-space t])
1514         ["Overstrike" gnus-article-treat-overstrike t]
1515         ["Emphasis" gnus-article-emphasize t]
1516         ["Word wrap" gnus-article-fill-cited-article t]
1517         ["CR" gnus-article-remove-cr t]
1518         ["Show X-Face" gnus-article-display-x-face t]
1519         ["Quoted-Printable" gnus-article-de-quoted-unreadable t]
1520         ["UnHTMLize" gnus-article-treat-html t]
1521         ["Rot 13" gnus-summary-caesar-message t]
1522         ["Unix pipe" gnus-summary-pipe-message t]
1523         ["Add buttons" gnus-article-add-buttons t]
1524         ["Add buttons to head" gnus-article-add-buttons-to-head t]
1525         ["Stop page breaking" gnus-summary-stop-page-breaking t]
1526         ["Toggle MIME" gnus-summary-toggle-mime t]
1527         ["Verbose header" gnus-summary-verbose-headers t]
1528         ["Toggle header" gnus-summary-toggle-header t])
1529        ("Output"
1530         ["Save in default format" gnus-summary-save-article t]
1531         ["Save in file" gnus-summary-save-article-file t]
1532         ["Save in Unix mail format" gnus-summary-save-article-mail t]
1533         ["Write to file" gnus-summary-write-article-mail t]
1534         ["Save in MH folder" gnus-summary-save-article-folder t]
1535         ["Save in VM folder" gnus-summary-save-article-vm t]
1536         ["Save in RMAIL mbox" gnus-summary-save-article-rmail t]
1537         ["Save body in file" gnus-summary-save-article-body-file t]
1538         ["Pipe through a filter" gnus-summary-pipe-output t]
1539         ["Add to SOUP packet" gnus-soup-add-article t]
1540         ["Print" gnus-summary-print-article t])
1541        ("Backend"
1542         ["Respool article..." gnus-summary-respool-article t]
1543         ["Move article..." gnus-summary-move-article
1544          (gnus-check-backend-function
1545           'request-move-article gnus-newsgroup-name)]
1546         ["Copy article..." gnus-summary-copy-article t]
1547         ["Crosspost article..." gnus-summary-crosspost-article
1548          (gnus-check-backend-function
1549           'request-replace-article gnus-newsgroup-name)]
1550         ["Import file..." gnus-summary-import-article t]
1551         ["Check if posted" gnus-summary-article-posted-p t]
1552         ["Edit article" gnus-summary-edit-article
1553          (not (gnus-group-read-only-p))]
1554         ["Delete article" gnus-summary-delete-article
1555          (gnus-check-backend-function
1556           'request-expire-articles gnus-newsgroup-name)]
1557         ["Query respool" gnus-summary-respool-query t]
1558         ["Delete expirable articles" gnus-summary-expire-articles-now
1559          (gnus-check-backend-function
1560           'request-expire-articles gnus-newsgroup-name)])
1561        ("Extract"
1562         ["Uudecode" gnus-uu-decode-uu t]
1563         ["Uudecode and save" gnus-uu-decode-uu-and-save t]
1564         ["Unshar" gnus-uu-decode-unshar t]
1565         ["Unshar and save" gnus-uu-decode-unshar-and-save t]
1566         ["Save" gnus-uu-decode-save t]
1567         ["Binhex" gnus-uu-decode-binhex t]
1568         ["Postscript" gnus-uu-decode-postscript t])
1569        ("Cache"
1570         ["Enter article" gnus-cache-enter-article t]
1571         ["Remove article" gnus-cache-remove-article t])
1572        ["Enter digest buffer" gnus-summary-enter-digest-group t]
1573        ["Isearch article..." gnus-summary-isearch-article t]
1574        ["Beginning of the article" gnus-summary-beginning-of-article t]
1575        ["End of the article" gnus-summary-end-of-article t]
1576        ["Fetch parent of article" gnus-summary-refer-parent-article t]
1577        ["Fetch referenced articles" gnus-summary-refer-references t]
1578        ["Fetch article with id..." gnus-summary-refer-article t]
1579        ["Redisplay" gnus-summary-show-article t]))
1580
1581     (easy-menu-define
1582      gnus-summary-thread-menu gnus-summary-mode-map ""
1583      '("Threads"
1584        ["Toggle threading" gnus-summary-toggle-threads t]
1585        ["Hide threads" gnus-summary-hide-all-threads t]
1586        ["Show threads" gnus-summary-show-all-threads t]
1587        ["Hide thread" gnus-summary-hide-thread t]
1588        ["Show thread" gnus-summary-show-thread t]
1589        ["Go to next thread" gnus-summary-next-thread t]
1590        ["Go to previous thread" gnus-summary-prev-thread t]
1591        ["Go down thread" gnus-summary-down-thread t]
1592        ["Go up thread" gnus-summary-up-thread t]
1593        ["Top of thread" gnus-summary-top-thread t]
1594        ["Mark thread as read" gnus-summary-kill-thread t]
1595        ["Lower thread score" gnus-summary-lower-thread t]
1596        ["Raise thread score" gnus-summary-raise-thread t]
1597        ["Rethread current" gnus-summary-rethread-current t]
1598        ))
1599
1600     (easy-menu-define
1601      gnus-summary-post-menu gnus-summary-mode-map ""
1602      '("Post"
1603        ["Post an article" gnus-summary-post-news t]
1604        ["Followup" gnus-summary-followup t]
1605        ["Followup and yank" gnus-summary-followup-with-original t]
1606        ["Supersede article" gnus-summary-supersede-article t]
1607        ["Cancel article" gnus-summary-cancel-article t]
1608        ["Reply" gnus-summary-reply t]
1609        ["Reply and yank" gnus-summary-reply-with-original t]
1610        ["Wide reply" gnus-summary-wide-reply t]
1611        ["Wide reply and yank" gnus-summary-wide-reply-with-original t]
1612        ["Mail forward" gnus-summary-mail-forward t]
1613        ["Post forward" gnus-summary-post-forward t]
1614        ["Digest and mail" gnus-uu-digest-mail-forward t]
1615        ["Digest and post" gnus-uu-digest-post-forward t]
1616        ["Resend message" gnus-summary-resend-message t]
1617        ["Send bounced mail" gnus-summary-resend-bounced-mail t]
1618        ["Send a mail" gnus-summary-mail-other-window t]
1619        ["Uuencode and post" gnus-uu-post-news t]
1620        ["Followup via news" gnus-summary-followup-to-mail t]
1621        ["Followup via news and yank"
1622         gnus-summary-followup-to-mail-with-original t]
1623        ;;("Draft"
1624        ;;["Send" gnus-summary-send-draft t]
1625        ;;["Send bounced" gnus-resend-bounced-mail t])
1626        ))
1627
1628     (easy-menu-define
1629      gnus-summary-misc-menu gnus-summary-mode-map ""
1630      '("Misc"
1631        ("Mark Read"
1632         ["Mark as read" gnus-summary-mark-as-read-forward t]
1633         ["Mark same subject and select"
1634          gnus-summary-kill-same-subject-and-select t]
1635         ["Mark same subject" gnus-summary-kill-same-subject t]
1636         ["Catchup" gnus-summary-catchup t]
1637         ["Catchup all" gnus-summary-catchup-all t]
1638         ["Catchup to here" gnus-summary-catchup-to-here t]
1639         ["Catchup region" gnus-summary-mark-region-as-read t]
1640         ["Mark excluded" gnus-summary-limit-mark-excluded-as-read t])
1641        ("Mark Various"
1642         ["Tick" gnus-summary-tick-article-forward t]
1643         ["Mark as dormant" gnus-summary-mark-as-dormant t]
1644         ["Remove marks" gnus-summary-clear-mark-forward t]
1645         ["Set expirable mark" gnus-summary-mark-as-expirable t]
1646         ["Set bookmark" gnus-summary-set-bookmark t]
1647         ["Remove bookmark" gnus-summary-remove-bookmark t])
1648        ("Mark Limit"
1649         ["Marks..." gnus-summary-limit-to-marks t]
1650         ["Subject..." gnus-summary-limit-to-subject t]
1651         ["Author..." gnus-summary-limit-to-author t]
1652         ["Age..." gnus-summary-limit-to-age t]
1653         ["Score" gnus-summary-limit-to-score t]
1654         ["Unread" gnus-summary-limit-to-unread t]
1655         ["Non-dormant" gnus-summary-limit-exclude-dormant t]
1656         ["Articles" gnus-summary-limit-to-articles t]
1657         ["Pop limit" gnus-summary-pop-limit t]
1658         ["Show dormant" gnus-summary-limit-include-dormant t]
1659         ["Hide childless dormant"
1660          gnus-summary-limit-exclude-childless-dormant t]
1661         ;;["Hide thread" gnus-summary-limit-exclude-thread t]
1662         ["Show expunged" gnus-summary-show-all-expunged t])
1663        ("Process Mark"
1664         ["Set mark" gnus-summary-mark-as-processable t]
1665         ["Remove mark" gnus-summary-unmark-as-processable t]
1666         ["Remove all marks" gnus-summary-unmark-all-processable t]
1667         ["Mark above" gnus-uu-mark-over t]
1668         ["Mark series" gnus-uu-mark-series t]
1669         ["Mark region" gnus-uu-mark-region t]
1670         ["Mark by regexp..." gnus-uu-mark-by-regexp t]
1671         ["Mark all" gnus-uu-mark-all t]
1672         ["Mark buffer" gnus-uu-mark-buffer t]
1673         ["Mark sparse" gnus-uu-mark-sparse t]
1674         ["Mark thread" gnus-uu-mark-thread t]
1675         ["Unmark thread" gnus-uu-unmark-thread t]
1676         ("Process Mark Sets"
1677          ["Kill" gnus-summary-kill-process-mark t]
1678          ["Yank" gnus-summary-yank-process-mark
1679           gnus-newsgroup-process-stack]
1680          ["Save" gnus-summary-save-process-mark t]))
1681        ("Scroll article"
1682         ["Page forward" gnus-summary-next-page t]
1683         ["Page backward" gnus-summary-prev-page t]
1684         ["Line forward" gnus-summary-scroll-up t])
1685        ("Move"
1686         ["Next unread article" gnus-summary-next-unread-article t]
1687         ["Previous unread article" gnus-summary-prev-unread-article t]
1688         ["Next article" gnus-summary-next-article t]
1689         ["Previous article" gnus-summary-prev-article t]
1690         ["Next unread subject" gnus-summary-next-unread-subject t]
1691         ["Previous unread subject" gnus-summary-prev-unread-subject t]
1692         ["Next article same subject" gnus-summary-next-same-subject t]
1693         ["Previous article same subject" gnus-summary-prev-same-subject t]
1694         ["First unread article" gnus-summary-first-unread-article t]
1695         ["Best unread article" gnus-summary-best-unread-article t]
1696         ["Go to subject number..." gnus-summary-goto-subject t]
1697         ["Go to article number..." gnus-summary-goto-article t]
1698         ["Go to the last article" gnus-summary-goto-last-article t]
1699         ["Pop article off history" gnus-summary-pop-article t])
1700        ("Sort"
1701         ["Sort by number" gnus-summary-sort-by-number t]
1702         ["Sort by author" gnus-summary-sort-by-author t]
1703         ["Sort by subject" gnus-summary-sort-by-subject t]
1704         ["Sort by date" gnus-summary-sort-by-date t]
1705         ["Sort by score" gnus-summary-sort-by-score t]
1706         ["Sort by lines" gnus-summary-sort-by-lines t])
1707        ("Help"
1708         ["Fetch group FAQ" gnus-summary-fetch-faq t]
1709         ["Describe group" gnus-summary-describe-group t]
1710         ["Read manual" gnus-info-find-node t])
1711        ("Modes"
1712         ["Pick and read" gnus-pick-mode t]
1713         ["Binary" gnus-binary-mode t])
1714        ("Regeneration"
1715         ["Regenerate" gnus-summary-prepare t]
1716         ["Insert cached articles" gnus-summary-insert-cached-articles t]
1717         ["Toggle threading" gnus-summary-toggle-threads t])
1718        ["Filter articles..." gnus-summary-execute-command t]
1719        ["Run command on subjects..." gnus-summary-universal-argument t]
1720        ["Search articles forward..." gnus-summary-search-article-forward t]
1721        ["Search articles backward..." gnus-summary-search-article-backward t]
1722        ["Toggle line truncation" gnus-summary-toggle-truncation t]
1723        ["Expand window" gnus-summary-expand-window t]
1724        ["Expire expirable articles" gnus-summary-expire-articles
1725         (gnus-check-backend-function
1726          'request-expire-articles gnus-newsgroup-name)]
1727        ["Edit local kill file" gnus-summary-edit-local-kill t]
1728        ["Edit main kill file" gnus-summary-edit-global-kill t]
1729        ("Exit"
1730         ["Catchup and exit" gnus-summary-catchup-and-exit t]
1731         ["Catchup all and exit" gnus-summary-catchup-and-exit t]
1732         ["Catchup and goto next" gnus-summary-catchup-and-goto-next-group t]
1733         ["Exit group" gnus-summary-exit t]
1734         ["Exit group without updating" gnus-summary-exit-no-update t]
1735         ["Exit and goto next group" gnus-summary-next-group t]
1736         ["Exit and goto prev group" gnus-summary-prev-group t]
1737         ["Reselect group" gnus-summary-reselect-current-group t]
1738         ["Rescan group" gnus-summary-rescan-group t]
1739         ["Update dribble" gnus-summary-save-newsrc t])))
1740
1741     (run-hooks 'gnus-summary-menu-hook)))
1742
1743 (defun gnus-score-set-default (var value)
1744   "A version of set that updates the GNU Emacs menu-bar."
1745   (set var value)
1746   ;; It is the message that forces the active status to be updated.
1747   (message ""))
1748
1749 (defun gnus-make-score-map (type)
1750   "Make a summary score map of type TYPE."
1751   (if t
1752       nil
1753     (let ((headers '(("author" "from" string)
1754                      ("subject" "subject" string)
1755                      ("article body" "body" string)
1756                      ("article head" "head" string)
1757                      ("xref" "xref" string)
1758                      ("lines" "lines" number)
1759                      ("followups to author" "followup" string)))
1760           (types '((number ("less than" <)
1761                            ("greater than" >)
1762                            ("equal" =))
1763                    (string ("substring" s)
1764                            ("exact string" e)
1765                            ("fuzzy string" f)
1766                            ("regexp" r))))
1767           (perms '(("temporary" (current-time-string))
1768                    ("permanent" nil)
1769                    ("immediate" now)))
1770           header)
1771       (list
1772        (apply
1773         'nconc
1774         (list
1775          (if (eq type 'lower)
1776              "Lower score"
1777            "Increase score"))
1778         (let (outh)
1779           (while headers
1780             (setq header (car headers))
1781             (setq outh
1782                   (cons
1783                    (apply
1784                     'nconc
1785                     (list (car header))
1786                     (let ((ts (cdr (assoc (nth 2 header) types)))
1787                           outt)
1788                       (while ts
1789                         (setq outt
1790                               (cons
1791                                (apply
1792                                 'nconc
1793                                 (list (caar ts))
1794                                 (let ((ps perms)
1795                                       outp)
1796                                   (while ps
1797                                     (setq outp
1798                                           (cons
1799                                            (vector
1800                                             (caar ps)
1801                                             (list
1802                                              'gnus-summary-score-entry
1803                                              (nth 1 header)
1804                                              (if (or (string= (nth 1 header)
1805                                                               "head")
1806                                                      (string= (nth 1 header)
1807                                                               "body"))
1808                                                  ""
1809                                                (list 'gnus-summary-header
1810                                                      (nth 1 header)))
1811                                              (list 'quote (nth 1 (car ts)))
1812                                              (list 'gnus-score-default nil)
1813                                              (nth 1 (car ps))
1814                                              t)
1815                                             t)
1816                                            outp))
1817                                     (setq ps (cdr ps)))
1818                                   (list (nreverse outp))))
1819                                outt))
1820                         (setq ts (cdr ts)))
1821                       (list (nreverse outt))))
1822                    outh))
1823             (setq headers (cdr headers)))
1824           (list (nreverse outh))))))))
1825
1826 \f
1827
1828 (defun gnus-summary-mode (&optional group)
1829   "Major mode for reading articles.
1830
1831 All normal editing commands are switched off.
1832 \\<gnus-summary-mode-map>
1833 Each line in this buffer represents one article.  To read an
1834 article, you can, for instance, type `\\[gnus-summary-next-page]'.  To move forwards
1835 and backwards while displaying articles, type `\\[gnus-summary-next-unread-article]' and `\\[gnus-summary-prev-unread-article]',
1836 respectively.
1837
1838 You can also post articles and send mail from this buffer.  To
1839 follow up an article, type `\\[gnus-summary-followup]'.  To mail a reply to the author
1840 of an article, type `\\[gnus-summary-reply]'.
1841
1842 There are approx. one gazillion commands you can execute in this
1843 buffer; read the info pages for more information (`\\[gnus-info-find-node]').
1844
1845 The following commands are available:
1846
1847 \\{gnus-summary-mode-map}"
1848   (interactive)
1849   (when (gnus-visual-p 'summary-menu 'menu)
1850     (gnus-summary-make-menu-bar))
1851   (kill-all-local-variables)
1852   (gnus-summary-make-local-variables)
1853   (gnus-make-thread-indent-array)
1854   (gnus-simplify-mode-line)
1855   (setq major-mode 'gnus-summary-mode)
1856   (setq mode-name "Summary")
1857   (make-local-variable 'minor-mode-alist)
1858   (use-local-map gnus-summary-mode-map)
1859   (buffer-disable-undo (current-buffer))
1860   (setq buffer-read-only t)             ;Disable modification
1861   (setq truncate-lines t)
1862   (setq selective-display t)
1863   (setq selective-display-ellipses t)   ;Display `...'
1864   (gnus-summary-set-display-table)
1865   (gnus-set-default-directory)
1866   (setq gnus-newsgroup-name group)
1867   (make-local-variable 'gnus-summary-line-format)
1868   (make-local-variable 'gnus-summary-line-format-spec)
1869   (make-local-variable 'gnus-summary-mark-positions)
1870   (make-local-hook 'post-command-hook)
1871   (add-hook 'post-command-hook 'gnus-clear-inboxes-moved nil t)
1872   (run-hooks 'gnus-summary-mode-hook)
1873   (gnus-update-format-specifications nil 'summary 'summary-mode 'summary-dummy)
1874   (gnus-update-summary-mark-positions))
1875
1876 (defun gnus-summary-make-local-variables ()
1877   "Make all the local summary buffer variables."
1878   (let ((locals gnus-summary-local-variables)
1879         global local)
1880     (while (setq local (pop locals))
1881       (if (consp local)
1882           (progn
1883             (if (eq (cdr local) 'global)
1884                 ;; Copy the global value of the variable.
1885                 (setq global (symbol-value (car local)))
1886               ;; Use the value from the list.
1887               (setq global (eval (cdr local))))
1888             (make-local-variable (car local))
1889             (set (car local) global))
1890         ;; Simple nil-valued local variable.
1891         (make-local-variable local)
1892         (set local nil)))))
1893
1894 (defun gnus-summary-clear-local-variables ()
1895   (let ((locals gnus-summary-local-variables))
1896     (while locals
1897       (if (consp (car locals))
1898           (and (vectorp (caar locals))
1899                (set (caar locals) nil))
1900         (and (vectorp (car locals))
1901              (set (car locals) nil)))
1902       (setq locals (cdr locals)))))
1903
1904 ;; Summary data functions.
1905
1906 (defmacro gnus-data-number (data)
1907   `(car ,data))
1908
1909 (defmacro gnus-data-set-number (data number)
1910   `(setcar ,data ,number))
1911
1912 (defmacro gnus-data-mark (data)
1913   `(nth 1 ,data))
1914
1915 (defmacro gnus-data-set-mark (data mark)
1916   `(setcar (nthcdr 1 ,data) ,mark))
1917
1918 (defmacro gnus-data-pos (data)
1919   `(nth 2 ,data))
1920
1921 (defmacro gnus-data-set-pos (data pos)
1922   `(setcar (nthcdr 2 ,data) ,pos))
1923
1924 (defmacro gnus-data-header (data)
1925   `(nth 3 ,data))
1926
1927 (defmacro gnus-data-set-header (data header)
1928   `(setf (nth 3 ,data) ,header))
1929
1930 (defmacro gnus-data-level (data)
1931   `(nth 4 ,data))
1932
1933 (defmacro gnus-data-unread-p (data)
1934   `(= (nth 1 ,data) gnus-unread-mark))
1935
1936 (defmacro gnus-data-read-p (data)
1937   `(/= (nth 1 ,data) gnus-unread-mark))
1938
1939 (defmacro gnus-data-pseudo-p (data)
1940   `(consp (nth 3 ,data)))
1941
1942 (defmacro gnus-data-find (number)
1943   `(assq ,number gnus-newsgroup-data))
1944
1945 (defmacro gnus-data-find-list (number &optional data)
1946   `(let ((bdata ,(or data 'gnus-newsgroup-data)))
1947      (memq (assq ,number bdata)
1948            bdata)))
1949
1950 (defmacro gnus-data-make (number mark pos header level)
1951   `(list ,number ,mark ,pos ,header ,level))
1952
1953 (defun gnus-data-enter (after-article number mark pos header level offset)
1954   (let ((data (gnus-data-find-list after-article)))
1955     (unless data
1956       (error "No such article: %d" after-article))
1957     (setcdr data (cons (gnus-data-make number mark pos header level)
1958                        (cdr data)))
1959     (setq gnus-newsgroup-data-reverse nil)
1960     (gnus-data-update-list (cddr data) offset)))
1961
1962 (defun gnus-data-enter-list (after-article list &optional offset)
1963   (when list
1964     (let ((data (and after-article (gnus-data-find-list after-article)))
1965           (ilist list))
1966       (or data (not after-article) (error "No such article: %d" after-article))
1967       ;; Find the last element in the list to be spliced into the main
1968       ;; list.
1969       (while (cdr list)
1970         (setq list (cdr list)))
1971       (if (not data)
1972           (progn
1973             (setcdr list gnus-newsgroup-data)
1974             (setq gnus-newsgroup-data ilist)
1975             (when offset
1976               (gnus-data-update-list (cdr list) offset)))
1977         (setcdr list (cdr data))
1978         (setcdr data ilist)
1979         (when offset
1980           (gnus-data-update-list (cdr list) offset)))
1981       (setq gnus-newsgroup-data-reverse nil))))
1982
1983 (defun gnus-data-remove (article &optional offset)
1984   (let ((data gnus-newsgroup-data))
1985     (if (= (gnus-data-number (car data)) article)
1986         (progn
1987           (setq gnus-newsgroup-data (cdr gnus-newsgroup-data)
1988                 gnus-newsgroup-data-reverse nil)
1989           (when offset
1990             (gnus-data-update-list gnus-newsgroup-data offset)))
1991       (while (cdr data)
1992         (when (= (gnus-data-number (cadr data)) article)
1993           (setcdr data (cddr data))
1994           (when offset
1995             (gnus-data-update-list (cdr data) offset))
1996           (setq data nil
1997                 gnus-newsgroup-data-reverse nil))
1998         (setq data (cdr data))))))
1999
2000 (defmacro gnus-data-list (backward)
2001   `(if ,backward
2002        (or gnus-newsgroup-data-reverse
2003            (setq gnus-newsgroup-data-reverse
2004                  (reverse gnus-newsgroup-data)))
2005      gnus-newsgroup-data))
2006
2007 (defun gnus-data-update-list (data offset)
2008   "Add OFFSET to the POS of all data entries in DATA."
2009   (while data
2010     (setcar (nthcdr 2 (car data)) (+ offset (nth 2 (car data))))
2011     (setq data (cdr data))))
2012
2013 (defun gnus-data-compute-positions ()
2014   "Compute the positions of all articles."
2015   (let ((data gnus-newsgroup-data)
2016         pos)
2017     (while data
2018       (when (setq pos (text-property-any
2019                        (point-min) (point-max)
2020                        'gnus-number (gnus-data-number (car data))))
2021         (gnus-data-set-pos (car data) (+ pos 3)))
2022       (setq data (cdr data)))))
2023
2024 (defun gnus-summary-article-pseudo-p (article)
2025   "Say whether this article is a pseudo article or not."
2026   (not (vectorp (gnus-data-header (gnus-data-find article)))))
2027
2028 (defmacro gnus-summary-article-sparse-p (article)
2029   "Say whether this article is a sparse article or not."
2030   `(memq ,article gnus-newsgroup-sparse))
2031
2032 (defmacro gnus-summary-article-ancient-p (article)
2033   "Say whether this article is a sparse article or not."
2034   `(memq ,article gnus-newsgroup-ancient))
2035
2036 (defun gnus-article-parent-p (number)
2037   "Say whether this article is a parent or not."
2038   (let ((data (gnus-data-find-list number)))
2039     (and (cdr data)                     ; There has to be an article after...
2040          (< (gnus-data-level (car data)) ; And it has to have a higher level.
2041             (gnus-data-level (nth 1 data))))))
2042
2043 (defun gnus-article-children (number)
2044   "Return a list of all children to NUMBER."
2045   (let* ((data (gnus-data-find-list number))
2046          (level (gnus-data-level (car data)))
2047          children)
2048     (setq data (cdr data))
2049     (while (and data
2050                 (= (gnus-data-level (car data)) (1+ level)))
2051       (push (gnus-data-number (car data)) children)
2052       (setq data (cdr data)))
2053     children))
2054
2055 (defmacro gnus-summary-skip-intangible ()
2056   "If the current article is intangible, then jump to a different article."
2057   '(let ((to (get-text-property (point) 'gnus-intangible)))
2058      (and to (gnus-summary-goto-subject to))))
2059
2060 (defmacro gnus-summary-article-intangible-p ()
2061   "Say whether this article is intangible or not."
2062   '(get-text-property (point) 'gnus-intangible))
2063
2064 (defun gnus-article-read-p (article)
2065   "Say whether ARTICLE is read or not."
2066   (not (or (memq article gnus-newsgroup-marked)
2067            (memq article gnus-newsgroup-unreads)
2068            (memq article gnus-newsgroup-unselected)
2069            (memq article gnus-newsgroup-dormant))))
2070
2071 ;; Some summary mode macros.
2072
2073 (defmacro gnus-summary-article-number ()
2074   "The article number of the article on the current line.
2075 If there isn's an article number here, then we return the current
2076 article number."
2077   '(progn
2078      (gnus-summary-skip-intangible)
2079      (or (get-text-property (point) 'gnus-number)
2080          (gnus-summary-last-subject))))
2081
2082 (defmacro gnus-summary-article-header (&optional number)
2083   `(gnus-data-header (gnus-data-find
2084                       ,(or number '(gnus-summary-article-number)))))
2085
2086 (defmacro gnus-summary-thread-level (&optional number)
2087   `(if (and (eq gnus-summary-make-false-root 'dummy)
2088             (get-text-property (point) 'gnus-intangible))
2089        0
2090      (gnus-data-level (gnus-data-find
2091                        ,(or number '(gnus-summary-article-number))))))
2092
2093 (defmacro gnus-summary-article-mark (&optional number)
2094   `(gnus-data-mark (gnus-data-find
2095                     ,(or number '(gnus-summary-article-number)))))
2096
2097 (defmacro gnus-summary-article-pos (&optional number)
2098   `(gnus-data-pos (gnus-data-find
2099                    ,(or number '(gnus-summary-article-number)))))
2100
2101 (defalias 'gnus-summary-subject-string 'gnus-summary-article-subject)
2102 (defmacro gnus-summary-article-subject (&optional number)
2103   "Return current subject string or nil if nothing."
2104   `(let ((headers
2105           ,(if number
2106                `(gnus-data-header (assq ,number gnus-newsgroup-data))
2107              '(gnus-data-header (assq (gnus-summary-article-number)
2108                                       gnus-newsgroup-data)))))
2109      (and headers
2110           (vectorp headers)
2111           (mail-header-subject headers))))
2112
2113 (defmacro gnus-summary-article-score (&optional number)
2114   "Return current article score."
2115   `(or (cdr (assq ,(or number '(gnus-summary-article-number))
2116                   gnus-newsgroup-scored))
2117        gnus-summary-default-score 0))
2118
2119 (defun gnus-summary-article-children (&optional number)
2120   (let* ((data (gnus-data-find-list (or number (gnus-summary-article-number))))
2121          (level (gnus-data-level (car data)))
2122          l children)
2123     (while (and (setq data (cdr data))
2124                 (> (setq l (gnus-data-level (car data))) level))
2125       (and (= (1+ level) l)
2126            (push (gnus-data-number (car data))
2127                  children)))
2128     (nreverse children)))
2129
2130 (defun gnus-summary-article-parent (&optional number)
2131   (let* ((data (gnus-data-find-list (or number (gnus-summary-article-number))
2132                                     (gnus-data-list t)))
2133          (level (gnus-data-level (car data))))
2134     (if (zerop level)
2135         ()                              ; This is a root.
2136       ;; We search until we find an article with a level less than
2137       ;; this one.  That function has to be the parent.
2138       (while (and (setq data (cdr data))
2139                   (not (< (gnus-data-level (car data)) level))))
2140       (and data (gnus-data-number (car data))))))
2141
2142 (defun gnus-unread-mark-p (mark)
2143   "Say whether MARK is the unread mark."
2144   (= mark gnus-unread-mark))
2145
2146 (defun gnus-read-mark-p (mark)
2147   "Say whether MARK is one of the marks that mark as read.
2148 This is all marks except unread, ticked, dormant, and expirable."
2149   (not (or (= mark gnus-unread-mark)
2150            (= mark gnus-ticked-mark)
2151            (= mark gnus-dormant-mark)
2152            (= mark gnus-expirable-mark))))
2153
2154 (defmacro gnus-article-mark (number)
2155   `(cond
2156     ((memq ,number gnus-newsgroup-unreads) gnus-unread-mark)
2157     ((memq ,number gnus-newsgroup-marked) gnus-ticked-mark)
2158     ((memq ,number gnus-newsgroup-dormant) gnus-dormant-mark)
2159     ((memq ,number gnus-newsgroup-expirable) gnus-expirable-mark)
2160     (t (or (cdr (assq ,number gnus-newsgroup-reads))
2161            gnus-ancient-mark))))
2162
2163 ;; Saving hidden threads.
2164
2165 (put 'gnus-save-hidden-threads 'lisp-indent-function 0)
2166 (put 'gnus-save-hidden-threads 'edebug-form-spec '(body))
2167
2168 (defmacro gnus-save-hidden-threads (&rest forms)
2169   "Save hidden threads, eval FORMS, and restore the hidden threads."
2170   (let ((config (make-symbol "config")))
2171     `(let ((,config (gnus-hidden-threads-configuration)))
2172        (unwind-protect
2173            (save-excursion
2174              ,@forms)
2175          (gnus-restore-hidden-threads-configuration ,config)))))
2176
2177 (defun gnus-hidden-threads-configuration ()
2178   "Return the current hidden threads configuration."
2179   (save-excursion
2180     (let (config)
2181       (goto-char (point-min))
2182       (while (search-forward "\r" nil t)
2183         (push (1- (point)) config))
2184       config)))
2185
2186 (defun gnus-restore-hidden-threads-configuration (config)
2187   "Restore hidden threads configuration from CONFIG."
2188   (let (point buffer-read-only)
2189     (while (setq point (pop config))
2190       (when (and (< point (point-max))
2191                  (goto-char point)
2192                  (= (following-char) ?\n))
2193         (subst-char-in-region point (1+ point) ?\n ?\r)))))
2194
2195 ;; Various summary mode internalish functions.
2196
2197 (defun gnus-mouse-pick-article (e)
2198   (interactive "e")
2199   (mouse-set-point e)
2200   (gnus-summary-next-page nil t))
2201
2202 (defun gnus-summary-set-display-table ()
2203   ;; Change the display table.  Odd characters have a tendency to mess
2204   ;; up nicely formatted displays - we make all possible glyphs
2205   ;; display only a single character.
2206
2207   ;; We start from the standard display table, if any.
2208   (let ((table (or (copy-sequence standard-display-table)
2209                    (make-display-table)))
2210         (i 32))
2211     ;; Nix out all the control chars...
2212     (while (>= (setq i (1- i)) 0)
2213       (aset table i [??]))
2214     ;; ... but not newline and cr, of course.  (cr is necessary for the
2215     ;; selective display).
2216     (aset table ?\n nil)
2217     (aset table ?\r nil)
2218     ;; We nix out any glyphs over 126 that are not set already.
2219     (let ((i 256))
2220       (while (>= (setq i (1- i)) 127)
2221         ;; Only modify if the entry is nil.
2222         (unless (aref table i)
2223           (aset table i [??]))))
2224     (setq buffer-display-table table)))
2225
2226 (defun gnus-summary-setup-buffer (group)
2227   "Initialize summary buffer."
2228   (let ((buffer (concat "*Summary " group "*")))
2229     (if (get-buffer buffer)
2230         (progn
2231           (set-buffer buffer)
2232           (setq gnus-summary-buffer (current-buffer))
2233           (not gnus-newsgroup-prepared))
2234       ;; Fix by Sudish Joseph <joseph@cis.ohio-state.edu>
2235       (setq gnus-summary-buffer (set-buffer (get-buffer-create buffer)))
2236       (gnus-add-current-to-buffer-list)
2237       (gnus-summary-mode group)
2238       (when gnus-carpal
2239         (gnus-carpal-setup-buffer 'summary))
2240       (unless gnus-single-article-buffer
2241         (make-local-variable 'gnus-article-buffer)
2242         (make-local-variable 'gnus-article-current)
2243         (make-local-variable 'gnus-original-article-buffer))
2244       (setq gnus-newsgroup-name group)
2245       t)))
2246
2247 (defun gnus-set-global-variables ()
2248   ;; Set the global equivalents of the summary buffer-local variables
2249   ;; to the latest values they had.  These reflect the summary buffer
2250   ;; that was in action when the last article was fetched.
2251   (when (eq major-mode 'gnus-summary-mode)
2252     (setq gnus-summary-buffer (current-buffer))
2253     (let ((name gnus-newsgroup-name)
2254           (marked gnus-newsgroup-marked)
2255           (unread gnus-newsgroup-unreads)
2256           (headers gnus-current-headers)
2257           (data gnus-newsgroup-data)
2258           (summary gnus-summary-buffer)
2259           (article-buffer gnus-article-buffer)
2260           (original gnus-original-article-buffer)
2261           (gac gnus-article-current)
2262           (reffed gnus-reffed-article-number)
2263           (score-file gnus-current-score-file))
2264       (save-excursion
2265         (set-buffer gnus-group-buffer)
2266         (setq gnus-newsgroup-name name)
2267         (setq gnus-newsgroup-marked marked)
2268         (setq gnus-newsgroup-unreads unread)
2269         (setq gnus-current-headers headers)
2270         (setq gnus-newsgroup-data data)
2271         (setq gnus-article-current gac)
2272         (setq gnus-summary-buffer summary)
2273         (setq gnus-article-buffer article-buffer)
2274         (setq gnus-original-article-buffer original)
2275         (setq gnus-reffed-article-number reffed)
2276         (setq gnus-current-score-file score-file)
2277         ;; The article buffer also has local variables.
2278         (when (gnus-buffer-live-p gnus-article-buffer)
2279           (set-buffer gnus-article-buffer)
2280           (setq gnus-summary-buffer summary))))))
2281
2282 (defun gnus-summary-article-unread-p (article)
2283   "Say whether ARTICLE is unread or not."
2284   (memq article gnus-newsgroup-unreads))
2285
2286 (defun gnus-summary-first-article-p (&optional article)
2287   "Return whether ARTICLE is the first article in the buffer."
2288   (if (not (setq article (or article (gnus-summary-article-number))))
2289       nil
2290     (eq article (caar gnus-newsgroup-data))))
2291
2292 (defun gnus-summary-last-article-p (&optional article)
2293   "Return whether ARTICLE is the last article in the buffer."
2294   (if (not (setq article (or article (gnus-summary-article-number))))
2295       t         ; All non-existent numbers are the last article.  :-)
2296     (not (cdr (gnus-data-find-list article)))))
2297
2298 (defun gnus-make-thread-indent-array ()
2299   (let ((n 200))
2300     (unless (and gnus-thread-indent-array
2301                  (= gnus-thread-indent-level gnus-thread-indent-array-level))
2302       (setq gnus-thread-indent-array (make-vector 201 "")
2303             gnus-thread-indent-array-level gnus-thread-indent-level)
2304       (while (>= n 0)
2305         (aset gnus-thread-indent-array n
2306               (make-string (* n gnus-thread-indent-level) ? ))
2307         (setq n (1- n))))))
2308
2309 (defun gnus-update-summary-mark-positions ()
2310   "Compute where the summary marks are to go."
2311   (save-excursion
2312     (when (and gnus-summary-buffer
2313                (get-buffer gnus-summary-buffer)
2314                (buffer-name (get-buffer gnus-summary-buffer)))
2315       (set-buffer gnus-summary-buffer))
2316     (let ((gnus-replied-mark 129)
2317           (gnus-score-below-mark 130)
2318           (gnus-score-over-mark 130)
2319           (spec gnus-summary-line-format-spec)
2320           thread gnus-visual pos)
2321       (save-excursion
2322         (gnus-set-work-buffer)
2323         (let ((gnus-summary-line-format-spec spec))
2324           (gnus-summary-insert-line
2325            [0 "" "" "" "" "" 0 0 ""]  0 nil 128 t nil "" nil 1)
2326           (goto-char (point-min))
2327           (setq pos (list (cons 'unread (and (search-forward "\200" nil t)
2328                                              (- (point) 2)))))
2329           (goto-char (point-min))
2330           (push (cons 'replied (and (search-forward "\201" nil t)
2331                                     (- (point) 2)))
2332                 pos)
2333           (goto-char (point-min))
2334           (push (cons 'score (and (search-forward "\202" nil t) (- (point) 2)))
2335                 pos)))
2336       (setq gnus-summary-mark-positions pos))))
2337
2338 (defun gnus-summary-insert-dummy-line (gnus-tmp-subject gnus-tmp-number)
2339   "Insert a dummy root in the summary buffer."
2340   (beginning-of-line)
2341   (gnus-add-text-properties
2342    (point) (progn (eval gnus-summary-dummy-line-format-spec) (point))
2343    (list 'gnus-number gnus-tmp-number 'gnus-intangible gnus-tmp-number)))
2344
2345 (defun gnus-summary-insert-line (gnus-tmp-header
2346                                  gnus-tmp-level gnus-tmp-current
2347                                  gnus-tmp-unread gnus-tmp-replied
2348                                  gnus-tmp-expirable gnus-tmp-subject-or-nil
2349                                  &optional gnus-tmp-dummy gnus-tmp-score
2350                                  gnus-tmp-process)
2351   (let* ((gnus-tmp-indentation (aref gnus-thread-indent-array gnus-tmp-level))
2352          (gnus-tmp-lines (mail-header-lines gnus-tmp-header))
2353          (gnus-tmp-score (or gnus-tmp-score gnus-summary-default-score 0))
2354          (gnus-tmp-score-char
2355           (if (or (null gnus-summary-default-score)
2356                   (<= (abs (- gnus-tmp-score gnus-summary-default-score))
2357                       gnus-summary-zcore-fuzz))
2358               ? 
2359             (if (< gnus-tmp-score gnus-summary-default-score)
2360                 gnus-score-below-mark gnus-score-over-mark)))
2361          (gnus-tmp-replied
2362           (cond (gnus-tmp-process gnus-process-mark)
2363                 ((memq gnus-tmp-current gnus-newsgroup-cached)
2364                  gnus-cached-mark)
2365                 (gnus-tmp-replied gnus-replied-mark)
2366                 ((memq gnus-tmp-current gnus-newsgroup-saved)
2367                  gnus-saved-mark)
2368                 (t gnus-unread-mark)))
2369          (gnus-tmp-from (mail-header-from gnus-tmp-header))
2370          (gnus-tmp-name
2371           (cond
2372            ((string-match "<[^>]+> *$" gnus-tmp-from)
2373             (let ((beg (match-beginning 0)))
2374               (or (and (string-match "^\"[^\"]*\"" gnus-tmp-from)
2375                        (substring gnus-tmp-from (1+ (match-beginning 0))
2376                                   (1- (match-end 0))))
2377                   (substring gnus-tmp-from 0 beg))))
2378            ((string-match "(.+)" gnus-tmp-from)
2379             (substring gnus-tmp-from
2380                        (1+ (match-beginning 0)) (1- (match-end 0))))
2381            (t gnus-tmp-from)))
2382          (gnus-tmp-subject (mail-header-subject gnus-tmp-header))
2383          (gnus-tmp-number (mail-header-number gnus-tmp-header))
2384          (gnus-tmp-opening-bracket (if gnus-tmp-dummy ?\< ?\[))
2385          (gnus-tmp-closing-bracket (if gnus-tmp-dummy ?\> ?\]))
2386          (buffer-read-only nil))
2387     (when (string= gnus-tmp-name "")
2388       (setq gnus-tmp-name gnus-tmp-from))
2389     (unless (numberp gnus-tmp-lines)
2390       (setq gnus-tmp-lines 0))
2391     (gnus-put-text-property
2392      (point)
2393      (progn (eval gnus-summary-line-format-spec) (point))
2394      'gnus-number gnus-tmp-number)
2395     (when (gnus-visual-p 'summary-highlight 'highlight)
2396       (forward-line -1)
2397       (run-hooks 'gnus-summary-update-hook)
2398       (forward-line 1))))
2399
2400 (defun gnus-summary-update-line (&optional dont-update)
2401   ;; Update summary line after change.
2402   (when (and gnus-summary-default-score
2403              (not gnus-summary-inhibit-highlight))
2404     (let* ((gnus-summary-inhibit-highlight t) ; Prevent recursion.
2405            (article (gnus-summary-article-number))
2406            (score (gnus-summary-article-score article)))
2407       (unless dont-update
2408         (if (and gnus-summary-mark-below
2409                  (< (gnus-summary-article-score)
2410                     gnus-summary-mark-below))
2411             ;; This article has a low score, so we mark it as read.
2412             (when (memq article gnus-newsgroup-unreads)
2413               (gnus-summary-mark-article-as-read gnus-low-score-mark))
2414           (when (eq (gnus-summary-article-mark) gnus-low-score-mark)
2415             ;; This article was previously marked as read on account
2416             ;; of a low score, but now it has risen, so we mark it as
2417             ;; unread.
2418             (gnus-summary-mark-article-as-unread gnus-unread-mark)))
2419         (gnus-summary-update-mark
2420          (if (or (null gnus-summary-default-score)
2421                  (<= (abs (- score gnus-summary-default-score))
2422                      gnus-summary-zcore-fuzz))
2423              ? 
2424            (if (< score gnus-summary-default-score)
2425                gnus-score-below-mark gnus-score-over-mark))
2426          'score))
2427       ;; Do visual highlighting.
2428       (when (gnus-visual-p 'summary-highlight 'highlight)
2429         (run-hooks 'gnus-summary-update-hook)))))
2430
2431 (defvar gnus-tmp-new-adopts nil)
2432
2433 (defun gnus-summary-number-of-articles-in-thread (thread &optional level char)
2434   "Return the number of articles in THREAD.
2435 This may be 0 in some cases -- if none of the articles in
2436 the thread are to be displayed."
2437   (let* ((number
2438           ;; Fix by Luc Van Eycken <Luc.VanEycken@esat.kuleuven.ac.be>.
2439           (cond
2440            ((not (listp thread))
2441             1)
2442            ((and (consp thread) (cdr thread))
2443             (apply
2444              '+ 1 (mapcar
2445                    'gnus-summary-number-of-articles-in-thread (cdr thread))))
2446            ((null thread)
2447             1)
2448            ((memq (mail-header-number (car thread)) gnus-newsgroup-limit)
2449             1)
2450            (t 0))))
2451     (when (and level (zerop level) gnus-tmp-new-adopts)
2452       (incf number
2453             (apply '+ (mapcar
2454                        'gnus-summary-number-of-articles-in-thread
2455                        gnus-tmp-new-adopts))))
2456     (if char
2457         (if (> number 1) gnus-not-empty-thread-mark
2458           gnus-empty-thread-mark)
2459       number)))
2460
2461 (defun gnus-summary-set-local-parameters (group)
2462   "Go through the local params of GROUP and set all variable specs in that list."
2463   (let ((params (gnus-group-find-parameter group))
2464         elem)
2465     (while params
2466       (setq elem (car params)
2467             params (cdr params))
2468       (and (consp elem)                 ; Has to be a cons.
2469            (consp (cdr elem))           ; The cdr has to be a list.
2470            (symbolp (car elem))         ; Has to be a symbol in there.
2471            (not (memq (car elem)
2472                       '(quit-config to-address to-list to-group)))
2473            (ignore-errors               ; So we set it.
2474              (make-local-variable (car elem))
2475              (set (car elem) (eval (nth 1 elem))))))))
2476
2477 (defun gnus-summary-read-group (group &optional show-all no-article
2478                                       kill-buffer no-display)
2479   "Start reading news in newsgroup GROUP.
2480 If SHOW-ALL is non-nil, already read articles are also listed.
2481 If NO-ARTICLE is non-nil, no article is selected initially.
2482 If NO-DISPLAY, don't generate a summary buffer."
2483   (let (result)
2484     (while (and group
2485                 (null (setq result
2486                             (let ((gnus-auto-select-next nil))
2487                               (gnus-summary-read-group-1
2488                                group show-all no-article
2489                                kill-buffer no-display))))
2490                 (eq gnus-auto-select-next 'quietly))
2491       (set-buffer gnus-group-buffer)
2492       (if (not (equal group (gnus-group-group-name)))
2493           (setq group (gnus-group-group-name))
2494         (setq group nil)))
2495     result))
2496
2497 (defun gnus-summary-read-group-1 (group show-all no-article
2498                                         kill-buffer no-display)
2499   ;; Killed foreign groups can't be entered.
2500   (when (and (not (gnus-group-native-p group))
2501              (not (gnus-gethash group gnus-newsrc-hashtb)))
2502     (error "Dead non-native groups can't be entered"))
2503   (gnus-message 5 "Retrieving newsgroup: %s..." group)
2504   (let* ((new-group (gnus-summary-setup-buffer group))
2505          (quit-config (gnus-group-quit-config group))
2506          (did-select (and new-group (gnus-select-newsgroup group show-all))))
2507     (cond
2508      ;; This summary buffer exists already, so we just select it.
2509      ((not new-group)
2510       (gnus-set-global-variables)
2511       (when kill-buffer
2512         (gnus-kill-or-deaden-summary kill-buffer))
2513       (gnus-configure-windows 'summary 'force)
2514       (gnus-set-mode-line 'summary)
2515       (gnus-summary-position-point)
2516       (message "")
2517       t)
2518      ;; We couldn't select this group.
2519      ((null did-select)
2520       (when (and (eq major-mode 'gnus-summary-mode)
2521                  (not (equal (current-buffer) kill-buffer)))
2522         (kill-buffer (current-buffer))
2523         (if (not quit-config)
2524             (progn
2525               (set-buffer gnus-group-buffer)
2526               (gnus-group-jump-to-group group)
2527               (gnus-group-next-unread-group 1))
2528           (gnus-handle-ephemeral-exit quit-config)))
2529       (gnus-message 3 "Can't select group")
2530       nil)
2531      ;; The user did a `C-g' while prompting for number of articles,
2532      ;; so we exit this group.
2533      ((eq did-select 'quit)
2534       (and (eq major-mode 'gnus-summary-mode)
2535            (not (equal (current-buffer) kill-buffer))
2536            (kill-buffer (current-buffer)))
2537       (when kill-buffer
2538         (gnus-kill-or-deaden-summary kill-buffer))
2539       (if (not quit-config)
2540           (progn
2541             (set-buffer gnus-group-buffer)
2542             (gnus-group-jump-to-group group)
2543             (gnus-group-next-unread-group 1)
2544             (gnus-configure-windows 'group 'force))
2545         (gnus-handle-ephemeral-exit quit-config))
2546       ;; Finally signal the quit.
2547       (signal 'quit nil))
2548      ;; The group was successfully selected.
2549      (t
2550       (gnus-set-global-variables)
2551       ;; Save the active value in effect when the group was entered.
2552       (setq gnus-newsgroup-active
2553             (gnus-copy-sequence
2554              (gnus-active gnus-newsgroup-name)))
2555       ;; You can change the summary buffer in some way with this hook.
2556       (run-hooks 'gnus-select-group-hook)
2557       ;; Set any local variables in the group parameters.
2558       (gnus-summary-set-local-parameters gnus-newsgroup-name)
2559       (gnus-update-format-specifications
2560        nil 'summary 'summary-mode 'summary-dummy)
2561       ;; Do score processing.
2562       (when gnus-use-scoring
2563         (gnus-possibly-score-headers))
2564       ;; Check whether to fill in the gaps in the threads.
2565       (when gnus-build-sparse-threads
2566         (gnus-build-sparse-threads))
2567       ;; Find the initial limit.
2568       (if gnus-show-threads
2569           (if show-all
2570               (let ((gnus-newsgroup-dormant nil))
2571                 (gnus-summary-initial-limit show-all))
2572             (gnus-summary-initial-limit show-all))
2573         (setq gnus-newsgroup-limit
2574               (mapcar
2575                (lambda (header) (mail-header-number header))
2576                gnus-newsgroup-headers)))
2577       ;; Generate the summary buffer.
2578       (unless no-display
2579         (gnus-summary-prepare))
2580       (when gnus-use-trees
2581         (gnus-tree-open group)
2582         (setq gnus-summary-highlight-line-function
2583               'gnus-tree-highlight-article))
2584       ;; If the summary buffer is empty, but there are some low-scored
2585       ;; articles or some excluded dormants, we include these in the
2586       ;; buffer.
2587       (when (and (zerop (buffer-size))
2588                  (not no-display))
2589         (cond (gnus-newsgroup-dormant
2590                (gnus-summary-limit-include-dormant))
2591               ((and gnus-newsgroup-scored show-all)
2592                (gnus-summary-limit-include-expunged t))))
2593       ;; Function `gnus-apply-kill-file' must be called in this hook.
2594       (run-hooks 'gnus-apply-kill-hook)
2595       (if (and (zerop (buffer-size))
2596                (not no-display))
2597           (progn
2598             ;; This newsgroup is empty.
2599             (gnus-summary-catchup-and-exit nil t)
2600             (gnus-message 6 "No unread news")
2601             (when kill-buffer
2602               (gnus-kill-or-deaden-summary kill-buffer))
2603             ;; Return nil from this function.
2604             nil)
2605         ;; Hide conversation thread subtrees.  We cannot do this in
2606         ;; gnus-summary-prepare-hook since kill processing may not
2607         ;; work with hidden articles.
2608         (and gnus-show-threads
2609              gnus-thread-hide-subtree
2610              (gnus-summary-hide-all-threads))
2611         ;; Show first unread article if requested.
2612         (if (and (not no-article)
2613                  (not no-display)
2614                  gnus-newsgroup-unreads
2615                  gnus-auto-select-first)
2616             (unless (if (eq gnus-auto-select-first 'best)
2617                         (gnus-summary-best-unread-article)
2618                       (gnus-summary-first-unread-article))
2619               (gnus-configure-windows 'summary))
2620           ;; Don't select any articles, just move point to the first
2621           ;; article in the group.
2622           (goto-char (point-min))
2623           (gnus-summary-position-point)
2624           (gnus-set-mode-line 'summary)
2625           (gnus-configure-windows 'summary 'force))
2626         (when kill-buffer
2627           (gnus-kill-or-deaden-summary kill-buffer))
2628         (when (get-buffer-window gnus-group-buffer t)
2629           ;; Gotta use windows, because recenter does weird stuff if
2630           ;; the current buffer ain't the displayed window.
2631           (let ((owin (selected-window)))
2632             (select-window (get-buffer-window gnus-group-buffer t))
2633             (when (gnus-group-goto-group group)
2634               (recenter))
2635             (select-window owin)))
2636         ;; Mark this buffer as "prepared".
2637         (setq gnus-newsgroup-prepared t)
2638         t)))))
2639
2640 (defun gnus-summary-prepare ()
2641   "Generate the summary buffer."
2642   (interactive)
2643   (let ((buffer-read-only nil))
2644     (erase-buffer)
2645     (setq gnus-newsgroup-data nil
2646           gnus-newsgroup-data-reverse nil)
2647     (run-hooks 'gnus-summary-generate-hook)
2648     ;; Generate the buffer, either with threads or without.
2649     (when gnus-newsgroup-headers
2650       (gnus-summary-prepare-threads
2651        (if gnus-show-threads
2652            (gnus-sort-gathered-threads
2653             (funcall gnus-summary-thread-gathering-function
2654                      (gnus-sort-threads
2655                       (gnus-cut-threads (gnus-make-threads)))))
2656          ;; Unthreaded display.
2657          (gnus-sort-articles gnus-newsgroup-headers))))
2658     (setq gnus-newsgroup-data (nreverse gnus-newsgroup-data))
2659     ;; Call hooks for modifying summary buffer.
2660     (goto-char (point-min))
2661     (run-hooks 'gnus-summary-prepare-hook)))
2662
2663 (defsubst gnus-general-simplify-subject (subject)
2664   "Simply subject by the same rules as gnus-gather-threads-by-subject."
2665   (setq subject
2666         (cond
2667          ;; Truncate the subject.
2668          ((numberp gnus-summary-gather-subject-limit)
2669           (setq subject (gnus-simplify-subject-re subject))
2670           (if (> (length subject) gnus-summary-gather-subject-limit)
2671               (substring subject 0 gnus-summary-gather-subject-limit)
2672             subject))
2673          ;; Fuzzily simplify it.
2674          ((eq 'fuzzy gnus-summary-gather-subject-limit)
2675           (gnus-simplify-subject-fuzzy subject))
2676          ;; Just remove the leading "Re:".
2677          (t
2678           (gnus-simplify-subject-re subject))))
2679
2680   (if (and gnus-summary-gather-exclude-subject
2681            (string-match gnus-summary-gather-exclude-subject subject))
2682       nil                               ; This article shouldn't be gathered
2683     subject))
2684
2685 (defun gnus-summary-simplify-subject-query ()
2686   "Query where the respool algorithm would put this article."
2687   (interactive)
2688   (gnus-set-global-variables)
2689   (gnus-summary-select-article)
2690   (message (gnus-general-simplify-subject (gnus-summary-article-subject))))
2691
2692 (defun gnus-gather-threads-by-subject (threads)
2693   "Gather threads by looking at Subject headers."
2694   (if (not gnus-summary-make-false-root)
2695       threads
2696     (let ((hashtb (gnus-make-hashtable 1024))
2697           (prev threads)
2698           (result threads)
2699           subject hthread whole-subject)
2700       (while threads
2701         (setq subject (gnus-general-simplify-subject
2702                        (setq whole-subject (mail-header-subject
2703                                             (caar threads)))))
2704         (when subject
2705           (if (setq hthread (gnus-gethash subject hashtb))
2706               (progn
2707                 ;; We enter a dummy root into the thread, if we
2708                 ;; haven't done that already.
2709                 (unless (stringp (caar hthread))
2710                   (setcar hthread (list whole-subject (car hthread))))
2711                 ;; We add this new gathered thread to this gathered
2712                 ;; thread.
2713                 (setcdr (car hthread)
2714                         (nconc (cdar hthread) (list (car threads))))
2715                 ;; Remove it from the list of threads.
2716                 (setcdr prev (cdr threads))
2717                 (setq threads prev))
2718             ;; Enter this thread into the hash table.
2719             (gnus-sethash subject threads hashtb)))
2720         (setq prev threads)
2721         (setq threads (cdr threads)))
2722       result)))
2723
2724 (defun gnus-gather-threads-by-references (threads)
2725   "Gather threads by looking at References headers."
2726   (let ((idhashtb (gnus-make-hashtable 1024))
2727         (thhashtb (gnus-make-hashtable 1024))
2728         (prev threads)
2729         (result threads)
2730         ids references id gthread gid entered ref)
2731     (while threads
2732       (when (setq references (mail-header-references (caar threads)))
2733         (setq id (mail-header-id (caar threads))
2734               ids (gnus-split-references references)
2735               entered nil)
2736         (while (setq ref (pop ids))
2737           (setq ids (delete ref ids))
2738           (if (not (setq gid (gnus-gethash ref idhashtb)))
2739               (progn
2740                 (gnus-sethash ref id idhashtb)
2741                 (gnus-sethash id threads thhashtb))
2742             (setq gthread (gnus-gethash gid thhashtb))
2743             (unless entered
2744               ;; We enter a dummy root into the thread, if we
2745               ;; haven't done that already.
2746               (unless (stringp (caar gthread))
2747                 (setcar gthread (list (mail-header-subject (caar gthread))
2748                                       (car gthread))))
2749               ;; We add this new gathered thread to this gathered
2750               ;; thread.
2751               (setcdr (car gthread)
2752                       (nconc (cdar gthread) (list (car threads)))))
2753             ;; Add it into the thread hash table.
2754             (gnus-sethash id gthread thhashtb)
2755             (setq entered t)
2756             ;; Remove it from the list of threads.
2757             (setcdr prev (cdr threads))
2758             (setq threads prev))))
2759       (setq prev threads)
2760       (setq threads (cdr threads)))
2761     result))
2762
2763 (defun gnus-sort-gathered-threads (threads)
2764   "Sort subtreads inside each gathered thread by article number."
2765   (let ((result threads))
2766     (while threads
2767       (when (stringp (caar threads))
2768         (setcdr (car threads)
2769                 (sort (cdar threads) 'gnus-thread-sort-by-number)))
2770       (setq threads (cdr threads)))
2771     result))
2772
2773 (defun gnus-thread-loop-p (root thread)
2774   "Say whether ROOT is in THREAD."
2775   (let ((stack (list thread))
2776         (infloop 0)
2777         th)
2778     (while (setq thread (pop stack))
2779       (setq th (cdr thread))
2780       (while (and th
2781                   (not (eq (caar th) root)))
2782         (pop th))
2783       (if th
2784           ;; We have found a loop.
2785           (let (ref-dep)
2786             (setcdr thread (delq (car th) (cdr thread)))
2787             (if (boundp (setq ref-dep (intern "none"
2788                                               gnus-newsgroup-dependencies)))
2789                 (setcdr (symbol-value ref-dep)
2790                         (nconc (cdr (symbol-value ref-dep))
2791                                (list (car th))))
2792               (set ref-dep (list nil (car th))))
2793             (setq infloop 1
2794                   stack nil))
2795         ;; Push all the subthreads onto the stack.
2796         (push (cdr thread) stack)))
2797     infloop))
2798
2799 (defun gnus-make-threads ()
2800   "Go through the dependency hashtb and find the roots.  Return all threads."
2801   (let (threads)
2802     (while (catch 'infloop
2803              (mapatoms
2804               (lambda (refs)
2805                 ;; Deal with self-referencing References loops.
2806                 (when (and (car (symbol-value refs))
2807                            (not (zerop
2808                                  (apply
2809                                   '+
2810                                   (mapcar
2811                                    (lambda (thread)
2812                                      (gnus-thread-loop-p
2813                                       (car (symbol-value refs)) thread))
2814                                    (cdr (symbol-value refs)))))))
2815                   (setq threads nil)
2816                   (throw 'infloop t))
2817                 (unless (car (symbol-value refs))
2818                   ;; These threads do not refer back to any other articles,
2819                   ;; so they're roots.
2820                   (setq threads (append (cdr (symbol-value refs)) threads))))
2821               gnus-newsgroup-dependencies)))
2822     threads))
2823
2824 (defun gnus-build-sparse-threads ()
2825   (let ((headers gnus-newsgroup-headers)
2826         (deps gnus-newsgroup-dependencies)
2827         header references generation relations
2828         cthread subject child end pthread relation)
2829     ;; First we create an alist of generations/relations, where
2830     ;; generations is how much we trust the relation, and the relation
2831     ;; is parent/child.
2832     (gnus-message 7 "Making sparse threads...")
2833     (save-excursion
2834       (nnheader-set-temp-buffer " *gnus sparse threads*")
2835       (while (setq header (pop headers))
2836         (when (and (setq references (mail-header-references header))
2837                    (not (string= references "")))
2838           (insert references)
2839           (setq child (mail-header-id header)
2840                 subject (mail-header-subject header))
2841           (setq generation 0)
2842           (while (search-backward ">" nil t)
2843             (setq end (1+ (point)))
2844             (when (search-backward "<" nil t)
2845               (push (list (incf generation)
2846                           child (setq child (buffer-substring (point) end))
2847                           subject)
2848                     relations)))
2849           (push (list (1+ generation) child nil subject) relations)
2850           (erase-buffer)))
2851       (kill-buffer (current-buffer)))
2852     ;; Sort over trustworthiness.
2853     (setq relations (sort relations (lambda (r1 r2) (< (car r1) (car r2)))))
2854     (while (setq relation (pop relations))
2855       (when (if (boundp (setq cthread (intern (cadr relation) deps)))
2856                 (unless (car (symbol-value cthread))
2857                   ;; Make this article the parent of these threads.
2858                   (setcar (symbol-value cthread)
2859                           (vector gnus-reffed-article-number
2860                                   (cadddr relation)
2861                                   "" ""
2862                                   (cadr relation)
2863                                   (or (caddr relation) "") 0 0 "")))
2864               (set cthread (list (vector gnus-reffed-article-number
2865                                          (cadddr relation)
2866                                          "" "" (cadr relation)
2867                                          (or (caddr relation) "") 0 0 ""))))
2868         (push gnus-reffed-article-number gnus-newsgroup-limit)
2869         (push gnus-reffed-article-number gnus-newsgroup-sparse)
2870         (push (cons gnus-reffed-article-number gnus-sparse-mark)
2871               gnus-newsgroup-reads)
2872         (decf gnus-reffed-article-number)
2873         ;; Make this new thread the child of its parent.
2874         (if (boundp (setq pthread (intern (or (caddr relation) "none") deps)))
2875             (setcdr (symbol-value pthread)
2876                     (nconc (cdr (symbol-value pthread))
2877                            (list (symbol-value cthread))))
2878           (set pthread (list nil (symbol-value cthread))))))
2879     (gnus-message 7 "Making sparse threads...done")))
2880
2881 (defun gnus-build-old-threads ()
2882   ;; Look at all the articles that refer back to old articles, and
2883   ;; fetch the headers for the articles that aren't there.  This will
2884   ;; build complete threads - if the roots haven't been expired by the
2885   ;; server, that is.
2886   (let (id heads)
2887     (mapatoms
2888      (lambda (refs)
2889        (when (not (car (symbol-value refs)))
2890          (setq heads (cdr (symbol-value refs)))
2891          (while heads
2892            (if (memq (mail-header-number (caar heads))
2893                      gnus-newsgroup-dormant)
2894                (setq heads (cdr heads))
2895              (setq id (symbol-name refs))
2896              (while (and (setq id (gnus-build-get-header id))
2897                          (not (car (gnus-gethash
2898                                     id gnus-newsgroup-dependencies)))))
2899              (setq heads nil)))))
2900      gnus-newsgroup-dependencies)))
2901
2902 (defun gnus-build-get-header (id)
2903   ;; Look through the buffer of NOV lines and find the header to
2904   ;; ID.  Enter this line into the dependencies hash table, and return
2905   ;; the id of the parent article (if any).
2906   (let ((deps gnus-newsgroup-dependencies)
2907         found header)
2908     (prog1
2909         (save-excursion
2910           (set-buffer nntp-server-buffer)
2911           (let ((case-fold-search nil))
2912             (goto-char (point-min))
2913             (while (and (not found)
2914                         (search-forward id nil t))
2915               (beginning-of-line)
2916               (setq found (looking-at
2917                            (format "^[^\t]*\t[^\t]*\t[^\t]*\t[^\t]*\t%s"
2918                                    (regexp-quote id))))
2919               (or found (beginning-of-line 2)))
2920             (when found
2921               (beginning-of-line)
2922               (and
2923                (setq header (gnus-nov-parse-line
2924                              (read (current-buffer)) deps))
2925                (gnus-parent-id (mail-header-references header))))))
2926       (when header
2927         (let ((number (mail-header-number header)))
2928           (push number gnus-newsgroup-limit)
2929           (push header gnus-newsgroup-headers)
2930           (if (memq number gnus-newsgroup-unselected)
2931               (progn
2932                 (push number gnus-newsgroup-unreads)
2933                 (setq gnus-newsgroup-unselected
2934                       (delq number gnus-newsgroup-unselected)))
2935             (push number gnus-newsgroup-ancient)))))))
2936
2937 (defun gnus-summary-update-article-line (article header)
2938   "Update the line for ARTICLE using HEADERS."
2939   (let* ((id (mail-header-id header))
2940          (thread (gnus-id-to-thread id)))
2941     (unless thread
2942       (error "Article in no thread"))
2943     ;; Update the thread.
2944     (setcar thread header)
2945     (gnus-summary-goto-subject article)
2946     (let* ((datal (gnus-data-find-list article))
2947            (data (car datal))
2948            (length (when (cdr datal)
2949                      (- (gnus-data-pos data)
2950                         (gnus-data-pos (cadr datal)))))
2951            (buffer-read-only nil)
2952            (level (gnus-summary-thread-level)))
2953       (gnus-delete-line)
2954       (gnus-summary-insert-line
2955        header level nil (gnus-article-mark article)
2956        (memq article gnus-newsgroup-replied)
2957        (memq article gnus-newsgroup-expirable)
2958        ;; Only insert the Subject string when it's different
2959        ;; from the previous Subject string.
2960        (if (gnus-subject-equal
2961             (condition-case ()
2962                 (mail-header-subject
2963                  (gnus-data-header
2964                   (cadr
2965                    (gnus-data-find-list
2966                     article
2967                     (gnus-data-list t)))))
2968               ;; Error on the side of excessive subjects.
2969               (error ""))
2970             (mail-header-subject header))
2971            ""
2972          (mail-header-subject header))
2973        nil (cdr (assq article gnus-newsgroup-scored))
2974        (memq article gnus-newsgroup-processable))
2975       (when length
2976         (gnus-data-update-list
2977          (cdr datal) (- length (- (gnus-data-pos data) (point))))))))
2978
2979 (defun gnus-summary-update-article (article &optional iheader)
2980   "Update ARTICLE in the summary buffer."
2981   (set-buffer gnus-summary-buffer)
2982   (let* ((header (or iheader (gnus-summary-article-header article)))
2983          (id (mail-header-id header))
2984          (data (gnus-data-find article))
2985          (thread (gnus-id-to-thread id))
2986          (references (mail-header-references header))
2987          (parent
2988           (gnus-id-to-thread
2989            (or (gnus-parent-id
2990                 (when (and references
2991                            (not (equal "" references)))
2992                   references))
2993                "none")))
2994          (buffer-read-only nil)
2995          (old (car thread))
2996          (number (mail-header-number header))
2997          pos)
2998     (when thread
2999       ;; !!! Should this be in or not?
3000       (unless iheader
3001         (setcar thread nil))
3002       (when parent
3003         (delq thread parent))
3004       (if (gnus-summary-insert-subject id header iheader)
3005           ;; Set the (possibly) new article number in the data structure.
3006           (gnus-data-set-number data (gnus-id-to-article id))
3007         (setcar thread old)
3008         nil))))
3009
3010 (defun gnus-rebuild-thread (id)
3011   "Rebuild the thread containing ID."
3012   (let ((buffer-read-only nil)
3013         old-pos current thread data)
3014     (if (not gnus-show-threads)
3015         (setq thread (list (car (gnus-id-to-thread id))))
3016       ;; Get the thread this article is part of.
3017       (setq thread (gnus-remove-thread id)))
3018     (setq old-pos (gnus-point-at-bol))
3019     (setq current (save-excursion
3020                     (and (zerop (forward-line -1))
3021                          (gnus-summary-article-number))))
3022     ;; If this is a gathered thread, we have to go some re-gathering.
3023     (when (stringp (car thread))
3024       (let ((subject (car thread))
3025             roots thr)
3026         (setq thread (cdr thread))
3027         (while thread
3028           (unless (memq (setq thr (gnus-id-to-thread
3029                                    (gnus-root-id
3030                                     (mail-header-id (caar thread)))))
3031                         roots)
3032             (push thr roots))
3033           (setq thread (cdr thread)))
3034         ;; We now have all (unique) roots.
3035         (if (= (length roots) 1)
3036             ;; All the loose roots are now one solid root.
3037             (setq thread (car roots))
3038           (setq thread (cons subject (gnus-sort-threads roots))))))
3039     (let (threads)
3040       ;; We then insert this thread into the summary buffer.
3041       (let (gnus-newsgroup-data gnus-newsgroup-threads)
3042         (if gnus-show-threads
3043             (gnus-summary-prepare-threads (gnus-cut-threads (list thread)))
3044           (gnus-summary-prepare-unthreaded thread))
3045         (setq data (nreverse gnus-newsgroup-data))
3046         (setq threads gnus-newsgroup-threads))
3047       ;; We splice the new data into the data structure.
3048       (gnus-data-enter-list current data (- (point) old-pos))
3049       (setq gnus-newsgroup-threads (nconc threads gnus-newsgroup-threads)))))
3050
3051 (defun gnus-number-to-header (number)
3052   "Return the header for article NUMBER."
3053   (let ((headers gnus-newsgroup-headers))
3054     (while (and headers
3055                 (not (= number (mail-header-number (car headers)))))
3056       (pop headers))
3057     (when headers
3058       (car headers))))
3059
3060 (defun gnus-parent-headers (headers &optional generation)
3061   "Return the headers of the GENERATIONeth parent of HEADERS."
3062   (unless generation
3063     (setq generation 1))
3064   (let ((parent t)
3065         references)
3066     (while (and parent headers (not (zerop generation)))
3067       (setq references (mail-header-references headers))
3068       (when (and references
3069                  (setq parent (gnus-parent-id references))
3070                  (setq headers (car (gnus-id-to-thread parent))))
3071         (decf generation)))
3072     headers))
3073
3074 (defun gnus-id-to-thread (id)
3075   "Return the (sub-)thread where ID appears."
3076   (gnus-gethash id gnus-newsgroup-dependencies))
3077
3078 (defun gnus-id-to-article (id)
3079   "Return the article number of ID."
3080   (let ((thread (gnus-id-to-thread id)))
3081     (when (and thread
3082                (car thread))
3083       (mail-header-number (car thread)))))
3084
3085 (defun gnus-id-to-header (id)
3086   "Return the article headers of ID."
3087   (car (gnus-id-to-thread id)))
3088
3089 (defun gnus-article-displayed-root-p (article)
3090   "Say whether ARTICLE is a root(ish) article."
3091   (let ((level (gnus-summary-thread-level article))
3092         (refs (mail-header-references  (gnus-summary-article-header article)))
3093         particle)
3094     (cond
3095      ((null level) nil)
3096      ((zerop level) t)
3097      ((null refs) t)
3098      ((null (gnus-parent-id refs)) t)
3099      ((and (= 1 level)
3100            (null (setq particle (gnus-id-to-article
3101                                  (gnus-parent-id refs))))
3102            (null (gnus-summary-thread-level particle)))))))
3103
3104 (defun gnus-root-id (id)
3105   "Return the id of the root of the thread where ID appears."
3106   (let (last-id prev)
3107     (while (and id (setq prev (car (gnus-gethash
3108                                     id gnus-newsgroup-dependencies))))
3109       (setq last-id id
3110             id (gnus-parent-id (mail-header-references prev))))
3111     last-id))
3112
3113 (defun gnus-remove-thread (id &optional dont-remove)
3114   "Remove the thread that has ID in it."
3115   (let ((dep gnus-newsgroup-dependencies)
3116         headers thread last-id)
3117     ;; First go up in this thread until we find the root.
3118     (setq last-id (gnus-root-id id))
3119     (setq headers (list (car (gnus-id-to-thread last-id))
3120                         (caadr (gnus-id-to-thread last-id))))
3121     ;; We have now found the real root of this thread.  It might have
3122     ;; been gathered into some loose thread, so we have to search
3123     ;; through the threads to find the thread we wanted.
3124     (let ((threads gnus-newsgroup-threads)
3125           sub)
3126       (while threads
3127         (setq sub (car threads))
3128         (if (stringp (car sub))
3129             ;; This is a gathered thread, so we look at the roots
3130             ;; below it to find whether this article is in this
3131             ;; gathered root.
3132             (progn
3133               (setq sub (cdr sub))
3134               (while sub
3135                 (when (member (caar sub) headers)
3136                   (setq thread (car threads)
3137                         threads nil
3138                         sub nil))
3139                 (setq sub (cdr sub))))
3140           ;; It's an ordinary thread, so we check it.
3141           (when (eq (car sub) (car headers))
3142             (setq thread sub
3143                   threads nil)))
3144         (setq threads (cdr threads)))
3145       ;; If this article is in no thread, then it's a root.
3146       (if thread
3147           (unless dont-remove
3148             (setq gnus-newsgroup-threads (delq thread gnus-newsgroup-threads)))
3149         (setq thread (gnus-gethash last-id dep)))
3150       (when thread
3151         (prog1
3152             thread                      ; We return this thread.
3153           (unless dont-remove
3154             (if (stringp (car thread))
3155                 (progn
3156                   ;; If we use dummy roots, then we have to remove the
3157                   ;; dummy root as well.
3158                   (when (eq gnus-summary-make-false-root 'dummy)
3159                     (gnus-delete-line)
3160                     (gnus-data-compute-positions))
3161                   (setq thread (cdr thread))
3162                   (while thread
3163                     (gnus-remove-thread-1 (car thread))
3164                     (setq thread (cdr thread))))
3165               (gnus-remove-thread-1 thread))))))))
3166
3167 (defun gnus-remove-thread-1 (thread)
3168   "Remove the thread THREAD recursively."
3169   (let ((number (mail-header-number (pop thread)))
3170         d)
3171     (setq thread (reverse thread))
3172     (while thread
3173       (gnus-remove-thread-1 (pop thread)))
3174     (when (setq d (gnus-data-find number))
3175       (goto-char (gnus-data-pos d))
3176       (gnus-data-remove
3177        number
3178        (- (gnus-point-at-bol)
3179           (prog1
3180               (1+ (gnus-point-at-eol))
3181             (gnus-delete-line)))))))
3182
3183 (defun gnus-sort-threads (threads)
3184   "Sort THREADS."
3185   (if (not gnus-thread-sort-functions)
3186       threads
3187     (gnus-message 7 "Sorting threads...")
3188     (prog1
3189         (sort threads (gnus-make-sort-function gnus-thread-sort-functions))
3190       (gnus-message 7 "Sorting threads...done"))))
3191
3192 (defun gnus-sort-articles (articles)
3193   "Sort ARTICLES."
3194   (when gnus-article-sort-functions
3195     (gnus-message 7 "Sorting articles...")
3196     (prog1
3197         (setq gnus-newsgroup-headers
3198               (sort articles (gnus-make-sort-function
3199                               gnus-article-sort-functions)))
3200       (gnus-message 7 "Sorting articles...done"))))
3201
3202 ;; Written by Hallvard B Furuseth <h.b.furuseth@usit.uio.no>.
3203 (defmacro gnus-thread-header (thread)
3204   ;; Return header of first article in THREAD.
3205   ;; Note that THREAD must never, ever be anything else than a variable -
3206   ;; using some other form will lead to serious barfage.
3207   (or (symbolp thread) (signal 'wrong-type-argument '(symbolp thread)))
3208   ;; (8% speedup to gnus-summary-prepare, just for fun :-)
3209   (list 'byte-code "\10\211:\203\17\0\211@;\203\16\0A@@\207" ;
3210         (vector thread) 2))
3211
3212 (defsubst gnus-article-sort-by-number (h1 h2)
3213   "Sort articles by article number."
3214   (< (mail-header-number h1)
3215      (mail-header-number h2)))
3216
3217 (defun gnus-thread-sort-by-number (h1 h2)
3218   "Sort threads by root article number."
3219   (gnus-article-sort-by-number
3220    (gnus-thread-header h1) (gnus-thread-header h2)))
3221
3222 (defsubst gnus-article-sort-by-lines (h1 h2)
3223   "Sort articles by article Lines header."
3224   (< (mail-header-lines h1)
3225      (mail-header-lines h2)))
3226
3227 (defun gnus-thread-sort-by-lines (h1 h2)
3228   "Sort threads by root article Lines header."
3229   (gnus-article-sort-by-lines
3230    (gnus-thread-header h1) (gnus-thread-header h2)))
3231
3232 (defsubst gnus-article-sort-by-author (h1 h2)
3233   "Sort articles by root author."
3234   (string-lessp
3235    (let ((extract (funcall
3236                    gnus-extract-address-components
3237                    (mail-header-from h1))))
3238      (or (car extract) (cadr extract) ""))
3239    (let ((extract (funcall
3240                    gnus-extract-address-components
3241                    (mail-header-from h2))))
3242      (or (car extract) (cadr extract) ""))))
3243
3244 (defun gnus-thread-sort-by-author (h1 h2)
3245   "Sort threads by root author."
3246   (gnus-article-sort-by-author
3247    (gnus-thread-header h1)  (gnus-thread-header h2)))
3248
3249 (defsubst gnus-article-sort-by-subject (h1 h2)
3250   "Sort articles by root subject."
3251   (string-lessp
3252    (downcase (gnus-simplify-subject-re (mail-header-subject h1)))
3253    (downcase (gnus-simplify-subject-re (mail-header-subject h2)))))
3254
3255 (defun gnus-thread-sort-by-subject (h1 h2)
3256   "Sort threads by root subject."
3257   (gnus-article-sort-by-subject
3258    (gnus-thread-header h1) (gnus-thread-header h2)))
3259
3260 (defsubst gnus-article-sort-by-date (h1 h2)
3261   "Sort articles by root article date."
3262   (gnus-time-less
3263    (gnus-date-get-time (mail-header-date h1))
3264    (gnus-date-get-time (mail-header-date h2))))
3265
3266 (defun gnus-thread-sort-by-date (h1 h2)
3267   "Sort threads by root article date."
3268   (gnus-article-sort-by-date
3269    (gnus-thread-header h1) (gnus-thread-header h2)))
3270
3271 (defsubst gnus-article-sort-by-score (h1 h2)
3272   "Sort articles by root article score.
3273 Unscored articles will be counted as having a score of zero."
3274   (> (or (cdr (assq (mail-header-number h1)
3275                     gnus-newsgroup-scored))
3276          gnus-summary-default-score 0)
3277      (or (cdr (assq (mail-header-number h2)
3278                     gnus-newsgroup-scored))
3279          gnus-summary-default-score 0)))
3280
3281 (defun gnus-thread-sort-by-score (h1 h2)
3282   "Sort threads by root article score."
3283   (gnus-article-sort-by-score
3284    (gnus-thread-header h1) (gnus-thread-header h2)))
3285
3286 (defun gnus-thread-sort-by-total-score (h1 h2)
3287   "Sort threads by the sum of all scores in the thread.
3288 Unscored articles will be counted as having a score of zero."
3289   (> (gnus-thread-total-score h1) (gnus-thread-total-score h2)))
3290
3291 (defun gnus-thread-total-score (thread)
3292   ;;  This function find the total score of THREAD.
3293   (cond ((null thread)
3294          0)
3295         ((consp thread)
3296          (if (stringp (car thread))
3297              (apply gnus-thread-score-function 0
3298                     (mapcar 'gnus-thread-total-score-1 (cdr thread)))
3299            (gnus-thread-total-score-1 thread)))
3300         (t
3301          (gnus-thread-total-score-1 (list thread)))))
3302
3303 (defun gnus-thread-total-score-1 (root)
3304   ;; This function find the total score of the thread below ROOT.
3305   (setq root (car root))
3306   (apply gnus-thread-score-function
3307          (or (append
3308               (mapcar 'gnus-thread-total-score
3309                       (cdr (gnus-gethash (mail-header-id root)
3310                                          gnus-newsgroup-dependencies)))
3311               (when (> (mail-header-number root) 0)
3312                 (list (or (cdr (assq (mail-header-number root)
3313                                      gnus-newsgroup-scored))
3314                           gnus-summary-default-score 0))))
3315              (list gnus-summary-default-score)
3316              '(0))))
3317
3318 ;; Added by Per Abrahamsen <amanda@iesd.auc.dk>.
3319 (defvar gnus-tmp-prev-subject nil)
3320 (defvar gnus-tmp-false-parent nil)
3321 (defvar gnus-tmp-root-expunged nil)
3322 (defvar gnus-tmp-dummy-line nil)
3323
3324 (defun gnus-summary-prepare-threads (threads)
3325   "Prepare summary buffer from THREADS and indentation LEVEL.
3326 THREADS is either a list of `(PARENT [(CHILD1 [(GRANDCHILD ...]...) ...])'
3327 or a straight list of headers."
3328   (gnus-message 7 "Generating summary...")
3329
3330   (setq gnus-newsgroup-threads threads)
3331   (beginning-of-line)
3332
3333   (let ((gnus-tmp-level 0)
3334         (default-score (or gnus-summary-default-score 0))
3335         (gnus-visual-p (gnus-visual-p 'summary-highlight 'highlight))
3336         thread number subject stack state gnus-tmp-gathered beg-match
3337         new-roots gnus-tmp-new-adopts thread-end
3338         gnus-tmp-header gnus-tmp-unread
3339         gnus-tmp-replied gnus-tmp-subject-or-nil
3340         gnus-tmp-dummy gnus-tmp-indentation gnus-tmp-lines gnus-tmp-score
3341         gnus-tmp-score-char gnus-tmp-from gnus-tmp-name
3342         gnus-tmp-number gnus-tmp-opening-bracket gnus-tmp-closing-bracket)
3343
3344     (setq gnus-tmp-prev-subject nil)
3345
3346     (if (vectorp (car threads))
3347         ;; If this is a straight (sic) list of headers, then a
3348         ;; threaded summary display isn't required, so we just create
3349         ;; an unthreaded one.
3350         (gnus-summary-prepare-unthreaded threads)
3351
3352       ;; Do the threaded display.
3353
3354       (while (or threads stack gnus-tmp-new-adopts new-roots)
3355
3356         (if (and (= gnus-tmp-level 0)
3357                  (not (setq gnus-tmp-dummy-line nil))
3358                  (or (not stack)
3359                      (= (caar stack) 0))
3360                  (not gnus-tmp-false-parent)
3361                  (or gnus-tmp-new-adopts new-roots))
3362             (if gnus-tmp-new-adopts
3363                 (setq gnus-tmp-level (if gnus-tmp-root-expunged 0 1)
3364                       thread (list (car gnus-tmp-new-adopts))
3365                       gnus-tmp-header (caar thread)
3366                       gnus-tmp-new-adopts (cdr gnus-tmp-new-adopts))
3367               (when new-roots
3368                 (setq thread (list (car new-roots))
3369                       gnus-tmp-header (caar thread)
3370                       new-roots (cdr new-roots))))
3371
3372           (if threads
3373               ;; If there are some threads, we do them before the
3374               ;; threads on the stack.
3375               (setq thread threads
3376                     gnus-tmp-header (caar thread))
3377             ;; There were no current threads, so we pop something off
3378             ;; the stack.
3379             (setq state (car stack)
3380                   gnus-tmp-level (car state)
3381                   thread (cdr state)
3382                   stack (cdr stack)
3383                   gnus-tmp-header (caar thread))))
3384
3385         (setq gnus-tmp-false-parent nil)
3386         (setq gnus-tmp-root-expunged nil)
3387         (setq thread-end nil)
3388
3389         (if (stringp gnus-tmp-header)
3390             ;; The header is a dummy root.
3391             (cond
3392              ((eq gnus-summary-make-false-root 'adopt)
3393               ;; We let the first article adopt the rest.
3394               (setq gnus-tmp-new-adopts (nconc gnus-tmp-new-adopts
3395                                                (cddar thread)))
3396               (setq gnus-tmp-gathered
3397                     (nconc (mapcar
3398                             (lambda (h) (mail-header-number (car h)))
3399                             (cddar thread))
3400                            gnus-tmp-gathered))
3401               (setq thread (cons (list (caar thread)
3402                                        (cadar thread))
3403                                  (cdr thread)))
3404               (setq gnus-tmp-level -1
3405                     gnus-tmp-false-parent t))
3406              ((eq gnus-summary-make-false-root 'empty)
3407               ;; We print adopted articles with empty subject fields.
3408               (setq gnus-tmp-gathered
3409                     (nconc (mapcar
3410                             (lambda (h) (mail-header-number (car h)))
3411                             (cddar thread))
3412                            gnus-tmp-gathered))
3413               (setq gnus-tmp-level -1))
3414              ((eq gnus-summary-make-false-root 'dummy)
3415               ;; We remember that we probably want to output a dummy
3416               ;; root.
3417               (setq gnus-tmp-dummy-line gnus-tmp-header)
3418               (setq gnus-tmp-prev-subject gnus-tmp-header))
3419              (t
3420               ;; We do not make a root for the gathered
3421               ;; sub-threads at all.
3422               (setq gnus-tmp-level -1)))
3423
3424           (setq number (mail-header-number gnus-tmp-header)
3425                 subject (mail-header-subject gnus-tmp-header))
3426
3427           (cond
3428            ;; If the thread has changed subject, we might want to make
3429            ;; this subthread into a root.
3430            ((and (null gnus-thread-ignore-subject)
3431                  (not (zerop gnus-tmp-level))
3432                  gnus-tmp-prev-subject
3433                  (not (inline
3434                         (gnus-subject-equal gnus-tmp-prev-subject subject))))
3435             (setq new-roots (nconc new-roots (list (car thread)))
3436                   thread-end t
3437                   gnus-tmp-header nil))
3438            ;; If the article lies outside the current limit,
3439            ;; then we do not display it.
3440            ((not (memq number gnus-newsgroup-limit))
3441             (setq gnus-tmp-gathered
3442                   (nconc (mapcar
3443                           (lambda (h) (mail-header-number (car h)))
3444                           (cdar thread))
3445                          gnus-tmp-gathered))
3446             (setq gnus-tmp-new-adopts (if (cdar thread)
3447                                           (append gnus-tmp-new-adopts
3448                                                   (cdar thread))
3449                                         gnus-tmp-new-adopts)
3450                   thread-end t
3451                   gnus-tmp-header nil)
3452             (when (zerop gnus-tmp-level)
3453               (setq gnus-tmp-root-expunged t)))
3454            ;; Perhaps this article is to be marked as read?
3455            ((and gnus-summary-mark-below
3456                  (< (or (cdr (assq number gnus-newsgroup-scored))
3457                         default-score)
3458                     gnus-summary-mark-below)
3459                  ;; Don't touch sparse articles.
3460                  (not (gnus-summary-article-sparse-p number))
3461                  (not (gnus-summary-article-ancient-p number)))
3462             (setq gnus-newsgroup-unreads
3463                   (delq number gnus-newsgroup-unreads))
3464             (if gnus-newsgroup-auto-expire
3465                 (push number gnus-newsgroup-expirable)
3466               (push (cons number gnus-low-score-mark)
3467                     gnus-newsgroup-reads))))
3468
3469           (when gnus-tmp-header
3470             ;; We may have an old dummy line to output before this
3471             ;; article.
3472             (when gnus-tmp-dummy-line
3473               (gnus-summary-insert-dummy-line
3474                gnus-tmp-dummy-line (mail-header-number gnus-tmp-header))
3475               (setq gnus-tmp-dummy-line nil))
3476
3477             ;; Compute the mark.
3478             (setq gnus-tmp-unread (gnus-article-mark number))
3479
3480             (push (gnus-data-make number gnus-tmp-unread (1+ (point))
3481                                   gnus-tmp-header gnus-tmp-level)
3482                   gnus-newsgroup-data)
3483
3484             ;; Actually insert the line.
3485             (setq
3486              gnus-tmp-subject-or-nil
3487              (cond
3488               ((and gnus-thread-ignore-subject
3489                     gnus-tmp-prev-subject
3490                     (not (inline (gnus-subject-equal
3491                                   gnus-tmp-prev-subject subject))))
3492                subject)
3493               ((zerop gnus-tmp-level)
3494                (if (and (eq gnus-summary-make-false-root 'empty)
3495                         (memq number gnus-tmp-gathered)
3496                         gnus-tmp-prev-subject
3497                         (inline (gnus-subject-equal
3498                                  gnus-tmp-prev-subject subject)))
3499                    gnus-summary-same-subject
3500                  subject))
3501               (t gnus-summary-same-subject)))
3502             (if (and (eq gnus-summary-make-false-root 'adopt)
3503                      (= gnus-tmp-level 1)
3504                      (memq number gnus-tmp-gathered))
3505                 (setq gnus-tmp-opening-bracket ?\<
3506                       gnus-tmp-closing-bracket ?\>)
3507               (setq gnus-tmp-opening-bracket ?\[
3508                     gnus-tmp-closing-bracket ?\]))
3509             (setq
3510              gnus-tmp-indentation
3511              (aref gnus-thread-indent-array gnus-tmp-level)
3512              gnus-tmp-lines (mail-header-lines gnus-tmp-header)
3513              gnus-tmp-score (or (cdr (assq number gnus-newsgroup-scored))
3514                                 gnus-summary-default-score 0)
3515              gnus-tmp-score-char
3516              (if (or (null gnus-summary-default-score)
3517                      (<= (abs (- gnus-tmp-score gnus-summary-default-score))
3518                          gnus-summary-zcore-fuzz))
3519                  ? 
3520                (if (< gnus-tmp-score gnus-summary-default-score)
3521                    gnus-score-below-mark gnus-score-over-mark))
3522              gnus-tmp-replied
3523              (cond ((memq number gnus-newsgroup-processable)
3524                     gnus-process-mark)
3525                    ((memq number gnus-newsgroup-cached)
3526                     gnus-cached-mark)
3527                    ((memq number gnus-newsgroup-replied)
3528                     gnus-replied-mark)
3529                    ((memq number gnus-newsgroup-saved)
3530                     gnus-saved-mark)
3531                    (t gnus-unread-mark))
3532              gnus-tmp-from (mail-header-from gnus-tmp-header)
3533              gnus-tmp-name
3534              (cond
3535               ((string-match "<[^>]+> *$" gnus-tmp-from)
3536                (setq beg-match (match-beginning 0))
3537                (or (and (string-match "^\"[^\"]*\"" gnus-tmp-from)
3538                         (substring gnus-tmp-from (1+ (match-beginning 0))
3539                                    (1- (match-end 0))))
3540                    (substring gnus-tmp-from 0 beg-match)))
3541               ((string-match "(.+)" gnus-tmp-from)
3542                (substring gnus-tmp-from
3543                           (1+ (match-beginning 0)) (1- (match-end 0))))
3544               (t gnus-tmp-from)))
3545             (when (string= gnus-tmp-name "")
3546               (setq gnus-tmp-name gnus-tmp-from))
3547             (unless (numberp gnus-tmp-lines)
3548               (setq gnus-tmp-lines 0))
3549             (gnus-put-text-property
3550              (point)
3551              (progn (eval gnus-summary-line-format-spec) (point))
3552              'gnus-number number)
3553             (when gnus-visual-p
3554               (forward-line -1)
3555               (run-hooks 'gnus-summary-update-hook)
3556               (forward-line 1))
3557
3558             (setq gnus-tmp-prev-subject subject)))
3559
3560         (when (nth 1 thread)
3561           (push (cons (max 0 gnus-tmp-level) (nthcdr 1 thread)) stack))
3562         (incf gnus-tmp-level)
3563         (setq threads (if thread-end nil (cdar thread)))
3564         (unless threads
3565           (setq gnus-tmp-level 0)))))
3566   (gnus-message 7 "Generating summary...done"))
3567
3568 (defun gnus-summary-prepare-unthreaded (headers)
3569   "Generate an unthreaded summary buffer based on HEADERS."
3570   (let (header number mark)
3571
3572     (beginning-of-line)
3573
3574     (while headers
3575       ;; We may have to root out some bad articles...
3576       (when (memq (setq number (mail-header-number
3577                                 (setq header (pop headers))))
3578                   gnus-newsgroup-limit)
3579         ;; Mark article as read when it has a low score.
3580         (when (and gnus-summary-mark-below
3581                    (< (or (cdr (assq number gnus-newsgroup-scored))
3582                           gnus-summary-default-score 0)
3583                       gnus-summary-mark-below)
3584                    (not (gnus-summary-article-ancient-p number)))
3585           (setq gnus-newsgroup-unreads
3586                 (delq number gnus-newsgroup-unreads))
3587           (if gnus-newsgroup-auto-expire
3588               (push number gnus-newsgroup-expirable)
3589             (push (cons number gnus-low-score-mark)
3590                   gnus-newsgroup-reads)))
3591
3592         (setq mark (gnus-article-mark number))
3593         (push (gnus-data-make number mark (1+ (point)) header 0)
3594               gnus-newsgroup-data)
3595         (gnus-summary-insert-line
3596          header 0 number
3597          mark (memq number gnus-newsgroup-replied)
3598          (memq number gnus-newsgroup-expirable)
3599          (mail-header-subject header) nil
3600          (cdr (assq number gnus-newsgroup-scored))
3601          (memq number gnus-newsgroup-processable))))))
3602
3603 (defun gnus-select-newsgroup (group &optional read-all)
3604   "Select newsgroup GROUP.
3605 If READ-ALL is non-nil, all articles in the group are selected."
3606   (let* ((entry (gnus-gethash group gnus-newsrc-hashtb))
3607          ;;!!! Dirty hack; should be removed.
3608          (gnus-summary-ignore-duplicates
3609           (if (eq (car (gnus-find-method-for-group group)) 'nnvirtual)
3610               t
3611             gnus-summary-ignore-duplicates))
3612          (info (nth 2 entry))
3613          articles fetched-articles cached)
3614
3615     (unless (gnus-check-server
3616              (setq gnus-current-select-method
3617                    (gnus-find-method-for-group group)))
3618       (error "Couldn't open server"))
3619
3620     (or (and entry (not (eq (car entry) t))) ; Either it's active...
3621         (gnus-activate-group group)     ; Or we can activate it...
3622         (progn                          ; Or we bug out.
3623           (when (equal major-mode 'gnus-summary-mode)
3624             (kill-buffer (current-buffer)))
3625           (error "Couldn't request group %s: %s"
3626                  group (gnus-status-message group))))
3627
3628     (unless (gnus-request-group group t)
3629       (when (equal major-mode 'gnus-summary-mode)
3630         (kill-buffer (current-buffer)))
3631       (error "Couldn't request group %s: %s"
3632              group (gnus-status-message group)))
3633
3634     (setq gnus-newsgroup-name group)
3635     (setq gnus-newsgroup-unselected nil)
3636     (setq gnus-newsgroup-unreads (gnus-list-of-unread-articles group))
3637
3638     ;; Adjust and set lists of article marks.
3639     (when info
3640       (gnus-adjust-marked-articles info))
3641
3642     ;; Kludge to avoid having cached articles nixed out in virtual groups.
3643     (when (gnus-virtual-group-p group)
3644       (setq cached gnus-newsgroup-cached))
3645
3646     (setq gnus-newsgroup-unreads
3647           (gnus-set-difference
3648            (gnus-set-difference gnus-newsgroup-unreads gnus-newsgroup-marked)
3649            gnus-newsgroup-dormant))
3650
3651     (setq gnus-newsgroup-processable nil)
3652
3653     (gnus-update-read-articles group gnus-newsgroup-unreads)
3654     (unless (gnus-ephemeral-group-p gnus-newsgroup-name)
3655       (gnus-group-update-group group))
3656
3657     (setq articles (gnus-articles-to-read group read-all))
3658
3659     (cond
3660      ((null articles)
3661       ;;(gnus-message 3 "Couldn't select newsgroup -- no articles to display")
3662       'quit)
3663      ((eq articles 0) nil)
3664      (t
3665       ;; Init the dependencies hash table.
3666       (setq gnus-newsgroup-dependencies
3667             (gnus-make-hashtable (length articles)))
3668       ;; Retrieve the headers and read them in.
3669       (gnus-message 5 "Fetching headers for %s..." gnus-newsgroup-name)
3670       (setq gnus-newsgroup-headers
3671             (if (eq 'nov
3672                     (setq gnus-headers-retrieved-by
3673                           (gnus-retrieve-headers
3674                            articles gnus-newsgroup-name
3675                            ;; We might want to fetch old headers, but
3676                            ;; not if there is only 1 article.
3677                            (and gnus-fetch-old-headers
3678                                 (or (and
3679                                      (not (eq gnus-fetch-old-headers 'some))
3680                                      (not (numberp gnus-fetch-old-headers)))
3681                                     (> (length articles) 1))))))
3682                 (gnus-get-newsgroup-headers-xover
3683                  articles nil nil gnus-newsgroup-name t)
3684               (gnus-get-newsgroup-headers)))
3685       (gnus-message 5 "Fetching headers for %s...done" gnus-newsgroup-name)
3686
3687       ;; Kludge to avoid having cached articles nixed out in virtual groups.
3688       (when cached
3689         (setq gnus-newsgroup-cached cached))
3690
3691       ;; Suppress duplicates?
3692       (when gnus-suppress-duplicates
3693         (gnus-dup-suppress-articles))
3694
3695       ;; Set the initial limit.
3696       (setq gnus-newsgroup-limit (copy-sequence articles))
3697       ;; Remove canceled articles from the list of unread articles.
3698       (setq gnus-newsgroup-unreads
3699             (gnus-set-sorted-intersection
3700              gnus-newsgroup-unreads
3701              (setq fetched-articles
3702                    (mapcar (lambda (headers) (mail-header-number headers))
3703                            gnus-newsgroup-headers))))
3704       ;; Removed marked articles that do not exist.
3705       (gnus-update-missing-marks
3706        (gnus-sorted-complement fetched-articles articles))
3707       ;; We might want to build some more threads first.
3708       (and gnus-fetch-old-headers
3709            (eq gnus-headers-retrieved-by 'nov)
3710            (gnus-build-old-threads))
3711       ;; Check whether auto-expire is to be done in this group.
3712       (setq gnus-newsgroup-auto-expire
3713             (gnus-group-auto-expirable-p group))
3714       ;; Set up the article buffer now, if necessary.
3715       (unless gnus-single-article-buffer
3716         (gnus-article-setup-buffer))
3717       ;; First and last article in this newsgroup.
3718       (when gnus-newsgroup-headers
3719         (setq gnus-newsgroup-begin
3720               (mail-header-number (car gnus-newsgroup-headers))
3721               gnus-newsgroup-end
3722               (mail-header-number
3723                (gnus-last-element gnus-newsgroup-headers))))
3724       ;; GROUP is successfully selected.
3725       (or gnus-newsgroup-headers t)))))
3726
3727 (defun gnus-articles-to-read (group &optional read-all)
3728   ;; Find out what articles the user wants to read.
3729   (let* ((articles
3730           ;; Select all articles if `read-all' is non-nil, or if there
3731           ;; are no unread articles.
3732           (if (or read-all
3733                   (and (zerop (length gnus-newsgroup-marked))
3734                        (zerop (length gnus-newsgroup-unreads)))
3735                   (eq (gnus-group-find-parameter group 'display)
3736                       'all))
3737               (gnus-uncompress-range (gnus-active group))
3738             (sort (append gnus-newsgroup-dormant gnus-newsgroup-marked
3739                           (copy-sequence gnus-newsgroup-unreads))
3740                   '<)))
3741          (scored-list (gnus-killed-articles gnus-newsgroup-killed articles))
3742          (scored (length scored-list))
3743          (number (length articles))
3744          (marked (+ (length gnus-newsgroup-marked)
3745                     (length gnus-newsgroup-dormant)))
3746          (select
3747           (cond
3748            ((numberp read-all)
3749             read-all)
3750            (t
3751             (condition-case ()
3752                 (cond
3753                  ((and (or (<= scored marked) (= scored number))
3754                        (numberp gnus-large-newsgroup)
3755                        (> number gnus-large-newsgroup))
3756                   (let ((input
3757                          (read-string
3758                           (format
3759                            "How many articles from %s (default %d): "
3760                            (gnus-limit-string gnus-newsgroup-name 35)
3761                            number))))
3762                     (if (string-match "^[ \t]*$" input) number input)))
3763                  ((and (> scored marked) (< scored number)
3764                        (> (- scored number) 20))
3765                   (let ((input
3766                          (read-string
3767                           (format "%s %s (%d scored, %d total): "
3768                                   "How many articles from"
3769                                   group scored number))))
3770                     (if (string-match "^[ \t]*$" input)
3771                         number input)))
3772                  (t number))
3773               (quit nil))))))
3774     (setq select (if (stringp select) (string-to-number select) select))
3775     (if (or (null select) (zerop select))
3776         select
3777       (if (and (not (zerop scored)) (<= (abs select) scored))
3778           (progn
3779             (setq articles (sort scored-list '<))
3780             (setq number (length articles)))
3781         (setq articles (copy-sequence articles)))
3782
3783       (when (< (abs select) number)
3784         (if (< select 0)
3785             ;; Select the N oldest articles.
3786             (setcdr (nthcdr (1- (abs select)) articles) nil)
3787           ;; Select the N most recent articles.
3788           (setq articles (nthcdr (- number select) articles))))
3789       (setq gnus-newsgroup-unselected
3790             (gnus-sorted-intersection
3791              gnus-newsgroup-unreads
3792              (gnus-sorted-complement gnus-newsgroup-unreads articles)))
3793       articles)))
3794
3795 (defun gnus-killed-articles (killed articles)
3796   (let (out)
3797     (while articles
3798       (when (inline (gnus-member-of-range (car articles) killed))
3799         (push (car articles) out))
3800       (setq articles (cdr articles)))
3801     out))
3802
3803 (defun gnus-uncompress-marks (marks)
3804   "Uncompress the mark ranges in MARKS."
3805   (let ((uncompressed '(score bookmark))
3806         out)
3807     (while marks
3808       (if (memq (caar marks) uncompressed)
3809           (push (car marks) out)
3810         (push (cons (caar marks) (gnus-uncompress-range (cdar marks))) out))
3811       (setq marks (cdr marks)))
3812     out))
3813
3814 (defun gnus-adjust-marked-articles (info)
3815   "Set all article lists and remove all marks that are no longer legal."
3816   (let* ((marked-lists (gnus-info-marks info))
3817          (active (gnus-active (gnus-info-group info)))
3818          (min (car active))
3819          (max (cdr active))
3820          (types gnus-article-mark-lists)
3821          (uncompressed '(score bookmark killed))
3822          marks var articles article mark)
3823
3824     (while marked-lists
3825       (setq marks (pop marked-lists))
3826       (set (setq var (intern (format "gnus-newsgroup-%s"
3827                                      (car (rassq (setq mark (car marks))
3828                                                  types)))))
3829            (if (memq (car marks) uncompressed) (cdr marks)
3830              (gnus-uncompress-range (cdr marks))))
3831
3832       (setq articles (symbol-value var))
3833
3834       ;; All articles have to be subsets of the active articles.
3835       (cond
3836        ;; Adjust "simple" lists.
3837        ((memq mark '(tick dormant expire reply save))
3838         (while articles
3839           (when (or (< (setq article (pop articles)) min) (> article max))
3840             (set var (delq article (symbol-value var))))))
3841        ;; Adjust assocs.
3842        ((memq mark uncompressed)
3843         (when (not (listp (cdr (symbol-value var))))
3844           (set var (list (symbol-value var))))
3845         (when (not (listp (cdr articles)))
3846           (setq articles (list articles)))
3847         (while articles
3848           (when (or (not (consp (setq article (pop articles))))
3849                     (< (car article) min)
3850                     (> (car article) max))
3851             (set var (delq article (symbol-value var))))))))))
3852
3853 (defun gnus-update-missing-marks (missing)
3854   "Go through the list of MISSING articles and remove them mark lists."
3855   (when missing
3856     (let ((types gnus-article-mark-lists)
3857           var m)
3858       ;; Go through all types.
3859       (while types
3860         (setq var (intern (format "gnus-newsgroup-%s" (car (pop types)))))
3861         (when (symbol-value var)
3862           ;; This list has articles.  So we delete all missing articles
3863           ;; from it.
3864           (setq m missing)
3865           (while m
3866             (set var (delq (pop m) (symbol-value var)))))))))
3867
3868 (defun gnus-update-marks ()
3869   "Enter the various lists of marked articles into the newsgroup info list."
3870   (let ((types gnus-article-mark-lists)
3871         (info (gnus-get-info gnus-newsgroup-name))
3872         (uncompressed '(score bookmark killed))
3873         type list newmarked symbol)
3874     (when info
3875       ;; Add all marks lists that are non-nil to the list of marks lists.
3876       (while (setq type (pop types))
3877         (when (setq list (symbol-value
3878                           (setq symbol
3879                                 (intern (format "gnus-newsgroup-%s"
3880                                                 (car type))))))
3881
3882           ;; Get rid of the entries of the articles that have the
3883           ;; default score.
3884           (when (and (eq (cdr type) 'score)
3885                      gnus-save-score
3886                      list)
3887             (let* ((arts list)
3888                    (prev (cons nil list))
3889                    (all prev))
3890               (while arts
3891                 (if (or (not (consp (car arts)))
3892                         (= (cdar arts) gnus-summary-default-score))
3893                     (setcdr prev (cdr arts))
3894                   (setq prev arts))
3895                 (setq arts (cdr arts)))
3896               (setq list (cdr all))))
3897
3898           (push (cons (cdr type)
3899                       (if (memq (cdr type) uncompressed) list
3900                         (gnus-compress-sequence
3901                          (set symbol (sort list '<)) t)))
3902                 newmarked)))
3903
3904       ;; Enter these new marks into the info of the group.
3905       (if (nthcdr 3 info)
3906           (setcar (nthcdr 3 info) newmarked)
3907         ;; Add the marks lists to the end of the info.
3908         (when newmarked
3909           (setcdr (nthcdr 2 info) (list newmarked))))
3910
3911       ;; Cut off the end of the info if there's nothing else there.
3912       (let ((i 5))
3913         (while (and (> i 2)
3914                     (not (nth i info)))
3915           (when (nthcdr (decf i) info)
3916             (setcdr (nthcdr i info) nil)))))))
3917
3918 (defun gnus-set-mode-line (where)
3919   "This function sets the mode line of the article or summary buffers.
3920 If WHERE is `summary', the summary mode line format will be used."
3921   ;; Is this mode line one we keep updated?
3922   (when (memq where gnus-updated-mode-lines)
3923     (let (mode-string)
3924       (save-excursion
3925         ;; We evaluate this in the summary buffer since these
3926         ;; variables are buffer-local to that buffer.
3927         (set-buffer gnus-summary-buffer)
3928         ;; We bind all these variables that are used in the `eval' form
3929         ;; below.
3930         (let* ((mformat (symbol-value
3931                          (intern
3932                           (format "gnus-%s-mode-line-format-spec" where))))
3933                (gnus-tmp-group-name gnus-newsgroup-name)
3934                (gnus-tmp-article-number (or gnus-current-article 0))
3935                (gnus-tmp-unread gnus-newsgroup-unreads)
3936                (gnus-tmp-unread-and-unticked (length gnus-newsgroup-unreads))
3937                (gnus-tmp-unselected (length gnus-newsgroup-unselected))
3938                (gnus-tmp-unread-and-unselected
3939                 (cond ((and (zerop gnus-tmp-unread-and-unticked)
3940                             (zerop gnus-tmp-unselected))
3941                        "")
3942                       ((zerop gnus-tmp-unselected)
3943                        (format "{%d more}" gnus-tmp-unread-and-unticked))
3944                       (t (format "{%d(+%d) more}"
3945                                  gnus-tmp-unread-and-unticked
3946                                  gnus-tmp-unselected))))
3947                (gnus-tmp-subject
3948                 (if (and gnus-current-headers
3949                          (vectorp gnus-current-headers))
3950                     (gnus-mode-string-quote
3951                      (mail-header-subject gnus-current-headers))
3952                   ""))
3953                bufname-length max-len
3954                gnus-tmp-header);; passed as argument to any user-format-funcs
3955           (setq mode-string (eval mformat))
3956           (setq bufname-length (if (string-match "%b" mode-string)
3957                                    (- (length
3958                                        (buffer-name
3959                                         (if (eq where 'summary)
3960                                             nil
3961                                           (get-buffer gnus-article-buffer))))
3962                                       2)
3963                                  0))
3964           (setq max-len (max 4 (if gnus-mode-non-string-length
3965                                    (- (window-width)
3966                                       gnus-mode-non-string-length
3967                                       bufname-length)
3968                                  (length mode-string))))
3969           ;; We might have to chop a bit of the string off...
3970           (when (> (length mode-string) max-len)
3971             (setq mode-string
3972                   (concat (gnus-truncate-string mode-string (- max-len 3))
3973                           "...")))
3974           ;; Pad the mode string a bit.
3975           (setq mode-string (format (format "%%-%ds" max-len) mode-string))))
3976       ;; Update the mode line.
3977       (setq mode-line-buffer-identification
3978             (gnus-mode-line-buffer-identification (list mode-string)))
3979       (set-buffer-modified-p t))))
3980
3981 (defun gnus-create-xref-hashtb (from-newsgroup headers unreads)
3982   "Go through the HEADERS list and add all Xrefs to a hash table.
3983 The resulting hash table is returned, or nil if no Xrefs were found."
3984   (let* ((virtual (gnus-virtual-group-p from-newsgroup))
3985          (prefix (if virtual "" (gnus-group-real-prefix from-newsgroup)))
3986          (xref-hashtb (gnus-make-hashtable))
3987          start group entry number xrefs header)
3988     (while headers
3989       (setq header (pop headers))
3990       (when (and (setq xrefs (mail-header-xref header))
3991                  (not (memq (setq number (mail-header-number header))
3992                             unreads)))
3993         (setq start 0)
3994         (while (string-match "\\([^ ]+\\)[:/]\\([0-9]+\\)" xrefs start)
3995           (setq start (match-end 0))
3996           (setq group (if prefix
3997                           (concat prefix (substring xrefs (match-beginning 1)
3998                                                     (match-end 1)))
3999                         (substring xrefs (match-beginning 1) (match-end 1))))
4000           (setq number
4001                 (string-to-int (substring xrefs (match-beginning 2)
4002                                           (match-end 2))))
4003           (if (setq entry (gnus-gethash group xref-hashtb))
4004               (setcdr entry (cons number (cdr entry)))
4005             (gnus-sethash group (cons number nil) xref-hashtb)))))
4006     (and start xref-hashtb)))
4007
4008 (defun gnus-mark-xrefs-as-read (from-newsgroup headers unreads)
4009   "Look through all the headers and mark the Xrefs as read."
4010   (let ((virtual (gnus-virtual-group-p from-newsgroup))
4011         name entry info xref-hashtb idlist method nth4)
4012     (save-excursion
4013       (set-buffer gnus-group-buffer)
4014       (when (setq xref-hashtb
4015                   (gnus-create-xref-hashtb from-newsgroup headers unreads))
4016         (mapatoms
4017          (lambda (group)
4018            (unless (string= from-newsgroup (setq name (symbol-name group)))
4019              (setq idlist (symbol-value group))
4020              ;; Dead groups are not updated.
4021              (and (prog1
4022                       (setq entry (gnus-gethash name gnus-newsrc-hashtb)
4023                             info (nth 2 entry))
4024                     (when (stringp (setq nth4 (gnus-info-method info)))
4025                       (setq nth4 (gnus-server-to-method nth4))))
4026                   ;; Only do the xrefs if the group has the same
4027                   ;; select method as the group we have just read.
4028                   (or (gnus-methods-equal-p
4029                        nth4 (gnus-find-method-for-group from-newsgroup))
4030                       virtual
4031                       (equal nth4 (setq method (gnus-find-method-for-group
4032                                                 from-newsgroup)))
4033                       (and (equal (car nth4) (car method))
4034                            (equal (nth 1 nth4) (nth 1 method))))
4035                   gnus-use-cross-reference
4036                   (or (not (eq gnus-use-cross-reference t))
4037                       virtual
4038                       ;; Only do cross-references on subscribed
4039                       ;; groups, if that is what is wanted.
4040                       (<= (gnus-info-level info) gnus-level-subscribed))
4041                   (gnus-group-make-articles-read name idlist))))
4042          xref-hashtb)))))
4043
4044 (defun gnus-group-make-articles-read (group articles)
4045   "Update the info of GROUP to say that ARTICLES are read."
4046   (let* ((num 0)
4047          (entry (gnus-gethash group gnus-newsrc-hashtb))
4048          (info (nth 2 entry))
4049          (active (gnus-active group))
4050          range)
4051     ;; First peel off all illegal article numbers.
4052     (when active
4053       (let ((ids articles)
4054             id first)
4055         (while (setq id (pop ids))
4056           (when (and first (> id (cdr active)))
4057             ;; We'll end up in this situation in one particular
4058             ;; obscure situation.  If you re-scan a group and get
4059             ;; a new article that is cross-posted to a different
4060             ;; group that has not been re-scanned, you might get
4061             ;; crossposted article that has a higher number than
4062             ;; Gnus believes possible.  So we re-activate this
4063             ;; group as well.  This might mean doing the
4064             ;; crossposting thingy will *increase* the number
4065             ;; of articles in some groups.  Tsk, tsk.
4066             (setq active (or (gnus-activate-group group) active)))
4067           (when (or (> id (cdr active))
4068                     (< id (car active)))
4069             (setq articles (delq id articles))))))
4070     (save-excursion
4071       (set-buffer gnus-group-buffer)
4072       (gnus-undo-register
4073         `(progn
4074            (gnus-info-set-marks ',info ',(gnus-info-marks info) t)
4075            (gnus-info-set-read ',info ',(gnus-info-read info))
4076            (gnus-get-unread-articles-in-group ',info (gnus-active ,group))
4077            (gnus-group-update-group ,group t))))
4078     ;; If the read list is nil, we init it.
4079     (and active
4080          (null (gnus-info-read info))
4081          (> (car active) 1)
4082          (gnus-info-set-read info (cons 1 (1- (car active)))))
4083     ;; Then we add the read articles to the range.
4084     (gnus-info-set-read
4085      info
4086      (setq range
4087            (gnus-add-to-range
4088             (gnus-info-read info) (setq articles (sort articles '<)))))
4089     ;; Then we have to re-compute how many unread
4090     ;; articles there are in this group.
4091     (when active
4092       (cond
4093        ((not range)
4094         (setq num (- (1+ (cdr active)) (car active))))
4095        ((not (listp (cdr range)))
4096         (setq num (- (cdr active) (- (1+ (cdr range))
4097                                      (car range)))))
4098        (t
4099         (while range
4100           (if (numberp (car range))
4101               (setq num (1+ num))
4102             (setq num (+ num (- (1+ (cdar range)) (caar range)))))
4103           (setq range (cdr range)))
4104         (setq num (- (cdr active) num))))
4105       ;; Update the number of unread articles.
4106       (setcar entry num)
4107       ;; Update the group buffer.
4108       (gnus-group-update-group group t))))
4109
4110 (defun gnus-methods-equal-p (m1 m2)
4111   (let ((m1 (or m1 gnus-select-method))
4112         (m2 (or m2 gnus-select-method)))
4113     (or (equal m1 m2)
4114         (and (eq (car m1) (car m2))
4115              (or (not (memq 'address (assoc (symbol-name (car m1))
4116                                             gnus-valid-select-methods)))
4117                  (equal (nth 1 m1) (nth 1 m2)))))))
4118
4119 (defvar gnus-newsgroup-none-id 0)
4120
4121 (defun gnus-get-newsgroup-headers (&optional dependencies force-new)
4122   (let ((cur nntp-server-buffer)
4123         (dependencies
4124          (or dependencies
4125              (save-excursion (set-buffer gnus-summary-buffer)
4126                              gnus-newsgroup-dependencies)))
4127         headers id id-dep ref-dep end ref)
4128     (save-excursion
4129       (set-buffer nntp-server-buffer)
4130       ;; Translate all TAB characters into SPACE characters.
4131       (subst-char-in-region (point-min) (point-max) ?\t ?  t)
4132       (run-hooks 'gnus-parse-headers-hook)
4133       (let ((case-fold-search t)
4134             in-reply-to header p lines)
4135         (goto-char (point-min))
4136         ;; Search to the beginning of the next header.  Error messages
4137         ;; do not begin with 2 or 3.
4138         (while (re-search-forward "^[23][0-9]+ " nil t)
4139           (setq id nil
4140                 ref nil)
4141           ;; This implementation of this function, with nine
4142           ;; search-forwards instead of the one re-search-forward and
4143           ;; a case (which basically was the old function) is actually
4144           ;; about twice as fast, even though it looks messier.  You
4145           ;; can't have everything, I guess.  Speed and elegance
4146           ;; doesn't always go hand in hand.
4147           (setq
4148            header
4149            (vector
4150             ;; Number.
4151             (prog1
4152                 (read cur)
4153               (end-of-line)
4154               (setq p (point))
4155               (narrow-to-region (point)
4156                                 (or (and (search-forward "\n.\n" nil t)
4157                                          (- (point) 2))
4158                                     (point))))
4159             ;; Subject.
4160             (progn
4161               (goto-char p)
4162               (if (search-forward "\nsubject: " nil t)
4163                   ;; 1997/5/4 by MORIOKA Tomohiko <morioka@jaist.ac.jp>
4164                   (funcall
4165                    gnus-unstructured-field-decoder (nnheader-header-value))
4166                 "(none)"))
4167             ;; From.
4168             (progn
4169               (goto-char p)
4170               (if (search-forward "\nfrom: " nil t)
4171                   ;; 1997/5/4 by MORIOKA Tomohiko <morioka@jaist.ac.jp>
4172                   (funcall
4173                    gnus-structured-field-decoder (nnheader-header-value))
4174                 "(nobody)"))
4175             ;; Date.
4176             (progn
4177               (goto-char p)
4178               (if (search-forward "\ndate: " nil t)
4179                   (nnheader-header-value) ""))
4180             ;; Message-ID.
4181             (progn
4182               (goto-char p)
4183               (setq id (if (search-forward "\nmessage-id:" nil t)
4184                            (buffer-substring
4185                             (1- (or (search-forward "<" nil t) (point)))
4186                             (or (search-forward ">" nil t) (point)))
4187                          ;; If there was no message-id, we just fake one
4188                          ;; to make subsequent routines simpler.
4189                          (nnheader-generate-fake-message-id))))
4190             ;; References.
4191             (progn
4192               (goto-char p)
4193               (if (search-forward "\nreferences: " nil t)
4194                   (progn
4195                     (setq end (point))
4196                     (prog1
4197                         (nnheader-header-value)
4198                       (setq ref
4199                             (buffer-substring
4200                              (progn
4201                                (end-of-line)
4202                                (search-backward ">" end t)
4203                                (1+ (point)))
4204                              (progn
4205                                (search-backward "<" end t)
4206                                (point))))))
4207                 ;; Get the references from the in-reply-to header if there
4208                 ;; were no references and the in-reply-to header looks
4209                 ;; promising.
4210                 (if (and (search-forward "\nin-reply-to: " nil t)
4211                          (setq in-reply-to (nnheader-header-value))
4212                          (string-match "<[^>]+>" in-reply-to))
4213                     (setq ref (substring in-reply-to (match-beginning 0)
4214                                          (match-end 0)))
4215                   (setq ref nil))))
4216             ;; Chars.
4217             0
4218             ;; Lines.
4219             (progn
4220               (goto-char p)
4221               (if (search-forward "\nlines: " nil t)
4222                   (if (numberp (setq lines (ignore-errors (read cur))))
4223                       lines 0)
4224                 0))
4225             ;; Xref.
4226             (progn
4227               (goto-char p)
4228               (and (search-forward "\nxref: " nil t)
4229                    (nnheader-header-value)))))
4230           (when (equal id ref)
4231             (setq ref nil))
4232           ;; We do the threading while we read the headers.  The
4233           ;; message-id and the last reference are both entered into
4234           ;; the same hash table.  Some tippy-toeing around has to be
4235           ;; done in case an article has arrived before the article
4236           ;; which it refers to.
4237           (if (boundp (setq id-dep (intern id dependencies)))
4238               (if (and (car (symbol-value id-dep))
4239                        (not force-new))
4240                   ;; An article with this Message-ID has already been seen.
4241                   (if gnus-summary-ignore-duplicates
4242                       ;; We ignore this one, except we add
4243                       ;; any additional Xrefs (in case the two articles
4244                       ;; came from different servers).
4245                       (progn
4246                         (mail-header-set-xref
4247                          (car (symbol-value id-dep))
4248                          (concat (or (mail-header-xref
4249                                       (car (symbol-value id-dep)))
4250                                      "")
4251                                  (or (mail-header-xref header) "")))
4252                         (setq header nil))
4253                     ;; We rename the Message-ID.
4254                     (set
4255                      (setq id-dep (intern (setq id (nnmail-message-id))
4256                                           dependencies))
4257                      (list header))
4258                     (mail-header-set-id header id))
4259                 (setcar (symbol-value id-dep) header))
4260             (set id-dep (list header)))
4261           (when  header
4262             (if (boundp (setq ref-dep (intern (or ref "none") dependencies)))
4263                 (setcdr (symbol-value ref-dep)
4264                         (nconc (cdr (symbol-value ref-dep))
4265                                (list (symbol-value id-dep))))
4266               (set ref-dep (list nil (symbol-value id-dep))))
4267             (push header headers))
4268           (goto-char (point-max))
4269           (widen))
4270         (nreverse headers)))))
4271
4272 ;; The following macros and functions were written by Felix Lee
4273 ;; <flee@cse.psu.edu>.
4274
4275 (defmacro gnus-nov-read-integer ()
4276   '(prog1
4277        (if (= (following-char) ?\t)
4278            0
4279          (let ((num (ignore-errors (read buffer))))
4280            (if (numberp num) num 0)))
4281      (unless (eobp)
4282        (forward-char 1))))
4283
4284 (defmacro gnus-nov-skip-field ()
4285   '(search-forward "\t" eol 'move))
4286
4287 (defmacro gnus-nov-field ()
4288   '(buffer-substring (point) (if (gnus-nov-skip-field) (1- (point)) eol)))
4289
4290 ;; (defvar gnus-nov-none-counter 0)
4291
4292 ;; This function has to be called with point after the article number
4293 ;; on the beginning of the line.
4294 (defun gnus-nov-parse-line (number dependencies &optional force-new)
4295   (let ((eol (gnus-point-at-eol))
4296         (buffer (current-buffer))
4297         header ref id id-dep ref-dep)
4298
4299     ;; overview: [num subject from date id refs chars lines misc]
4300     (unwind-protect
4301         (progn
4302           (narrow-to-region (point) eol)
4303           (unless (eobp)
4304             (forward-char))
4305
4306           (setq header
4307                 (vector
4308                  number                 ; number
4309                  ;; 1997/5/4 by MORIOKA Tomohiko <morioka@jaist.ac.jp>
4310                  (funcall
4311                   gnus-unstructured-field-decoder (gnus-nov-field)) ; subject
4312                  (funcall
4313                   gnus-structured-field-decoder (gnus-nov-field))   ; from
4314                  (gnus-nov-field)       ; date
4315                  (setq id (or (gnus-nov-field)
4316                               (nnheader-generate-fake-message-id))) ; id
4317                  (progn
4318                    (let ((beg (point)))
4319                      (search-forward "\t" eol)
4320                      (if (search-backward ">" beg t)
4321                          (setq ref
4322                                (buffer-substring
4323                                 (1+ (point))
4324                                 (search-backward "<" beg t)))
4325                        (setq ref nil))
4326                      (goto-char beg))
4327                    (gnus-nov-field))    ; refs
4328                  (gnus-nov-read-integer) ; chars
4329                  (gnus-nov-read-integer) ; lines
4330                  (if (= (following-char) ?\n)
4331                      nil
4332                    (gnus-nov-field))))) ; misc
4333
4334       (widen))
4335
4336     ;; We build the thread tree.
4337     (when (equal id ref)
4338       ;; This article refers back to itself.  Naughty, naughty.
4339       (setq ref nil))
4340     (if (boundp (setq id-dep (intern id dependencies)))
4341         (if (and (car (symbol-value id-dep))
4342                  (not force-new))
4343             ;; An article with this Message-ID has already been seen.
4344             (if gnus-summary-ignore-duplicates
4345                 ;; We ignore this one, except we add any additional
4346                 ;; Xrefs (in case the two articles came from different
4347                 ;; servers.
4348                 (progn
4349                   (mail-header-set-xref
4350                    (car (symbol-value id-dep))
4351                    (concat (or (mail-header-xref
4352                                 (car (symbol-value id-dep)))
4353                                "")
4354                            (or (mail-header-xref header) "")))
4355                   (setq header nil))
4356               ;; We rename the Message-ID.
4357               (set
4358                (setq id-dep (intern (setq id (nnmail-message-id))
4359                                     dependencies))
4360                (list header))
4361               (mail-header-set-id header id))
4362           (setcar (symbol-value id-dep) header))
4363       (set id-dep (list header)))
4364     (when header
4365       (if (boundp (setq ref-dep (intern (or ref "none") dependencies)))
4366           (setcdr (symbol-value ref-dep)
4367                   (nconc (cdr (symbol-value ref-dep))
4368                          (list (symbol-value id-dep))))
4369         (set ref-dep (list nil (symbol-value id-dep)))))
4370     header))
4371
4372 ;; Goes through the xover lines and returns a list of vectors
4373 (defun gnus-get-newsgroup-headers-xover (sequence &optional
4374                                                   force-new dependencies
4375                                                   group also-fetch-heads)
4376   "Parse the news overview data in the server buffer, and return a
4377 list of headers that match SEQUENCE (see `nntp-retrieve-headers')."
4378   ;; Get the Xref when the users reads the articles since most/some
4379   ;; NNTP servers do not include Xrefs when using XOVER.
4380   (setq gnus-article-internal-prepare-hook '(gnus-article-get-xrefs))
4381   (let ((cur nntp-server-buffer)
4382         (dependencies (or dependencies gnus-newsgroup-dependencies))
4383         number headers header)
4384     (save-excursion
4385       (set-buffer nntp-server-buffer)
4386       ;; Allow the user to mangle the headers before parsing them.
4387       (run-hooks 'gnus-parse-headers-hook)
4388       (goto-char (point-min))
4389       (while (not (eobp))
4390         (condition-case ()
4391             (while (and sequence (not (eobp)))
4392               (setq number (read cur))
4393               (while (and sequence
4394                           (< (car sequence) number))
4395                 (setq sequence (cdr sequence)))
4396               (and sequence
4397                    (eq number (car sequence))
4398                    (progn
4399                      (setq sequence (cdr sequence))
4400                      (setq header (inline
4401                                     (gnus-nov-parse-line
4402                                      number dependencies force-new))))
4403                    (push header headers))
4404               (forward-line 1))
4405           (error
4406            (gnus-error 4 "Strange nov line (%d)"
4407                        (count-lines (point-min) (point)))))
4408         (forward-line 1))
4409       ;; A common bug in inn is that if you have posted an article and
4410       ;; then retrieves the active file, it will answer correctly --
4411       ;; the new article is included.  However, a NOV entry for the
4412       ;; article may not have been generated yet, so this may fail.
4413       ;; We work around this problem by retrieving the last few
4414       ;; headers using HEAD.
4415       (if (or (not also-fetch-heads)
4416               (not sequence))
4417           ;; We (probably) got all the headers.
4418           (nreverse headers)
4419         (let ((gnus-nov-is-evil t))
4420           (nconc
4421            (nreverse headers)
4422            (when (gnus-retrieve-headers sequence group)
4423              (gnus-get-newsgroup-headers))))))))
4424
4425 (defun gnus-article-get-xrefs ()
4426   "Fill in the Xref value in `gnus-current-headers', if necessary.
4427 This is meant to be called in `gnus-article-internal-prepare-hook'."
4428   (let ((headers (save-excursion (set-buffer gnus-summary-buffer)
4429                                  gnus-current-headers)))
4430     (or (not gnus-use-cross-reference)
4431         (not headers)
4432         (and (mail-header-xref headers)
4433              (not (string= (mail-header-xref headers) "")))
4434         (let ((case-fold-search t)
4435               xref)
4436           (save-restriction
4437             (nnheader-narrow-to-headers)
4438             (goto-char (point-min))
4439             (when (or (and (eq (downcase (following-char)) ?x)
4440                            (looking-at "Xref:"))
4441                       (search-forward "\nXref:" nil t))
4442               (goto-char (1+ (match-end 0)))
4443               (setq xref (buffer-substring (point)
4444                                            (progn (end-of-line) (point))))
4445               (mail-header-set-xref headers xref)))))))
4446
4447 (defun gnus-summary-insert-subject (id &optional old-header use-old-header)
4448   "Find article ID and insert the summary line for that article."
4449   (let ((header (if (and old-header use-old-header)
4450                     old-header (gnus-read-header id)))
4451         (number (and (numberp id) id))
4452         pos d)
4453     (when header
4454       ;; Rebuild the thread that this article is part of and go to the
4455       ;; article we have fetched.
4456       (when (and (not gnus-show-threads)
4457                  old-header)
4458         (when (setq d (gnus-data-find (mail-header-number old-header)))
4459           (goto-char (gnus-data-pos d))
4460           (gnus-data-remove
4461            number
4462            (- (gnus-point-at-bol)
4463               (prog1
4464                   (1+ (gnus-point-at-eol))
4465                 (gnus-delete-line))))))
4466       (when old-header
4467         (mail-header-set-number header (mail-header-number old-header)))
4468       (setq gnus-newsgroup-sparse
4469             (delq (setq number (mail-header-number header))
4470                   gnus-newsgroup-sparse))
4471       (setq gnus-newsgroup-ancient (delq number gnus-newsgroup-ancient))
4472       (gnus-rebuild-thread (mail-header-id header))
4473       (gnus-summary-goto-subject number nil t))
4474     (when (and (numberp number)
4475                (> number 0))
4476       ;; We have to update the boundaries even if we can't fetch the
4477       ;; article if ID is a number -- so that the next `P' or `N'
4478       ;; command will fetch the previous (or next) article even
4479       ;; if the one we tried to fetch this time has been canceled.
4480       (when (> number gnus-newsgroup-end)
4481         (setq gnus-newsgroup-end number))
4482       (when (< number gnus-newsgroup-begin)
4483         (setq gnus-newsgroup-begin number))
4484       (setq gnus-newsgroup-unselected
4485             (delq number gnus-newsgroup-unselected)))
4486     ;; Report back a success?
4487     (and header (mail-header-number header))))
4488
4489 ;;; Process/prefix in the summary buffer
4490
4491 (defun gnus-summary-work-articles (n)
4492   "Return a list of articles to be worked upon.  The prefix argument,
4493 the list of process marked articles, and the current article will be
4494 taken into consideration."
4495   (cond
4496    (n
4497     ;; A numerical prefix has been given.
4498     (setq n (prefix-numeric-value n))
4499     (let ((backward (< n 0))
4500           (n (abs (prefix-numeric-value n)))
4501           articles article)
4502       (save-excursion
4503         (while
4504             (and (> n 0)
4505                  (push (setq article (gnus-summary-article-number))
4506                        articles)
4507                  (if backward
4508                      (gnus-summary-find-prev nil article)
4509                    (gnus-summary-find-next nil article)))
4510           (decf n)))
4511       (nreverse articles)))
4512    ((gnus-region-active-p)
4513     ;; Work on the region between point and mark.
4514     (let ((max (max (point) (mark)))
4515           articles article)
4516       (save-excursion
4517         (goto-char (min (point) (mark)))
4518         (while
4519             (and
4520              (push (setq article (gnus-summary-article-number)) articles)
4521              (gnus-summary-find-next nil article)
4522              (< (point) max)))
4523         (nreverse articles))))
4524    (gnus-newsgroup-processable
4525     ;; There are process-marked articles present.
4526     ;; Save current state.
4527     (gnus-summary-save-process-mark)
4528     ;; Return the list.
4529     (reverse gnus-newsgroup-processable))
4530    (t
4531     ;; Just return the current article.
4532     (list (gnus-summary-article-number)))))
4533
4534 (defun gnus-summary-save-process-mark ()
4535   "Push the current set of process marked articles on the stack."
4536   (interactive)
4537   (push (copy-sequence gnus-newsgroup-processable)
4538         gnus-newsgroup-process-stack))
4539
4540 (defun gnus-summary-kill-process-mark ()
4541   "Push the current set of process marked articles on the stack and unmark."
4542   (interactive)
4543   (gnus-summary-save-process-mark)
4544   (gnus-summary-unmark-all-processable))
4545
4546 (defun gnus-summary-yank-process-mark ()
4547   "Pop the last process mark state off the stack and restore it."
4548   (interactive)
4549   (unless gnus-newsgroup-process-stack
4550     (error "Empty mark stack"))
4551   (gnus-summary-process-mark-set (pop gnus-newsgroup-process-stack)))
4552
4553 (defun gnus-summary-process-mark-set (set)
4554   "Make SET into the current process marked articles."
4555   (gnus-summary-unmark-all-processable)
4556   (while set
4557     (gnus-summary-set-process-mark (pop set))))
4558
4559 ;;; Searching and stuff
4560
4561 (defun gnus-summary-search-group (&optional backward use-level)
4562   "Search for next unread newsgroup.
4563 If optional argument BACKWARD is non-nil, search backward instead."
4564   (save-excursion
4565     (set-buffer gnus-group-buffer)
4566     (when (gnus-group-search-forward
4567            backward nil (if use-level (gnus-group-group-level) nil))
4568       (gnus-group-group-name))))
4569
4570 (defun gnus-summary-best-group (&optional exclude-group)
4571   "Find the name of the best unread group.
4572 If EXCLUDE-GROUP, do not go to this group."
4573   (save-excursion
4574     (set-buffer gnus-group-buffer)
4575     (save-excursion
4576       (gnus-group-best-unread-group exclude-group))))
4577
4578 (defun gnus-summary-find-next (&optional unread article backward)
4579   (if backward (gnus-summary-find-prev)
4580     (let* ((dummy (gnus-summary-article-intangible-p))
4581            (article (or article (gnus-summary-article-number)))
4582            (arts (gnus-data-find-list article))
4583            result)
4584       (when (and (not dummy)
4585                  (or (not gnus-summary-check-current)
4586                      (not unread)
4587                      (not (gnus-data-unread-p (car arts)))))
4588         (setq arts (cdr arts)))
4589       (when (setq result
4590                   (if unread
4591                       (progn
4592                         (while arts
4593                           (when (gnus-data-unread-p (car arts))
4594                             (setq result (car arts)
4595                                   arts nil))
4596                           (setq arts (cdr arts)))
4597                         result)
4598                     (car arts)))
4599         (goto-char (gnus-data-pos result))
4600         (gnus-data-number result)))))
4601
4602 (defun gnus-summary-find-prev (&optional unread article)
4603   (let* ((eobp (eobp))
4604          (article (or article (gnus-summary-article-number)))
4605          (arts (gnus-data-find-list article (gnus-data-list 'rev)))
4606          result)
4607     (when (and (not eobp)
4608                (or (not gnus-summary-check-current)
4609                    (not unread)
4610                    (not (gnus-data-unread-p (car arts)))))
4611       (setq arts (cdr arts)))
4612     (when (setq result
4613                 (if unread
4614                     (progn
4615                       (while arts
4616                         (when (gnus-data-unread-p (car arts))
4617                           (setq result (car arts)
4618                                 arts nil))
4619                         (setq arts (cdr arts)))
4620                       result)
4621                   (car arts)))
4622       (goto-char (gnus-data-pos result))
4623       (gnus-data-number result))))
4624
4625 (defun gnus-summary-find-subject (subject &optional unread backward article)
4626   (let* ((simp-subject (gnus-simplify-subject-fully subject))
4627          (article (or article (gnus-summary-article-number)))
4628          (articles (gnus-data-list backward))
4629          (arts (gnus-data-find-list article articles))
4630          result)
4631     (when (or (not gnus-summary-check-current)
4632               (not unread)
4633               (not (gnus-data-unread-p (car arts))))
4634       (setq arts (cdr arts)))
4635     (while arts
4636       (and (or (not unread)
4637                (gnus-data-unread-p (car arts)))
4638            (vectorp (gnus-data-header (car arts)))
4639            (gnus-subject-equal
4640             simp-subject (mail-header-subject (gnus-data-header (car arts))) t)
4641            (setq result (car arts)
4642                  arts nil))
4643       (setq arts (cdr arts)))
4644     (and result
4645          (goto-char (gnus-data-pos result))
4646          (gnus-data-number result))))
4647
4648 (defun gnus-summary-search-forward (&optional unread subject backward)
4649   "Search forward for an article.
4650 If UNREAD, look for unread articles.  If SUBJECT, look for
4651 articles with that subject.  If BACKWARD, search backward instead."
4652   (cond (subject (gnus-summary-find-subject subject unread backward))
4653         (backward (gnus-summary-find-prev unread))
4654         (t (gnus-summary-find-next unread))))
4655
4656 (defun gnus-recenter (&optional n)
4657   "Center point in window and redisplay frame.
4658 Also do horizontal recentering."
4659   (interactive "P")
4660   (when (and gnus-auto-center-summary
4661              (not (eq gnus-auto-center-summary 'vertical)))
4662     (gnus-horizontal-recenter))
4663   (recenter n))
4664
4665 (defun gnus-summary-recenter ()
4666   "Center point in the summary window.
4667 If `gnus-auto-center-summary' is nil, or the article buffer isn't
4668 displayed, no centering will be performed."
4669   ;; Suggested by earle@mahendo.JPL.NASA.GOV (Greg Earle).
4670   ;; Recenter only when requested.  Suggested by popovich@park.cs.columbia.edu.
4671   (let* ((top (cond ((< (window-height) 4) 0)
4672                     ((< (window-height) 7) 1)
4673                     (t 2)))
4674          (height (1- (window-height)))
4675          (bottom (save-excursion (goto-char (point-max))
4676                                  (forward-line (- height))
4677                                  (point)))
4678          (window (get-buffer-window (current-buffer))))
4679     ;; The user has to want it.
4680     (when gnus-auto-center-summary
4681       (when (get-buffer-window gnus-article-buffer)
4682         ;; Only do recentering when the article buffer is displayed,
4683         ;; Set the window start to either `bottom', which is the biggest
4684         ;; possible valid number, or the second line from the top,
4685         ;; whichever is the least.
4686         (set-window-start
4687          window (min bottom (save-excursion
4688                               (forward-line (- top)) (point)))))
4689       ;; Do horizontal recentering while we're at it.
4690       (when (and (get-buffer-window (current-buffer) t)
4691                  (not (eq gnus-auto-center-summary 'vertical)))
4692         (let ((selected (selected-window)))
4693           (select-window (get-buffer-window (current-buffer) t))
4694           (gnus-summary-position-point)
4695           (gnus-horizontal-recenter)
4696           (select-window selected))))))
4697
4698 (defun gnus-summary-jump-to-group (newsgroup)
4699   "Move point to NEWSGROUP in group mode buffer."
4700   ;; Keep update point of group mode buffer if visible.
4701   (if (eq (current-buffer) (get-buffer gnus-group-buffer))
4702       (save-window-excursion
4703         ;; Take care of tree window mode.
4704         (when (get-buffer-window gnus-group-buffer)
4705           (pop-to-buffer gnus-group-buffer))
4706         (gnus-group-jump-to-group newsgroup))
4707     (save-excursion
4708       ;; Take care of tree window mode.
4709       (if (get-buffer-window gnus-group-buffer)
4710           (pop-to-buffer gnus-group-buffer)
4711         (set-buffer gnus-group-buffer))
4712       (gnus-group-jump-to-group newsgroup))))
4713
4714 ;; This function returns a list of article numbers based on the
4715 ;; difference between the ranges of read articles in this group and
4716 ;; the range of active articles.
4717 (defun gnus-list-of-unread-articles (group)
4718   (let* ((read (gnus-info-read (gnus-get-info group)))
4719          (active (or (gnus-active group) (gnus-activate-group group)))
4720          (last (cdr active))
4721          first nlast unread)
4722     ;; If none are read, then all are unread.
4723     (if (not read)
4724         (setq first (car active))
4725       ;; If the range of read articles is a single range, then the
4726       ;; first unread article is the article after the last read
4727       ;; article.  Sounds logical, doesn't it?
4728       (if (not (listp (cdr read)))
4729           (setq first (1+ (cdr read)))
4730         ;; `read' is a list of ranges.
4731         (when (/= (setq nlast (or (and (numberp (car read)) (car read))
4732                                   (caar read)))
4733                   1)
4734           (setq first 1))
4735         (while read
4736           (when first
4737             (while (< first nlast)
4738               (push first unread)
4739               (setq first (1+ first))))
4740           (setq first (1+ (if (atom (car read)) (car read) (cdar read))))
4741           (setq nlast (if (atom (cadr read)) (cadr read) (caadr read)))
4742           (setq read (cdr read)))))
4743     ;; And add the last unread articles.
4744     (while (<= first last)
4745       (push first unread)
4746       (setq first (1+ first)))
4747     ;; Return the list of unread articles.
4748     (nreverse unread)))
4749
4750 (defun gnus-list-of-read-articles (group)
4751   "Return a list of unread, unticked and non-dormant articles."
4752   (let* ((info (gnus-get-info group))
4753          (marked (gnus-info-marks info))
4754          (active (gnus-active group)))
4755     (and info active
4756          (gnus-set-difference
4757           (gnus-sorted-complement
4758            (gnus-uncompress-range active)
4759            (gnus-list-of-unread-articles group))
4760           (append
4761            (gnus-uncompress-range (cdr (assq 'dormant marked)))
4762            (gnus-uncompress-range (cdr (assq 'tick marked))))))))
4763
4764 ;; Various summary commands
4765
4766 (defun gnus-summary-universal-argument (arg)
4767   "Perform any operation on all articles that are process/prefixed."
4768   (interactive "P")
4769   (gnus-set-global-variables)
4770   (let ((articles (gnus-summary-work-articles arg))
4771         func article)
4772     (if (eq
4773          (setq
4774           func
4775           (key-binding
4776            (read-key-sequence
4777             (substitute-command-keys
4778              "\\<gnus-summary-mode-map>\\[gnus-summary-universal-argument]"
4779              ))))
4780          'undefined)
4781         (gnus-error 1 "Undefined key")
4782       (save-excursion
4783         (while articles
4784           (gnus-summary-goto-subject (setq article (pop articles)))
4785           (let (gnus-newsgroup-processable)
4786             (command-execute func))
4787           (gnus-summary-remove-process-mark article)))))
4788   (gnus-summary-position-point))
4789
4790 (defun gnus-summary-toggle-truncation (&optional arg)
4791   "Toggle truncation of summary lines.
4792 With arg, turn line truncation on iff arg is positive."
4793   (interactive "P")
4794   (setq truncate-lines
4795         (if (null arg) (not truncate-lines)
4796           (> (prefix-numeric-value arg) 0)))
4797   (redraw-display))
4798
4799 (defun gnus-summary-reselect-current-group (&optional all rescan)
4800   "Exit and then reselect the current newsgroup.
4801 The prefix argument ALL means to select all articles."
4802   (interactive "P")
4803   (gnus-set-global-variables)
4804   (when (gnus-ephemeral-group-p gnus-newsgroup-name)
4805     (error "Ephemeral groups can't be reselected"))
4806   (let ((current-subject (gnus-summary-article-number))
4807         (group gnus-newsgroup-name))
4808     (setq gnus-newsgroup-begin nil)
4809     (gnus-summary-exit)
4810     ;; We have to adjust the point of group mode buffer because
4811     ;; point was moved to the next unread newsgroup by exiting.
4812     (gnus-summary-jump-to-group group)
4813     (when rescan
4814       (save-excursion
4815         (gnus-group-get-new-news-this-group 1)))
4816     (gnus-group-read-group all t)
4817     (gnus-summary-goto-subject current-subject nil t)))
4818
4819 (defun gnus-summary-rescan-group (&optional all)
4820   "Exit the newsgroup, ask for new articles, and select the newsgroup."
4821   (interactive "P")
4822   (gnus-summary-reselect-current-group all t))
4823
4824 (defun gnus-summary-update-info (&optional non-destructive)
4825   (save-excursion
4826     (let ((group gnus-newsgroup-name))
4827       (when gnus-newsgroup-kill-headers
4828         (setq gnus-newsgroup-killed
4829               (gnus-compress-sequence
4830                (nconc
4831                 (gnus-set-sorted-intersection
4832                  (gnus-uncompress-range gnus-newsgroup-killed)
4833                  (setq gnus-newsgroup-unselected
4834                        (sort gnus-newsgroup-unselected '<)))
4835                 (setq gnus-newsgroup-unreads
4836                       (sort gnus-newsgroup-unreads '<)))
4837                t)))
4838       (unless (listp (cdr gnus-newsgroup-killed))
4839         (setq gnus-newsgroup-killed (list gnus-newsgroup-killed)))
4840       (let ((headers gnus-newsgroup-headers))
4841         (when (and (not gnus-save-score)
4842                    (not non-destructive))
4843           (setq gnus-newsgroup-scored nil))
4844         ;; Set the new ranges of read articles.
4845         (save-excursion
4846           (set-buffer gnus-group-buffer)
4847           (gnus-undo-force-boundary))
4848         (gnus-update-read-articles
4849          group (append gnus-newsgroup-unreads gnus-newsgroup-unselected))
4850         ;; Set the current article marks.
4851         (gnus-update-marks)
4852         ;; Do the cross-ref thing.
4853         (when gnus-use-cross-reference
4854           (gnus-mark-xrefs-as-read group headers gnus-newsgroup-unreads))
4855         ;; Do adaptive scoring, and possibly save score files.
4856         (when gnus-newsgroup-adaptive
4857           (gnus-score-adaptive))
4858         (when gnus-use-scoring
4859           (gnus-score-save))
4860         ;; Do not switch windows but change the buffer to work.
4861         (set-buffer gnus-group-buffer)
4862         (unless (gnus-ephemeral-group-p gnus-newsgroup-name)
4863           (gnus-group-update-group group))))))
4864
4865 (defun gnus-summary-save-newsrc (&optional force)
4866   "Save the current number of read/marked articles in the dribble buffer.
4867 The dribble buffer will then be saved.
4868 If FORCE (the prefix), also save the .newsrc file(s)."
4869   (interactive "P")
4870   (gnus-summary-update-info t)
4871   (if force
4872       (gnus-save-newsrc-file)
4873     (gnus-dribble-save)))
4874
4875 (defun gnus-summary-exit (&optional temporary)
4876   "Exit reading current newsgroup, and then return to group selection mode.
4877 gnus-exit-group-hook is called with no arguments if that value is non-nil."
4878   (interactive)
4879   (gnus-set-global-variables)
4880   (gnus-kill-save-kill-buffer)
4881   (let* ((group gnus-newsgroup-name)
4882          (quit-config (gnus-group-quit-config gnus-newsgroup-name))
4883          (mode major-mode)
4884          (group-point nil)
4885          (buf (current-buffer)))
4886     (run-hooks 'gnus-summary-prepare-exit-hook)
4887     ;; If we have several article buffers, we kill them at exit.
4888     (unless gnus-single-article-buffer
4889       (gnus-kill-buffer gnus-original-article-buffer)
4890       (setq gnus-article-current nil))
4891     (when gnus-use-cache
4892       (gnus-cache-possibly-remove-articles)
4893       (gnus-cache-save-buffers))
4894     (gnus-async-prefetch-remove-group group)
4895     (when gnus-suppress-duplicates
4896       (gnus-dup-enter-articles))
4897     (when gnus-use-trees
4898       (gnus-tree-close group))
4899     ;; Make all changes in this group permanent.
4900     (unless quit-config
4901       (run-hooks 'gnus-exit-group-hook)
4902       (gnus-summary-update-info))
4903     (gnus-close-group group)
4904     ;; Make sure where we were, and go to next newsgroup.
4905     (set-buffer gnus-group-buffer)
4906     (unless quit-config
4907       (gnus-group-jump-to-group group))
4908     (run-hooks 'gnus-summary-exit-hook)
4909     (unless quit-config
4910       (gnus-group-next-unread-group 1))
4911     (setq group-point (point))
4912     (if temporary
4913         nil                             ;Nothing to do.
4914       ;; If we have several article buffers, we kill them at exit.
4915       (unless gnus-single-article-buffer
4916         (gnus-kill-buffer gnus-article-buffer)
4917         (gnus-kill-buffer gnus-original-article-buffer)
4918         (setq gnus-article-current nil))
4919       (set-buffer buf)
4920       (if (not gnus-kill-summary-on-exit)
4921           (gnus-deaden-summary)
4922         ;; We set all buffer-local variables to nil.  It is unclear why
4923         ;; this is needed, but if we don't, buffer-local variables are
4924         ;; not garbage-collected, it seems.  This would the lead to en
4925         ;; ever-growing Emacs.
4926         (gnus-summary-clear-local-variables)
4927         (when (get-buffer gnus-article-buffer)
4928           (bury-buffer gnus-article-buffer))
4929         ;; We clear the global counterparts of the buffer-local
4930         ;; variables as well, just to be on the safe side.
4931         (set-buffer gnus-group-buffer)
4932         (gnus-summary-clear-local-variables)
4933         ;; Return to group mode buffer.
4934         (when (eq mode 'gnus-summary-mode)
4935           (gnus-kill-buffer buf)))
4936       (setq gnus-current-select-method gnus-select-method)
4937       (pop-to-buffer gnus-group-buffer)
4938       ;; Clear the current group name.
4939       (if (not quit-config)
4940           (progn
4941             (goto-char group-point)
4942             (gnus-configure-windows 'group 'force))
4943         (gnus-handle-ephemeral-exit quit-config))
4944       (unless quit-config
4945         (setq gnus-newsgroup-name nil)))))
4946
4947 (defalias 'gnus-summary-quit 'gnus-summary-exit-no-update)
4948 (defun gnus-summary-exit-no-update (&optional no-questions)
4949   "Quit reading current newsgroup without updating read article info."
4950   (interactive)
4951   (gnus-set-global-variables)
4952   (let* ((group gnus-newsgroup-name)
4953          (quit-config (gnus-group-quit-config group)))
4954     (when (or no-questions
4955               gnus-expert-user
4956               (gnus-y-or-n-p "Discard changes to this group and exit? "))
4957       ;; If we have several article buffers, we kill them at exit.
4958       (unless gnus-single-article-buffer
4959         (gnus-kill-buffer gnus-article-buffer)
4960         (gnus-kill-buffer gnus-original-article-buffer)
4961         (setq gnus-article-current nil))
4962       (if (not gnus-kill-summary-on-exit)
4963           (gnus-deaden-summary)
4964         (gnus-close-group group)
4965         (gnus-summary-clear-local-variables)
4966         (set-buffer gnus-group-buffer)
4967         (gnus-summary-clear-local-variables)
4968         (when (get-buffer gnus-summary-buffer)
4969           (kill-buffer gnus-summary-buffer)))
4970       (unless gnus-single-article-buffer
4971         (setq gnus-article-current nil))
4972       (when gnus-use-trees
4973         (gnus-tree-close group))
4974       (gnus-async-prefetch-remove-group group)
4975       (when (get-buffer gnus-article-buffer)
4976         (bury-buffer gnus-article-buffer))
4977       ;; Return to the group buffer.
4978       (gnus-configure-windows 'group 'force)
4979       ;; Clear the current group name.
4980       (setq gnus-newsgroup-name nil)
4981       (when (equal (gnus-group-group-name) group)
4982         (gnus-group-next-unread-group 1))
4983       (when quit-config
4984         (gnus-handle-ephemeral-exit quit-config)))))
4985
4986 (defun gnus-handle-ephemeral-exit (quit-config)
4987   "Handle movement when leaving an ephemeral group.  The state
4988 which existed when entering the ephemeral is reset."
4989   (if (not (buffer-name (car quit-config)))
4990       (gnus-configure-windows 'group 'force)
4991     (set-buffer (car quit-config))
4992     (cond ((eq major-mode 'gnus-summary-mode)
4993            (gnus-set-global-variables))
4994           ((eq major-mode 'gnus-article-mode)
4995            (save-excursion
4996              ;; The `gnus-summary-buffer' variable may point
4997              ;; to the old summary buffer when using a single
4998              ;; article buffer.
4999              (unless (gnus-buffer-live-p gnus-summary-buffer)
5000                (set-buffer gnus-group-buffer))
5001              (set-buffer gnus-summary-buffer)
5002              (gnus-set-global-variables))))
5003     (if (or (eq (cdr quit-config) 'article)
5004             (eq (cdr quit-config) 'pick))
5005         (progn
5006           ;; The current article may be from the ephemeral group
5007           ;; thus it is best that we reload this article
5008           (gnus-summary-show-article)
5009           (if (and (boundp 'gnus-pick-mode) (symbol-value 'gnus-pick-mode))
5010               (gnus-configure-windows 'pick 'force)
5011             (gnus-configure-windows (cdr quit-config) 'force)))
5012       (gnus-configure-windows (cdr quit-config) 'force))
5013     (when (eq major-mode 'gnus-summary-mode)
5014       (gnus-summary-next-subject 1 nil t)
5015       (gnus-summary-recenter)
5016       (gnus-summary-position-point))))
5017
5018 ;;; Dead summaries.
5019
5020 (defvar gnus-dead-summary-mode-map nil)
5021
5022 (unless gnus-dead-summary-mode-map
5023   (setq gnus-dead-summary-mode-map (make-keymap))
5024   (suppress-keymap gnus-dead-summary-mode-map)
5025   (substitute-key-definition
5026    'undefined 'gnus-summary-wake-up-the-dead gnus-dead-summary-mode-map)
5027   (let ((keys '("\C-d" "\r" "\177" [delete])))
5028     (while keys
5029       (define-key gnus-dead-summary-mode-map
5030         (pop keys) 'gnus-summary-wake-up-the-dead))))
5031
5032 (defvar gnus-dead-summary-mode nil
5033   "Minor mode for Gnus summary buffers.")
5034
5035 (defun gnus-dead-summary-mode (&optional arg)
5036   "Minor mode for Gnus summary buffers."
5037   (interactive "P")
5038   (when (eq major-mode 'gnus-summary-mode)
5039     (make-local-variable 'gnus-dead-summary-mode)
5040     (setq gnus-dead-summary-mode
5041           (if (null arg) (not gnus-dead-summary-mode)
5042             (> (prefix-numeric-value arg) 0)))
5043     (when gnus-dead-summary-mode
5044       (gnus-add-minor-mode
5045        'gnus-dead-summary-mode " Dead" gnus-dead-summary-mode-map))))
5046
5047 (defun gnus-deaden-summary ()
5048   "Make the current summary buffer into a dead summary buffer."
5049   ;; Kill any previous dead summary buffer.
5050   (when (and gnus-dead-summary
5051              (buffer-name gnus-dead-summary))
5052     (save-excursion
5053       (set-buffer gnus-dead-summary)
5054       (when gnus-dead-summary-mode
5055         (kill-buffer (current-buffer)))))
5056   ;; Make this the current dead summary.
5057   (setq gnus-dead-summary (current-buffer))
5058   (gnus-dead-summary-mode 1)
5059   (let ((name (buffer-name)))
5060     (when (string-match "Summary" name)
5061       (rename-buffer
5062        (concat (substring name 0 (match-beginning 0)) "Dead "
5063                (substring name (match-beginning 0)))
5064        t))))
5065
5066 (defun gnus-kill-or-deaden-summary (buffer)
5067   "Kill or deaden the summary BUFFER."
5068   (when (and (buffer-name buffer)
5069              (not gnus-single-article-buffer))
5070     (save-excursion
5071       (set-buffer buffer)
5072       (gnus-kill-buffer gnus-article-buffer)
5073       (gnus-kill-buffer gnus-original-article-buffer)))
5074   (cond (gnus-kill-summary-on-exit
5075          (when (and gnus-use-trees
5076                     (and (get-buffer buffer)
5077                          (buffer-name (get-buffer buffer))))
5078            (save-excursion
5079              (set-buffer (get-buffer buffer))
5080              (gnus-tree-close gnus-newsgroup-name)))
5081          (gnus-kill-buffer buffer))
5082         ((and (get-buffer buffer)
5083               (buffer-name (get-buffer buffer)))
5084          (save-excursion
5085            (set-buffer buffer)
5086            (gnus-deaden-summary)))))
5087
5088 (defun gnus-summary-wake-up-the-dead (&rest args)
5089   "Wake up the dead summary buffer."
5090   (interactive)
5091   (gnus-dead-summary-mode -1)
5092   (let ((name (buffer-name)))
5093     (when (string-match "Dead " name)
5094       (rename-buffer
5095        (concat (substring name 0 (match-beginning 0))
5096                (substring name (match-end 0)))
5097        t)))
5098   (gnus-message 3 "This dead summary is now alive again"))
5099
5100 ;; Suggested by Andrew Eskilsson <pi92ae@pt.hk-r.se>.
5101 (defun gnus-summary-fetch-faq (&optional faq-dir)
5102   "Fetch the FAQ for the current group.
5103 If FAQ-DIR (the prefix), prompt for a directory to search for the faq
5104 in."
5105   (interactive
5106    (list
5107     (when current-prefix-arg
5108       (completing-read
5109        "Faq dir: " (and (listp gnus-group-faq-directory)
5110                         (mapcar (lambda (file) (list file))
5111                                 gnus-group-faq-directory))))))
5112   (let (gnus-faq-buffer)
5113     (when (setq gnus-faq-buffer
5114                 (gnus-group-fetch-faq gnus-newsgroup-name faq-dir))
5115       (gnus-configure-windows 'summary-faq))))
5116
5117 ;; Suggested by Per Abrahamsen <amanda@iesd.auc.dk>.
5118 (defun gnus-summary-describe-group (&optional force)
5119   "Describe the current newsgroup."
5120   (interactive "P")
5121   (gnus-group-describe-group force gnus-newsgroup-name))
5122
5123 (defun gnus-summary-describe-briefly ()
5124   "Describe summary mode commands briefly."
5125   (interactive)
5126   (gnus-message 6
5127                 (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")))
5128
5129 ;; Walking around group mode buffer from summary mode.
5130
5131 (defun gnus-summary-next-group (&optional no-article target-group backward)
5132   "Exit current newsgroup and then select next unread newsgroup.
5133 If prefix argument NO-ARTICLE is non-nil, no article is selected
5134 initially.  If NEXT-GROUP, go to this group.  If BACKWARD, go to
5135 previous group instead."
5136   (interactive "P")
5137   (gnus-set-global-variables)
5138   ;; Stop pre-fetching.
5139   (gnus-async-halt-prefetch)
5140   (let ((current-group gnus-newsgroup-name)
5141         (current-buffer (current-buffer))
5142         entered)
5143     ;; First we semi-exit this group to update Xrefs and all variables.
5144     ;; We can't do a real exit, because the window conf must remain
5145     ;; the same in case the user is prompted for info, and we don't
5146     ;; want the window conf to change before that...
5147     (gnus-summary-exit t)
5148     (while (not entered)
5149       ;; Then we find what group we are supposed to enter.
5150       (set-buffer gnus-group-buffer)
5151       (gnus-group-jump-to-group current-group)
5152       (setq target-group
5153             (or target-group
5154                 (if (eq gnus-keep-same-level 'best)
5155                     (gnus-summary-best-group gnus-newsgroup-name)
5156                   (gnus-summary-search-group backward gnus-keep-same-level))))
5157       (if (not target-group)
5158           ;; There are no further groups, so we return to the group
5159           ;; buffer.
5160           (progn
5161             (gnus-message 5 "Returning to the group buffer")
5162             (setq entered t)
5163             (when (gnus-buffer-live-p current-buffer)
5164               (set-buffer current-buffer)
5165               (gnus-summary-exit))
5166             (run-hooks 'gnus-group-no-more-groups-hook))
5167         ;; We try to enter the target group.
5168         (gnus-group-jump-to-group target-group)
5169         (let ((unreads (gnus-group-group-unread)))
5170           (if (and (or (eq t unreads)
5171                        (and unreads (not (zerop unreads))))
5172                    (gnus-summary-read-group
5173                     target-group nil no-article
5174                     (and (buffer-name current-buffer) current-buffer)))
5175               (setq entered t)
5176             (setq current-group target-group
5177                   target-group nil)))))))
5178
5179 (defun gnus-summary-prev-group (&optional no-article)
5180   "Exit current newsgroup and then select previous unread newsgroup.
5181 If prefix argument NO-ARTICLE is non-nil, no article is selected initially."
5182   (interactive "P")
5183   (gnus-summary-next-group no-article nil t))
5184
5185 ;; Walking around summary lines.
5186
5187 (defun gnus-summary-first-subject (&optional unread)
5188   "Go to the first unread subject.
5189 If UNREAD is non-nil, go to the first unread article.
5190 Returns the article selected or nil if there are no unread articles."
5191   (interactive "P")
5192   (prog1
5193       (cond
5194        ;; Empty summary.
5195        ((null gnus-newsgroup-data)
5196         (gnus-message 3 "No articles in the group")
5197         nil)
5198        ;; Pick the first article.
5199        ((not unread)
5200         (goto-char (gnus-data-pos (car gnus-newsgroup-data)))
5201         (gnus-data-number (car gnus-newsgroup-data)))
5202        ;; No unread articles.
5203        ((null gnus-newsgroup-unreads)
5204         (gnus-message 3 "No more unread articles")
5205         nil)
5206        ;; Find the first unread article.
5207        (t
5208         (let ((data gnus-newsgroup-data))
5209           (while (and data
5210                       (not (gnus-data-unread-p (car data))))
5211             (setq data (cdr data)))
5212           (when data
5213             (goto-char (gnus-data-pos (car data)))
5214             (gnus-data-number (car data))))))
5215     (gnus-summary-position-point)))
5216
5217 (defun gnus-summary-next-subject (n &optional unread dont-display)
5218   "Go to next N'th summary line.
5219 If N is negative, go to the previous N'th subject line.
5220 If UNREAD is non-nil, only unread articles are selected.
5221 The difference between N and the actual number of steps taken is
5222 returned."
5223   (interactive "p")
5224   (let ((backward (< n 0))
5225         (n (abs n)))
5226     (while (and (> n 0)
5227                 (if backward
5228                     (gnus-summary-find-prev unread)
5229                   (gnus-summary-find-next unread)))
5230       (setq n (1- n)))
5231     (when (/= 0 n)
5232       (gnus-message 7 "No more%s articles"
5233                     (if unread " unread" "")))
5234     (unless dont-display
5235       (gnus-summary-recenter)
5236       (gnus-summary-position-point))
5237     n))
5238
5239 (defun gnus-summary-next-unread-subject (n)
5240   "Go to next N'th unread summary line."
5241   (interactive "p")
5242   (gnus-summary-next-subject n t))
5243
5244 (defun gnus-summary-prev-subject (n &optional unread)
5245   "Go to previous N'th summary line.
5246 If optional argument UNREAD is non-nil, only unread article is selected."
5247   (interactive "p")
5248   (gnus-summary-next-subject (- n) unread))
5249
5250 (defun gnus-summary-prev-unread-subject (n)
5251   "Go to previous N'th unread summary line."
5252   (interactive "p")
5253   (gnus-summary-next-subject (- n) t))
5254
5255 (defun gnus-summary-goto-subject (article &optional force silent)
5256   "Go the subject line of ARTICLE.
5257 If FORCE, also allow jumping to articles not currently shown."
5258   (interactive "nArticle number: ")
5259   (let ((b (point))
5260         (data (gnus-data-find article)))
5261     ;; We read in the article if we have to.
5262     (and (not data)
5263          force
5264          (gnus-summary-insert-subject article (and (vectorp force) force) t)
5265          (setq data (gnus-data-find article)))
5266     (goto-char b)
5267     (if (not data)
5268         (progn
5269           (unless silent
5270             (gnus-message 3 "Can't find article %d" article))
5271           nil)
5272       (goto-char (gnus-data-pos data))
5273       article)))
5274
5275 ;; Walking around summary lines with displaying articles.
5276
5277 (defun gnus-summary-expand-window (&optional arg)
5278   "Make the summary buffer take up the entire Emacs frame.
5279 Given a prefix, will force an `article' buffer configuration."
5280   (interactive "P")
5281   (gnus-set-global-variables)
5282   (if arg
5283       (gnus-configure-windows 'article 'force)
5284     (gnus-configure-windows 'summary 'force)))
5285
5286 (defun gnus-summary-display-article (article &optional all-header)
5287   "Display ARTICLE in article buffer."
5288   (gnus-set-global-variables)
5289   (if (null article)
5290       nil
5291     (prog1
5292         (if gnus-summary-display-article-function
5293             (funcall gnus-summary-display-article-function article all-header)
5294           (gnus-article-prepare article all-header))
5295       (run-hooks 'gnus-select-article-hook)
5296       (when (and gnus-current-article
5297                  (not (zerop gnus-current-article)))
5298         (gnus-summary-goto-subject gnus-current-article))
5299       (gnus-summary-recenter)
5300       (when (and gnus-use-trees gnus-show-threads)
5301         (gnus-possibly-generate-tree article)
5302         (gnus-highlight-selected-tree article))
5303       ;; Successfully display article.
5304       (gnus-article-set-window-start
5305        (cdr (assq article gnus-newsgroup-bookmarks))))))
5306
5307 (defun gnus-summary-select-article (&optional all-headers force pseudo article)
5308   "Select the current article.
5309 If ALL-HEADERS is non-nil, show all header fields.  If FORCE is
5310 non-nil, the article will be re-fetched even if it already present in
5311 the article buffer.  If PSEUDO is non-nil, pseudo-articles will also
5312 be displayed."
5313   ;; Make sure we are in the summary buffer to work around bbdb bug.
5314   (unless (eq major-mode 'gnus-summary-mode)
5315     (set-buffer gnus-summary-buffer))
5316   (let ((article (or article (gnus-summary-article-number)))
5317         (all-headers (not (not all-headers))) ;Must be T or NIL.
5318         gnus-summary-display-article-function
5319         did)
5320     (and (not pseudo)
5321          (gnus-summary-article-pseudo-p article)
5322          (error "This is a pseudo-article"))
5323     (prog1
5324         (save-excursion
5325           (set-buffer gnus-summary-buffer)
5326           (if (or (and gnus-single-article-buffer
5327                        (or (null gnus-current-article)
5328                            (null gnus-article-current)
5329                            (null (get-buffer gnus-article-buffer))
5330                            (not (eq article (cdr gnus-article-current)))
5331                            (not (equal (car gnus-article-current)
5332                                        gnus-newsgroup-name))))
5333                   (and (not gnus-single-article-buffer)
5334                        (or (null gnus-current-article)
5335                            (not (eq gnus-current-article article))))
5336                   force)
5337               ;; The requested article is different from the current article.
5338               (prog1
5339                   (gnus-summary-display-article article all-headers)
5340                 (setq did article))
5341             (when (or all-headers gnus-show-all-headers)
5342               (gnus-article-show-all-headers))
5343             'old))
5344       (when did
5345         (gnus-article-set-window-start
5346          (cdr (assq article gnus-newsgroup-bookmarks)))))))
5347
5348 (defun gnus-summary-set-current-mark (&optional current-mark)
5349   "Obsolete function."
5350   nil)
5351
5352 (defun gnus-summary-next-article (&optional unread subject backward push)
5353   "Select the next article.
5354 If UNREAD, only unread articles are selected.
5355 If SUBJECT, only articles with SUBJECT are selected.
5356 If BACKWARD, the previous article is selected instead of the next."
5357   (interactive "P")
5358   (gnus-set-global-variables)
5359   (cond
5360    ;; Is there such an article?
5361    ((and (gnus-summary-search-forward unread subject backward)
5362          (or (gnus-summary-display-article (gnus-summary-article-number))
5363              (eq (gnus-summary-article-mark) gnus-canceled-mark)))
5364     (gnus-summary-position-point))
5365    ;; If not, we try the first unread, if that is wanted.
5366    ((and subject
5367          gnus-auto-select-same
5368          (gnus-summary-first-unread-article))
5369     (gnus-summary-position-point)
5370     (gnus-message 6 "Wrapped"))
5371    ;; Try to get next/previous article not displayed in this group.
5372    ((and gnus-auto-extend-newsgroup
5373          (not unread) (not subject))
5374     (gnus-summary-goto-article
5375      (if backward (1- gnus-newsgroup-begin) (1+ gnus-newsgroup-end))
5376      nil t))
5377    ;; Go to next/previous group.
5378    (t
5379     (unless (gnus-ephemeral-group-p gnus-newsgroup-name)
5380       (gnus-summary-jump-to-group gnus-newsgroup-name))
5381     (let ((cmd last-command-char)
5382           (point
5383            (save-excursion
5384              (set-buffer gnus-group-buffer)
5385              (point)))
5386           (group
5387            (if (eq gnus-keep-same-level 'best)
5388                (gnus-summary-best-group gnus-newsgroup-name)
5389              (gnus-summary-search-group backward gnus-keep-same-level))))
5390       ;; For some reason, the group window gets selected.  We change
5391       ;; it back.
5392       (select-window (get-buffer-window (current-buffer)))
5393       ;; Select next unread newsgroup automagically.
5394       (cond
5395        ((or (not gnus-auto-select-next)
5396             (not cmd))
5397         (gnus-message 7 "No more%s articles" (if unread " unread" "")))
5398        ((or (eq gnus-auto-select-next 'quietly)
5399             (and (eq gnus-auto-select-next 'slightly-quietly)
5400                  push)
5401             (and (eq gnus-auto-select-next 'almost-quietly)
5402                  (gnus-summary-last-article-p)))
5403         ;; Select quietly.
5404         (if (gnus-ephemeral-group-p gnus-newsgroup-name)
5405             (gnus-summary-exit)
5406           (gnus-message 7 "No more%s articles (%s)..."
5407                         (if unread " unread" "")
5408                         (if group (concat "selecting " group)
5409                           "exiting"))
5410           (gnus-summary-next-group nil group backward)))
5411        (t
5412         (when (gnus-key-press-event-p last-input-event)
5413           (gnus-summary-walk-group-buffer
5414            gnus-newsgroup-name cmd unread backward point))))))))
5415
5416 (defun gnus-summary-walk-group-buffer (from-group cmd unread backward start)
5417   (let ((keystrokes '((?\C-n (gnus-group-next-unread-group 1))
5418                       (?\C-p (gnus-group-prev-unread-group 1))))
5419         (cursor-in-echo-area t)
5420         keve key group ended)
5421     (save-excursion
5422       (set-buffer gnus-group-buffer)
5423       (goto-char start)
5424       (setq group
5425             (if (eq gnus-keep-same-level 'best)
5426                 (gnus-summary-best-group gnus-newsgroup-name)
5427               (gnus-summary-search-group backward gnus-keep-same-level))))
5428     (while (not ended)
5429       (gnus-message
5430        5 "No more%s articles%s" (if unread " unread" "")
5431        (if (and group
5432                 (not (gnus-ephemeral-group-p gnus-newsgroup-name)))
5433            (format " (Type %s for %s [%s])"
5434                    (single-key-description cmd) group
5435                    (car (gnus-gethash group gnus-newsrc-hashtb)))
5436          (format " (Type %s to exit %s)"
5437                  (single-key-description cmd)
5438                  gnus-newsgroup-name)))
5439       ;; Confirm auto selection.
5440       (setq key (car (setq keve (gnus-read-event-char))))
5441       (setq ended t)
5442       (cond
5443        ((assq key keystrokes)
5444         (let ((obuf (current-buffer)))
5445           (switch-to-buffer gnus-group-buffer)
5446           (when group
5447             (gnus-group-jump-to-group group))
5448           (eval (cadr (assq key keystrokes)))
5449           (setq group (gnus-group-group-name))
5450           (switch-to-buffer obuf))
5451         (setq ended nil))
5452        ((equal key cmd)
5453         (if (or (not group)
5454                 (gnus-ephemeral-group-p gnus-newsgroup-name))
5455             (gnus-summary-exit)
5456           (gnus-summary-next-group nil group backward)))
5457        (t
5458         (push (cdr keve) unread-command-events))))))
5459
5460 (defun gnus-summary-next-unread-article ()
5461   "Select unread article after current one."
5462   (interactive)
5463   (gnus-summary-next-article
5464    (or (not (eq gnus-summary-goto-unread 'never))
5465        (gnus-summary-last-article-p (gnus-summary-article-number)))
5466    (and gnus-auto-select-same
5467         (gnus-summary-article-subject))))
5468
5469 (defun gnus-summary-prev-article (&optional unread subject)
5470   "Select the article after the current one.
5471 If UNREAD is non-nil, only unread articles are selected."
5472   (interactive "P")
5473   (gnus-summary-next-article unread subject t))
5474
5475 (defun gnus-summary-prev-unread-article ()
5476   "Select unread article before current one."
5477   (interactive)
5478   (gnus-summary-prev-article
5479    (or (not (eq gnus-summary-goto-unread 'never))
5480        (gnus-summary-first-article-p (gnus-summary-article-number)))
5481    (and gnus-auto-select-same
5482         (gnus-summary-article-subject))))
5483
5484 (defun gnus-summary-next-page (&optional lines circular)
5485   "Show next page of the selected article.
5486 If at the end of the current article, select the next article.
5487 LINES says how many lines should be scrolled up.
5488
5489 If CIRCULAR is non-nil, go to the start of the article instead of
5490 selecting the next article when reaching the end of the current
5491 article."
5492   (interactive "P")
5493   (setq gnus-summary-buffer (current-buffer))
5494   (gnus-set-global-variables)
5495   (let ((article (gnus-summary-article-number))
5496         (article-window (get-buffer-window gnus-article-buffer t))
5497         endp)
5498     (gnus-configure-windows 'article)
5499     (if (eq (cdr (assq article gnus-newsgroup-reads)) gnus-canceled-mark)
5500         (if (and (eq gnus-summary-goto-unread 'never)
5501                  (not (gnus-summary-last-article-p article)))
5502             (gnus-summary-next-article)
5503           (gnus-summary-next-unread-article))
5504       (if (or (null gnus-current-article)
5505               (null gnus-article-current)
5506               (/= article (cdr gnus-article-current))
5507               (not (equal (car gnus-article-current) gnus-newsgroup-name)))
5508           ;; Selected subject is different from current article's.
5509           (gnus-summary-display-article article)
5510         (when article-window
5511           (gnus-eval-in-buffer-window gnus-article-buffer
5512             (setq endp (gnus-article-next-page lines)))
5513           (when endp
5514             (cond (circular
5515                    (gnus-summary-beginning-of-article))
5516                   (lines
5517                    (gnus-message 3 "End of message"))
5518                   ((null lines)
5519                    (if (and (eq gnus-summary-goto-unread 'never)
5520                             (not (gnus-summary-last-article-p article)))
5521                        (gnus-summary-next-article)
5522                      (gnus-summary-next-unread-article))))))))
5523     (gnus-summary-recenter)
5524     (gnus-summary-position-point)))
5525
5526 (defun gnus-summary-prev-page (&optional lines move)
5527   "Show previous page of selected article.
5528 Argument LINES specifies lines to be scrolled down.
5529 If MOVE, move to the previous unread article if point is at
5530 the beginning of the buffer."
5531   (interactive "P")
5532   (gnus-set-global-variables)
5533   (let ((article (gnus-summary-article-number))
5534         (article-window (get-buffer-window gnus-article-buffer t))
5535         endp)
5536     (gnus-configure-windows 'article)
5537     (if (or (null gnus-current-article)
5538             (null gnus-article-current)
5539             (/= article (cdr gnus-article-current))
5540             (not (equal (car gnus-article-current) gnus-newsgroup-name)))
5541         ;; Selected subject is different from current article's.
5542         (gnus-summary-display-article article)
5543       (gnus-summary-recenter)
5544       (when article-window
5545         (gnus-eval-in-buffer-window gnus-article-buffer
5546           (setq endp (gnus-article-prev-page lines)))
5547         (when (and move endp)
5548           (cond (lines
5549                  (gnus-message 3 "Beginning of message"))
5550                 ((null lines)
5551                  (if (and (eq gnus-summary-goto-unread 'never)
5552                           (not (gnus-summary-first-article-p article)))
5553                      (gnus-summary-prev-article)
5554                    (gnus-summary-prev-unread-article))))))))
5555   (gnus-summary-position-point))
5556
5557 (defun gnus-summary-prev-page-or-article (&optional lines)
5558   "Show previous page of selected article.
5559 Argument LINES specifies lines to be scrolled down.
5560 If at the beginning of the article, go to the next article."
5561   (interactive "P")
5562   (gnus-summary-prev-page lines t))
5563
5564 (defun gnus-summary-scroll-up (lines)
5565   "Scroll up (or down) one line current article.
5566 Argument LINES specifies lines to be scrolled up (or down if negative)."
5567   (interactive "p")
5568   (gnus-set-global-variables)
5569   (gnus-configure-windows 'article)
5570   (gnus-summary-show-thread)
5571   (when (eq (gnus-summary-select-article nil nil 'pseudo) 'old)
5572     (gnus-eval-in-buffer-window gnus-article-buffer
5573       (cond ((> lines 0)
5574              (when (gnus-article-next-page lines)
5575                (gnus-message 3 "End of message")))
5576             ((< lines 0)
5577              (gnus-article-prev-page (- lines))))))
5578   (gnus-summary-recenter)
5579   (gnus-summary-position-point))
5580
5581 (defun gnus-summary-next-same-subject ()
5582   "Select next article which has the same subject as current one."
5583   (interactive)
5584   (gnus-set-global-variables)
5585   (gnus-summary-next-article nil (gnus-summary-article-subject)))
5586
5587 (defun gnus-summary-prev-same-subject ()
5588   "Select previous article which has the same subject as current one."
5589   (interactive)
5590   (gnus-set-global-variables)
5591   (gnus-summary-prev-article nil (gnus-summary-article-subject)))
5592
5593 (defun gnus-summary-next-unread-same-subject ()
5594   "Select next unread article which has the same subject as current one."
5595   (interactive)
5596   (gnus-set-global-variables)
5597   (gnus-summary-next-article t (gnus-summary-article-subject)))
5598
5599 (defun gnus-summary-prev-unread-same-subject ()
5600   "Select previous unread article which has the same subject as current one."
5601   (interactive)
5602   (gnus-set-global-variables)
5603   (gnus-summary-prev-article t (gnus-summary-article-subject)))
5604
5605 (defun gnus-summary-first-unread-article ()
5606   "Select the first unread article.
5607 Return nil if there are no unread articles."
5608   (interactive)
5609   (gnus-set-global-variables)
5610   (prog1
5611       (when (gnus-summary-first-subject t)
5612         (gnus-summary-show-thread)
5613         (gnus-summary-first-subject t)
5614         (gnus-summary-display-article (gnus-summary-article-number)))
5615     (gnus-summary-position-point)))
5616
5617 (defun gnus-summary-first-article ()
5618   "Select the first article.
5619 Return nil if there are no articles."
5620   (interactive)
5621   (gnus-set-global-variables)
5622   (prog1
5623       (when (gnus-summary-first-subject)
5624       (gnus-summary-show-thread)
5625       (gnus-summary-first-subject)
5626       (gnus-summary-display-article (gnus-summary-article-number)))
5627     (gnus-summary-position-point)))
5628
5629 (defun gnus-summary-best-unread-article ()
5630   "Select the unread article with the highest score."
5631   (interactive)
5632   (gnus-set-global-variables)
5633   (let ((best -1000000)
5634         (data gnus-newsgroup-data)
5635         article score)
5636     (while data
5637       (and (gnus-data-unread-p (car data))
5638            (> (setq score
5639                     (gnus-summary-article-score (gnus-data-number (car data))))
5640               best)
5641            (setq best score
5642                  article (gnus-data-number (car data))))
5643       (setq data (cdr data)))
5644     (prog1
5645         (if article
5646             (gnus-summary-goto-article article)
5647           (error "No unread articles"))
5648       (gnus-summary-position-point))))
5649
5650 (defun gnus-summary-last-subject ()
5651   "Go to the last displayed subject line in the group."
5652   (let ((article (gnus-data-number (car (gnus-data-list t)))))
5653     (when article
5654       (gnus-summary-goto-subject article))))
5655
5656 (defun gnus-summary-goto-article (article &optional all-headers force)
5657   "Fetch ARTICLE and display it if it exists.
5658 If ALL-HEADERS is non-nil, no header lines are hidden."
5659   (interactive
5660    (list
5661     (string-to-int
5662      (completing-read
5663       "Article number: "
5664       (mapcar (lambda (number) (list (int-to-string number)))
5665               gnus-newsgroup-limit)))
5666     current-prefix-arg
5667     t))
5668   (prog1
5669       (if (gnus-summary-goto-subject article force)
5670           (gnus-summary-display-article article all-headers)
5671         (gnus-message 4 "Couldn't go to article %s" article) nil)
5672     (gnus-summary-position-point)))
5673
5674 (defun gnus-summary-goto-last-article ()
5675   "Go to the previously read article."
5676   (interactive)
5677   (prog1
5678       (when gnus-last-article
5679         (gnus-summary-goto-article gnus-last-article))
5680     (gnus-summary-position-point)))
5681
5682 (defun gnus-summary-pop-article (number)
5683   "Pop one article off the history and go to the previous.
5684 NUMBER articles will be popped off."
5685   (interactive "p")
5686   (let (to)
5687     (setq gnus-newsgroup-history
5688           (cdr (setq to (nthcdr number gnus-newsgroup-history))))
5689     (if to
5690         (gnus-summary-goto-article (car to))
5691       (error "Article history empty")))
5692   (gnus-summary-position-point))
5693
5694 ;; Summary commands and functions for limiting the summary buffer.
5695
5696 (defun gnus-summary-limit-to-articles (n)
5697   "Limit the summary buffer to the next N articles.
5698 If not given a prefix, use the process marked articles instead."
5699   (interactive "P")
5700   (gnus-set-global-variables)
5701   (prog1
5702       (let ((articles (gnus-summary-work-articles n)))
5703         (setq gnus-newsgroup-processable nil)
5704         (gnus-summary-limit articles))
5705     (gnus-summary-position-point)))
5706
5707 (defun gnus-summary-pop-limit (&optional total)
5708   "Restore the previous limit.
5709 If given a prefix, remove all limits."
5710   (interactive "P")
5711   (gnus-set-global-variables)
5712   (when total
5713     (setq gnus-newsgroup-limits
5714           (list (mapcar (lambda (h) (mail-header-number h))
5715                         gnus-newsgroup-headers))))
5716   (unless gnus-newsgroup-limits
5717     (error "No limit to pop"))
5718   (prog1
5719       (gnus-summary-limit nil 'pop)
5720     (gnus-summary-position-point)))
5721
5722 (defun gnus-summary-limit-to-subject (subject &optional header)
5723   "Limit the summary buffer to articles that have subjects that match a regexp."
5724   (interactive "sLimit to subject (regexp): ")
5725   (unless header
5726     (setq header "subject"))
5727   (when (not (equal "" subject))
5728     (prog1
5729         (let ((articles (gnus-summary-find-matching
5730                          (or header "subject") subject 'all)))
5731           (unless articles
5732             (error "Found no matches for \"%s\"" subject))
5733           (gnus-summary-limit articles))
5734       (gnus-summary-position-point))))
5735
5736 (defun gnus-summary-limit-to-author (from)
5737   "Limit the summary buffer to articles that have authors that match a regexp."
5738   (interactive "sLimit to author (regexp): ")
5739   (gnus-summary-limit-to-subject from "from"))
5740
5741 (defun gnus-summary-limit-to-age (age &optional younger-p)
5742   "Limit the summary buffer to articles that are older than (or equal) AGE days.
5743 If YOUNGER-P (the prefix) is non-nil, limit the summary buffer to
5744 articles that are younger than AGE days."
5745   (interactive "nTime in days: \nP")
5746   (prog1
5747       (let ((data gnus-newsgroup-data)
5748             (cutoff (nnmail-days-to-time age))
5749             articles d date is-younger)
5750         (while (setq d (pop data))
5751           (when (and (vectorp (gnus-data-header d))
5752                      (setq date (mail-header-date (gnus-data-header d))))
5753             (setq is-younger (nnmail-time-less
5754                               (nnmail-time-since (nnmail-date-to-time date))
5755                               cutoff))
5756             (when (if younger-p is-younger (not is-younger))
5757               (push (gnus-data-number d) articles))))
5758         (gnus-summary-limit (nreverse articles)))
5759     (gnus-summary-position-point)))
5760
5761 (defalias 'gnus-summary-delete-marked-as-read 'gnus-summary-limit-to-unread)
5762 (make-obsolete
5763  'gnus-summary-delete-marked-as-read 'gnus-summary-limit-to-unread)
5764
5765 (defun gnus-summary-limit-to-unread (&optional all)
5766   "Limit the summary buffer to articles that are not marked as read.
5767 If ALL is non-nil, limit strictly to unread articles."
5768   (interactive "P")
5769   (if all
5770       (gnus-summary-limit-to-marks (char-to-string gnus-unread-mark))
5771     (gnus-summary-limit-to-marks
5772      ;; Concat all the marks that say that an article is read and have
5773      ;; those removed.
5774      (list gnus-del-mark gnus-read-mark gnus-ancient-mark
5775            gnus-killed-mark gnus-kill-file-mark
5776            gnus-low-score-mark gnus-expirable-mark
5777            gnus-canceled-mark gnus-catchup-mark gnus-sparse-mark
5778            gnus-duplicate-mark gnus-souped-mark)
5779      'reverse)))
5780
5781 (defalias 'gnus-summary-delete-marked-with 'gnus-summary-limit-exclude-marks)
5782 (make-obsolete 'gnus-summary-delete-marked-with
5783                'gnus-summary-limit-exlude-marks)
5784
5785 (defun gnus-summary-limit-exclude-marks (marks &optional reverse)
5786   "Exclude articles that are marked with MARKS (e.g. \"DK\").
5787 If REVERSE, limit the summary buffer to articles that are marked
5788 with MARKS.  MARKS can either be a string of marks or a list of marks.
5789 Returns how many articles were removed."
5790   (interactive "sMarks: ")
5791   (gnus-summary-limit-to-marks marks t))
5792
5793 (defun gnus-summary-limit-to-marks (marks &optional reverse)
5794   "Limit the summary buffer to articles that are marked with MARKS (e.g. \"DK\").
5795 If REVERSE (the prefix), limit the summary buffer to articles that are
5796 not marked with MARKS.  MARKS can either be a string of marks or a
5797 list of marks.
5798 Returns how many articles were removed."
5799   (interactive (list (read-string "Marks: ") current-prefix-arg))
5800   (gnus-set-global-variables)
5801   (prog1
5802       (let ((data gnus-newsgroup-data)
5803             (marks (if (listp marks) marks
5804                      (append marks nil))) ; Transform to list.
5805             articles)
5806         (while data
5807           (when (if reverse (not (memq (gnus-data-mark (car data)) marks))
5808                   (memq (gnus-data-mark (car data)) marks))
5809             (push (gnus-data-number (car data)) articles))
5810           (setq data (cdr data)))
5811         (gnus-summary-limit articles))
5812     (gnus-summary-position-point)))
5813
5814 (defun gnus-summary-limit-to-score (&optional score)
5815   "Limit to articles with score at or above SCORE."
5816   (interactive "P")
5817   (gnus-set-global-variables)
5818   (setq score (if score
5819                   (prefix-numeric-value score)
5820                 (or gnus-summary-default-score 0)))
5821   (let ((data gnus-newsgroup-data)
5822         articles)
5823     (while data
5824       (when (>= (gnus-summary-article-score (gnus-data-number (car data)))
5825                 score)
5826         (push (gnus-data-number (car data)) articles))
5827       (setq data (cdr data)))
5828     (prog1
5829         (gnus-summary-limit articles)
5830       (gnus-summary-position-point))))
5831
5832 (defun gnus-summary-limit-include-dormant ()
5833   "Display all the hidden articles that are marked as dormant."
5834   (interactive)
5835   (gnus-set-global-variables)
5836   (unless gnus-newsgroup-dormant
5837     (error "There are no dormant articles in this group"))
5838   (prog1
5839       (gnus-summary-limit (append gnus-newsgroup-dormant gnus-newsgroup-limit))
5840     (gnus-summary-position-point)))
5841
5842 (defun gnus-summary-limit-exclude-dormant ()
5843   "Hide all dormant articles."
5844   (interactive)
5845   (gnus-set-global-variables)
5846   (prog1
5847       (gnus-summary-limit-to-marks (list gnus-dormant-mark) 'reverse)
5848     (gnus-summary-position-point)))
5849
5850 (defun gnus-summary-limit-exclude-childless-dormant ()
5851   "Hide all dormant articles that have no children."
5852   (interactive)
5853   (gnus-set-global-variables)
5854   (let ((data (gnus-data-list t))
5855         articles d children)
5856     ;; Find all articles that are either not dormant or have
5857     ;; children.
5858     (while (setq d (pop data))
5859       (when (or (not (= (gnus-data-mark d) gnus-dormant-mark))
5860                 (and (setq children
5861                            (gnus-article-children (gnus-data-number d)))
5862                      (let (found)
5863                        (while children
5864                          (when (memq (car children) articles)
5865                            (setq children nil
5866                                  found t))
5867                          (pop children))
5868                        found)))
5869         (push (gnus-data-number d) articles)))
5870     ;; Do the limiting.
5871     (prog1
5872         (gnus-summary-limit articles)
5873       (gnus-summary-position-point))))
5874
5875 (defun gnus-summary-limit-mark-excluded-as-read (&optional all)
5876   "Mark all unread excluded articles as read.
5877 If ALL, mark even excluded ticked and dormants as read."
5878   (interactive "P")
5879   (let ((articles (gnus-sorted-complement
5880                    (sort
5881                     (mapcar (lambda (h) (mail-header-number h))
5882                             gnus-newsgroup-headers)
5883                     '<)
5884                    (sort gnus-newsgroup-limit '<)))
5885         article)
5886     (setq gnus-newsgroup-unreads gnus-newsgroup-limit)
5887     (if all
5888         (setq gnus-newsgroup-dormant nil
5889               gnus-newsgroup-marked nil
5890               gnus-newsgroup-reads
5891               (nconc
5892                (mapcar (lambda (n) (cons n gnus-catchup-mark)) articles)
5893                gnus-newsgroup-reads))
5894       (while (setq article (pop articles))
5895         (unless (or (memq article gnus-newsgroup-dormant)
5896                     (memq article gnus-newsgroup-marked))
5897           (push (cons article gnus-catchup-mark) gnus-newsgroup-reads))))))
5898
5899 (defun gnus-summary-limit (articles &optional pop)
5900   (if pop
5901       ;; We pop the previous limit off the stack and use that.
5902       (setq articles (car gnus-newsgroup-limits)
5903             gnus-newsgroup-limits (cdr gnus-newsgroup-limits))
5904     ;; We use the new limit, so we push the old limit on the stack.
5905     (push gnus-newsgroup-limit gnus-newsgroup-limits))
5906   ;; Set the limit.
5907   (setq gnus-newsgroup-limit articles)
5908   (let ((total (length gnus-newsgroup-data))
5909         (data (gnus-data-find-list (gnus-summary-article-number)))
5910         (gnus-summary-mark-below nil)   ; Inhibit this.
5911         found)
5912     ;; This will do all the work of generating the new summary buffer
5913     ;; according to the new limit.
5914     (gnus-summary-prepare)
5915     ;; Hide any threads, possibly.
5916     (and gnus-show-threads
5917          gnus-thread-hide-subtree
5918          (gnus-summary-hide-all-threads))
5919     ;; Try to return to the article you were at, or one in the
5920     ;; neighborhood.
5921     (when data
5922       ;; We try to find some article after the current one.
5923       (while data
5924         (when (gnus-summary-goto-subject (gnus-data-number (car data)) nil t)
5925           (setq data nil
5926                 found t))
5927         (setq data (cdr data))))
5928     (unless found
5929       ;; If there is no data, that means that we were after the last
5930       ;; article.  The same goes when we can't find any articles
5931       ;; after the current one.
5932       (goto-char (point-max))
5933       (gnus-summary-find-prev))
5934     ;; We return how many articles were removed from the summary
5935     ;; buffer as a result of the new limit.
5936     (- total (length gnus-newsgroup-data))))
5937
5938 (defsubst gnus-invisible-cut-children (threads)
5939   (let ((num 0))
5940     (while threads
5941       (when (memq (mail-header-number (caar threads)) gnus-newsgroup-limit)
5942         (incf num))
5943       (pop threads))
5944     (< num 2)))
5945
5946 (defsubst gnus-cut-thread (thread)
5947   "Go forwards in the thread until we find an article that we want to display."
5948   (when (or (eq gnus-fetch-old-headers 'some)
5949             (eq gnus-build-sparse-threads 'some)
5950             (eq gnus-build-sparse-threads 'more))
5951     ;; Deal with old-fetched headers and sparse threads.
5952     (while (and
5953             thread
5954             (or
5955              (gnus-summary-article-sparse-p (mail-header-number (car thread)))
5956              (gnus-summary-article-ancient-p
5957               (mail-header-number (car thread))))
5958             (progn
5959               (if (<= (length (cdr thread)) 1)
5960                   (setq gnus-newsgroup-limit
5961                         (delq (mail-header-number (car thread))
5962                               gnus-newsgroup-limit)
5963                         thread (cadr thread))
5964                 (when (gnus-invisible-cut-children (cdr thread))
5965                   (let ((th (cdr thread)))
5966                     (while th
5967                       (if (memq (mail-header-number (caar th))
5968                                 gnus-newsgroup-limit)
5969                           (setq thread (car th)
5970                                 th nil)
5971                         (setq th (cdr th)))))))))))
5972   thread)
5973
5974 (defun gnus-cut-threads (threads)
5975   "Cut off all uninteresting articles from the beginning of threads."
5976   (when (or (eq gnus-fetch-old-headers 'some)
5977             (eq gnus-build-sparse-threads 'some)
5978             (eq gnus-build-sparse-threads 'more))
5979     (let ((th threads))
5980       (while th
5981         (setcar th (gnus-cut-thread (car th)))
5982         (setq th (cdr th)))))
5983   ;; Remove nixed out threads.
5984   (delq nil threads))
5985
5986 (defun gnus-summary-initial-limit (&optional show-if-empty)
5987   "Figure out what the initial limit is supposed to be on group entry.
5988 This entails weeding out unwanted dormants, low-scored articles,
5989 fetch-old-headers verbiage, and so on."
5990   ;; Most groups have nothing to remove.
5991   (if (or gnus-inhibit-limiting
5992           (and (null gnus-newsgroup-dormant)
5993                (not (eq gnus-fetch-old-headers 'some))
5994                (null gnus-summary-expunge-below)
5995                (not (eq gnus-build-sparse-threads 'some))
5996                (not (eq gnus-build-sparse-threads 'more))
5997                (null gnus-thread-expunge-below)
5998                (not gnus-use-nocem)))
5999       ()                                ; Do nothing.
6000     (push gnus-newsgroup-limit gnus-newsgroup-limits)
6001     (setq gnus-newsgroup-limit nil)
6002     (mapatoms
6003      (lambda (node)
6004        (unless (car (symbol-value node))
6005          ;; These threads have no parents -- they are roots.
6006          (let ((nodes (cdr (symbol-value node)))
6007                thread)
6008            (while nodes
6009              (if (and gnus-thread-expunge-below
6010                       (< (gnus-thread-total-score (car nodes))
6011                          gnus-thread-expunge-below))
6012                  (gnus-expunge-thread (pop nodes))
6013                (setq thread (pop nodes))
6014                (gnus-summary-limit-children thread))))))
6015      gnus-newsgroup-dependencies)
6016     ;; If this limitation resulted in an empty group, we might
6017     ;; pop the previous limit and use it instead.
6018     (when (and (not gnus-newsgroup-limit)
6019                show-if-empty)
6020       (setq gnus-newsgroup-limit (pop gnus-newsgroup-limits)))
6021     gnus-newsgroup-limit))
6022
6023 (defun gnus-summary-limit-children (thread)
6024   "Return 1 if this subthread is visible and 0 if it is not."
6025   ;; First we get the number of visible children to this thread.  This
6026   ;; is done by recursing down the thread using this function, so this
6027   ;; will really go down to a leaf article first, before slowly
6028   ;; working its way up towards the root.
6029   (when thread
6030     (let ((children
6031            (if (cdr thread)
6032                (apply '+ (mapcar 'gnus-summary-limit-children
6033                                  (cdr thread)))
6034              0))
6035           (number (mail-header-number (car thread)))
6036           score)
6037       (if (and
6038            (not (memq number gnus-newsgroup-marked))
6039            (or
6040             ;; If this article is dormant and has absolutely no visible
6041             ;; children, then this article isn't visible.
6042             (and (memq number gnus-newsgroup-dormant)
6043                  (zerop children))
6044             ;; If this is "fetch-old-headered" and there is no
6045             ;; visible children, then we don't want this article.
6046             (and (eq gnus-fetch-old-headers 'some)
6047                  (gnus-summary-article-ancient-p number)
6048                  (zerop children))
6049             ;; If this is a sparsely inserted article with no children,
6050             ;; we don't want it.
6051             (and (eq gnus-build-sparse-threads 'some)
6052                  (gnus-summary-article-sparse-p number)
6053                  (zerop children))
6054             ;; If we use expunging, and this article is really
6055             ;; low-scored, then we don't want this article.
6056             (when (and gnus-summary-expunge-below
6057                        (< (setq score
6058                                 (or (cdr (assq number gnus-newsgroup-scored))
6059                                     gnus-summary-default-score))
6060                           gnus-summary-expunge-below))
6061               ;; We increase the expunge-tally here, but that has
6062               ;; nothing to do with the limits, really.
6063               (incf gnus-newsgroup-expunged-tally)
6064               ;; We also mark as read here, if that's wanted.
6065               (when (and gnus-summary-mark-below
6066                          (< score gnus-summary-mark-below))
6067                 (setq gnus-newsgroup-unreads
6068                       (delq number gnus-newsgroup-unreads))
6069                 (if gnus-newsgroup-auto-expire
6070                     (push number gnus-newsgroup-expirable)
6071                   (push (cons number gnus-low-score-mark)
6072                         gnus-newsgroup-reads)))
6073               t)
6074             ;; Check NoCeM things.
6075             (if (and gnus-use-nocem
6076                      (gnus-nocem-unwanted-article-p
6077                       (mail-header-id (car thread))))
6078                 (progn
6079                   (setq gnus-newsgroup-unreads
6080                         (delq number gnus-newsgroup-unreads))
6081                   t))))
6082           ;; Nope, invisible article.
6083           0
6084         ;; Ok, this article is to be visible, so we add it to the limit
6085         ;; and return 1.
6086         (push number gnus-newsgroup-limit)
6087         1))))
6088
6089 (defun gnus-expunge-thread (thread)
6090   "Mark all articles in THREAD as read."
6091   (let* ((number (mail-header-number (car thread))))
6092     (incf gnus-newsgroup-expunged-tally)
6093     ;; We also mark as read here, if that's wanted.
6094     (setq gnus-newsgroup-unreads
6095           (delq number gnus-newsgroup-unreads))
6096     (if gnus-newsgroup-auto-expire
6097         (push number gnus-newsgroup-expirable)
6098       (push (cons number gnus-low-score-mark)
6099             gnus-newsgroup-reads)))
6100   ;; Go recursively through all subthreads.
6101   (mapcar 'gnus-expunge-thread (cdr thread)))
6102
6103 ;; Summary article oriented commands
6104
6105 (defun gnus-summary-refer-parent-article (n)
6106   "Refer parent article N times.
6107 If N is negative, go to ancestor -N instead.
6108 The difference between N and the number of articles fetched is returned."
6109   (interactive "p")
6110   (gnus-set-global-variables)
6111   (let ((skip 1)
6112         error header ref)
6113     (when (not (natnump n))
6114       (setq skip (abs n)
6115             n 1))
6116     (while (and (> n 0)
6117                 (not error))
6118       (setq header (gnus-summary-article-header))
6119       (if (and (eq (mail-header-number header)
6120                    (cdr gnus-article-current))
6121                (equal gnus-newsgroup-name
6122                       (car gnus-article-current)))
6123           ;; If we try to find the parent of the currently
6124           ;; displayed article, then we take a look at the actual
6125           ;; References header, since this is slightly more
6126           ;; reliable than the References field we got from the
6127           ;; server.
6128           (save-excursion
6129             (set-buffer gnus-original-article-buffer)
6130             (nnheader-narrow-to-headers)
6131             (unless (setq ref (message-fetch-field "references"))
6132               (setq ref (message-fetch-field "in-reply-to")))
6133             (widen))
6134         (setq ref
6135               ;; It's not the current article, so we take a bet on
6136               ;; the value we got from the server.
6137               (mail-header-references header)))
6138       (if (and ref
6139                (not (equal ref "")))
6140           (unless (gnus-summary-refer-article (gnus-parent-id ref skip))
6141             (gnus-message 1 "Couldn't find parent"))
6142         (gnus-message 1 "No references in article %d"
6143                       (gnus-summary-article-number))
6144         (setq error t))
6145       (decf n))
6146     (gnus-summary-position-point)
6147     n))
6148
6149 (defun gnus-summary-refer-references ()
6150   "Fetch all articles mentioned in the References header.
6151 Return how many articles were fetched."
6152   (interactive)
6153   (gnus-set-global-variables)
6154   (let ((ref (mail-header-references (gnus-summary-article-header)))
6155         (current (gnus-summary-article-number))
6156         (n 0))
6157     (if (or (not ref)
6158             (equal ref ""))
6159         (error "No References in the current article")
6160       ;; For each Message-ID in the References header...
6161       (while (string-match "<[^>]*>" ref)
6162         (incf n)
6163         ;; ... fetch that article.
6164         (gnus-summary-refer-article
6165          (prog1 (match-string 0 ref)
6166            (setq ref (substring ref (match-end 0))))))
6167       (gnus-summary-goto-subject current)
6168       (gnus-summary-position-point)
6169       n)))
6170
6171 (defun gnus-summary-refer-article (message-id &optional arg)
6172   "Fetch an article specified by MESSAGE-ID.
6173 If ARG (the prefix), fetch the article using `gnus-refer-article-method'
6174 or `gnus-select-method', no matter what backend the article comes from."
6175   (interactive "sMessage-ID: \nP")
6176   (when (and (stringp message-id)
6177              (not (zerop (length message-id))))
6178     ;; Construct the correct Message-ID if necessary.
6179     ;; Suggested by tale@pawl.rpi.edu.
6180     (unless (string-match "^<" message-id)
6181       (setq message-id (concat "<" message-id)))
6182     (unless (string-match ">$" message-id)
6183       (setq message-id (concat message-id ">")))
6184     (let* ((header (gnus-id-to-header message-id))
6185            (sparse (and header
6186                         (gnus-summary-article-sparse-p
6187                          (mail-header-number header))
6188                         (memq (mail-header-number header)
6189                               gnus-newsgroup-limit))))
6190       (if (and header
6191                (or (not (gnus-summary-article-sparse-p
6192                          (mail-header-number header)))
6193                    sparse))
6194           (prog1
6195               ;; The article is present in the buffer, so we just go to it.
6196               (gnus-summary-goto-article
6197                (mail-header-number header) nil t)
6198             (when sparse
6199               (gnus-summary-update-article (mail-header-number header))))
6200         ;; We fetch the article
6201         (let ((gnus-override-method
6202                (cond ((gnus-news-group-p gnus-newsgroup-name)
6203                       gnus-refer-article-method)
6204                      (arg
6205                       (or gnus-refer-article-method gnus-select-method))
6206                      (t nil)))
6207               number)
6208           ;; Start the special refer-article method, if necessary.
6209           (when (and gnus-refer-article-method
6210                      (gnus-news-group-p gnus-newsgroup-name))
6211             (gnus-check-server gnus-refer-article-method))
6212           ;; Fetch the header, and display the article.
6213           (if (setq number (gnus-summary-insert-subject message-id))
6214               (gnus-summary-select-article nil nil nil number)
6215             (gnus-message 3 "Couldn't fetch article %s" message-id)))))))
6216
6217 (defun gnus-summary-enter-digest-group (&optional force)
6218   "Enter an nndoc group based on the current article.
6219 If FORCE, force a digest interpretation.  If not, try
6220 to guess what the document format is."
6221   (interactive "P")
6222   (gnus-set-global-variables)
6223   (let ((conf gnus-current-window-configuration))
6224     (save-excursion
6225       (gnus-summary-select-article))
6226     (setq gnus-current-window-configuration conf)
6227     (let* ((name (format "%s-%d"
6228                          (gnus-group-prefixed-name
6229                           gnus-newsgroup-name (list 'nndoc ""))
6230                          (save-excursion
6231                            (set-buffer gnus-summary-buffer)
6232                            gnus-current-article)))
6233            (ogroup gnus-newsgroup-name)
6234            (params (append (gnus-info-params (gnus-get-info ogroup))
6235                            (list (cons 'to-group ogroup))
6236                            (list (cons 'save-article-group ogroup))))
6237            (case-fold-search t)
6238            (buf (current-buffer))
6239            dig)
6240       (save-excursion
6241         (setq dig (nnheader-set-temp-buffer " *gnus digest buffer*"))
6242         (insert-buffer-substring gnus-original-article-buffer)
6243         ;; Remove lines that may lead nndoc to misinterpret the
6244         ;; document type.
6245         (narrow-to-region
6246          (goto-char (point-min))
6247          (or (search-forward "\n\n" nil t) (point)))
6248         (goto-char (point-min))
6249         (delete-matching-lines "^\\(Path\\):\\|^From ")
6250         (widen))
6251       (unwind-protect
6252           (if (gnus-group-read-ephemeral-group
6253                name `(nndoc ,name (nndoc-address ,(get-buffer dig))
6254                             (nndoc-article-type
6255                              ,(if force 'digest 'guess))) t)
6256               ;; Make all postings to this group go to the parent group.
6257               (nconc (gnus-info-params (gnus-get-info name))
6258                      params)
6259             ;; Couldn't select this doc group.
6260             (switch-to-buffer buf)
6261             (gnus-set-global-variables)
6262             (gnus-configure-windows 'summary)
6263             (gnus-message 3 "Article couldn't be entered?"))
6264         (kill-buffer dig)))))
6265
6266 (defun gnus-summary-read-document (n)
6267   "Open a new group based on the current article(s).
6268 This will allow you to read digests and other similar
6269 documents as newsgroups.
6270 Obeys the standard process/prefix convention."
6271   (interactive "P")
6272   (let* ((articles (gnus-summary-work-articles n))
6273          (ogroup gnus-newsgroup-name)
6274          (params (append (gnus-info-params (gnus-get-info ogroup))
6275                          (list (cons 'to-group ogroup))))
6276          article group egroup groups vgroup)
6277     (while (setq article (pop articles))
6278       (setq group (format "%s-%d" gnus-newsgroup-name article))
6279       (gnus-summary-remove-process-mark article)
6280       (when (gnus-summary-display-article article)
6281         (save-excursion
6282           (nnheader-temp-write nil
6283             (insert-buffer-substring gnus-original-article-buffer)
6284             ;; Remove some headers that may lead nndoc to make
6285             ;; the wrong guess.
6286             (message-narrow-to-head)
6287             (goto-char (point-min))
6288             (delete-matching-lines "^\\(Path\\):\\|^From ")
6289             (widen)
6290             (if (setq egroup
6291                       (gnus-group-read-ephemeral-group
6292                        group `(nndoc ,group (nndoc-address ,(current-buffer))
6293                                      (nndoc-article-type guess))
6294                        t nil t))
6295                 (progn
6296                   ;; Make all postings to this group go to the parent group.
6297                   (nconc (gnus-info-params (gnus-get-info egroup))
6298                          params)
6299                   (push egroup groups))
6300               ;; Couldn't select this doc group.
6301               (gnus-error 3 "Article couldn't be entered"))))))
6302     ;; Now we have selected all the documents.
6303     (cond
6304      ((not groups)
6305       (error "None of the articles could be interpreted as documents"))
6306      ((gnus-group-read-ephemeral-group
6307        (setq vgroup (format
6308                      "nnvirtual:%s-%s" gnus-newsgroup-name
6309                      (format-time-string "%Y%m%dT%H%M%S" (current-time))))
6310        `(nnvirtual ,vgroup (nnvirtual-component-groups ,groups))
6311        t
6312        (cons (current-buffer) 'summary)))
6313      (t
6314       (error "Couldn't select virtual nndoc group")))))
6315
6316 (defun gnus-summary-isearch-article (&optional regexp-p)
6317   "Do incremental search forward on the current article.
6318 If REGEXP-P (the prefix) is non-nil, do regexp isearch."
6319   (interactive "P")
6320   (gnus-set-global-variables)
6321   (gnus-summary-select-article)
6322   (gnus-configure-windows 'article)
6323   (gnus-eval-in-buffer-window gnus-article-buffer
6324     ;;(goto-char (point-min))
6325     (isearch-forward regexp-p)))
6326
6327 (defun gnus-summary-search-article-forward (regexp &optional backward)
6328   "Search for an article containing REGEXP forward.
6329 If BACKWARD, search backward instead."
6330   (interactive
6331    (list (read-string
6332           (format "Search article %s (regexp%s): "
6333                   (if current-prefix-arg "backward" "forward")
6334                   (if gnus-last-search-regexp
6335                       (concat ", default " gnus-last-search-regexp)
6336                     "")))
6337          current-prefix-arg))
6338   (gnus-set-global-variables)
6339   (if (string-equal regexp "")
6340       (setq regexp (or gnus-last-search-regexp ""))
6341     (setq gnus-last-search-regexp regexp))
6342   (if (gnus-summary-search-article regexp backward)
6343       (gnus-summary-show-thread)
6344     (error "Search failed: \"%s\"" regexp)))
6345
6346 (defun gnus-summary-search-article-backward (regexp)
6347   "Search for an article containing REGEXP backward."
6348   (interactive
6349    (list (read-string
6350           (format "Search article backward (regexp%s): "
6351                   (if gnus-last-search-regexp
6352                       (concat ", default " gnus-last-search-regexp)
6353                     "")))))
6354   (gnus-summary-search-article-forward regexp 'backward))
6355
6356 (defun gnus-summary-search-article (regexp &optional backward)
6357   "Search for an article containing REGEXP.
6358 Optional argument BACKWARD means do search for backward.
6359 `gnus-select-article-hook' is not called during the search."
6360   ;; We have to require this here to make sure that the following
6361   ;; dynamic binding isn't shadowed by autoloading.
6362   (require 'gnus-async)
6363   (let ((gnus-select-article-hook nil)  ;Disable hook.
6364         (gnus-article-display-hook nil)
6365         (gnus-mark-article-hook nil)    ;Inhibit marking as read.
6366         (gnus-use-article-prefetch nil)
6367         (gnus-xmas-force-redisplay nil) ;Inhibit XEmacs redisplay.
6368         (gnus-use-trees nil)            ;Inhibit updating tree buffer.
6369         (sum (current-buffer))
6370         (found nil)
6371         point)
6372     (gnus-save-hidden-threads
6373       (gnus-summary-select-article)
6374       (set-buffer gnus-article-buffer)
6375       (when backward
6376         (forward-line -1))
6377       (while (not found)
6378         (gnus-message 7 "Searching article: %d..." (cdr gnus-article-current))
6379         (if (if backward
6380                 (re-search-backward regexp nil t)
6381               (re-search-forward regexp nil t))
6382             ;; We found the regexp.
6383             (progn
6384               (setq found 'found)
6385               (beginning-of-line)
6386               (set-window-start
6387                (get-buffer-window (current-buffer))
6388                (point))
6389               (forward-line 1)
6390               (set-buffer sum)
6391               (setq point (point)))
6392           ;; We didn't find it, so we go to the next article.
6393           (set-buffer sum)
6394           (setq found 'not)
6395           (while (eq found 'not)
6396             (if (not (if backward (gnus-summary-find-prev)
6397                        (gnus-summary-find-next)))
6398                 ;; No more articles.
6399                 (setq found t)
6400               ;; Select the next article and adjust point.
6401               (unless (gnus-summary-article-sparse-p
6402                        (gnus-summary-article-number))
6403                 (setq found nil)
6404                 (gnus-summary-select-article)
6405                 (set-buffer gnus-article-buffer)
6406                 (widen)
6407                 (goto-char (if backward (point-max) (point-min))))))))
6408       (gnus-message 7 ""))
6409     ;; Return whether we found the regexp.
6410     (when (eq found 'found)
6411       (goto-char point)
6412       (gnus-summary-show-thread)
6413       (gnus-summary-goto-subject gnus-current-article)
6414       (gnus-summary-position-point)
6415       t)))
6416
6417 (defun gnus-summary-find-matching (header regexp &optional backward unread
6418                                           not-case-fold)
6419   "Return a list of all articles that match REGEXP on HEADER.
6420 The search stars on the current article and goes forwards unless
6421 BACKWARD is non-nil.  If BACKWARD is `all', do all articles.
6422 If UNREAD is non-nil, only unread articles will
6423 be taken into consideration.  If NOT-CASE-FOLD, case won't be folded
6424 in the comparisons."
6425   (let ((data (if (eq backward 'all) gnus-newsgroup-data
6426                 (gnus-data-find-list
6427                  (gnus-summary-article-number) (gnus-data-list backward))))
6428         (func `(lambda (h) (,(intern (concat "mail-header-" header)) h)))
6429         (case-fold-search (not not-case-fold))
6430         articles d)
6431     (unless (fboundp (intern (concat "mail-header-" header)))
6432       (error "%s is not a valid header" header))
6433     (while data
6434       (setq d (car data))
6435       (and (or (not unread)             ; We want all articles...
6436                (gnus-data-unread-p d))  ; Or just unreads.
6437            (vectorp (gnus-data-header d)) ; It's not a pseudo.
6438            (string-match regexp (funcall func (gnus-data-header d))) ; Match.
6439            (push (gnus-data-number d) articles)) ; Success!
6440       (setq data (cdr data)))
6441     (nreverse articles)))
6442
6443 (defun gnus-summary-execute-command (header regexp command &optional backward)
6444   "Search forward for an article whose HEADER matches REGEXP and execute COMMAND.
6445 If HEADER is an empty string (or nil), the match is done on the entire
6446 article.  If BACKWARD (the prefix) is non-nil, search backward instead."
6447   (interactive
6448    (list (let ((completion-ignore-case t))
6449            (completing-read
6450             "Header name: "
6451             (mapcar (lambda (string) (list string))
6452                     '("Number" "Subject" "From" "Lines" "Date"
6453                       "Message-ID" "Xref" "References" "Body"))
6454             nil 'require-match))
6455          (read-string "Regexp: ")
6456          (read-key-sequence "Command: ")
6457          current-prefix-arg))
6458   (when (equal header "Body")
6459     (setq header ""))
6460   (gnus-set-global-variables)
6461   ;; Hidden thread subtrees must be searched as well.
6462   (gnus-summary-show-all-threads)
6463   ;; We don't want to change current point nor window configuration.
6464   (save-excursion
6465     (save-window-excursion
6466       (gnus-message 6 "Executing %s..." (key-description command))
6467       ;; We'd like to execute COMMAND interactively so as to give arguments.
6468       (gnus-execute header regexp
6469                     `(call-interactively ',(key-binding command))
6470                     backward)
6471       (gnus-message 6 "Executing %s...done" (key-description command)))))
6472
6473 (defun gnus-summary-beginning-of-article ()
6474   "Scroll the article back to the beginning."
6475   (interactive)
6476   (gnus-set-global-variables)
6477   (gnus-summary-select-article)
6478   (gnus-configure-windows 'article)
6479   (gnus-eval-in-buffer-window gnus-article-buffer
6480     (widen)
6481     (goto-char (point-min))
6482     (when gnus-page-broken
6483       (gnus-narrow-to-page))))
6484
6485 (defun gnus-summary-end-of-article ()
6486   "Scroll to the end of the article."
6487   (interactive)
6488   (gnus-set-global-variables)
6489   (gnus-summary-select-article)
6490   (gnus-configure-windows 'article)
6491   (gnus-eval-in-buffer-window gnus-article-buffer
6492     (widen)
6493     (goto-char (point-max))
6494     (recenter -3)
6495     (when gnus-page-broken
6496       (gnus-narrow-to-page))))
6497
6498 (defun gnus-summary-print-article (&optional filename)
6499   "Generate and print a PostScript image of the article buffer.
6500
6501 If the optional argument FILENAME is nil, send the image to the printer.
6502 If FILENAME is a string, save the PostScript image in a file with that
6503 name.  If FILENAME is a number, prompt the user for the name of the file
6504 to save in."
6505   (interactive (list (ps-print-preprint current-prefix-arg)))
6506   (gnus-summary-select-article)
6507   (gnus-eval-in-buffer-window gnus-article-buffer
6508     (let ((buffer (generate-new-buffer " *print*")))
6509       (unwind-protect
6510           (progn
6511             (copy-to-buffer buffer (point-min) (point-max))
6512             (set-buffer buffer)
6513             (gnus-article-delete-invisible-text)
6514             (run-hooks 'gnus-ps-print-hook)
6515             (ps-print-buffer-with-faces filename))
6516         (kill-buffer buffer)))))
6517
6518 (defun gnus-summary-show-article (&optional arg)
6519   "Force re-fetching of the current article.
6520 If ARG (the prefix) is non-nil, show the raw article without any
6521 article massaging functions being run."
6522   (interactive "P")
6523   (gnus-set-global-variables)
6524   (if (not arg)
6525       ;; Select the article the normal way.
6526       (gnus-summary-select-article nil 'force)
6527     ;; Bind the article treatment functions to nil.
6528     (let ((gnus-have-all-headers t)
6529           gnus-article-display-hook
6530           gnus-article-prepare-hook
6531           gnus-break-pages
6532           gnus-show-mime
6533           gnus-visual)
6534       (gnus-summary-select-article nil 'force)))
6535   (gnus-summary-goto-subject gnus-current-article)
6536   (gnus-summary-position-point))
6537
6538 (defun gnus-summary-verbose-headers (&optional arg)
6539   "Toggle permanent full header display.
6540 If ARG is a positive number, turn header display on.
6541 If ARG is a negative number, turn header display off."
6542   (interactive "P")
6543   (gnus-set-global-variables)
6544   (setq gnus-show-all-headers
6545         (cond ((or (not (numberp arg))
6546                    (zerop arg))
6547                (not gnus-show-all-headers))
6548               ((natnump arg)
6549                t)))
6550   (gnus-summary-show-article))
6551
6552 (defun gnus-summary-toggle-header (&optional arg)
6553   "Show the headers if they are hidden, or hide them if they are shown.
6554 If ARG is a positive number, show the entire header.
6555 If ARG is a negative number, hide the unwanted header lines."
6556   (interactive "P")
6557   (gnus-set-global-variables)
6558   (save-excursion
6559     (set-buffer gnus-article-buffer)
6560     (let* ((buffer-read-only nil)
6561            (inhibit-point-motion-hooks t)
6562            (hidden (text-property-any
6563                     (goto-char (point-min)) (search-forward "\n\n")
6564                     'invisible t))
6565            e)
6566       (goto-char (point-min))
6567       (when (search-forward "\n\n" nil t)
6568         (delete-region (point-min) (1- (point))))
6569       (goto-char (point-min))
6570       (save-excursion
6571         (set-buffer gnus-original-article-buffer)
6572         (goto-char (point-min))
6573         (setq e (1- (or (search-forward "\n\n" nil t) (point-max)))))
6574       (insert-buffer-substring gnus-original-article-buffer 1 e)
6575       (let ((article-inhibit-hiding t))
6576         (run-hooks 'gnus-article-display-hook))
6577       (when (or (not hidden) (and (numberp arg) (< arg 0)))
6578         (gnus-article-hide-headers)))))
6579
6580 (defun gnus-summary-show-all-headers ()
6581   "Make all header lines visible."
6582   (interactive)
6583   (gnus-set-global-variables)
6584   (gnus-article-show-all-headers))
6585
6586 (defun gnus-summary-toggle-mime (&optional arg)
6587   "Toggle MIME processing.
6588 If ARG is a positive number, turn MIME processing on."
6589   (interactive "P")
6590   (gnus-set-global-variables)
6591   (setq gnus-show-mime
6592         (if (null arg) (not gnus-show-mime)
6593           (> (prefix-numeric-value arg) 0)))
6594   (gnus-summary-select-article t 'force))
6595
6596 (defun gnus-summary-caesar-message (&optional arg)
6597   "Caesar rotate the current article by 13.
6598 The numerical prefix specifies how many places to rotate each letter
6599 forward."
6600   (interactive "P")
6601   (gnus-set-global-variables)
6602   (gnus-summary-select-article)
6603   (let ((mail-header-separator ""))
6604     (gnus-eval-in-buffer-window gnus-article-buffer
6605       (save-restriction
6606         (widen)
6607         (let ((start (window-start))
6608               buffer-read-only)
6609           (message-caesar-buffer-body arg)
6610           (set-window-start (get-buffer-window (current-buffer)) start))))))
6611
6612 (defun gnus-summary-stop-page-breaking ()
6613   "Stop page breaking in the current article."
6614   (interactive)
6615   (gnus-set-global-variables)
6616   (gnus-summary-select-article)
6617   (gnus-eval-in-buffer-window gnus-article-buffer
6618     (widen)
6619     (when (gnus-visual-p 'page-marker)
6620       (let ((buffer-read-only nil))
6621         (gnus-remove-text-with-property 'gnus-prev)
6622         (gnus-remove-text-with-property 'gnus-next)))))
6623
6624 (defun gnus-summary-move-article (&optional n to-newsgroup
6625                                             select-method action)
6626   "Move the current article to a different newsgroup.
6627 If N is a positive number, move the N next articles.
6628 If N is a negative number, move the N previous articles.
6629 If N is nil and any articles have been marked with the process mark,
6630 move those articles instead.
6631 If TO-NEWSGROUP is string, do not prompt for a newsgroup to move to.
6632 If SELECT-METHOD is non-nil, do not move to a specific newsgroup, but
6633 re-spool using this method.
6634
6635 For this function to work, both the current newsgroup and the
6636 newsgroup that you want to move to have to support the `request-move'
6637 and `request-accept' functions."
6638   (interactive "P")
6639   (unless action
6640     (setq action 'move))
6641   (gnus-set-global-variables)
6642   ;; Disable marking as read.
6643   (let (gnus-mark-article-hook)
6644     (save-window-excursion
6645       (gnus-summary-select-article)))
6646   ;; Check whether the source group supports the required functions.
6647   (cond ((and (eq action 'move)
6648               (not (gnus-check-backend-function
6649                     'request-move-article gnus-newsgroup-name)))
6650          (error "The current group does not support article moving"))
6651         ((and (eq action 'crosspost)
6652               (not (gnus-check-backend-function
6653                     'request-replace-article gnus-newsgroup-name)))
6654          (error "The current group does not support article editing")))
6655   (let ((articles (gnus-summary-work-articles n))
6656         (prefix (gnus-group-real-prefix gnus-newsgroup-name))
6657         (names '((move "Move" "Moving")
6658                  (copy "Copy" "Copying")
6659                  (crosspost "Crosspost" "Crossposting")))
6660         (copy-buf (save-excursion
6661                     (nnheader-set-temp-buffer " *copy article*")))
6662         art-group to-method new-xref article to-groups)
6663     (unless (assq action names)
6664       (error "Unknown action %s" action))
6665     ;; Read the newsgroup name.
6666     (when (and (not to-newsgroup)
6667                (not select-method))
6668       (setq to-newsgroup
6669             (gnus-read-move-group-name
6670              (cadr (assq action names))
6671              (symbol-value (intern (format "gnus-current-%s-group" action)))
6672              articles prefix))
6673       (set (intern (format "gnus-current-%s-group" action)) to-newsgroup))
6674     (setq to-method (or select-method
6675                         (gnus-group-name-to-method to-newsgroup)))
6676     ;; Check the method we are to move this article to...
6677     (unless (gnus-check-backend-function
6678              'request-accept-article (car to-method))
6679       (error "%s does not support article copying" (car to-method)))
6680     (unless (gnus-check-server to-method)
6681       (error "Can't open server %s" (car to-method)))
6682     (gnus-message 6 "%s to %s: %s..."
6683                   (caddr (assq action names))
6684                   (or (car select-method) to-newsgroup) articles)
6685     (while articles
6686       (setq article (pop articles))
6687       (setq
6688        art-group
6689        (cond
6690         ;; Move the article.
6691         ((eq action 'move)
6692          ;; Remove this article from future suppression.
6693          (gnus-dup-unsuppress-article article)
6694          (gnus-request-move-article
6695           article                       ; Article to move
6696           gnus-newsgroup-name           ; From newsgroup
6697           (nth 1 (gnus-find-method-for-group
6698                   gnus-newsgroup-name)) ; Server
6699           (list 'gnus-request-accept-article
6700                 to-newsgroup (list 'quote select-method)
6701                 (not articles))         ; Accept form
6702           (not articles)))              ; Only save nov last time
6703         ;; Copy the article.
6704         ((eq action 'copy)
6705          (save-excursion
6706            (set-buffer copy-buf)
6707            (gnus-request-article-this-buffer article gnus-newsgroup-name)
6708            (gnus-request-accept-article
6709             to-newsgroup select-method (not articles))))
6710         ;; Crosspost the article.
6711         ((eq action 'crosspost)
6712          (let ((xref (message-tokenize-header
6713                       (mail-header-xref (gnus-summary-article-header article))
6714                       " ")))
6715            (setq new-xref (concat (gnus-group-real-name gnus-newsgroup-name)
6716                                   ":" article))
6717            (unless xref
6718              (setq xref (list (system-name))))
6719            (setq new-xref
6720                  (concat
6721                   (mapconcat 'identity
6722                              (delete "Xref:" (delete new-xref xref))
6723                              " ")
6724                   " " new-xref))
6725            (save-excursion
6726              (set-buffer copy-buf)
6727              ;; First put the article in the destination group.
6728              (gnus-request-article-this-buffer article gnus-newsgroup-name)
6729              (when (consp (setq art-group
6730                                 (gnus-request-accept-article
6731                                  to-newsgroup select-method (not articles))))
6732                (setq new-xref (concat new-xref " " (car art-group)
6733                                       ":" (cdr art-group)))
6734                ;; Now we have the new Xrefs header, so we insert
6735                ;; it and replace the new article.
6736                (nnheader-replace-header "Xref" new-xref)
6737                (gnus-request-replace-article
6738                 (cdr art-group) to-newsgroup (current-buffer))
6739                art-group))))))
6740       (cond
6741        ((not art-group)
6742         (gnus-message 1 "Couldn't %s article %s"
6743                       (cadr (assq action names)) article))
6744        ((and (eq art-group 'junk)
6745              (eq action 'move))
6746         (gnus-summary-mark-article article gnus-canceled-mark)
6747         (gnus-message 4 "Deleted article %s" article))
6748        (t
6749         (let* ((entry
6750                 (or
6751                  (gnus-gethash (car art-group) gnus-newsrc-hashtb)
6752                  (gnus-gethash
6753                   (gnus-group-prefixed-name
6754                    (car art-group)
6755                    (or select-method
6756                        (gnus-find-method-for-group to-newsgroup)))
6757                   gnus-newsrc-hashtb)))
6758                (info (nth 2 entry))
6759                (to-group (gnus-info-group info)))
6760           ;; Update the group that has been moved to.
6761           (when (and info
6762                      (memq action '(move copy)))
6763             (unless (member to-group to-groups)
6764               (push to-group to-groups))
6765
6766             (unless (memq article gnus-newsgroup-unreads)
6767               (gnus-info-set-read
6768                info (gnus-add-to-range (gnus-info-read info)
6769                                        (list (cdr art-group)))))
6770
6771             ;; Copy any marks over to the new group.
6772             (let ((marks gnus-article-mark-lists)
6773                   (to-article (cdr art-group)))
6774
6775               ;; See whether the article is to be put in the cache.
6776               (when gnus-use-cache
6777                 (gnus-cache-possibly-enter-article
6778                  to-group to-article
6779                  (let ((header (copy-sequence
6780                                 (gnus-summary-article-header article))))
6781                    (mail-header-set-number header to-article)
6782                    header)
6783                  (memq article gnus-newsgroup-marked)
6784                  (memq article gnus-newsgroup-dormant)
6785                  (memq article gnus-newsgroup-unreads)))
6786
6787               (when (and (equal to-group gnus-newsgroup-name)
6788                          (not (memq article gnus-newsgroup-unreads)))
6789                 ;; Mark this article as read in this group.
6790                 (push (cons to-article gnus-read-mark) gnus-newsgroup-reads)
6791                 (setcdr (gnus-active to-group) to-article)
6792                 (setcdr gnus-newsgroup-active to-article))
6793
6794               (while marks
6795                 (when (memq article (symbol-value
6796                                      (intern (format "gnus-newsgroup-%s"
6797                                                      (caar marks)))))
6798                   ;; If the other group is the same as this group,
6799                   ;; then we have to add the mark to the list.
6800                   (when (equal to-group gnus-newsgroup-name)
6801                     (set (intern (format "gnus-newsgroup-%s" (caar marks)))
6802                          (cons to-article
6803                                (symbol-value
6804                                 (intern (format "gnus-newsgroup-%s"
6805                                                 (caar marks)))))))
6806                   ;; Copy the marks to other group.
6807                   (gnus-add-marked-articles
6808                    to-group (cdar marks) (list to-article) info))
6809                 (setq marks (cdr marks)))
6810
6811               (gnus-dribble-enter
6812                (concat "(gnus-group-set-info '"
6813                        (gnus-prin1-to-string (gnus-get-info to-group))
6814                        ")"))))
6815
6816           ;; Update the Xref header in this article to point to
6817           ;; the new crossposted article we have just created.
6818           (when (eq action 'crosspost)
6819             (save-excursion
6820               (set-buffer copy-buf)
6821               (gnus-request-article-this-buffer article gnus-newsgroup-name)
6822               (nnheader-replace-header "Xref" new-xref)
6823               (gnus-request-replace-article
6824                article gnus-newsgroup-name (current-buffer)))))
6825
6826         (gnus-summary-goto-subject article)
6827         (when (eq action 'move)
6828           (gnus-summary-mark-article article gnus-canceled-mark))))
6829       (gnus-summary-remove-process-mark article))
6830     ;; Re-activate all groups that have been moved to.
6831     (while to-groups
6832       (save-excursion
6833         (set-buffer gnus-group-buffer)
6834         (when (gnus-group-goto-group (car to-groups) t)
6835           (gnus-group-get-new-news-this-group 1 t))
6836         (pop to-groups)))
6837
6838     (gnus-kill-buffer copy-buf)
6839     (gnus-summary-position-point)
6840     (gnus-set-mode-line 'summary)))
6841
6842 (defun gnus-summary-copy-article (&optional n to-newsgroup select-method)
6843   "Move the current article to a different newsgroup.
6844 If TO-NEWSGROUP is string, do not prompt for a newsgroup to move to.
6845 If SELECT-METHOD is non-nil, do not move to a specific newsgroup, but
6846 re-spool using this method."
6847   (interactive "P")
6848   (gnus-summary-move-article n to-newsgroup select-method 'copy))
6849
6850 (defun gnus-summary-crosspost-article (&optional n)
6851   "Crosspost the current article to some other group."
6852   (interactive "P")
6853   (gnus-summary-move-article n nil nil 'crosspost))
6854
6855 (defcustom gnus-summary-respool-default-method nil
6856   "Default method for respooling an article.
6857 If nil, use to the current newsgroup method."
6858   :type 'gnus-select-method-name
6859   :group 'gnus-summary-mail)
6860
6861 (defun gnus-summary-respool-article (&optional n method)
6862   "Respool the current article.
6863 The article will be squeezed through the mail spooling process again,
6864 which means that it will be put in some mail newsgroup or other
6865 depending on `nnmail-split-methods'.
6866 If N is a positive number, respool the N next articles.
6867 If N is a negative number, respool the N previous articles.
6868 If N is nil and any articles have been marked with the process mark,
6869 respool those articles instead.
6870
6871 Respooling can be done both from mail groups and \"real\" newsgroups.
6872 In the former case, the articles in question will be moved from the
6873 current group into whatever groups they are destined to.  In the
6874 latter case, they will be copied into the relevant groups."
6875   (interactive
6876    (list current-prefix-arg
6877          (let* ((methods (gnus-methods-using 'respool))
6878                 (methname
6879                  (symbol-name (or gnus-summary-respool-default-method
6880                                   (car (gnus-find-method-for-group
6881                                         gnus-newsgroup-name)))))
6882                 (method
6883                  (gnus-completing-read
6884                   methname "What backend do you want to use when respooling?"
6885                   methods nil t nil 'gnus-mail-method-history))
6886                 ms)
6887            (cond
6888             ((zerop (length (setq ms (gnus-servers-using-backend
6889                                       (intern method)))))
6890              (list (intern method) ""))
6891             ((= 1 (length ms))
6892              (car ms))
6893             (t
6894              (let ((ms-alist (mapcar (lambda (m) (cons (cadr m) m)) ms)))
6895                (cdr (assoc (completing-read "Server name: " ms-alist nil t)
6896                            ms-alist))))))))
6897   (gnus-set-global-variables)
6898   (unless method
6899     (error "No method given for respooling"))
6900   (if (assoc (symbol-name
6901               (car (gnus-find-method-for-group gnus-newsgroup-name)))
6902              (gnus-methods-using 'respool))
6903       (gnus-summary-move-article n nil method)
6904     (gnus-summary-copy-article n nil method)))
6905
6906 (defun gnus-summary-import-article (file)
6907   "Import a random file into a mail newsgroup."
6908   (interactive "fImport file: ")
6909   (gnus-set-global-variables)
6910   (let ((group gnus-newsgroup-name)
6911         (now (current-time))
6912         atts lines)
6913     (unless (gnus-check-backend-function 'request-accept-article group)
6914       (error "%s does not support article importing" group))
6915     (or (file-readable-p file)
6916         (not (file-regular-p file))
6917         (error "Can't read %s" file))
6918     (save-excursion
6919       (set-buffer (get-buffer-create " *import file*"))
6920       (buffer-disable-undo (current-buffer))
6921       (erase-buffer)
6922       (insert-file-contents file)
6923       (goto-char (point-min))
6924       (unless (nnheader-article-p)
6925         ;; This doesn't look like an article, so we fudge some headers.
6926         (setq atts (file-attributes file)
6927               lines (count-lines (point-min) (point-max)))
6928         (insert "From: " (read-string "From: ") "\n"
6929                 "Subject: " (read-string "Subject: ") "\n"
6930                 "Date: " (timezone-make-date-arpa-standard
6931                           (current-time-string (nth 5 atts))
6932                           (current-time-zone now)
6933                           (current-time-zone now))
6934                 "\n"
6935                 "Message-ID: " (message-make-message-id) "\n"
6936                 "Lines: " (int-to-string lines) "\n"
6937                 "Chars: " (int-to-string (nth 7 atts)) "\n\n"))
6938       (gnus-request-accept-article group nil t)
6939       (kill-buffer (current-buffer)))))
6940
6941 (defun gnus-summary-article-posted-p ()
6942   "Say whether the current (mail) article is available from `gnus-select-method' as well.
6943 This will be the case if the article has both been mailed and posted."
6944   (interactive)
6945   (let ((id (mail-header-references (gnus-summary-article-header)))
6946         (gnus-override-method
6947          (or gnus-refer-article-method gnus-select-method)))
6948     (if (gnus-request-head id "")
6949         (gnus-message 2 "The current message was found on %s"
6950                       gnus-override-method)
6951       (gnus-message 2 "The current message couldn't be found on %s"
6952                     gnus-override-method)
6953       nil)))
6954
6955 (defun gnus-summary-expire-articles (&optional now)
6956   "Expire all articles that are marked as expirable in the current group."
6957   (interactive)
6958   (gnus-set-global-variables)
6959   (when (gnus-check-backend-function
6960          'request-expire-articles gnus-newsgroup-name)
6961     ;; This backend supports expiry.
6962     (let* ((total (gnus-group-total-expirable-p gnus-newsgroup-name))
6963            (expirable (if total
6964                           (progn
6965                             ;; We need to update the info for
6966                             ;; this group for `gnus-list-of-read-articles'
6967                             ;; to give us the right answer.
6968                             (run-hooks 'gnus-exit-group-hook)
6969                             (gnus-summary-update-info)
6970                             (gnus-list-of-read-articles gnus-newsgroup-name))
6971                         (setq gnus-newsgroup-expirable
6972                               (sort gnus-newsgroup-expirable '<))))
6973            (expiry-wait (if now 'immediate
6974                           (gnus-group-find-parameter
6975                            gnus-newsgroup-name 'expiry-wait)))
6976            es)
6977       (when expirable
6978         ;; There are expirable articles in this group, so we run them
6979         ;; through the expiry process.
6980         (gnus-message 6 "Expiring articles...")
6981         ;; The list of articles that weren't expired is returned.
6982         (if expiry-wait
6983             (let ((nnmail-expiry-wait-function nil)
6984                   (nnmail-expiry-wait expiry-wait))
6985               (setq es (gnus-request-expire-articles
6986                         expirable gnus-newsgroup-name)))
6987           (setq es (gnus-request-expire-articles
6988                     expirable gnus-newsgroup-name)))
6989         (unless total
6990           (setq gnus-newsgroup-expirable es))
6991         ;; We go through the old list of expirable, and mark all
6992         ;; really expired articles as nonexistent.
6993         (unless (eq es expirable)       ;If nothing was expired, we don't mark.
6994           (let ((gnus-use-cache nil))
6995             (while expirable
6996               (unless (memq (car expirable) es)
6997                 (when (gnus-data-find (car expirable))
6998                   (gnus-summary-mark-article
6999                    (car expirable) gnus-canceled-mark)))
7000               (setq expirable (cdr expirable)))))
7001         (gnus-message 6 "Expiring articles...done")))))
7002
7003 (defun gnus-summary-expire-articles-now ()
7004   "Expunge all expirable articles in the current group.
7005 This means that *all* articles that are marked as expirable will be
7006 deleted forever, right now."
7007   (interactive)
7008   (gnus-set-global-variables)
7009   (or gnus-expert-user
7010       (gnus-yes-or-no-p
7011        "Are you really, really, really sure you want to delete all these messages? ")
7012       (error "Phew!"))
7013   (gnus-summary-expire-articles t))
7014
7015 ;; Suggested by Jack Vinson <vinson@unagi.cis.upenn.edu>.
7016 (defun gnus-summary-delete-article (&optional n)
7017   "Delete the N next (mail) articles.
7018 This command actually deletes articles.  This is not a marking
7019 command.  The article will disappear forever from your life, never to
7020 return.
7021 If N is negative, delete backwards.
7022 If N is nil and articles have been marked with the process mark,
7023 delete these instead."
7024   (interactive "P")
7025   (gnus-set-global-variables)
7026   (unless (gnus-check-backend-function 'request-expire-articles
7027                                        gnus-newsgroup-name)
7028     (error "The current newsgroup does not support article deletion"))
7029   ;; Compute the list of articles to delete.
7030   (let ((articles (gnus-summary-work-articles n))
7031         not-deleted)
7032     (if (and gnus-novice-user
7033              (not (gnus-yes-or-no-p
7034                    (format "Do you really want to delete %s forever? "
7035                            (if (> (length articles) 1)
7036                                (format "these %s articles" (length articles))
7037                              "this article")))))
7038         ()
7039       ;; Delete the articles.
7040       (setq not-deleted (gnus-request-expire-articles
7041                          articles gnus-newsgroup-name 'force))
7042       (while articles
7043         (gnus-summary-remove-process-mark (car articles))
7044         ;; The backend might not have been able to delete the article
7045         ;; after all.
7046         (unless (memq (car articles) not-deleted)
7047           (gnus-summary-mark-article (car articles) gnus-canceled-mark))
7048         (setq articles (cdr articles)))
7049       (when not-deleted
7050         (gnus-message 4 "Couldn't delete articles %s" not-deleted)))
7051     (gnus-summary-position-point)
7052     (gnus-set-mode-line 'summary)
7053     not-deleted))
7054
7055 (defun gnus-summary-edit-article (&optional force)
7056   "Edit the current article.
7057 This will have permanent effect only in mail groups.
7058 If FORCE is non-nil, allow editing of articles even in read-only
7059 groups."
7060   (interactive "P")
7061   (save-excursion
7062     (set-buffer gnus-summary-buffer)
7063     (gnus-set-global-variables)
7064     (when (and (not force)
7065                (gnus-group-read-only-p))
7066       (error "The current newsgroup does not support article editing"))
7067     ;; Select article if needed.
7068     (unless (eq (gnus-summary-article-number)
7069                 gnus-current-article)
7070       (gnus-summary-select-article t))
7071     (gnus-article-date-original)
7072     (gnus-article-edit-article
7073      `(lambda ()
7074         (gnus-summary-edit-article-done
7075          ,(or (mail-header-references gnus-current-headers) "")
7076          ,(gnus-group-read-only-p) ,gnus-summary-buffer)))))
7077
7078 (defalias 'gnus-summary-edit-article-postpone 'gnus-article-edit-exit)
7079
7080 (defun gnus-summary-edit-article-done (&optional references read-only buffer)
7081   "Make edits to the current article permanent."
7082   (interactive)
7083   ;; Replace the article.
7084   (if (and (not read-only)
7085            (not (gnus-request-replace-article
7086                  (cdr gnus-article-current) (car gnus-article-current)
7087                  (current-buffer))))
7088       (error "Couldn't replace article")
7089     ;; Update the summary buffer.
7090     (if (and references
7091              (equal (message-tokenize-header references " ")
7092                     (message-tokenize-header
7093                      (or (message-fetch-field "references") "") " ")))
7094         ;; We only have to update this line.
7095         (save-excursion
7096           (save-restriction
7097             (message-narrow-to-head)
7098             (let ((head (buffer-string))
7099                   header)
7100               (nnheader-temp-write nil
7101                 (insert (format "211 %d Article retrieved.\n"
7102                                 (cdr gnus-article-current)))
7103                 (insert head)
7104                 (insert ".\n")
7105                 (let ((nntp-server-buffer (current-buffer)))
7106                   (setq header (car (gnus-get-newsgroup-headers
7107                                      (save-excursion
7108                                        (set-buffer gnus-summary-buffer)
7109                                        gnus-newsgroup-dependencies)
7110                                      t))))
7111                 (save-excursion
7112                   (set-buffer gnus-summary-buffer)
7113                   (gnus-data-set-header
7114                    (gnus-data-find (cdr gnus-article-current))
7115                    header)
7116                   (gnus-summary-update-article-line
7117                    (cdr gnus-article-current) header))))))
7118       ;; Update threads.
7119       (set-buffer (or buffer gnus-summary-buffer))
7120       (gnus-summary-update-article (cdr gnus-article-current)))
7121     ;; Prettify the article buffer again.
7122     (save-excursion
7123       (set-buffer gnus-article-buffer)
7124       (run-hooks 'gnus-article-display-hook)
7125       (set-buffer gnus-original-article-buffer)
7126       (gnus-request-article
7127        (cdr gnus-article-current) (car gnus-article-current) (current-buffer)))
7128     ;; Prettify the summary buffer line.
7129     (when (gnus-visual-p 'summary-highlight 'highlight)
7130       (run-hooks 'gnus-visual-mark-article-hook))))
7131
7132 (defun gnus-summary-edit-wash (key)
7133   "Perform editing command in the article buffer."
7134   (interactive
7135    (list
7136     (progn
7137       (message "%s" (concat (this-command-keys) "- "))
7138       (read-char))))
7139   (message "")
7140   (gnus-summary-edit-article)
7141   (execute-kbd-macro (concat (this-command-keys) key))
7142   (gnus-article-edit-done))
7143
7144 ;;; Respooling
7145
7146 (defun gnus-summary-respool-query (&optional silent)
7147   "Query where the respool algorithm would put this article."
7148   (interactive)
7149   (gnus-set-global-variables)
7150   (let (gnus-mark-article-hook)
7151     (gnus-summary-select-article)
7152     (save-excursion
7153       (set-buffer gnus-original-article-buffer)
7154       (save-restriction
7155         (message-narrow-to-head)
7156         (let ((groups (nnmail-article-group 'identity)))
7157           (unless silent
7158             (if groups
7159                 (message "This message would go to %s"
7160                          (mapconcat 'car groups ", "))
7161               (message "This message would go to no groups"))
7162             groups))))))
7163
7164 ;; Summary marking commands.
7165
7166 (defun gnus-summary-kill-same-subject-and-select (&optional unmark)
7167   "Mark articles which has the same subject as read, and then select the next.
7168 If UNMARK is positive, remove any kind of mark.
7169 If UNMARK is negative, tick articles."
7170   (interactive "P")
7171   (gnus-set-global-variables)
7172   (when unmark
7173     (setq unmark (prefix-numeric-value unmark)))
7174   (let ((count
7175          (gnus-summary-mark-same-subject
7176           (gnus-summary-article-subject) unmark)))
7177     ;; Select next unread article.  If auto-select-same mode, should
7178     ;; select the first unread article.
7179     (gnus-summary-next-article t (and gnus-auto-select-same
7180                                       (gnus-summary-article-subject)))
7181     (gnus-message 7 "%d article%s marked as %s"
7182                   count (if (= count 1) " is" "s are")
7183                   (if unmark "unread" "read"))))
7184
7185 (defun gnus-summary-kill-same-subject (&optional unmark)
7186   "Mark articles which has the same subject as read.
7187 If UNMARK is positive, remove any kind of mark.
7188 If UNMARK is negative, tick articles."
7189   (interactive "P")
7190   (gnus-set-global-variables)
7191   (when unmark
7192     (setq unmark (prefix-numeric-value unmark)))
7193   (let ((count
7194          (gnus-summary-mark-same-subject
7195           (gnus-summary-article-subject) unmark)))
7196     ;; If marked as read, go to next unread subject.
7197     (when (null unmark)
7198       ;; Go to next unread subject.
7199       (gnus-summary-next-subject 1 t))
7200     (gnus-message 7 "%d articles are marked as %s"
7201                   count (if unmark "unread" "read"))))
7202
7203 (defun gnus-summary-mark-same-subject (subject &optional unmark)
7204   "Mark articles with same SUBJECT as read, and return marked number.
7205 If optional argument UNMARK is positive, remove any kinds of marks.
7206 If optional argument UNMARK is negative, mark articles as unread instead."
7207   (let ((count 1))
7208     (save-excursion
7209       (cond
7210        ((null unmark)                   ; Mark as read.
7211         (while (and
7212                 (progn
7213                   (gnus-summary-mark-article-as-read gnus-killed-mark)
7214                   (gnus-summary-show-thread) t)
7215                 (gnus-summary-find-subject subject))
7216           (setq count (1+ count))))
7217        ((> unmark 0)                    ; Tick.
7218         (while (and
7219                 (progn
7220                   (gnus-summary-mark-article-as-unread gnus-ticked-mark)
7221                   (gnus-summary-show-thread) t)
7222                 (gnus-summary-find-subject subject))
7223           (setq count (1+ count))))
7224        (t                               ; Mark as unread.
7225         (while (and
7226                 (progn
7227                   (gnus-summary-mark-article-as-unread gnus-unread-mark)
7228                   (gnus-summary-show-thread) t)
7229                 (gnus-summary-find-subject subject))
7230           (setq count (1+ count)))))
7231       (gnus-set-mode-line 'summary)
7232       ;; Return the number of marked articles.
7233       count)))
7234
7235 (defun gnus-summary-mark-as-processable (n &optional unmark)
7236   "Set the process mark on the next N articles.
7237 If N is negative, mark backward instead.  If UNMARK is non-nil, remove
7238 the process mark instead.  The difference between N and the actual
7239 number of articles marked is returned."
7240   (interactive "p")
7241   (gnus-set-global-variables)
7242   (let ((backward (< n 0))
7243         (n (abs n)))
7244     (while (and
7245             (> n 0)
7246             (if unmark
7247                 (gnus-summary-remove-process-mark
7248                  (gnus-summary-article-number))
7249               (gnus-summary-set-process-mark (gnus-summary-article-number)))
7250             (zerop (gnus-summary-next-subject (if backward -1 1) nil t)))
7251       (setq n (1- n)))
7252     (when (/= 0 n)
7253       (gnus-message 7 "No more articles"))
7254     (gnus-summary-recenter)
7255     (gnus-summary-position-point)
7256     n))
7257
7258 (defun gnus-summary-unmark-as-processable (n)
7259   "Remove the process mark from the next N articles.
7260 If N is negative, mark backward instead.  The difference between N and
7261 the actual number of articles marked is returned."
7262   (interactive "p")
7263   (gnus-set-global-variables)
7264   (gnus-summary-mark-as-processable n t))
7265
7266 (defun gnus-summary-unmark-all-processable ()
7267   "Remove the process mark from all articles."
7268   (interactive)
7269   (gnus-set-global-variables)
7270   (save-excursion
7271     (while gnus-newsgroup-processable
7272       (gnus-summary-remove-process-mark (car gnus-newsgroup-processable))))
7273   (gnus-summary-position-point))
7274
7275 (defun gnus-summary-mark-as-expirable (n)
7276   "Mark N articles forward as expirable.
7277 If N is negative, mark backward instead.  The difference between N and
7278 the actual number of articles marked is returned."
7279   (interactive "p")
7280   (gnus-set-global-variables)
7281   (gnus-summary-mark-forward n gnus-expirable-mark))
7282
7283 (defun gnus-summary-mark-article-as-replied (article)
7284   "Mark ARTICLE replied and update the summary line."
7285   (push article gnus-newsgroup-replied)
7286   (let ((buffer-read-only nil))
7287     (when (gnus-summary-goto-subject article)
7288       (gnus-summary-update-secondary-mark article))))
7289
7290 (defun gnus-summary-set-bookmark (article)
7291   "Set a bookmark in current article."
7292   (interactive (list (gnus-summary-article-number)))
7293   (gnus-set-global-variables)
7294   (when (or (not (get-buffer gnus-article-buffer))
7295             (not gnus-current-article)
7296             (not gnus-article-current)
7297             (not (equal gnus-newsgroup-name (car gnus-article-current))))
7298     (error "No current article selected"))
7299   ;; Remove old bookmark, if one exists.
7300   (let ((old (assq article gnus-newsgroup-bookmarks)))
7301     (when old
7302       (setq gnus-newsgroup-bookmarks
7303             (delq old gnus-newsgroup-bookmarks))))
7304   ;; Set the new bookmark, which is on the form
7305   ;; (article-number . line-number-in-body).
7306   (push
7307    (cons article
7308          (save-excursion
7309            (set-buffer gnus-article-buffer)
7310            (count-lines
7311             (min (point)
7312                  (save-excursion
7313                    (goto-char (point-min))
7314                    (search-forward "\n\n" nil t)
7315                    (point)))
7316             (point))))
7317    gnus-newsgroup-bookmarks)
7318   (gnus-message 6 "A bookmark has been added to the current article."))
7319
7320 (defun gnus-summary-remove-bookmark (article)
7321   "Remove the bookmark from the current article."
7322   (interactive (list (gnus-summary-article-number)))
7323   (gnus-set-global-variables)
7324   ;; Remove old bookmark, if one exists.
7325   (let ((old (assq article gnus-newsgroup-bookmarks)))
7326     (if old
7327         (progn
7328           (setq gnus-newsgroup-bookmarks
7329                 (delq old gnus-newsgroup-bookmarks))
7330           (gnus-message 6 "Removed bookmark."))
7331       (gnus-message 6 "No bookmark in current article."))))
7332
7333 ;; Suggested by Daniel Quinlan <quinlan@best.com>.
7334 (defun gnus-summary-mark-as-dormant (n)
7335   "Mark N articles forward as dormant.
7336 If N is negative, mark backward instead.  The difference between N and
7337 the actual number of articles marked is returned."
7338   (interactive "p")
7339   (gnus-set-global-variables)
7340   (gnus-summary-mark-forward n gnus-dormant-mark))
7341
7342 (defun gnus-summary-set-process-mark (article)
7343   "Set the process mark on ARTICLE and update the summary line."
7344   (setq gnus-newsgroup-processable
7345         (cons article
7346               (delq article gnus-newsgroup-processable)))
7347   (when (gnus-summary-goto-subject article)
7348     (gnus-summary-show-thread)
7349     (gnus-summary-update-secondary-mark article)))
7350
7351 (defun gnus-summary-remove-process-mark (article)
7352   "Remove the process mark from ARTICLE and update the summary line."
7353   (setq gnus-newsgroup-processable (delq article gnus-newsgroup-processable))
7354   (when (gnus-summary-goto-subject article)
7355     (gnus-summary-show-thread)
7356     (gnus-summary-update-secondary-mark article)))
7357
7358 (defun gnus-summary-set-saved-mark (article)
7359   "Set the process mark on ARTICLE and update the summary line."
7360   (push article gnus-newsgroup-saved)
7361   (when (gnus-summary-goto-subject article)
7362     (gnus-summary-update-secondary-mark article)))
7363
7364 (defun gnus-summary-mark-forward (n &optional mark no-expire)
7365   "Mark N articles as read forwards.
7366 If N is negative, mark backwards instead.  Mark with MARK, ?r by default.
7367 The difference between N and the actual number of articles marked is
7368 returned."
7369   (interactive "p")
7370   (gnus-set-global-variables)
7371   (let ((backward (< n 0))
7372         (gnus-summary-goto-unread
7373          (and gnus-summary-goto-unread
7374               (not (eq gnus-summary-goto-unread 'never))
7375               (not (memq mark (list gnus-unread-mark
7376                                     gnus-ticked-mark gnus-dormant-mark)))))
7377         (n (abs n))
7378         (mark (or mark gnus-del-mark)))
7379     (while (and (> n 0)
7380                 (gnus-summary-mark-article nil mark no-expire)
7381                 (zerop (gnus-summary-next-subject
7382                         (if backward -1 1)
7383                         (and gnus-summary-goto-unread
7384                              (not (eq gnus-summary-goto-unread 'never)))
7385                         t)))
7386       (setq n (1- n)))
7387     (when (/= 0 n)
7388       (gnus-message 7 "No more %sarticles" (if mark "" "unread ")))
7389     (gnus-summary-recenter)
7390     (gnus-summary-position-point)
7391     (gnus-set-mode-line 'summary)
7392     n))
7393
7394 (defun gnus-summary-mark-article-as-read (mark)
7395   "Mark the current article quickly as read with MARK."
7396   (let ((article (gnus-summary-article-number)))
7397     (setq gnus-newsgroup-unreads (delq article gnus-newsgroup-unreads))
7398     (setq gnus-newsgroup-marked (delq article gnus-newsgroup-marked))
7399     (setq gnus-newsgroup-dormant (delq article gnus-newsgroup-dormant))
7400     (push (cons article mark) gnus-newsgroup-reads)
7401     ;; Possibly remove from cache, if that is used.
7402     (when gnus-use-cache
7403       (gnus-cache-enter-remove-article article))
7404     ;; Allow the backend to change the mark.
7405     (setq mark (gnus-request-update-mark gnus-newsgroup-name article mark))
7406     ;; Check for auto-expiry.
7407     (when (and gnus-newsgroup-auto-expire
7408                (or (= mark gnus-killed-mark) (= mark gnus-del-mark)
7409                    (= mark gnus-catchup-mark) (= mark gnus-low-score-mark)
7410                    (= mark gnus-ancient-mark)
7411                    (= mark gnus-read-mark) (= mark gnus-souped-mark)
7412                    (= mark gnus-duplicate-mark)))
7413       (setq mark gnus-expirable-mark)
7414       (push article gnus-newsgroup-expirable))
7415     ;; Set the mark in the buffer.
7416     (gnus-summary-update-mark mark 'unread)
7417     t))
7418
7419 (defun gnus-summary-mark-article-as-unread (mark)
7420   "Mark the current article quickly as unread with MARK."
7421   (let ((article (gnus-summary-article-number)))
7422     (if (< article 0)
7423         (gnus-error 1 "Unmarkable article")
7424       (setq gnus-newsgroup-marked (delq article gnus-newsgroup-marked))
7425       (setq gnus-newsgroup-dormant (delq article gnus-newsgroup-dormant))
7426       (setq gnus-newsgroup-expirable (delq article gnus-newsgroup-expirable))
7427       (setq gnus-newsgroup-reads (delq article gnus-newsgroup-reads))
7428       (cond ((= mark gnus-ticked-mark)
7429              (push article gnus-newsgroup-marked))
7430             ((= mark gnus-dormant-mark)
7431              (push article gnus-newsgroup-dormant))
7432             (t
7433              (push article gnus-newsgroup-unreads)))
7434       (setq gnus-newsgroup-reads
7435             (delq (assq article gnus-newsgroup-reads)
7436                   gnus-newsgroup-reads))
7437
7438       ;; See whether the article is to be put in the cache.
7439       (and gnus-use-cache
7440            (vectorp (gnus-summary-article-header article))
7441            (save-excursion
7442              (gnus-cache-possibly-enter-article
7443               gnus-newsgroup-name article
7444               (gnus-summary-article-header article)
7445               (= mark gnus-ticked-mark)
7446               (= mark gnus-dormant-mark) (= mark gnus-unread-mark))))
7447
7448       ;; Fix the mark.
7449       (gnus-summary-update-mark mark 'unread))
7450     t))
7451
7452 (defun gnus-summary-mark-article (&optional article mark no-expire)
7453   "Mark ARTICLE with MARK.  MARK can be any character.
7454 Four MARK strings are reserved: `? ' (unread), `?!' (ticked),
7455 `??' (dormant) and `?E' (expirable).
7456 If MARK is nil, then the default character `?D' is used.
7457 If ARTICLE is nil, then the article on the current line will be
7458 marked."
7459   ;; The mark might be a string.
7460   (when (stringp mark)
7461     (setq mark (aref mark 0)))
7462   ;; If no mark is given, then we check auto-expiring.
7463   (and (not no-expire)
7464        gnus-newsgroup-auto-expire
7465        (or (not mark)
7466            (and (gnus-characterp mark)
7467                 (or (= mark gnus-killed-mark) (= mark gnus-del-mark)
7468                     (= mark gnus-catchup-mark) (= mark gnus-low-score-mark)
7469                     (= mark gnus-read-mark) (= mark gnus-souped-mark)
7470                     (= mark gnus-duplicate-mark))))
7471        (setq mark gnus-expirable-mark))
7472   (let* ((mark (or mark gnus-del-mark))
7473          (article (or article (gnus-summary-article-number))))
7474     (unless article
7475       (error "No article on current line"))
7476     (if (or (= mark gnus-unread-mark)
7477             (= mark gnus-ticked-mark)
7478             (= mark gnus-dormant-mark))
7479         (gnus-mark-article-as-unread article mark)
7480       (gnus-mark-article-as-read article mark))
7481
7482     ;; See whether the article is to be put in the cache.
7483     (and gnus-use-cache
7484          (not (= mark gnus-canceled-mark))
7485          (vectorp (gnus-summary-article-header article))
7486          (save-excursion
7487            (gnus-cache-possibly-enter-article
7488             gnus-newsgroup-name article
7489             (gnus-summary-article-header article)
7490             (= mark gnus-ticked-mark)
7491             (= mark gnus-dormant-mark) (= mark gnus-unread-mark))))
7492
7493     (when (gnus-summary-goto-subject article nil t)
7494       (let ((buffer-read-only nil))
7495         (gnus-summary-show-thread)
7496         ;; Fix the mark.
7497         (gnus-summary-update-mark mark 'unread)
7498         t))))
7499
7500 (defun gnus-summary-update-secondary-mark (article)
7501   "Update the secondary (read, process, cache) mark."
7502   (gnus-summary-update-mark
7503    (cond ((memq article gnus-newsgroup-processable)
7504           gnus-process-mark)
7505          ((memq article gnus-newsgroup-cached)
7506           gnus-cached-mark)
7507          ((memq article gnus-newsgroup-replied)
7508           gnus-replied-mark)
7509          ((memq article gnus-newsgroup-saved)
7510           gnus-saved-mark)
7511          (t gnus-unread-mark))
7512    'replied)
7513   (when (gnus-visual-p 'summary-highlight 'highlight)
7514     (run-hooks 'gnus-summary-update-hook))
7515   t)
7516
7517 (defun gnus-summary-update-mark (mark type)
7518   (let ((forward (cdr (assq type gnus-summary-mark-positions)))
7519         (buffer-read-only nil))
7520     (re-search-backward "[\n\r]" (gnus-point-at-bol) 'move-to-limit)
7521     (when (looking-at "\r")
7522       (incf forward))
7523     (when (and forward
7524                (<= (+ forward (point)) (point-max)))
7525       ;; Go to the right position on the line.
7526       (goto-char (+ forward (point)))
7527       ;; Replace the old mark with the new mark.
7528       (subst-char-in-region (point) (1+ (point)) (following-char) mark)
7529       ;; Optionally update the marks by some user rule.
7530       (when (eq type 'unread)
7531         (gnus-data-set-mark
7532          (gnus-data-find (gnus-summary-article-number)) mark)
7533         (gnus-summary-update-line (eq mark gnus-unread-mark))))))
7534
7535 (defun gnus-mark-article-as-read (article &optional mark)
7536   "Enter ARTICLE in the pertinent lists and remove it from others."
7537   ;; Make the article expirable.
7538   (let ((mark (or mark gnus-del-mark)))
7539     (if (= mark gnus-expirable-mark)
7540         (push article gnus-newsgroup-expirable)
7541       (setq gnus-newsgroup-expirable (delq article gnus-newsgroup-expirable)))
7542     ;; Remove from unread and marked lists.
7543     (setq gnus-newsgroup-unreads (delq article gnus-newsgroup-unreads))
7544     (setq gnus-newsgroup-marked (delq article gnus-newsgroup-marked))
7545     (setq gnus-newsgroup-dormant (delq article gnus-newsgroup-dormant))
7546     (push (cons article mark) gnus-newsgroup-reads)
7547     ;; Possibly remove from cache, if that is used.
7548     (when gnus-use-cache
7549       (gnus-cache-enter-remove-article article))))
7550
7551 (defun gnus-mark-article-as-unread (article &optional mark)
7552   "Enter ARTICLE in the pertinent lists and remove it from others."
7553   (let ((mark (or mark gnus-ticked-mark)))
7554     (setq gnus-newsgroup-marked (delq article gnus-newsgroup-marked)
7555           gnus-newsgroup-dormant (delq article gnus-newsgroup-dormant)
7556           gnus-newsgroup-expirable (delq article gnus-newsgroup-expirable)
7557           gnus-newsgroup-unreads (delq article gnus-newsgroup-unreads))
7558
7559     ;; Unsuppress duplicates?
7560     (when gnus-suppress-duplicates
7561       (gnus-dup-unsuppress-article article))
7562
7563     (cond ((= mark gnus-ticked-mark)
7564            (push article gnus-newsgroup-marked))
7565           ((= mark gnus-dormant-mark)
7566            (push article gnus-newsgroup-dormant))
7567           (t
7568            (push article gnus-newsgroup-unreads)))
7569     (setq gnus-newsgroup-reads
7570           (delq (assq article gnus-newsgroup-reads)
7571                 gnus-newsgroup-reads))))
7572
7573 (defalias 'gnus-summary-mark-as-unread-forward
7574   'gnus-summary-tick-article-forward)
7575 (make-obsolete 'gnus-summary-mark-as-unread-forward
7576                'gnus-summary-tick-article-forward)
7577 (defun gnus-summary-tick-article-forward (n)
7578   "Tick N articles forwards.
7579 If N is negative, tick backwards instead.
7580 The difference between N and the number of articles ticked is returned."
7581   (interactive "p")
7582   (gnus-summary-mark-forward n gnus-ticked-mark))
7583
7584 (defalias 'gnus-summary-mark-as-unread-backward
7585   'gnus-summary-tick-article-backward)
7586 (make-obsolete 'gnus-summary-mark-as-unread-backward
7587                'gnus-summary-tick-article-backward)
7588 (defun gnus-summary-tick-article-backward (n)
7589   "Tick N articles backwards.
7590 The difference between N and the number of articles ticked is returned."
7591   (interactive "p")
7592   (gnus-summary-mark-forward (- n) gnus-ticked-mark))
7593
7594 (defalias 'gnus-summary-mark-as-unread 'gnus-summary-tick-article)
7595 (make-obsolete 'gnus-summary-mark-as-unread 'gnus-summary-tick-article)
7596 (defun gnus-summary-tick-article (&optional article clear-mark)
7597   "Mark current article as unread.
7598 Optional 1st argument ARTICLE specifies article number to be marked as unread.
7599 Optional 2nd argument CLEAR-MARK remove any kinds of mark."
7600   (interactive)
7601   (gnus-summary-mark-article article (if clear-mark gnus-unread-mark
7602                                        gnus-ticked-mark)))
7603
7604 (defun gnus-summary-mark-as-read-forward (n)
7605   "Mark N articles as read forwards.
7606 If N is negative, mark backwards instead.
7607 The difference between N and the actual number of articles marked is
7608 returned."
7609   (interactive "p")
7610   (gnus-summary-mark-forward n gnus-del-mark t))
7611
7612 (defun gnus-summary-mark-as-read-backward (n)
7613   "Mark the N articles as read backwards.
7614 The difference between N and the actual number of articles marked is
7615 returned."
7616   (interactive "p")
7617   (gnus-summary-mark-forward (- n) gnus-del-mark t))
7618
7619 (defun gnus-summary-mark-as-read (&optional article mark)
7620   "Mark current article as read.
7621 ARTICLE specifies the article to be marked as read.
7622 MARK specifies a string to be inserted at the beginning of the line."
7623   (gnus-summary-mark-article article mark))
7624
7625 (defun gnus-summary-clear-mark-forward (n)
7626   "Clear marks from N articles forward.
7627 If N is negative, clear backward instead.
7628 The difference between N and the number of marks cleared is returned."
7629   (interactive "p")
7630   (gnus-summary-mark-forward n gnus-unread-mark))
7631
7632 (defun gnus-summary-clear-mark-backward (n)
7633   "Clear marks from N articles backward.
7634 The difference between N and the number of marks cleared is returned."
7635   (interactive "p")
7636   (gnus-summary-mark-forward (- n) gnus-unread-mark))
7637
7638 (defun gnus-summary-mark-unread-as-read ()
7639   "Intended to be used by `gnus-summary-mark-article-hook'."
7640   (when (memq gnus-current-article gnus-newsgroup-unreads)
7641     (gnus-summary-mark-article gnus-current-article gnus-read-mark)))
7642
7643 (defun gnus-summary-mark-read-and-unread-as-read ()
7644   "Intended to be used by `gnus-summary-mark-article-hook'."
7645   (let ((mark (gnus-summary-article-mark)))
7646     (when (or (gnus-unread-mark-p mark)
7647               (gnus-read-mark-p mark))
7648       (gnus-summary-mark-article gnus-current-article gnus-read-mark))))
7649
7650 (defun gnus-summary-mark-region-as-read (point mark all)
7651   "Mark all unread articles between point and mark as read.
7652 If given a prefix, mark all articles between point and mark as read,
7653 even ticked and dormant ones."
7654   (interactive "r\nP")
7655   (save-excursion
7656     (let (article)
7657       (goto-char point)
7658       (beginning-of-line)
7659       (while (and
7660               (< (point) mark)
7661               (progn
7662                 (when (or all
7663                           (memq (setq article (gnus-summary-article-number))
7664                                 gnus-newsgroup-unreads))
7665                   (gnus-summary-mark-article article gnus-del-mark))
7666                 t)
7667               (gnus-summary-find-next))))))
7668
7669 (defun gnus-summary-mark-below (score mark)
7670   "Mark articles with score less than SCORE with MARK."
7671   (interactive "P\ncMark: ")
7672   (gnus-set-global-variables)
7673   (setq score (if score
7674                   (prefix-numeric-value score)
7675                 (or gnus-summary-default-score 0)))
7676   (save-excursion
7677     (set-buffer gnus-summary-buffer)
7678     (goto-char (point-min))
7679     (while
7680         (progn
7681           (and (< (gnus-summary-article-score) score)
7682                (gnus-summary-mark-article nil mark))
7683           (gnus-summary-find-next)))))
7684
7685 (defun gnus-summary-kill-below (&optional score)
7686   "Mark articles with score below SCORE as read."
7687   (interactive "P")
7688   (gnus-set-global-variables)
7689   (gnus-summary-mark-below score gnus-killed-mark))
7690
7691 (defun gnus-summary-clear-above (&optional score)
7692   "Clear all marks from articles with score above SCORE."
7693   (interactive "P")
7694   (gnus-set-global-variables)
7695   (gnus-summary-mark-above score gnus-unread-mark))
7696
7697 (defun gnus-summary-tick-above (&optional score)
7698   "Tick all articles with score above SCORE."
7699   (interactive "P")
7700   (gnus-set-global-variables)
7701   (gnus-summary-mark-above score gnus-ticked-mark))
7702
7703 (defun gnus-summary-mark-above (score mark)
7704   "Mark articles with score over SCORE with MARK."
7705   (interactive "P\ncMark: ")
7706   (gnus-set-global-variables)
7707   (setq score (if score
7708                   (prefix-numeric-value score)
7709                 (or gnus-summary-default-score 0)))
7710   (save-excursion
7711     (set-buffer gnus-summary-buffer)
7712     (goto-char (point-min))
7713     (while (and (progn
7714                   (when (> (gnus-summary-article-score) score)
7715                     (gnus-summary-mark-article nil mark))
7716                   t)
7717                 (gnus-summary-find-next)))))
7718
7719 ;; Suggested by Daniel Quinlan <quinlan@best.com>.
7720 (defalias 'gnus-summary-show-all-expunged 'gnus-summary-limit-include-expunged)
7721 (defun gnus-summary-limit-include-expunged (&optional no-error)
7722   "Display all the hidden articles that were expunged for low scores."
7723   (interactive)
7724   (gnus-set-global-variables)
7725   (let ((buffer-read-only nil))
7726     (let ((scored gnus-newsgroup-scored)
7727           headers h)
7728       (while scored
7729         (unless (gnus-summary-goto-subject (caar scored))
7730           (and (setq h (gnus-summary-article-header (caar scored)))
7731                (< (cdar scored) gnus-summary-expunge-below)
7732                (push h headers)))
7733         (setq scored (cdr scored)))
7734       (if (not headers)
7735           (when (not no-error)
7736             (error "No expunged articles hidden"))
7737         (goto-char (point-min))
7738         (gnus-summary-prepare-unthreaded (nreverse headers))
7739         (goto-char (point-min))
7740         (gnus-summary-position-point)
7741         t))))
7742
7743 (defun gnus-summary-catchup (&optional all quietly to-here not-mark)
7744   "Mark all unread articles in this newsgroup as read.
7745 If prefix argument ALL is non-nil, ticked and dormant articles will
7746 also be marked as read.
7747 If QUIETLY is non-nil, no questions will be asked.
7748 If TO-HERE is non-nil, it should be a point in the buffer.  All
7749 articles before this point will be marked as read.
7750 Note that this function will only catch up the unread article
7751 in the current summary buffer limitation.
7752 The number of articles marked as read is returned."
7753   (interactive "P")
7754   (gnus-set-global-variables)
7755   (prog1
7756       (save-excursion
7757         (when (or quietly
7758                   (not gnus-interactive-catchup) ;Without confirmation?
7759                   gnus-expert-user
7760                   (gnus-y-or-n-p
7761                    (if all
7762                        "Mark absolutely all articles as read? "
7763                      "Mark all unread articles as read? ")))
7764           (if (and not-mark
7765                    (not gnus-newsgroup-adaptive)
7766                    (not gnus-newsgroup-auto-expire)
7767                    (not gnus-suppress-duplicates)
7768                    (or (not gnus-use-cache)
7769                        (not (eq gnus-use-cache 'passive))))
7770               (progn
7771                 (when all
7772                   (setq gnus-newsgroup-marked nil
7773                         gnus-newsgroup-dormant nil))
7774                 (setq gnus-newsgroup-unreads nil))
7775             ;; We actually mark all articles as canceled, which we
7776             ;; have to do when using auto-expiry or adaptive scoring.
7777             (gnus-summary-show-all-threads)
7778             (when (gnus-summary-first-subject (not all))
7779               (while (and
7780                       (if to-here (< (point) to-here) t)
7781                       (gnus-summary-mark-article-as-read gnus-catchup-mark)
7782                       (gnus-summary-find-next (not all)))))
7783             (gnus-set-mode-line 'summary))
7784           t))
7785     (gnus-summary-position-point)))
7786
7787 (defun gnus-summary-catchup-to-here (&optional all)
7788   "Mark all unticked articles before the current one as read.
7789 If ALL is non-nil, also mark ticked and dormant articles as read."
7790   (interactive "P")
7791   (gnus-set-global-variables)
7792   (save-excursion
7793     (gnus-save-hidden-threads
7794       (let ((beg (point)))
7795         ;; We check that there are unread articles.
7796         (when (or all (gnus-summary-find-prev))
7797           (gnus-summary-catchup all t beg)))))
7798   (gnus-summary-position-point))
7799
7800 (defun gnus-summary-catchup-all (&optional quietly)
7801   "Mark all articles in this newsgroup as read."
7802   (interactive "P")
7803   (gnus-set-global-variables)
7804   (gnus-summary-catchup t quietly))
7805
7806 (defun gnus-summary-catchup-and-exit (&optional all quietly)
7807   "Mark all articles not marked as unread in this newsgroup as read, then exit.
7808 If prefix argument ALL is non-nil, all articles are marked as read."
7809   (interactive "P")
7810   (gnus-set-global-variables)
7811   (when (gnus-summary-catchup all quietly nil 'fast)
7812     ;; Select next newsgroup or exit.
7813     (if (eq gnus-auto-select-next 'quietly)
7814         (gnus-summary-next-group nil)
7815       (gnus-summary-exit))))
7816
7817 (defun gnus-summary-catchup-all-and-exit (&optional quietly)
7818   "Mark all articles in this newsgroup as read, and then exit."
7819   (interactive "P")
7820   (gnus-set-global-variables)
7821   (gnus-summary-catchup-and-exit t quietly))
7822
7823 ;; Suggested by "Arne Eofsson" <arne@hodgkin.mbi.ucla.edu>.
7824 (defun gnus-summary-catchup-and-goto-next-group (&optional all)
7825   "Mark all articles in this group as read and select the next group.
7826 If given a prefix, mark all articles, unread as well as ticked, as
7827 read."
7828   (interactive "P")
7829   (gnus-set-global-variables)
7830   (save-excursion
7831     (gnus-summary-catchup all))
7832   (gnus-summary-next-article t nil nil t))
7833
7834 ;; Thread-based commands.
7835
7836 (defun gnus-summary-articles-in-thread (&optional article)
7837   "Return a list of all articles in the current thread.
7838 If ARTICLE is non-nil, return all articles in the thread that starts
7839 with that article."
7840   (let* ((article (or article (gnus-summary-article-number)))
7841          (data (gnus-data-find-list article))
7842          (top-level (gnus-data-level (car data)))
7843          (top-subject
7844           (cond ((null gnus-thread-operation-ignore-subject)
7845                  (gnus-simplify-subject-re
7846                   (mail-header-subject (gnus-data-header (car data)))))
7847                 ((eq gnus-thread-operation-ignore-subject 'fuzzy)
7848                  (gnus-simplify-subject-fuzzy
7849                   (mail-header-subject (gnus-data-header (car data)))))
7850                 (t nil)))
7851          (end-point (save-excursion
7852                       (if (gnus-summary-go-to-next-thread)
7853                           (point) (point-max))))
7854          articles)
7855     (while (and data
7856                 (< (gnus-data-pos (car data)) end-point))
7857       (when (or (not top-subject)
7858                 (string= top-subject
7859                          (if (eq gnus-thread-operation-ignore-subject 'fuzzy)
7860                              (gnus-simplify-subject-fuzzy
7861                               (mail-header-subject
7862                                (gnus-data-header (car data))))
7863                            (gnus-simplify-subject-re
7864                             (mail-header-subject
7865                              (gnus-data-header (car data)))))))
7866         (push (gnus-data-number (car data)) articles))
7867       (unless (and (setq data (cdr data))
7868                    (> (gnus-data-level (car data)) top-level))
7869         (setq data nil)))
7870     ;; Return the list of articles.
7871     (nreverse articles)))
7872
7873 (defun gnus-summary-rethread-current ()
7874   "Rethread the thread the current article is part of."
7875   (interactive)
7876   (gnus-set-global-variables)
7877   (let* ((gnus-show-threads t)
7878          (article (gnus-summary-article-number))
7879          (id (mail-header-id (gnus-summary-article-header)))
7880          (gnus-newsgroup-threads (list (gnus-id-to-thread (gnus-root-id id)))))
7881     (unless id
7882       (error "No article on the current line"))
7883     (gnus-rebuild-thread id)
7884     (gnus-summary-goto-subject article)))
7885
7886 (defun gnus-summary-reparent-thread ()
7887   "Make the current article child of the marked (or previous) article.
7888
7889 Note that the re-threading will only work if `gnus-thread-ignore-subject'
7890 is non-nil or the Subject: of both articles are the same."
7891   (interactive)
7892   (unless (not (gnus-group-read-only-p))
7893     (error "The current newsgroup does not support article editing"))
7894   (unless (<= (length gnus-newsgroup-processable) 1)
7895     (error "No more than one article may be marked"))
7896   (save-window-excursion
7897     (let ((gnus-article-buffer " *reparent*")
7898           (current-article (gnus-summary-article-number))
7899           ;; First grab the marked article, otherwise one line up.
7900           (parent-article (if (not (null gnus-newsgroup-processable))
7901                               (car gnus-newsgroup-processable)
7902                             (save-excursion
7903                               (if (eq (forward-line -1) 0)
7904                                   (gnus-summary-article-number)
7905                                 (error "Beginning of summary buffer"))))))
7906       (unless (not (eq current-article parent-article))
7907         (error "An article may not be self-referential"))
7908       (let ((message-id (mail-header-id
7909                          (gnus-summary-article-header parent-article))))
7910         (unless (and message-id (not (equal message-id "")))
7911           (error "No message-id in desired parent"))
7912         (gnus-summary-select-article t t nil current-article)
7913         (set-buffer gnus-original-article-buffer)
7914         (let ((buf (format "%s" (buffer-string))))
7915           (nnheader-temp-write nil
7916             (insert buf)
7917             (goto-char (point-min))
7918             (if (search-forward-regexp "^References: " nil t)
7919                 (insert message-id " " )
7920               (insert "References: " message-id "\n"))
7921             (unless (gnus-request-replace-article
7922                      current-article (car gnus-article-current)
7923                      (current-buffer))
7924               (error "Couldn't replace article"))))
7925         (set-buffer gnus-summary-buffer)
7926         (gnus-summary-unmark-all-processable)
7927         (gnus-summary-rethread-current)
7928         (gnus-message 3 "Article %d is now the child of article %d"
7929                       current-article parent-article)))))
7930
7931 (defun gnus-summary-toggle-threads (&optional arg)
7932   "Toggle showing conversation threads.
7933 If ARG is positive number, turn showing conversation threads on."
7934   (interactive "P")
7935   (gnus-set-global-variables)
7936   (let ((current (or (gnus-summary-article-number) gnus-newsgroup-end)))
7937     (setq gnus-show-threads
7938           (if (null arg) (not gnus-show-threads)
7939             (> (prefix-numeric-value arg) 0)))
7940     (gnus-summary-prepare)
7941     (gnus-summary-goto-subject current)
7942     (gnus-message 6 "Threading is now %s" (if gnus-show-threads "on" "off"))
7943     (gnus-summary-position-point)))
7944
7945 (defun gnus-summary-show-all-threads ()
7946   "Show all threads."
7947   (interactive)
7948   (gnus-set-global-variables)
7949   (save-excursion
7950     (let ((buffer-read-only nil))
7951       (subst-char-in-region (point-min) (point-max) ?\^M ?\n t)))
7952   (gnus-summary-position-point))
7953
7954 (defun gnus-summary-show-thread ()
7955   "Show thread subtrees.
7956 Returns nil if no thread was there to be shown."
7957   (interactive)
7958   (gnus-set-global-variables)
7959   (let ((buffer-read-only nil)
7960         (orig (point))
7961         ;; first goto end then to beg, to have point at beg after let
7962         (end (progn (end-of-line) (point)))
7963         (beg (progn (beginning-of-line) (point))))
7964     (prog1
7965         ;; Any hidden lines here?
7966         (search-forward "\r" end t)
7967       (subst-char-in-region beg end ?\^M ?\n t)
7968       (goto-char orig)
7969       (gnus-summary-position-point))))
7970
7971 (defun gnus-summary-hide-all-threads ()
7972   "Hide all thread subtrees."
7973   (interactive)
7974   (gnus-set-global-variables)
7975   (save-excursion
7976     (goto-char (point-min))
7977     (gnus-summary-hide-thread)
7978     (while (zerop (gnus-summary-next-thread 1 t))
7979       (gnus-summary-hide-thread)))
7980   (gnus-summary-position-point))
7981
7982 (defun gnus-summary-hide-thread ()
7983   "Hide thread subtrees.
7984 Returns nil if no threads were there to be hidden."
7985   (interactive)
7986   (gnus-set-global-variables)
7987   (let ((buffer-read-only nil)
7988         (start (point))
7989         (article (gnus-summary-article-number)))
7990     (goto-char start)
7991     ;; Go forward until either the buffer ends or the subthread
7992     ;; ends.
7993     (when (and (not (eobp))
7994                (or (zerop (gnus-summary-next-thread 1 t))
7995                    (goto-char (point-max))))
7996       (prog1
7997           (if (and (> (point) start)
7998                    (search-backward "\n" start t))
7999               (progn
8000                 (subst-char-in-region start (point) ?\n ?\^M)
8001                 (gnus-summary-goto-subject article))
8002             (goto-char start)
8003             nil)
8004         ;;(gnus-summary-position-point)
8005         ))))
8006
8007 (defun gnus-summary-go-to-next-thread (&optional previous)
8008   "Go to the same level (or less) next thread.
8009 If PREVIOUS is non-nil, go to previous thread instead.
8010 Return the article number moved to, or nil if moving was impossible."
8011   (let ((level (gnus-summary-thread-level))
8012         (way (if previous -1 1))
8013         (beg (point)))
8014     (forward-line way)
8015     (while (and (not (eobp))
8016                 (< level (gnus-summary-thread-level)))
8017       (forward-line way))
8018     (if (eobp)
8019         (progn
8020           (goto-char beg)
8021           nil)
8022       (setq beg (point))
8023       (prog1
8024           (gnus-summary-article-number)
8025         (goto-char beg)))))
8026
8027 (defun gnus-summary-next-thread (n &optional silent)
8028   "Go to the same level next N'th thread.
8029 If N is negative, search backward instead.
8030 Returns the difference between N and the number of skips actually
8031 done.
8032
8033 If SILENT, don't output messages."
8034   (interactive "p")
8035   (gnus-set-global-variables)
8036   (let ((backward (< n 0))
8037         (n (abs n)))
8038     (while (and (> n 0)
8039                 (gnus-summary-go-to-next-thread backward))
8040       (decf n))
8041     (unless silent
8042       (gnus-summary-position-point))
8043     (when (and (not silent) (/= 0 n))
8044       (gnus-message 7 "No more threads"))
8045     n))
8046
8047 (defun gnus-summary-prev-thread (n)
8048   "Go to the same level previous N'th thread.
8049 Returns the difference between N and the number of skips actually
8050 done."
8051   (interactive "p")
8052   (gnus-set-global-variables)
8053   (gnus-summary-next-thread (- n)))
8054
8055 (defun gnus-summary-go-down-thread ()
8056   "Go down one level in the current thread."
8057   (let ((children (gnus-summary-article-children)))
8058     (when children
8059       (gnus-summary-goto-subject (car children)))))
8060
8061 (defun gnus-summary-go-up-thread ()
8062   "Go up one level in the current thread."
8063   (let ((parent (gnus-summary-article-parent)))
8064     (when parent
8065       (gnus-summary-goto-subject parent))))
8066
8067 (defun gnus-summary-down-thread (n)
8068   "Go down thread N steps.
8069 If N is negative, go up instead.
8070 Returns the difference between N and how many steps down that were
8071 taken."
8072   (interactive "p")
8073   (gnus-set-global-variables)
8074   (let ((up (< n 0))
8075         (n (abs n)))
8076     (while (and (> n 0)
8077                 (if up (gnus-summary-go-up-thread)
8078                   (gnus-summary-go-down-thread)))
8079       (setq n (1- n)))
8080     (gnus-summary-position-point)
8081     (when (/= 0 n)
8082       (gnus-message 7 "Can't go further"))
8083     n))
8084
8085 (defun gnus-summary-up-thread (n)
8086   "Go up thread N steps.
8087 If N is negative, go up instead.
8088 Returns the difference between N and how many steps down that were
8089 taken."
8090   (interactive "p")
8091   (gnus-set-global-variables)
8092   (gnus-summary-down-thread (- n)))
8093
8094 (defun gnus-summary-top-thread ()
8095   "Go to the top of the thread."
8096   (interactive)
8097   (gnus-set-global-variables)
8098   (while (gnus-summary-go-up-thread))
8099   (gnus-summary-article-number))
8100
8101 (defun gnus-summary-kill-thread (&optional unmark)
8102   "Mark articles under current thread as read.
8103 If the prefix argument is positive, remove any kinds of marks.
8104 If the prefix argument is negative, tick articles instead."
8105   (interactive "P")
8106   (gnus-set-global-variables)
8107   (when unmark
8108     (setq unmark (prefix-numeric-value unmark)))
8109   (let ((articles (gnus-summary-articles-in-thread)))
8110     (save-excursion
8111       ;; Expand the thread.
8112       (gnus-summary-show-thread)
8113       ;; Mark all the articles.
8114       (while articles
8115         (gnus-summary-goto-subject (car articles))
8116         (cond ((null unmark)
8117                (gnus-summary-mark-article-as-read gnus-killed-mark))
8118               ((> unmark 0)
8119                (gnus-summary-mark-article-as-unread gnus-unread-mark))
8120               (t
8121                (gnus-summary-mark-article-as-unread gnus-ticked-mark)))
8122         (setq articles (cdr articles))))
8123     ;; Hide killed subtrees.
8124     (and (null unmark)
8125          gnus-thread-hide-killed
8126          (gnus-summary-hide-thread))
8127     ;; If marked as read, go to next unread subject.
8128     (when (null unmark)
8129       ;; Go to next unread subject.
8130       (gnus-summary-next-subject 1 t)))
8131   (gnus-set-mode-line 'summary))
8132
8133 ;; Summary sorting commands
8134
8135 (defun gnus-summary-sort-by-number (&optional reverse)
8136   "Sort the summary buffer by article number.
8137 Argument REVERSE means reverse order."
8138   (interactive "P")
8139   (gnus-summary-sort 'number reverse))
8140
8141 (defun gnus-summary-sort-by-author (&optional reverse)
8142   "Sort the summary buffer by author name alphabetically.
8143 If case-fold-search is non-nil, case of letters is ignored.
8144 Argument REVERSE means reverse order."
8145   (interactive "P")
8146   (gnus-summary-sort 'author reverse))
8147
8148 (defun gnus-summary-sort-by-subject (&optional reverse)
8149   "Sort the summary buffer by subject alphabetically.  `Re:'s are ignored.
8150 If case-fold-search is non-nil, case of letters is ignored.
8151 Argument REVERSE means reverse order."
8152   (interactive "P")
8153   (gnus-summary-sort 'subject reverse))
8154
8155 (defun gnus-summary-sort-by-date (&optional reverse)
8156   "Sort the summary buffer by date.
8157 Argument REVERSE means reverse order."
8158   (interactive "P")
8159   (gnus-summary-sort 'date reverse))
8160
8161 (defun gnus-summary-sort-by-score (&optional reverse)
8162   "Sort the summary buffer by score.
8163 Argument REVERSE means reverse order."
8164   (interactive "P")
8165   (gnus-summary-sort 'score reverse))
8166
8167 (defun gnus-summary-sort-by-lines (&optional reverse)
8168   "Sort the summary buffer by article length.
8169 Argument REVERSE means reverse order."
8170   (interactive "P")
8171   (gnus-summary-sort 'lines reverse))
8172
8173 (defun gnus-summary-sort (predicate reverse)
8174   "Sort summary buffer by PREDICATE.  REVERSE means reverse order."
8175   (gnus-set-global-variables)
8176   (let* ((thread (intern (format "gnus-thread-sort-by-%s" predicate)))
8177          (article (intern (format "gnus-article-sort-by-%s" predicate)))
8178          (gnus-thread-sort-functions
8179           (list
8180            (if (not reverse)
8181                thread
8182              `(lambda (t1 t2)
8183                 (,thread t2 t1)))))
8184          (gnus-article-sort-functions
8185           (list
8186            (if (not reverse)
8187                article
8188              `(lambda (t1 t2)
8189                 (,article t2 t1)))))
8190          (buffer-read-only)
8191          (gnus-summary-prepare-hook nil))
8192     ;; We do the sorting by regenerating the threads.
8193     (gnus-summary-prepare)
8194     ;; Hide subthreads if needed.
8195     (when (and gnus-show-threads gnus-thread-hide-subtree)
8196       (gnus-summary-hide-all-threads))))
8197
8198 ;; Summary saving commands.
8199
8200 (defun gnus-summary-save-article (&optional n not-saved)
8201   "Save the current article using the default saver function.
8202 If N is a positive number, save the N next articles.
8203 If N is a negative number, save the N previous articles.
8204 If N is nil and any articles have been marked with the process mark,
8205 save those articles instead.
8206 The variable `gnus-default-article-saver' specifies the saver function."
8207   (interactive "P")
8208   (gnus-set-global-variables)
8209   (let* ((articles (gnus-summary-work-articles n))
8210          (save-buffer (save-excursion
8211                         (nnheader-set-temp-buffer " *Gnus Save*")))
8212          (num (length articles))
8213          header article file)
8214     (while articles
8215       (setq header (gnus-summary-article-header
8216                     (setq article (pop articles))))
8217       (if (not (vectorp header))
8218           ;; This is a pseudo-article.
8219           (if (assq 'name header)
8220               (gnus-copy-file (cdr (assq 'name header)))
8221             (gnus-message 1 "Article %d is unsaveable" article))
8222         ;; This is a real article.
8223         (save-window-excursion
8224           (gnus-summary-select-article t nil nil article))
8225         (save-excursion
8226           (set-buffer save-buffer)
8227           (erase-buffer)
8228           (insert-buffer-substring gnus-original-article-buffer))
8229         (setq file (gnus-article-save save-buffer file num))
8230         (gnus-summary-remove-process-mark article)
8231         (unless not-saved
8232           (gnus-summary-set-saved-mark article))))
8233     (gnus-kill-buffer save-buffer)
8234     (gnus-summary-position-point)
8235     (gnus-set-mode-line 'summary)
8236     n))
8237
8238 (defun gnus-summary-pipe-output (&optional arg)
8239   "Pipe the current article to a subprocess.
8240 If N is a positive number, pipe the N next articles.
8241 If N is a negative number, pipe the N previous articles.
8242 If N is nil and any articles have been marked with the process mark,
8243 pipe those articles instead."
8244   (interactive "P")
8245   (gnus-set-global-variables)
8246   (let ((gnus-default-article-saver 'gnus-summary-save-in-pipe))
8247     (gnus-summary-save-article arg t))
8248   (gnus-configure-windows 'pipe))
8249
8250 (defun gnus-summary-save-article-mail (&optional arg)
8251   "Append the current article to an mail file.
8252 If N is a positive number, save the N next articles.
8253 If N is a negative number, save the N previous articles.
8254 If N is nil and any articles have been marked with the process mark,
8255 save those articles instead."
8256   (interactive "P")
8257   (gnus-set-global-variables)
8258   (let ((gnus-default-article-saver 'gnus-summary-save-in-mail))
8259     (gnus-summary-save-article arg)))
8260
8261 (defun gnus-summary-save-article-rmail (&optional arg)
8262   "Append the current article to an rmail file.
8263 If N is a positive number, save the N next articles.
8264 If N is a negative number, save the N previous articles.
8265 If N is nil and any articles have been marked with the process mark,
8266 save those articles instead."
8267   (interactive "P")
8268   (gnus-set-global-variables)
8269   (let ((gnus-default-article-saver 'gnus-summary-save-in-rmail))
8270     (gnus-summary-save-article arg)))
8271
8272 (defun gnus-summary-save-article-file (&optional arg)
8273   "Append the current article to a file.
8274 If N is a positive number, save the N next articles.
8275 If N is a negative number, save the N previous articles.
8276 If N is nil and any articles have been marked with the process mark,
8277 save those articles instead."
8278   (interactive "P")
8279   (gnus-set-global-variables)
8280   (let ((gnus-default-article-saver 'gnus-summary-save-in-file))
8281     (gnus-summary-save-article arg)))
8282
8283 (defun gnus-summary-write-article-file (&optional arg)
8284   "Write the current article to a file, deleting the previous file.
8285 If N is a positive number, save the N next articles.
8286 If N is a negative number, save the N previous articles.
8287 If N is nil and any articles have been marked with the process mark,
8288 save those articles instead."
8289   (interactive "P")
8290   (gnus-set-global-variables)
8291   (let ((gnus-default-article-saver 'gnus-summary-write-to-file))
8292     (gnus-summary-save-article arg)))
8293
8294 (defun gnus-summary-save-article-body-file (&optional arg)
8295   "Append the current article body to a file.
8296 If N is a positive number, save the N next articles.
8297 If N is a negative number, save the N previous articles.
8298 If N is nil and any articles have been marked with the process mark,
8299 save those articles instead."
8300   (interactive "P")
8301   (gnus-set-global-variables)
8302   (let ((gnus-default-article-saver 'gnus-summary-save-body-in-file))
8303     (gnus-summary-save-article arg)))
8304
8305 (defun gnus-summary-pipe-message (program)
8306   "Pipe the current article through PROGRAM."
8307   (interactive "sProgram: ")
8308   (gnus-set-global-variables)
8309   (gnus-summary-select-article)
8310   (let ((mail-header-separator "")
8311         (art-buf (get-buffer gnus-article-buffer)))
8312     (gnus-eval-in-buffer-window gnus-article-buffer
8313       (save-restriction
8314         (widen)
8315         (let ((start (window-start))
8316               buffer-read-only)
8317           (message-pipe-buffer-body program)
8318           (set-window-start (get-buffer-window (current-buffer)) start))))))
8319
8320 (defun gnus-get-split-value (methods)
8321   "Return a value based on the split METHODS."
8322   (let (split-name method result match)
8323     (when methods
8324       (save-excursion
8325         (set-buffer gnus-original-article-buffer)
8326         (save-restriction
8327           (nnheader-narrow-to-headers)
8328           (while methods
8329             (goto-char (point-min))
8330             (setq method (pop methods))
8331             (setq match (car method))
8332             (when (cond
8333                    ((stringp match)
8334                     ;; Regular expression.
8335                     (ignore-errors
8336                       (re-search-forward match nil t)))
8337                    ((gnus-functionp match)
8338                     ;; Function.
8339                     (save-restriction
8340                       (widen)
8341                       (setq result (funcall match gnus-newsgroup-name))))
8342                    ((consp match)
8343                     ;; Form.
8344                     (save-restriction
8345                       (widen)
8346                       (setq result (eval match)))))
8347               (setq split-name (append (cdr method) split-name))
8348               (cond ((stringp result)
8349                      (push (expand-file-name
8350                             result gnus-article-save-directory)
8351                            split-name))
8352                     ((consp result)
8353                      (setq split-name (append result split-name)))))))))
8354     split-name))
8355
8356 (defun gnus-valid-move-group-p (group)
8357   (and (boundp group)
8358        (symbol-name group)
8359        (memq 'respool
8360              (assoc (symbol-name
8361                      (car (gnus-find-method-for-group
8362                            (symbol-name group))))
8363                     gnus-valid-select-methods))))
8364
8365 (defun gnus-read-move-group-name (prompt default articles prefix)
8366   "Read a group name."
8367   (let* ((split-name (gnus-get-split-value gnus-move-split-methods))
8368          (minibuffer-confirm-incomplete nil) ; XEmacs
8369          (prom
8370           (format "%s %s to:"
8371                   prompt
8372                   (if (> (length articles) 1)
8373                       (format "these %d articles" (length articles))
8374                     "this article")))
8375          (to-newsgroup
8376           (cond
8377            ((null split-name)
8378             (gnus-completing-read default prom
8379                                   gnus-active-hashtb
8380                                   'gnus-valid-move-group-p
8381                                   nil prefix
8382                                   'gnus-group-history))
8383            ((= 1 (length split-name))
8384             (gnus-completing-read (car split-name) prom
8385                                   gnus-active-hashtb
8386                                   'gnus-valid-move-group-p
8387                                   nil nil
8388                                   'gnus-group-history))
8389            (t
8390             (gnus-completing-read nil prom
8391                                   (mapcar (lambda (el) (list el))
8392                                           (nreverse split-name))
8393                                   nil nil nil
8394                                   'gnus-group-history)))))
8395     (when to-newsgroup
8396       (if (or (string= to-newsgroup "")
8397               (string= to-newsgroup prefix))
8398           (setq to-newsgroup default))
8399       (unless to-newsgroup
8400         (error "No group name entered"))
8401       (or (gnus-active to-newsgroup)
8402           (gnus-activate-group to-newsgroup)
8403           (if (gnus-y-or-n-p (format "No such group: %s.  Create it? "
8404                                      to-newsgroup))
8405               (or (and (gnus-request-create-group
8406                         to-newsgroup (gnus-group-name-to-method to-newsgroup))
8407                        (gnus-activate-group to-newsgroup nil nil
8408                                             (gnus-group-name-to-method
8409                                              to-newsgroup)))
8410                   (error "Couldn't create group %s" to-newsgroup)))
8411           (error "No such group: %s" to-newsgroup)))
8412     to-newsgroup))
8413
8414 ;; Summary extract commands
8415
8416 (defun gnus-summary-insert-pseudos (pslist &optional not-view)
8417   (let ((buffer-read-only nil)
8418         (article (gnus-summary-article-number))
8419         after-article b e)
8420     (unless (gnus-summary-goto-subject article)
8421       (error "No such article: %d" article))
8422     (gnus-summary-position-point)
8423     ;; If all commands are to be bunched up on one line, we collect
8424     ;; them here.
8425     (unless gnus-view-pseudos-separately
8426       (let ((ps (setq pslist (sort pslist 'gnus-pseudos<)))
8427             files action)
8428         (while ps
8429           (setq action (cdr (assq 'action (car ps))))
8430           (setq files (list (cdr (assq 'name (car ps)))))
8431           (while (and ps (cdr ps)
8432                       (string= (or action "1")
8433                                (or (cdr (assq 'action (cadr ps))) "2")))
8434             (push (cdr (assq 'name (cadr ps))) files)
8435             (setcdr ps (cddr ps)))
8436           (when files
8437             (when (not (string-match "%s" action))
8438               (push " " files))
8439             (push " " files)
8440             (when (assq 'execute (car ps))
8441               (setcdr (assq 'execute (car ps))
8442                       (funcall (if (string-match "%s" action)
8443                                    'format 'concat)
8444                                action
8445                                (mapconcat
8446                                 (lambda (f)
8447                                   (if (equal f " ")
8448                                       f
8449                                     (gnus-quote-arg-for-sh-or-csh f)))
8450                                 files " ")))))
8451           (setq ps (cdr ps)))))
8452     (if (and gnus-view-pseudos (not not-view))
8453         (while pslist
8454           (when (assq 'execute (car pslist))
8455             (gnus-execute-command (cdr (assq 'execute (car pslist)))
8456                                   (eq gnus-view-pseudos 'not-confirm)))
8457           (setq pslist (cdr pslist)))
8458       (save-excursion
8459         (while pslist
8460           (setq after-article (or (cdr (assq 'article (car pslist)))
8461                                   (gnus-summary-article-number)))
8462           (gnus-summary-goto-subject after-article)
8463           (forward-line 1)
8464           (setq b (point))
8465           (insert "    " (file-name-nondirectory
8466                           (cdr (assq 'name (car pslist))))
8467                   ": " (or (cdr (assq 'execute (car pslist))) "") "\n")
8468           (setq e (point))
8469           (forward-line -1)             ; back to `b'
8470           (gnus-add-text-properties
8471            b (1- e) (list 'gnus-number gnus-reffed-article-number
8472                           gnus-mouse-face-prop gnus-mouse-face))
8473           (gnus-data-enter
8474            after-article gnus-reffed-article-number
8475            gnus-unread-mark b (car pslist) 0 (- e b))
8476           (push gnus-reffed-article-number gnus-newsgroup-unreads)
8477           (setq gnus-reffed-article-number (1- gnus-reffed-article-number))
8478           (setq pslist (cdr pslist)))))))
8479
8480 (defun gnus-pseudos< (p1 p2)
8481   (let ((c1 (cdr (assq 'action p1)))
8482         (c2 (cdr (assq 'action p2))))
8483     (and c1 c2 (string< c1 c2))))
8484
8485 (defun gnus-request-pseudo-article (props)
8486   (cond ((assq 'execute props)
8487          (gnus-execute-command (cdr (assq 'execute props)))))
8488   (let ((gnus-current-article (gnus-summary-article-number)))
8489     (run-hooks 'gnus-mark-article-hook)))
8490
8491 (defun gnus-execute-command (command &optional automatic)
8492   (save-excursion
8493     (gnus-article-setup-buffer)
8494     (set-buffer gnus-article-buffer)
8495     (setq buffer-read-only nil)
8496     (let ((command (if automatic command
8497                      (read-string "Command: " (cons command 0)))))
8498       (erase-buffer)
8499       (insert "$ " command "\n\n")
8500       (if gnus-view-pseudo-asynchronously
8501           (start-process "gnus-execute" (current-buffer) shell-file-name
8502                          shell-command-switch command)
8503         (call-process shell-file-name nil t nil
8504                       shell-command-switch command)))))
8505
8506 ;; Summary kill commands.
8507
8508 (defun gnus-summary-edit-global-kill (article)
8509   "Edit the \"global\" kill file."
8510   (interactive (list (gnus-summary-article-number)))
8511   (gnus-set-global-variables)
8512   (gnus-group-edit-global-kill article))
8513
8514 (defun gnus-summary-edit-local-kill ()
8515   "Edit a local kill file applied to the current newsgroup."
8516   (interactive)
8517   (gnus-set-global-variables)
8518   (setq gnus-current-headers (gnus-summary-article-header))
8519   (gnus-set-global-variables)
8520   (gnus-group-edit-local-kill
8521    (gnus-summary-article-number) gnus-newsgroup-name))
8522
8523 ;;; Header reading.
8524
8525 (defun gnus-read-header (id &optional header)
8526   "Read the headers of article ID and enter them into the Gnus system."
8527   (let ((group gnus-newsgroup-name)
8528         (gnus-override-method
8529          (and (gnus-news-group-p gnus-newsgroup-name)
8530               gnus-refer-article-method))
8531         where)
8532     ;; First we check to see whether the header in question is already
8533     ;; fetched.
8534     (if (stringp id)
8535         ;; This is a Message-ID.
8536         (setq header (or header (gnus-id-to-header id)))
8537       ;; This is an article number.
8538       (setq header (or header (gnus-summary-article-header id))))
8539     (if (and header
8540              (not (gnus-summary-article-sparse-p (mail-header-number header))))
8541         ;; We have found the header.
8542         header
8543       ;; We have to really fetch the header to this article.
8544       (save-excursion
8545         (set-buffer nntp-server-buffer)
8546         (when (setq where (gnus-request-head id group))
8547           (nnheader-fold-continuation-lines)
8548           (goto-char (point-max))
8549           (insert ".\n")
8550           (goto-char (point-min))
8551           (insert "211 ")
8552           (princ (cond
8553                   ((numberp id) id)
8554                   ((cdr where) (cdr where))
8555                   (header (mail-header-number header))
8556                   (t gnus-reffed-article-number))
8557                  (current-buffer))
8558           (insert " Article retrieved.\n"))
8559         (if (or (not where)
8560                 (not (setq header (car (gnus-get-newsgroup-headers nil t)))))
8561             ()                          ; Malformed head.
8562           (unless (gnus-summary-article-sparse-p (mail-header-number header))
8563             (when (and (stringp id)
8564                        (not (string= (gnus-group-real-name group)
8565                                      (car where))))
8566               ;; If we fetched by Message-ID and the article came
8567               ;; from a different group, we fudge some bogus article
8568               ;; numbers for this article.
8569               (mail-header-set-number header gnus-reffed-article-number))
8570             (save-excursion
8571               (set-buffer gnus-summary-buffer)
8572               (decf gnus-reffed-article-number)
8573               (gnus-remove-header (mail-header-number header))
8574               (push header gnus-newsgroup-headers)
8575               (setq gnus-current-headers header)
8576               (push (mail-header-number header) gnus-newsgroup-limit)))
8577           header)))))
8578
8579 (defun gnus-remove-header (number)
8580   "Remove header NUMBER from `gnus-newsgroup-headers'."
8581   (if (and gnus-newsgroup-headers
8582            (= number (mail-header-number (car gnus-newsgroup-headers))))
8583       (pop gnus-newsgroup-headers)
8584     (let ((headers gnus-newsgroup-headers))
8585       (while (and (cdr headers)
8586                   (not (= number (mail-header-number (cadr headers)))))
8587         (pop headers))
8588       (when (cdr headers)
8589         (setcdr headers (cddr headers))))))
8590
8591 ;;;
8592 ;;; summary highlights
8593 ;;;
8594
8595 (defun gnus-highlight-selected-summary ()
8596   ;; Added by Per Abrahamsen <amanda@iesd.auc.dk>.
8597   ;; Highlight selected article in summary buffer
8598   (when gnus-summary-selected-face
8599     (save-excursion
8600       (let* ((beg (progn (beginning-of-line) (point)))
8601              (end (progn (end-of-line) (point)))
8602              ;; Fix by Mike Dugan <dugan@bucrf16.bu.edu>.
8603              (from (if (get-text-property beg gnus-mouse-face-prop)
8604                        beg
8605                      (or (next-single-property-change
8606                           beg gnus-mouse-face-prop nil end)
8607                          beg)))
8608              (to
8609               (if (= from end)
8610                   (- from 2)
8611                 (or (next-single-property-change
8612                      from gnus-mouse-face-prop nil end)
8613                     end))))
8614         ;; If no mouse-face prop on line we will have to = from = end,
8615         ;; so we highlight the entire line instead.
8616         (when (= (+ to 2) from)
8617           (setq from beg)
8618           (setq to end))
8619         (if gnus-newsgroup-selected-overlay
8620             ;; Move old overlay.
8621             (gnus-move-overlay
8622              gnus-newsgroup-selected-overlay from to (current-buffer))
8623           ;; Create new overlay.
8624           (gnus-overlay-put
8625            (setq gnus-newsgroup-selected-overlay (gnus-make-overlay from to))
8626            'face gnus-summary-selected-face))))))
8627
8628 ;; New implementation by Christian Limpach <Christian.Limpach@nice.ch>.
8629 (defun gnus-summary-highlight-line ()
8630   "Highlight current line according to `gnus-summary-highlight'."
8631   (let* ((list gnus-summary-highlight)
8632          (p (point))
8633          (end (progn (end-of-line) (point)))
8634          ;; now find out where the line starts and leave point there.
8635          (beg (progn (beginning-of-line) (point)))
8636          (article (gnus-summary-article-number))
8637          (score (or (cdr (assq (or article gnus-current-article)
8638                                gnus-newsgroup-scored))
8639                     gnus-summary-default-score 0))
8640          (mark (or (gnus-summary-article-mark) gnus-unread-mark))
8641          (inhibit-read-only t))
8642     ;; Eval the cars of the lists until we find a match.
8643     (let ((default gnus-summary-default-score))
8644       (while (and list
8645                   (not (eval (caar list))))
8646         (setq list (cdr list))))
8647     (let ((face (cdar list)))
8648       (unless (eq face (get-text-property beg 'face))
8649         (gnus-put-text-property
8650          beg end 'face
8651          (setq face (if (boundp face) (symbol-value face) face)))
8652         (when gnus-summary-highlight-line-function
8653           (funcall gnus-summary-highlight-line-function article face))))
8654     (goto-char p)))
8655
8656 (defun gnus-update-read-articles (group unread)
8657   "Update the list of read articles in GROUP."
8658   (let* ((active (or gnus-newsgroup-active (gnus-active group)))
8659          (entry (gnus-gethash group gnus-newsrc-hashtb))
8660          (info (nth 2 entry))
8661          (prev 1)
8662          (unread (sort (copy-sequence unread) '<))
8663          read)
8664     (if (or (not info) (not active))
8665         ;; There is no info on this group if it was, in fact,
8666         ;; killed.  Gnus stores no information on killed groups, so
8667         ;; there's nothing to be done.
8668         ;; One could store the information somewhere temporarily,
8669         ;; perhaps...  Hmmm...
8670         ()
8671       ;; Remove any negative articles numbers.
8672       (while (and unread (< (car unread) 0))
8673         (setq unread (cdr unread)))
8674       ;; Remove any expired article numbers
8675       (while (and unread (< (car unread) (car active)))
8676         (setq unread (cdr unread)))
8677       ;; Compute the ranges of read articles by looking at the list of
8678       ;; unread articles.
8679       (while unread
8680         (when (/= (car unread) prev)
8681           (push (if (= prev (1- (car unread))) prev
8682                   (cons prev (1- (car unread))))
8683                 read))
8684         (setq prev (1+ (car unread)))
8685         (setq unread (cdr unread)))
8686       (when (<= prev (cdr active))
8687         (push (cons prev (cdr active)) read))
8688       (save-excursion
8689         (set-buffer gnus-group-buffer)
8690         (gnus-undo-register
8691           `(progn
8692              (gnus-info-set-marks ',info ',(gnus-info-marks info) t)
8693              (gnus-info-set-read ',info ',(gnus-info-read info))
8694              (gnus-get-unread-articles-in-group ',info (gnus-active ,group))
8695              (gnus-group-update-group ,group t))))
8696       ;; Enter this list into the group info.
8697       (gnus-info-set-read
8698        info (if (> (length read) 1) (nreverse read) read))
8699       ;; Set the number of unread articles in gnus-newsrc-hashtb.
8700       (gnus-get-unread-articles-in-group info (gnus-active group))
8701       t)))
8702
8703 (defun gnus-offer-save-summaries ()
8704   "Offer to save all active summary buffers."
8705   (save-excursion
8706     (let ((buflist (buffer-list))
8707           buffers bufname)
8708       ;; Go through all buffers and find all summaries.
8709       (while buflist
8710         (and (setq bufname (buffer-name (car buflist)))
8711              (string-match "Summary" bufname)
8712              (save-excursion
8713                (set-buffer bufname)
8714                ;; We check that this is, indeed, a summary buffer.
8715                (and (eq major-mode 'gnus-summary-mode)
8716                     ;; Also make sure this isn't bogus.
8717                     gnus-newsgroup-prepared
8718                     ;; Also make sure that this isn't a dead summary buffer.
8719                     (not gnus-dead-summary-mode)))
8720              (push bufname buffers))
8721         (setq buflist (cdr buflist)))
8722       ;; Go through all these summary buffers and offer to save them.
8723       (when buffers
8724         (map-y-or-n-p
8725          "Update summary buffer %s? "
8726          (lambda (buf) (switch-to-buffer buf) (gnus-summary-exit))
8727          buffers)))))
8728
8729 (gnus-ems-redefine)
8730
8731 (provide 'gnus-sum)
8732
8733 (run-hooks 'gnus-sum-load-hook)
8734
8735 ;;; gnus-sum.el ends here