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